@restura/core 0.2.1 → 1.0.0-beta.1

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
@@ -1,39 +1,10 @@
1
1
  "use strict";
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
- var __defProps = Object.defineProperties;
5
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
8
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
6
  var __getProtoOf = Object.getPrototypeOf;
10
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
11
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
12
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
- var __spreadValues = (a, b) => {
14
- for (var prop in b || (b = {}))
15
- if (__hasOwnProp.call(b, prop))
16
- __defNormalProp(a, prop, b[prop]);
17
- if (__getOwnPropSymbols)
18
- for (var prop of __getOwnPropSymbols(b)) {
19
- if (__propIsEnum.call(b, prop))
20
- __defNormalProp(a, prop, b[prop]);
21
- }
22
- return a;
23
- };
24
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
25
- var __objRest = (source, exclude) => {
26
- var target = {};
27
- for (var prop in source)
28
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
29
- target[prop] = source[prop];
30
- if (source != null && __getOwnPropSymbols)
31
- for (var prop of __getOwnPropSymbols(source)) {
32
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
33
- target[prop] = source[prop];
34
- }
35
- return target;
36
- };
37
8
  var __export = (target, all) => {
38
9
  for (var name in all)
39
10
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -92,13 +63,12 @@ var import_winston = __toESM(require("winston"));
92
63
  var import_logform = require("logform");
93
64
 
94
65
  // src/logger/loggerConfigSchema.ts
95
- var import_zod = require("zod");
96
- var loggerConfigSchema = import_zod.z.object({
97
- level: import_zod.z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
66
+ var import_v4 = require("zod/v4");
67
+ var loggerConfigSchema = import_v4.z.object({
68
+ level: import_v4.z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
98
69
  });
99
70
 
100
71
  // src/logger/logger.ts
101
- var loggerConfig = import_internal.config.validate("logger", loggerConfigSchema);
102
72
  var consoleFormat = import_logform.format.combine(
103
73
  import_logform.format.timestamp({
104
74
  format: "YYYY-MM-DD HH:mm:ss.sss"
@@ -107,11 +77,11 @@ var consoleFormat = import_logform.format.combine(
107
77
  import_logform.format.padLevels(),
108
78
  import_logform.format.colorize({ all: true }),
109
79
  import_logform.format.printf((info) => {
110
- return `[${info.timestamp}] ${info.level} ${info.message}`;
80
+ return `[${info.timestamp}] ${info.level} ${info.message}`;
111
81
  })
112
82
  );
113
83
  var logger = import_winston.default.createLogger({
114
- level: loggerConfig.level,
84
+ level: "info",
115
85
  format: import_logform.format.combine(
116
86
  import_logform.format.timestamp({
117
87
  format: "YYYY-MM-DD HH:mm:ss.sss"
@@ -119,29 +89,30 @@ var logger = import_winston.default.createLogger({
119
89
  import_logform.format.errors({ stack: true }),
120
90
  import_logform.format.json()
121
91
  ),
122
- //defaultMeta: { service: 'user-service' },
123
- transports: [
124
- //
125
- // - Write to all logs with level `info` and below to `combined.log`
126
- // - Write all logs error (and below) to `error.log`.
127
- // - Write all logs to standard out.
128
- //
129
- // new winston.transports.File({ filename: 'error.log', level: 'error' }),
130
- // new winston.transports.File({ filename: 'combined.log' }),
131
- new import_winston.default.transports.Console({ format: consoleFormat })
132
- ]
92
+ transports: [new import_winston.default.transports.Console({ format: consoleFormat })]
133
93
  });
94
+ (async () => {
95
+ try {
96
+ const loggerConfig = await import_internal.config.validate("logger", loggerConfigSchema);
97
+ if (loggerConfig.level) {
98
+ logger.level = loggerConfig.level;
99
+ }
100
+ } catch (error) {
101
+ console.error("Failed to load logger config:", error);
102
+ throw new Error(
103
+ `Failed to initialize logger configuration: ${error instanceof Error ? error.message : String(error)}`
104
+ );
105
+ }
106
+ })();
134
107
 
135
108
  // src/restura/eventManager.ts
136
109
  var import_bluebird = __toESM(require("bluebird"));
137
110
  var EventManager = class {
138
- constructor() {
139
- this.actionHandlers = {
140
- DATABASE_ROW_DELETE: [],
141
- DATABASE_ROW_INSERT: [],
142
- DATABASE_COLUMN_UPDATE: []
143
- };
144
- }
111
+ actionHandlers = {
112
+ DATABASE_ROW_DELETE: [],
113
+ DATABASE_ROW_INSERT: [],
114
+ DATABASE_COLUMN_UPDATE: []
115
+ };
145
116
  addRowInsertHandler(onInsert, filter) {
146
117
  this.actionHandlers.DATABASE_ROW_INSERT.push({
147
118
  callback: onInsert,
@@ -338,6 +309,10 @@ var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
338
309
  return HtmlStatusCodes2;
339
310
  })(HtmlStatusCodes || {});
340
311
  var RsError = class _RsError {
312
+ err;
313
+ msg;
314
+ status;
315
+ stack;
341
316
  constructor(errCode, message) {
342
317
  this.err = errCode;
343
318
  this.msg = message || "";
@@ -490,9 +465,7 @@ var import_fs = __toESM(require("fs"));
490
465
  var import_path = __toESM(require("path"));
491
466
  var import_internal2 = require("@restura/internal");
492
467
  var CustomApiFactory = class {
493
- constructor() {
494
- this.customApis = {};
495
- }
468
+ customApis = {};
496
469
  async loadApiFiles(baseFolderPath) {
497
470
  const apiVersions = ["v1"];
498
471
  for (const apiVersion of apiVersions) {
@@ -506,11 +479,10 @@ var CustomApiFactory = class {
506
479
  return this.customApis[customApiName];
507
480
  }
508
481
  async addDirectory(directoryPath, apiVersion) {
509
- var _a2;
510
482
  const entries = await import_fs.default.promises.readdir(directoryPath, {
511
483
  withFileTypes: true
512
484
  });
513
- const isTsx2 = (_a2 = process.argv[1]) == null ? void 0 : _a2.endsWith(".ts");
485
+ const isTsx2 = process.argv[1]?.endsWith(".ts");
514
486
  const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
515
487
  const extension = isTsx2 || isTsNode2 ? "ts" : "js";
516
488
  const shouldEndWith = `.api.${apiVersion}.${extension}`;
@@ -575,6 +547,8 @@ var SqlUtils = class _SqlUtils {
575
547
 
576
548
  // src/restura/validators/ResponseValidator.ts
577
549
  var ResponseValidator = class _ResponseValidator {
550
+ rootMap;
551
+ database;
578
552
  constructor(schema) {
579
553
  this.database = schema.database;
580
554
  this.rootMap = {};
@@ -650,7 +624,7 @@ var ResponseValidator = class _ResponseValidator {
650
624
  if (path5.length === 0 || path5.length > 2 || path5[0] === "") return { validator: "any", isOptional: false };
651
625
  const tableName = path5.length == 2 ? path5[0] : name, columnName = path5.length == 2 ? path5[1] : path5[0];
652
626
  const table = this.database.find((t) => t.name == tableName);
653
- const column = table == null ? void 0 : table.columns.find((c) => c.name == columnName);
627
+ const column = table?.columns.find((c) => c.name == columnName);
654
628
  if (!table || !column) return { validator: "any", isOptional: false };
655
629
  let validator = SqlUtils.convertDatabaseTypeToTypescript(
656
630
  column.type,
@@ -734,10 +708,12 @@ var ResponseValidator = class _ResponseValidator {
734
708
  var ApiTree = class _ApiTree {
735
709
  constructor(namespace, database) {
736
710
  this.database = database;
737
- this.data = [];
738
711
  this.namespace = namespace;
739
712
  this.children = /* @__PURE__ */ new Map();
740
713
  }
714
+ namespace;
715
+ data = [];
716
+ children;
741
717
  static createRootNode(database) {
742
718
  return new _ApiTree(null, database);
743
719
  }
@@ -881,7 +857,7 @@ var ApiTree = class _ApiTree {
881
857
  tableName = tableAliasSplit[1];
882
858
  table = this.database.find((t) => t.name == tableName);
883
859
  }
884
- const column = table == null ? void 0 : table.columns.find((c) => c.name == columnName);
860
+ const column = table?.columns.find((c) => c.name == columnName);
885
861
  if (!table || !column) return { responseType: "any", isNullable: false };
886
862
  return {
887
863
  responseType: SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value),
@@ -912,16 +888,17 @@ function apiGenerator(schema) {
912
888
  ${schema.customTypes.join("\n")}
913
889
  }`;
914
890
  }
915
- return import_prettier.default.format(apiString, __spreadValues({
916
- parser: "typescript"
917
- }, {
918
- trailingComma: "none",
919
- tabWidth: 4,
920
- useTabs: true,
921
- endOfLine: "lf",
922
- printWidth: 120,
923
- singleQuote: true
924
- }));
891
+ return import_prettier.default.format(apiString, {
892
+ parser: "typescript",
893
+ ...{
894
+ trailingComma: "none",
895
+ tabWidth: 4,
896
+ useTabs: true,
897
+ endOfLine: "lf",
898
+ printWidth: 120,
899
+ singleQuote: true
900
+ }
901
+ });
925
902
  }
926
903
 
927
904
  // src/restura/generators/customTypeValidationGenerator.ts
@@ -932,7 +909,7 @@ var TJS = __toESM(require("typescript-json-schema"));
932
909
  function customTypeValidationGenerator(currentSchema) {
933
910
  const schemaObject = {};
934
911
  const customInterfaceNames = currentSchema.customTypes.map((customType) => {
935
- const matches = customType.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
912
+ const matches = customType.match(/(?<=interface\s)(\w+)|(?<=type\s)(\w+)/g);
936
913
  if (matches && matches.length > 0) return matches[0];
937
914
  return "";
938
915
  }).filter(Boolean);
@@ -976,16 +953,17 @@ function modelGenerator(schema) {
976
953
  modelString += convertTable(table);
977
954
  }
978
955
  modelString += `}`;
979
- return import_prettier2.default.format(modelString, __spreadValues({
980
- parser: "typescript"
981
- }, {
982
- trailingComma: "none",
983
- tabWidth: 4,
984
- useTabs: true,
985
- endOfLine: "lf",
986
- printWidth: 120,
987
- singleQuote: true
988
- }));
956
+ return import_prettier2.default.format(modelString, {
957
+ parser: "typescript",
958
+ ...{
959
+ trailingComma: "none",
960
+ tabWidth: 4,
961
+ useTabs: true,
962
+ endOfLine: "lf",
963
+ printWidth: 120,
964
+ singleQuote: true
965
+ }
966
+ });
989
967
  }
990
968
  function convertTable(table) {
991
969
  let modelString = ` export interface ${import_core_utils2.StringUtils.capitalizeFirst(table.name)} {
@@ -1037,20 +1015,21 @@ function addApiResponseFunctions(req, res, next) {
1037
1015
  htmlStatusCode = 500;
1038
1016
  }
1039
1017
  }
1040
- const errorData = __spreadValues({
1018
+ const errorData = {
1041
1019
  err: shortError,
1042
- msg
1043
- }, restura.resturaConfig.sendErrorStackTrace && stack ? { stack } : {});
1020
+ msg,
1021
+ ...restura.resturaConfig.sendErrorStackTrace && stack ? { stack } : {}
1022
+ };
1044
1023
  res.status(htmlStatusCode).send(errorData);
1045
1024
  };
1046
1025
  next();
1047
1026
  }
1048
1027
 
1049
- // src/restura/middleware/authenticateUser.ts
1050
- function authenticateUser(applicationAuthenticateHandler) {
1028
+ // src/restura/middleware/authenticateRequester.ts
1029
+ function authenticateRequester(applicationAuthenticateHandler) {
1051
1030
  return (req, res, next) => {
1052
- applicationAuthenticateHandler(req, res, (userDetails) => {
1053
- req.requesterDetails = __spreadValues({ host: req.hostname, ipAddress: req.ip || "" }, userDetails);
1031
+ applicationAuthenticateHandler(req, res, (authenticatedRequesterDetails) => {
1032
+ req.requesterDetails = { host: req.hostname, ipAddress: req.ip || "", ...authenticatedRequesterDetails };
1054
1033
  next();
1055
1034
  });
1056
1035
  };
@@ -1082,110 +1061,97 @@ var getMulterUpload = (directory) => {
1082
1061
  };
1083
1062
 
1084
1063
  // src/restura/schemas/resturaSchema.ts
1085
- var import_zod3 = require("zod");
1064
+ var import_zod2 = require("zod");
1086
1065
 
1087
1066
  // src/restura/schemas/validatorDataSchema.ts
1088
- var import_zod2 = require("zod");
1089
- var validatorDataSchemeValue = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.string()), import_zod2.z.number(), import_zod2.z.array(import_zod2.z.number())]);
1090
- var validatorDataSchema = import_zod2.z.object({
1091
- type: import_zod2.z.enum(["TYPE_CHECK", "MIN", "MAX", "ONE_OF"]),
1067
+ var import_zod = require("zod");
1068
+ var validatorDataSchemeValue = import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.string()), import_zod.z.number(), import_zod.z.array(import_zod.z.number())]);
1069
+ var validatorDataSchema = import_zod.z.object({
1070
+ type: import_zod.z.enum(["TYPE_CHECK", "MIN", "MAX", "ONE_OF"]),
1092
1071
  value: validatorDataSchemeValue
1093
1072
  }).strict();
1094
1073
 
1095
1074
  // src/restura/schemas/resturaSchema.ts
1096
- var orderBySchema = import_zod3.z.object({
1097
- columnName: import_zod3.z.string(),
1098
- order: import_zod3.z.enum(["ASC", "DESC"]),
1099
- tableName: import_zod3.z.string()
1075
+ var orderBySchema = import_zod2.z.object({
1076
+ columnName: import_zod2.z.string(),
1077
+ order: import_zod2.z.enum(["ASC", "DESC"]),
1078
+ tableName: import_zod2.z.string()
1100
1079
  }).strict();
1101
- var groupBySchema = import_zod3.z.object({
1102
- columnName: import_zod3.z.string(),
1103
- tableName: import_zod3.z.string()
1080
+ var groupBySchema = import_zod2.z.object({
1081
+ columnName: import_zod2.z.string(),
1082
+ tableName: import_zod2.z.string()
1104
1083
  }).strict();
1105
- var whereDataSchema = import_zod3.z.object({
1106
- tableName: import_zod3.z.string().optional(),
1107
- columnName: import_zod3.z.string().optional(),
1108
- operator: import_zod3.z.enum([
1109
- "=",
1110
- "<",
1111
- ">",
1112
- "<=",
1113
- ">=",
1114
- "!=",
1115
- "LIKE",
1116
- "NOT LIKE",
1117
- "IN",
1118
- "NOT IN",
1119
- "STARTS WITH",
1120
- "ENDS WITH",
1121
- "IS",
1122
- "IS NOT"
1123
- ]).optional(),
1124
- value: import_zod3.z.string().or(import_zod3.z.number()).optional(),
1125
- custom: import_zod3.z.string().optional(),
1126
- conjunction: import_zod3.z.enum(["AND", "OR"]).optional()
1084
+ var whereDataSchema = import_zod2.z.object({
1085
+ tableName: import_zod2.z.string().optional(),
1086
+ columnName: import_zod2.z.string().optional(),
1087
+ operator: import_zod2.z.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH", "IS", "IS NOT"]).optional(),
1088
+ value: import_zod2.z.string().or(import_zod2.z.number()).optional(),
1089
+ custom: import_zod2.z.string().optional(),
1090
+ conjunction: import_zod2.z.enum(["AND", "OR"]).optional()
1127
1091
  }).strict();
1128
- var assignmentDataSchema = import_zod3.z.object({
1129
- name: import_zod3.z.string(),
1130
- value: import_zod3.z.string()
1092
+ var assignmentDataSchema = import_zod2.z.object({
1093
+ name: import_zod2.z.string(),
1094
+ value: import_zod2.z.string()
1131
1095
  }).strict();
1132
- var joinDataSchema = import_zod3.z.object({
1133
- table: import_zod3.z.string(),
1134
- localColumnName: import_zod3.z.string().optional(),
1135
- foreignColumnName: import_zod3.z.string().optional(),
1136
- custom: import_zod3.z.string().optional(),
1137
- type: import_zod3.z.enum(["LEFT", "INNER"]),
1138
- alias: import_zod3.z.string().optional()
1096
+ var joinDataSchema = import_zod2.z.object({
1097
+ table: import_zod2.z.string(),
1098
+ localColumnName: import_zod2.z.string().optional(),
1099
+ foreignColumnName: import_zod2.z.string().optional(),
1100
+ custom: import_zod2.z.string().optional(),
1101
+ type: import_zod2.z.enum(["LEFT", "INNER"]),
1102
+ alias: import_zod2.z.string().optional()
1139
1103
  }).strict();
1140
- var requestDataSchema = import_zod3.z.object({
1141
- name: import_zod3.z.string(),
1142
- required: import_zod3.z.boolean(),
1143
- isNullable: import_zod3.z.boolean().optional(),
1144
- validator: import_zod3.z.array(validatorDataSchema)
1104
+ var requestDataSchema = import_zod2.z.object({
1105
+ name: import_zod2.z.string(),
1106
+ required: import_zod2.z.boolean(),
1107
+ isNullable: import_zod2.z.boolean().optional(),
1108
+ validator: import_zod2.z.array(validatorDataSchema)
1145
1109
  }).strict();
1146
- var responseDataSchema = import_zod3.z.object({
1147
- name: import_zod3.z.string(),
1148
- selector: import_zod3.z.string().optional(),
1149
- subquery: import_zod3.z.object({
1150
- table: import_zod3.z.string(),
1151
- joins: import_zod3.z.array(joinDataSchema),
1152
- where: import_zod3.z.array(whereDataSchema),
1153
- properties: import_zod3.z.array(import_zod3.z.lazy(() => responseDataSchema)),
1110
+ var responseDataSchema = import_zod2.z.object({
1111
+ name: import_zod2.z.string(),
1112
+ selector: import_zod2.z.string().optional(),
1113
+ subquery: import_zod2.z.object({
1114
+ table: import_zod2.z.string(),
1115
+ joins: import_zod2.z.array(joinDataSchema),
1116
+ where: import_zod2.z.array(whereDataSchema),
1117
+ properties: import_zod2.z.array(import_zod2.z.lazy(() => responseDataSchema)),
1154
1118
  // Explicit type for the lazy schema
1155
1119
  groupBy: groupBySchema.optional(),
1156
1120
  orderBy: orderBySchema.optional()
1157
1121
  }).optional(),
1158
- type: import_zod3.z.string().optional()
1122
+ type: import_zod2.z.string().optional()
1123
+ // Type allows you to override the type of the response, used in custom selectors
1159
1124
  }).strict();
1160
- var routeDataBaseSchema = import_zod3.z.object({
1161
- method: import_zod3.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
1162
- name: import_zod3.z.string(),
1163
- description: import_zod3.z.string(),
1164
- path: import_zod3.z.string(),
1165
- roles: import_zod3.z.array(import_zod3.z.string())
1125
+ var routeDataBaseSchema = import_zod2.z.object({
1126
+ method: import_zod2.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
1127
+ name: import_zod2.z.string(),
1128
+ description: import_zod2.z.string(),
1129
+ path: import_zod2.z.string(),
1130
+ roles: import_zod2.z.array(import_zod2.z.string()),
1131
+ scopes: import_zod2.z.array(import_zod2.z.string())
1166
1132
  }).strict();
1167
1133
  var standardRouteSchema = routeDataBaseSchema.extend({
1168
- type: import_zod3.z.enum(["ONE", "ARRAY", "PAGED"]),
1169
- table: import_zod3.z.string(),
1170
- joins: import_zod3.z.array(joinDataSchema),
1171
- assignments: import_zod3.z.array(assignmentDataSchema),
1172
- where: import_zod3.z.array(whereDataSchema),
1173
- request: import_zod3.z.array(requestDataSchema),
1174
- response: import_zod3.z.array(responseDataSchema),
1134
+ type: import_zod2.z.enum(["ONE", "ARRAY", "PAGED"]),
1135
+ table: import_zod2.z.string(),
1136
+ joins: import_zod2.z.array(joinDataSchema),
1137
+ assignments: import_zod2.z.array(assignmentDataSchema),
1138
+ where: import_zod2.z.array(whereDataSchema),
1139
+ request: import_zod2.z.array(requestDataSchema),
1140
+ response: import_zod2.z.array(responseDataSchema),
1175
1141
  groupBy: groupBySchema.optional(),
1176
1142
  orderBy: orderBySchema.optional()
1177
1143
  }).strict();
1178
1144
  var customRouteSchema = routeDataBaseSchema.extend({
1179
- type: import_zod3.z.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
1180
- responseType: import_zod3.z.union([import_zod3.z.string(), import_zod3.z.enum(["string", "number", "boolean"])]),
1181
- requestType: import_zod3.z.string().optional(),
1182
- request: import_zod3.z.array(requestDataSchema).optional(),
1183
- table: import_zod3.z.undefined(),
1184
- joins: import_zod3.z.undefined(),
1185
- assignments: import_zod3.z.undefined(),
1186
- fileUploadType: import_zod3.z.enum(["SINGLE", "MULTIPLE"]).optional()
1145
+ type: import_zod2.z.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
1146
+ responseType: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.enum(["string", "number", "boolean"])]),
1147
+ requestType: import_zod2.z.string().optional(),
1148
+ request: import_zod2.z.array(requestDataSchema).optional(),
1149
+ table: import_zod2.z.undefined(),
1150
+ joins: import_zod2.z.undefined(),
1151
+ assignments: import_zod2.z.undefined(),
1152
+ fileUploadType: import_zod2.z.enum(["SINGLE", "MULTIPLE"]).optional()
1187
1153
  }).strict();
1188
- var postgresColumnNumericTypesSchema = import_zod3.z.enum([
1154
+ var postgresColumnNumericTypesSchema = import_zod2.z.enum([
1189
1155
  "SMALLINT",
1190
1156
  // 2 bytes, -32,768 to 32,767
1191
1157
  "INTEGER",
@@ -1205,7 +1171,7 @@ var postgresColumnNumericTypesSchema = import_zod3.z.enum([
1205
1171
  "BIGSERIAL"
1206
1172
  // auto-incrementing big integer
1207
1173
  ]);
1208
- var postgresColumnStringTypesSchema = import_zod3.z.enum([
1174
+ var postgresColumnStringTypesSchema = import_zod2.z.enum([
1209
1175
  "CHAR",
1210
1176
  // fixed-length, blank-padded
1211
1177
  "VARCHAR",
@@ -1215,7 +1181,7 @@ var postgresColumnStringTypesSchema = import_zod3.z.enum([
1215
1181
  "BYTEA"
1216
1182
  // binary data
1217
1183
  ]);
1218
- var postgresColumnDateTypesSchema = import_zod3.z.enum([
1184
+ var postgresColumnDateTypesSchema = import_zod2.z.enum([
1219
1185
  "DATE",
1220
1186
  // calendar date (year, month, day)
1221
1187
  "TIMESTAMP",
@@ -1227,13 +1193,13 @@ var postgresColumnDateTypesSchema = import_zod3.z.enum([
1227
1193
  "INTERVAL"
1228
1194
  // time span
1229
1195
  ]);
1230
- var postgresColumnJsonTypesSchema = import_zod3.z.enum([
1196
+ var postgresColumnJsonTypesSchema = import_zod2.z.enum([
1231
1197
  "JSON",
1232
1198
  // stores JSON data as raw text
1233
1199
  "JSONB"
1234
1200
  // stores JSON data in a binary format, optimized for query performance
1235
1201
  ]);
1236
- var mariaDbColumnNumericTypesSchema = import_zod3.z.enum([
1202
+ var mariaDbColumnNumericTypesSchema = import_zod2.z.enum([
1237
1203
  "BOOLEAN",
1238
1204
  // 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
1239
1205
  "TINYINT",
@@ -1253,7 +1219,7 @@ var mariaDbColumnNumericTypesSchema = import_zod3.z.enum([
1253
1219
  "DOUBLE"
1254
1220
  // 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.
1255
1221
  ]);
1256
- var mariaDbColumnStringTypesSchema = import_zod3.z.enum([
1222
+ var mariaDbColumnStringTypesSchema = import_zod2.z.enum([
1257
1223
  "CHAR",
1258
1224
  // 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.
1259
1225
  "VARCHAR",
@@ -1279,7 +1245,7 @@ var mariaDbColumnStringTypesSchema = import_zod3.z.enum([
1279
1245
  "ENUM"
1280
1246
  // Enum type
1281
1247
  ]);
1282
- var mariaDbColumnDateTypesSchema = import_zod3.z.enum([
1248
+ var mariaDbColumnDateTypesSchema = import_zod2.z.enum([
1283
1249
  "DATE",
1284
1250
  // 4-bytes Date has year, month, and day.
1285
1251
  "DATETIME",
@@ -1289,9 +1255,9 @@ var mariaDbColumnDateTypesSchema = import_zod3.z.enum([
1289
1255
  "TIMESTAMP"
1290
1256
  // 4-bytes Values are stored as the number of seconds since 1970-01-01 00:00:00 UTC, and optionally microseconds.
1291
1257
  ]);
1292
- var columnDataSchema = import_zod3.z.object({
1293
- name: import_zod3.z.string(),
1294
- type: import_zod3.z.union([
1258
+ var columnDataSchema = import_zod2.z.object({
1259
+ name: import_zod2.z.string(),
1260
+ type: import_zod2.z.union([
1295
1261
  postgresColumnNumericTypesSchema,
1296
1262
  postgresColumnStringTypesSchema,
1297
1263
  postgresColumnDateTypesSchema,
@@ -1300,24 +1266,25 @@ var columnDataSchema = import_zod3.z.object({
1300
1266
  mariaDbColumnStringTypesSchema,
1301
1267
  mariaDbColumnDateTypesSchema
1302
1268
  ]),
1303
- isNullable: import_zod3.z.boolean(),
1304
- roles: import_zod3.z.array(import_zod3.z.string()),
1305
- comment: import_zod3.z.string().optional(),
1306
- default: import_zod3.z.string().optional(),
1307
- value: import_zod3.z.string().optional(),
1308
- isPrimary: import_zod3.z.boolean().optional(),
1309
- isUnique: import_zod3.z.boolean().optional(),
1310
- hasAutoIncrement: import_zod3.z.boolean().optional(),
1311
- length: import_zod3.z.number().optional()
1269
+ isNullable: import_zod2.z.boolean(),
1270
+ roles: import_zod2.z.array(import_zod2.z.string()),
1271
+ scopes: import_zod2.z.array(import_zod2.z.string()),
1272
+ comment: import_zod2.z.string().optional(),
1273
+ default: import_zod2.z.string().optional(),
1274
+ value: import_zod2.z.string().optional(),
1275
+ isPrimary: import_zod2.z.boolean().optional(),
1276
+ isUnique: import_zod2.z.boolean().optional(),
1277
+ hasAutoIncrement: import_zod2.z.boolean().optional(),
1278
+ length: import_zod2.z.number().optional()
1312
1279
  }).strict();
1313
- var indexDataSchema = import_zod3.z.object({
1314
- name: import_zod3.z.string(),
1315
- columns: import_zod3.z.array(import_zod3.z.string()),
1316
- isUnique: import_zod3.z.boolean(),
1317
- isPrimaryKey: import_zod3.z.boolean(),
1318
- order: import_zod3.z.enum(["ASC", "DESC"])
1280
+ var indexDataSchema = import_zod2.z.object({
1281
+ name: import_zod2.z.string(),
1282
+ columns: import_zod2.z.array(import_zod2.z.string()),
1283
+ isUnique: import_zod2.z.boolean(),
1284
+ isPrimaryKey: import_zod2.z.boolean(),
1285
+ order: import_zod2.z.enum(["ASC", "DESC"])
1319
1286
  }).strict();
1320
- var foreignKeyActionsSchema = import_zod3.z.enum([
1287
+ var foreignKeyActionsSchema = import_zod2.z.enum([
1321
1288
  "CASCADE",
1322
1289
  // CASCADE action for foreign keys
1323
1290
  "SET NULL",
@@ -1329,39 +1296,41 @@ var foreignKeyActionsSchema = import_zod3.z.enum([
1329
1296
  "SET DEFAULT"
1330
1297
  // SET DEFAULT action for foreign keys
1331
1298
  ]);
1332
- var foreignKeyDataSchema = import_zod3.z.object({
1333
- name: import_zod3.z.string(),
1334
- column: import_zod3.z.string(),
1335
- refTable: import_zod3.z.string(),
1336
- refColumn: import_zod3.z.string(),
1299
+ var foreignKeyDataSchema = import_zod2.z.object({
1300
+ name: import_zod2.z.string(),
1301
+ column: import_zod2.z.string(),
1302
+ refTable: import_zod2.z.string(),
1303
+ refColumn: import_zod2.z.string(),
1337
1304
  onDelete: foreignKeyActionsSchema,
1338
1305
  onUpdate: foreignKeyActionsSchema
1339
1306
  }).strict();
1340
- var checkConstraintDataSchema = import_zod3.z.object({
1341
- name: import_zod3.z.string(),
1342
- check: import_zod3.z.string()
1307
+ var checkConstraintDataSchema = import_zod2.z.object({
1308
+ name: import_zod2.z.string(),
1309
+ check: import_zod2.z.string()
1343
1310
  }).strict();
1344
- var tableDataSchema = import_zod3.z.object({
1345
- name: import_zod3.z.string(),
1346
- columns: import_zod3.z.array(columnDataSchema),
1347
- indexes: import_zod3.z.array(indexDataSchema),
1348
- foreignKeys: import_zod3.z.array(foreignKeyDataSchema),
1349
- checkConstraints: import_zod3.z.array(checkConstraintDataSchema),
1350
- roles: import_zod3.z.array(import_zod3.z.string()),
1351
- notify: import_zod3.z.union([import_zod3.z.literal("ALL"), import_zod3.z.array(import_zod3.z.string())]).optional()
1311
+ var tableDataSchema = import_zod2.z.object({
1312
+ name: import_zod2.z.string(),
1313
+ columns: import_zod2.z.array(columnDataSchema),
1314
+ indexes: import_zod2.z.array(indexDataSchema),
1315
+ foreignKeys: import_zod2.z.array(foreignKeyDataSchema),
1316
+ checkConstraints: import_zod2.z.array(checkConstraintDataSchema),
1317
+ roles: import_zod2.z.array(import_zod2.z.string()),
1318
+ scopes: import_zod2.z.array(import_zod2.z.string()),
1319
+ notify: import_zod2.z.union([import_zod2.z.literal("ALL"), import_zod2.z.array(import_zod2.z.string())]).optional()
1352
1320
  }).strict();
1353
- var endpointDataSchema = import_zod3.z.object({
1354
- name: import_zod3.z.string(),
1355
- description: import_zod3.z.string(),
1356
- baseUrl: import_zod3.z.string(),
1357
- routes: import_zod3.z.array(import_zod3.z.union([standardRouteSchema, customRouteSchema]))
1321
+ var endpointDataSchema = import_zod2.z.object({
1322
+ name: import_zod2.z.string(),
1323
+ description: import_zod2.z.string(),
1324
+ baseUrl: import_zod2.z.string(),
1325
+ routes: import_zod2.z.array(import_zod2.z.union([standardRouteSchema, customRouteSchema]))
1358
1326
  }).strict();
1359
- var resturaSchema = import_zod3.z.object({
1360
- database: import_zod3.z.array(tableDataSchema),
1361
- endpoints: import_zod3.z.array(endpointDataSchema),
1362
- globalParams: import_zod3.z.array(import_zod3.z.string()),
1363
- roles: import_zod3.z.array(import_zod3.z.string()),
1364
- customTypes: import_zod3.z.array(import_zod3.z.string())
1327
+ var resturaSchema = import_zod2.z.object({
1328
+ database: import_zod2.z.array(tableDataSchema),
1329
+ endpoints: import_zod2.z.array(endpointDataSchema),
1330
+ globalParams: import_zod2.z.array(import_zod2.z.string()),
1331
+ roles: import_zod2.z.array(import_zod2.z.string()),
1332
+ scopes: import_zod2.z.array(import_zod2.z.string()),
1333
+ customTypes: import_zod2.z.array(import_zod2.z.string())
1365
1334
  }).strict();
1366
1335
  async function isSchemaValid(schemaToCheck) {
1367
1336
  try {
@@ -1376,7 +1345,7 @@ async function isSchemaValid(schemaToCheck) {
1376
1345
  // src/restura/validators/requestValidator.ts
1377
1346
  var import_core_utils3 = require("@redskytech/core-utils");
1378
1347
  var import_jsonschema = __toESM(require("jsonschema"));
1379
- var import_zod4 = require("zod");
1348
+ var import_zod3 = require("zod");
1380
1349
 
1381
1350
  // src/restura/utils/utils.ts
1382
1351
  function addQuotesToStrings(variable) {
@@ -1458,31 +1427,31 @@ function isValidType(type, requestValue) {
1458
1427
  try {
1459
1428
  expectValidType(type, requestValue);
1460
1429
  return true;
1461
- } catch (e) {
1430
+ } catch {
1462
1431
  return false;
1463
1432
  }
1464
1433
  }
1465
1434
  function expectValidType(type, requestValue) {
1466
1435
  if (type === "number") {
1467
- return import_zod4.z.number().parse(requestValue);
1436
+ return import_zod3.z.number().parse(requestValue);
1468
1437
  }
1469
1438
  if (type === "string") {
1470
- return import_zod4.z.string().parse(requestValue);
1439
+ return import_zod3.z.string().parse(requestValue);
1471
1440
  }
1472
1441
  if (type === "boolean") {
1473
- return import_zod4.z.boolean().parse(requestValue);
1442
+ return import_zod3.z.boolean().parse(requestValue);
1474
1443
  }
1475
1444
  if (type === "string[]") {
1476
- return import_zod4.z.array(import_zod4.z.string()).parse(requestValue);
1445
+ return import_zod3.z.array(import_zod3.z.string()).parse(requestValue);
1477
1446
  }
1478
1447
  if (type === "number[]") {
1479
- return import_zod4.z.array(import_zod4.z.number()).parse(requestValue);
1448
+ return import_zod3.z.array(import_zod3.z.number()).parse(requestValue);
1480
1449
  }
1481
1450
  if (type === "any[]") {
1482
- return import_zod4.z.array(import_zod4.z.any()).parse(requestValue);
1451
+ return import_zod3.z.array(import_zod3.z.any()).parse(requestValue);
1483
1452
  }
1484
1453
  if (type === "object") {
1485
- return import_zod4.z.object({}).strict().parse(requestValue);
1454
+ return import_zod3.z.object({}).strict().parse(requestValue);
1486
1455
  }
1487
1456
  }
1488
1457
  function performTypeCheck(requestValue, validator, requestParamName) {
@@ -1494,7 +1463,7 @@ function performTypeCheck(requestValue, validator, requestParamName) {
1494
1463
  }
1495
1464
  try {
1496
1465
  validatorDataSchemeValue.parse(validator.value);
1497
- } catch (e) {
1466
+ } catch {
1498
1467
  throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
1499
1468
  }
1500
1469
  }
@@ -1585,18 +1554,18 @@ async function schemaValidation(req, res, next) {
1585
1554
  }
1586
1555
 
1587
1556
  // src/restura/schemas/resturaConfigSchema.ts
1588
- var import_zod5 = require("zod");
1589
- var _a;
1590
- var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
1557
+ var import_v42 = require("zod/v4");
1558
+ var isTsx = process.argv[1]?.endsWith(".ts");
1591
1559
  var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
1592
1560
  var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
1593
- var resturaConfigSchema = import_zod5.z.object({
1594
- authToken: import_zod5.z.string().min(1, "Missing Restura Auth Token"),
1595
- sendErrorStackTrace: import_zod5.z.boolean().default(false),
1596
- schemaFilePath: import_zod5.z.string().default(process.cwd() + "/restura.schema.json"),
1597
- customApiFolderPath: import_zod5.z.string().default(process.cwd() + customApiFolderPath),
1598
- generatedTypesPath: import_zod5.z.string().default(process.cwd() + "/src/@types"),
1599
- fileTempCachePath: import_zod5.z.string().optional()
1561
+ var resturaConfigSchema = import_v42.z.object({
1562
+ authToken: import_v42.z.string().min(1, "Missing Restura Auth Token"),
1563
+ sendErrorStackTrace: import_v42.z.boolean().default(false),
1564
+ schemaFilePath: import_v42.z.string().default(process.cwd() + "/restura.schema.json"),
1565
+ customApiFolderPath: import_v42.z.string().default(process.cwd() + customApiFolderPath),
1566
+ generatedTypesPath: import_v42.z.string().default(process.cwd() + "/src/@types"),
1567
+ fileTempCachePath: import_v42.z.string().optional(),
1568
+ scratchDatabaseSuffix: import_v42.z.string().optional()
1600
1569
  });
1601
1570
 
1602
1571
  // src/restura/sql/PsqlEngine.ts
@@ -1682,13 +1651,14 @@ function SQL(strings, ...values) {
1682
1651
 
1683
1652
  // src/restura/sql/PsqlConnection.ts
1684
1653
  var PsqlConnection = class {
1654
+ instanceId;
1685
1655
  constructor(instanceId) {
1686
1656
  this.instanceId = instanceId || import_crypto.default.randomUUID();
1687
1657
  }
1688
1658
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1689
1659
  async queryOne(query, options, requesterDetails) {
1690
1660
  const formattedQuery = questionMarksToOrderedParams(query);
1691
- const meta = __spreadValues({ connectionInstanceId: this.instanceId }, requesterDetails);
1661
+ const meta = { connectionInstanceId: this.instanceId, ...requesterDetails };
1692
1662
  this.logSqlStatement(formattedQuery, options, meta);
1693
1663
  const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
1694
1664
  `;
@@ -1701,7 +1671,7 @@ var PsqlConnection = class {
1701
1671
  return response.rows[0];
1702
1672
  } catch (error) {
1703
1673
  if (RsError.isRsError(error)) throw error;
1704
- if ((error == null ? void 0 : error.routine) === "_bt_check_unique") {
1674
+ if (error?.routine === "_bt_check_unique") {
1705
1675
  throw new RsError("DUPLICATE", error.message);
1706
1676
  }
1707
1677
  throw new RsError("DATABASE_ERROR", `${error.message}`);
@@ -1710,7 +1680,7 @@ var PsqlConnection = class {
1710
1680
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1711
1681
  async runQuery(query, options, requesterDetails) {
1712
1682
  const formattedQuery = questionMarksToOrderedParams(query);
1713
- const meta = __spreadValues({ connectionInstanceId: this.instanceId }, requesterDetails);
1683
+ const meta = { connectionInstanceId: this.instanceId, ...requesterDetails };
1714
1684
  this.logSqlStatement(formattedQuery, options, meta);
1715
1685
  const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
1716
1686
  `;
@@ -1720,7 +1690,7 @@ var PsqlConnection = class {
1720
1690
  this.logQueryDuration(startTime);
1721
1691
  return response.rows;
1722
1692
  } catch (error) {
1723
- if ((error == null ? void 0 : error.routine) === "_bt_check_unique") {
1693
+ if (error?.routine === "_bt_check_unique") {
1724
1694
  throw new RsError("DUPLICATE", error.message);
1725
1695
  }
1726
1696
  throw new RsError("DATABASE_ERROR", `${error.message}`);
@@ -1770,13 +1740,20 @@ var PsqlPool = class extends PsqlConnection {
1770
1740
  super();
1771
1741
  this.poolConfig = poolConfig;
1772
1742
  this.pool = new Pool(poolConfig);
1773
- this.queryOne("SELECT NOW();", [], { isSystemUser: true, role: "", host: "localhost", ipAddress: "" }).then(() => {
1743
+ this.queryOne("SELECT NOW();", [], {
1744
+ isSystemUser: true,
1745
+ role: "",
1746
+ host: "localhost",
1747
+ ipAddress: "",
1748
+ scopes: []
1749
+ }).then(() => {
1774
1750
  logger.info("Connected to PostgreSQL database");
1775
1751
  }).catch((error) => {
1776
1752
  logger.error("Error connecting to database", error);
1777
1753
  process.exit(1);
1778
1754
  });
1779
1755
  }
1756
+ pool;
1780
1757
  async query(query, values) {
1781
1758
  return this.pool.query(query, values);
1782
1759
  }
@@ -1786,7 +1763,12 @@ var PsqlPool = class extends PsqlConnection {
1786
1763
  var import_core_utils4 = require("@redskytech/core-utils");
1787
1764
  var SqlEngine = class {
1788
1765
  async runQueryForRoute(req, routeData, schema) {
1789
- if (!this.doesRoleHavePermissionToTable(req.requesterDetails.role, schema, routeData.table))
1766
+ if (!this.canRequesterAccessTable(
1767
+ req.requesterDetails.role,
1768
+ req.requesterDetails.scopes,
1769
+ schema,
1770
+ routeData.table
1771
+ ))
1790
1772
  throw new RsError("FORBIDDEN", "You do not have permission to access this table");
1791
1773
  switch (routeData.method) {
1792
1774
  case "POST":
@@ -1805,7 +1787,7 @@ var SqlEngine = class {
1805
1787
  if (!tableSchema) throw new RsError("SCHEMA_ERROR", `Table ${tableName} not found in schema`);
1806
1788
  return tableSchema;
1807
1789
  }
1808
- doesRoleHavePermissionToColumn(role, schema, item, joins) {
1790
+ canRequesterAccessColumn(requesterRole, requesterScopes, schema, item, joins) {
1809
1791
  if (item.type) return true;
1810
1792
  if (item.selector) {
1811
1793
  let tableName = item.selector.split(".")[0];
@@ -1821,26 +1803,36 @@ var SqlEngine = class {
1821
1803
  const columnSchema = tableSchema.columns.find((item2) => item2.name === columnName);
1822
1804
  if (!columnSchema)
1823
1805
  throw new RsError("SCHEMA_ERROR", `Column ${columnName} not found in table ${tableName}`);
1824
- const doesColumnHaveRoles = import_core_utils4.ObjectUtils.isArrayWithData(columnSchema.roles);
1825
- if (!doesColumnHaveRoles) return true;
1826
- if (!role) return false;
1827
- return columnSchema.roles.includes(role);
1806
+ if (import_core_utils4.ObjectUtils.isArrayWithData(columnSchema.roles)) {
1807
+ if (!requesterRole) return false;
1808
+ return columnSchema.roles.includes(requesterRole);
1809
+ }
1810
+ if (import_core_utils4.ObjectUtils.isArrayWithData(columnSchema.scopes)) {
1811
+ if (!requesterScopes) return false;
1812
+ return columnSchema.scopes.some((scope) => requesterScopes.includes(scope));
1813
+ }
1814
+ return true;
1828
1815
  }
1829
1816
  if (item.subquery) {
1830
1817
  return import_core_utils4.ObjectUtils.isArrayWithData(
1831
1818
  item.subquery.properties.filter((nestedItem) => {
1832
- return this.doesRoleHavePermissionToColumn(role, schema, nestedItem, joins);
1819
+ return this.canRequesterAccessColumn(requesterRole, requesterScopes, schema, nestedItem, joins);
1833
1820
  })
1834
1821
  );
1835
1822
  }
1836
1823
  return false;
1837
1824
  }
1838
- doesRoleHavePermissionToTable(userRole, schema, tableName) {
1825
+ canRequesterAccessTable(requesterRole, requesterScopes, schema, tableName) {
1839
1826
  const tableSchema = this.getTableSchema(schema, tableName);
1840
- const doesTableHaveRoles = import_core_utils4.ObjectUtils.isArrayWithData(tableSchema.roles);
1841
- if (!doesTableHaveRoles) return true;
1842
- if (!userRole) return false;
1843
- return tableSchema.roles.includes(userRole);
1827
+ if (import_core_utils4.ObjectUtils.isArrayWithData(tableSchema.roles)) {
1828
+ if (!requesterRole) return false;
1829
+ return tableSchema.roles.includes(requesterRole);
1830
+ }
1831
+ if (import_core_utils4.ObjectUtils.isArrayWithData(tableSchema.scopes)) {
1832
+ if (!requesterScopes) return false;
1833
+ return tableSchema.scopes.some((scope) => requesterScopes.includes(scope));
1834
+ }
1835
+ return true;
1844
1836
  }
1845
1837
  replaceParamKeywords(value, routeData, req, sqlParams) {
1846
1838
  let returnValue = value;
@@ -1849,11 +1841,10 @@ var SqlEngine = class {
1849
1841
  return returnValue;
1850
1842
  }
1851
1843
  replaceLocalParamKeywords(value, routeData, req, sqlParams) {
1852
- var _a2;
1853
1844
  if (!routeData.request) return value;
1854
1845
  const data = req.data;
1855
1846
  if (typeof value === "string") {
1856
- (_a2 = value.match(/\$[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a2.forEach((param) => {
1847
+ value.match(/\$[a-zA-Z][a-zA-Z0-9_]+/g)?.forEach((param) => {
1857
1848
  const requestParam = routeData.request.find((item) => {
1858
1849
  return item.name === param.replace("$", "");
1859
1850
  });
@@ -1866,9 +1857,8 @@ var SqlEngine = class {
1866
1857
  return value;
1867
1858
  }
1868
1859
  replaceGlobalParamKeywords(value, routeData, req, sqlParams) {
1869
- var _a2;
1870
1860
  if (typeof value === "string") {
1871
- (_a2 = value.match(/#[a-zA-Z][a-zA-Z0-9_]+/g)) == null ? void 0 : _a2.forEach((param) => {
1861
+ value.match(/#[a-zA-Z][a-zA-Z0-9_]+/g)?.forEach((param) => {
1872
1862
  param = param.replace("#", "");
1873
1863
  const globalParamValue = req.requesterDetails[param];
1874
1864
  if (!globalParamValue)
@@ -1958,10 +1948,32 @@ negate = "!"
1958
1948
  operator = "and"i / "or"i
1959
1949
 
1960
1950
 
1961
- column = left:text "." right:text { return \`\${quoteSqlIdentity(left)}.\${quoteSqlIdentity(right)}\`; }
1962
- /
1963
- text:text { return quoteSqlIdentity(text); }
1951
+ column = first:text rest:("." text)* {
1952
+ const partsArray = [first];
1953
+ if (rest && rest.length > 0) {
1954
+ partsArray.push(...rest.map(item => item[1]));
1955
+ }
1964
1956
 
1957
+ if (partsArray.length > 3) {
1958
+ throw new SyntaxError('Column path cannot have more than 3 parts (table.column.jsonField)');
1959
+ }
1960
+
1961
+ if (partsArray.length === 1) {
1962
+ return quoteSqlIdentity(partsArray[0]);
1963
+ }
1964
+ const tableName = quoteSqlIdentity(partsArray[0]);
1965
+
1966
+ // If we only have two parts (table.column), use regular dot notation
1967
+ if (partsArray.length === 2) {
1968
+ return tableName + "." + quoteSqlIdentity(partsArray[1]);
1969
+ }
1970
+
1971
+ // For JSON paths (more than 2 parts), first part is a column, last part uses ->>
1972
+ const jsonColumn = quoteSqlIdentity(partsArray[1]);
1973
+ const lastPart = partsArray[partsArray.length - 1];
1974
+ const result = tableName + "." + jsonColumn + "->>'" + lastPart + "'";
1975
+ return result;
1976
+ }
1965
1977
 
1966
1978
  text = text:[a-z0-9 \\t\\r\\n\\-_:@']i+ { return text.join(""); }
1967
1979
 
@@ -1991,19 +2003,24 @@ var filterPsqlParser_default = filterPsqlParser;
1991
2003
  var { Client, types } = import_pg2.default;
1992
2004
  var systemUser = {
1993
2005
  role: "",
2006
+ scopes: [],
1994
2007
  host: "",
1995
2008
  ipAddress: "",
1996
2009
  isSystemUser: true
1997
2010
  };
1998
2011
  var PsqlEngine = class extends SqlEngine {
1999
- constructor(psqlConnectionPool, shouldListenForDbTriggers = false) {
2012
+ constructor(psqlConnectionPool, shouldListenForDbTriggers = false, scratchDatabaseSuffix = "") {
2000
2013
  super();
2001
2014
  this.psqlConnectionPool = psqlConnectionPool;
2002
2015
  this.setupPgReturnTypes();
2003
2016
  if (shouldListenForDbTriggers) {
2004
2017
  this.setupTriggerListeners = this.listenForDbTriggers();
2005
2018
  }
2019
+ this.scratchDbName = `${psqlConnectionPool.poolConfig.database}_scratch${scratchDatabaseSuffix ? `_${scratchDatabaseSuffix}` : ""}`;
2006
2020
  }
2021
+ setupTriggerListeners;
2022
+ triggerClient;
2023
+ scratchDbName = "";
2007
2024
  async close() {
2008
2025
  if (this.triggerClient) {
2009
2026
  await this.triggerClient.end();
@@ -2132,27 +2149,22 @@ var PsqlEngine = class extends SqlEngine {
2132
2149
  sqlStatements.push(triggers.join("\n"));
2133
2150
  return sqlStatements.join("\n\n");
2134
2151
  }
2135
- async getScratchPool() {
2136
- var _a2, _b;
2152
+ async getNewPublicSchemaAndScratchPool() {
2137
2153
  const scratchDbExists = await this.psqlConnectionPool.runQuery(
2138
2154
  `SELECT *
2139
2155
  FROM pg_database
2140
- WHERE datname = '${this.psqlConnectionPool.poolConfig.database}_scratch';`,
2156
+ WHERE datname = '${this.scratchDbName}';`,
2141
2157
  [],
2142
2158
  systemUser
2143
2159
  );
2144
2160
  if (scratchDbExists.length === 0) {
2145
- await this.psqlConnectionPool.runQuery(
2146
- `CREATE DATABASE ${this.psqlConnectionPool.poolConfig.database}_scratch;`,
2147
- [],
2148
- systemUser
2149
- );
2161
+ await this.psqlConnectionPool.runQuery(`CREATE DATABASE ${this.scratchDbName};`, [], systemUser);
2150
2162
  }
2151
2163
  const scratchPool = new PsqlPool({
2152
2164
  host: this.psqlConnectionPool.poolConfig.host,
2153
2165
  port: this.psqlConnectionPool.poolConfig.port,
2154
2166
  user: this.psqlConnectionPool.poolConfig.user,
2155
- database: this.psqlConnectionPool.poolConfig.database + "_scratch",
2167
+ database: this.scratchDbName,
2156
2168
  password: this.psqlConnectionPool.poolConfig.password,
2157
2169
  max: this.psqlConnectionPool.poolConfig.max,
2158
2170
  idleTimeoutMillis: this.psqlConnectionPool.poolConfig.idleTimeoutMillis,
@@ -2165,16 +2177,17 @@ var PsqlEngine = class extends SqlEngine {
2165
2177
  systemUser
2166
2178
  );
2167
2179
  const schemaComment = await this.psqlConnectionPool.runQuery(
2168
- `SELECT pg_description.description
2169
- FROM pg_description
2170
- JOIN pg_namespace ON pg_namespace.oid = pg_description.objoid
2171
- WHERE pg_namespace.nspname = 'public';`,
2180
+ `
2181
+ SELECT pg_description.description
2182
+ FROM pg_description
2183
+ JOIN pg_namespace ON pg_namespace.oid = pg_description.objoid
2184
+ WHERE pg_namespace.nspname = 'public';`,
2172
2185
  [],
2173
2186
  systemUser
2174
2187
  );
2175
- if ((_a2 = schemaComment[0]) == null ? void 0 : _a2.description) {
2188
+ if (schemaComment[0]?.description) {
2176
2189
  await scratchPool.runQuery(
2177
- `COMMENT ON SCHEMA public IS '${(_b = schemaComment[0]) == null ? void 0 : _b.description}';`,
2190
+ `COMMENT ON SCHEMA public IS '${schemaComment[0]?.description}';`,
2178
2191
  [],
2179
2192
  systemUser
2180
2193
  );
@@ -2182,7 +2195,7 @@ var PsqlEngine = class extends SqlEngine {
2182
2195
  return scratchPool;
2183
2196
  }
2184
2197
  async diffDatabaseToSchema(schema) {
2185
- const scratchPool = await this.getScratchPool();
2198
+ const scratchPool = await this.getNewPublicSchemaAndScratchPool();
2186
2199
  await this.createDatabaseFromSchema(schema, scratchPool);
2187
2200
  const originalClient = new Client({
2188
2201
  database: this.psqlConnectionPool.poolConfig.database,
@@ -2192,7 +2205,7 @@ var PsqlEngine = class extends SqlEngine {
2192
2205
  port: this.psqlConnectionPool.poolConfig.port
2193
2206
  });
2194
2207
  const scratchClient = new Client({
2195
- database: this.psqlConnectionPool.poolConfig.database + "_scratch",
2208
+ database: this.scratchDbName,
2196
2209
  user: this.psqlConnectionPool.poolConfig.user,
2197
2210
  password: this.psqlConnectionPool.poolConfig.password,
2198
2211
  host: this.psqlConnectionPool.poolConfig.host,
@@ -2207,24 +2220,30 @@ var PsqlEngine = class extends SqlEngine {
2207
2220
  await Promise.all(endPromises);
2208
2221
  return diff.join("\n");
2209
2222
  }
2210
- createNestedSelect(req, schema, item, routeData, userRole, sqlParams) {
2223
+ createNestedSelect(req, schema, item, routeData, sqlParams) {
2211
2224
  if (!item.subquery) return "";
2212
2225
  if (!import_core_utils5.ObjectUtils.isArrayWithData(
2213
2226
  item.subquery.properties.filter((nestedItem) => {
2214
- return this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
2215
- ...routeData.joins,
2216
- ...item.subquery.joins
2217
- ]);
2227
+ return this.canRequesterAccessColumn(
2228
+ req.requesterDetails.role,
2229
+ req.requesterDetails.scopes,
2230
+ schema,
2231
+ nestedItem,
2232
+ [...routeData.joins, ...item.subquery.joins]
2233
+ );
2218
2234
  })
2219
2235
  )) {
2220
2236
  return "'[]'";
2221
2237
  }
2222
2238
  return `COALESCE((SELECT JSON_AGG(JSON_BUILD_OBJECT(
2223
2239
  ${item.subquery.properties.map((nestedItem) => {
2224
- if (!this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
2225
- ...routeData.joins,
2226
- ...item.subquery.joins
2227
- ])) {
2240
+ if (!this.canRequesterAccessColumn(
2241
+ req.requesterDetails.role,
2242
+ req.requesterDetails.scopes,
2243
+ schema,
2244
+ nestedItem,
2245
+ [...routeData.joins, ...item.subquery.joins]
2246
+ )) {
2228
2247
  return;
2229
2248
  }
2230
2249
  if (nestedItem.subquery) {
@@ -2234,7 +2253,6 @@ var PsqlEngine = class extends SqlEngine {
2234
2253
  schema,
2235
2254
  nestedItem,
2236
2255
  routeData,
2237
- userRole,
2238
2256
  sqlParams
2239
2257
  )}`;
2240
2258
  }
@@ -2243,7 +2261,7 @@ var PsqlEngine = class extends SqlEngine {
2243
2261
  ))
2244
2262
  FROM
2245
2263
  "${item.subquery.table}"
2246
- ${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, userRole, sqlParams)}
2264
+ ${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, sqlParams)}
2247
2265
  ${this.generateWhereClause(req, item.subquery.where, routeData, sqlParams)}
2248
2266
  ), '[]')`;
2249
2267
  }
@@ -2253,7 +2271,7 @@ var PsqlEngine = class extends SqlEngine {
2253
2271
  (routeData.assignments || []).forEach((assignment) => {
2254
2272
  parameterObj[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
2255
2273
  });
2256
- const query = insertObjectQuery(routeData.table, __spreadValues(__spreadValues({}, req.data), parameterObj));
2274
+ const query = insertObjectQuery(routeData.table, { ...req.data, ...parameterObj });
2257
2275
  const createdItem = await this.psqlConnectionPool.queryOne(
2258
2276
  query,
2259
2277
  sqlParams,
@@ -2268,24 +2286,29 @@ var PsqlEngine = class extends SqlEngine {
2268
2286
  };
2269
2287
  const whereData = [whereId];
2270
2288
  req.data = { id: insertId };
2271
- return this.executeGetRequest(req, __spreadProps(__spreadValues({}, routeData), { where: whereData }), schema);
2289
+ return this.executeGetRequest(req, { ...routeData, where: whereData }, schema);
2272
2290
  }
2273
2291
  async executeGetRequest(req, routeData, schema) {
2274
2292
  const DEFAULT_PAGED_PAGE_NUMBER = 0;
2275
2293
  const DEFAULT_PAGED_PER_PAGE_NUMBER = 25;
2276
2294
  const sqlParams = [];
2277
- const userRole = req.requesterDetails.role;
2278
2295
  let sqlStatement = "";
2279
2296
  const selectColumns = [];
2280
2297
  routeData.response.forEach((item) => {
2281
- if (item.subquery || this.doesRoleHavePermissionToColumn(userRole, schema, item, routeData.joins))
2298
+ if (item.subquery || this.canRequesterAccessColumn(
2299
+ req.requesterDetails.role,
2300
+ req.requesterDetails.scopes,
2301
+ schema,
2302
+ item,
2303
+ routeData.joins
2304
+ ))
2282
2305
  selectColumns.push(item);
2283
2306
  });
2284
2307
  if (!selectColumns.length) throw new RsError("FORBIDDEN", `You do not have permission to access this data.`);
2285
2308
  let selectStatement = "SELECT \n";
2286
2309
  selectStatement += ` ${selectColumns.map((item) => {
2287
2310
  if (item.subquery) {
2288
- return `${this.createNestedSelect(req, schema, item, routeData, userRole, sqlParams)} AS ${escapeColumnName(
2311
+ return `${this.createNestedSelect(req, schema, item, routeData, sqlParams)} AS ${escapeColumnName(
2289
2312
  item.name
2290
2313
  )}`;
2291
2314
  }
@@ -2300,7 +2323,6 @@ var PsqlEngine = class extends SqlEngine {
2300
2323
  routeData.table,
2301
2324
  routeData,
2302
2325
  schema,
2303
- userRole,
2304
2326
  sqlParams
2305
2327
  );
2306
2328
  sqlStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
@@ -2344,7 +2366,7 @@ var PsqlEngine = class extends SqlEngine {
2344
2366
  }
2345
2367
  async executeUpdateRequest(req, routeData, schema) {
2346
2368
  const sqlParams = [];
2347
- const _a2 = req.body, { id } = _a2, bodyNoId = __objRest(_a2, ["id"]);
2369
+ const { id, ...bodyNoId } = req.body;
2348
2370
  const table = schema.database.find((item) => {
2349
2371
  return item.name === routeData.table;
2350
2372
  });
@@ -2373,7 +2395,6 @@ var PsqlEngine = class extends SqlEngine {
2373
2395
  routeData.table,
2374
2396
  routeData,
2375
2397
  schema,
2376
- req.requesterDetails.role,
2377
2398
  sqlParams
2378
2399
  );
2379
2400
  const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
@@ -2385,10 +2406,15 @@ DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
2385
2406
  await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
2386
2407
  return true;
2387
2408
  }
2388
- generateJoinStatements(req, joins, baseTable, routeData, schema, userRole, sqlParams) {
2409
+ generateJoinStatements(req, joins, baseTable, routeData, schema, sqlParams) {
2389
2410
  let joinStatements = "";
2390
2411
  joins.forEach((item) => {
2391
- if (!this.doesRoleHavePermissionToTable(userRole, schema, item.table))
2412
+ if (!this.canRequesterAccessTable(
2413
+ req.requesterDetails.role,
2414
+ req.requesterDetails.scopes,
2415
+ schema,
2416
+ item.table
2417
+ ))
2392
2418
  throw new RsError("FORBIDDEN", "You do not have permission to access this table");
2393
2419
  if (item.custom) {
2394
2420
  const customReplaced = this.replaceParamKeywords(item.custom, routeData, req, sqlParams);
@@ -2443,41 +2469,36 @@ DELETE FROM "${routeData.table}" ${joinStatement} ${whereClause}`;
2443
2469
  `Invalid where clause in route ${routeData.name}, missing required fields if not custom`
2444
2470
  );
2445
2471
  let operator = item.operator;
2446
- let value = item.value;
2447
2472
  if (operator === "LIKE") {
2448
- value = `'%${value}%'`;
2449
- } else if (operator === "NOT LIKE") {
2450
- value = `'%${value}%'`;
2473
+ item.value = `'%${item.value}%'`;
2451
2474
  } else if (operator === "STARTS WITH") {
2452
2475
  operator = "LIKE";
2453
- value = `'${value}%'`;
2476
+ item.value = `'${item.value}%'`;
2454
2477
  } else if (operator === "ENDS WITH") {
2455
2478
  operator = "LIKE";
2456
- value = `'%${value}'`;
2479
+ item.value = `'%${item.value}'`;
2457
2480
  }
2458
- const replacedValue = this.replaceParamKeywords(value, routeData, req, sqlParams);
2481
+ const replacedValue = this.replaceParamKeywords(item.value, routeData, req, sqlParams);
2459
2482
  whereClause += ` ${item.conjunction || ""} "${item.tableName}"."${item.columnName}" ${operator.replace("LIKE", "ILIKE")} ${["IN", "NOT IN"].includes(operator) ? `(${replacedValue})` : replacedValue}
2460
2483
  `;
2461
2484
  });
2462
2485
  const data = req.data;
2463
- if (routeData.type === "PAGED" && !!(data == null ? void 0 : data.filter)) {
2486
+ if (routeData.type === "PAGED" && !!data?.filter) {
2464
2487
  let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
2465
- var _a2;
2466
2488
  const requestParam = routeData.request.find((item) => {
2467
2489
  return item.name === value.replace("$", "");
2468
2490
  });
2469
2491
  if (!requestParam)
2470
2492
  throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
2471
- return ((_a2 = data[requestParam.name]) == null ? void 0 : _a2.toString()) || "";
2493
+ return data[requestParam.name]?.toString() || "";
2472
2494
  });
2473
2495
  statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
2474
- var _a2;
2475
2496
  const requestParam = routeData.request.find((item) => {
2476
2497
  return item.name === value.replace("#", "");
2477
2498
  });
2478
2499
  if (!requestParam)
2479
2500
  throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
2480
- return ((_a2 = data[requestParam.name]) == null ? void 0 : _a2.toString()) || "";
2501
+ return data[requestParam.name]?.toString() || "";
2481
2502
  });
2482
2503
  statement = filterPsqlParser_default.parse(statement);
2483
2504
  if (whereClause.startsWith("WHERE")) {
@@ -2718,8 +2739,9 @@ var import_internal3 = require("@restura/internal");
2718
2739
  var import_bluebird3 = __toESM(require("bluebird"));
2719
2740
  var os2 = __toESM(require("os"));
2720
2741
  var TempCache = class {
2742
+ location;
2743
+ maxDurationDays = 7;
2721
2744
  constructor(location) {
2722
- this.maxDurationDays = 7;
2723
2745
  this.location = location || os2.tmpdir();
2724
2746
  import_internal3.FileUtils.ensureDir(this.location).catch((e) => {
2725
2747
  throw e;
@@ -2744,15 +2766,24 @@ var TempCache = class {
2744
2766
 
2745
2767
  // src/restura/restura.ts
2746
2768
  var ResturaEngine = class {
2747
- constructor() {
2748
- this.publicEndpoints = {
2749
- GET: [],
2750
- POST: [],
2751
- PUT: [],
2752
- PATCH: [],
2753
- DELETE: []
2754
- };
2755
- }
2769
+ // Make public so other modules can access without re-parsing the config
2770
+ resturaConfig;
2771
+ multerCommonUpload;
2772
+ resturaRouter;
2773
+ publicEndpoints = {
2774
+ GET: [],
2775
+ POST: [],
2776
+ PUT: [],
2777
+ PATCH: [],
2778
+ DELETE: []
2779
+ };
2780
+ expressApp;
2781
+ schema;
2782
+ responseValidator;
2783
+ authenticationHandler;
2784
+ customTypeValidation;
2785
+ psqlConnectionPool;
2786
+ psqlEngine;
2756
2787
  /**
2757
2788
  * Initializes the Restura engine with the provided Express application.
2758
2789
  *
@@ -2760,11 +2791,11 @@ var ResturaEngine = class {
2760
2791
  * @returns A promise that resolves when the initialization is complete.
2761
2792
  */
2762
2793
  async init(app, authenticationHandler, psqlConnectionPool) {
2763
- this.resturaConfig = import_internal4.config.validate("restura", resturaConfigSchema);
2794
+ this.resturaConfig = await import_internal4.config.validate("restura", resturaConfigSchema);
2764
2795
  this.multerCommonUpload = getMulterUpload(this.resturaConfig.fileTempCachePath);
2765
2796
  new TempCache(this.resturaConfig.fileTempCachePath);
2766
2797
  this.psqlConnectionPool = psqlConnectionPool;
2767
- this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true);
2798
+ this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true, this.resturaConfig.scratchDatabaseSuffix);
2768
2799
  await customApiFactory_default.loadApiFiles(this.resturaConfig.customApiFolderPath);
2769
2800
  this.authenticationHandler = authenticationHandler;
2770
2801
  app.use((0, import_compression.default)());
@@ -2773,7 +2804,7 @@ var ResturaEngine = class {
2773
2804
  app.use((0, import_cookie_parser.default)());
2774
2805
  app.disable("x-powered-by");
2775
2806
  app.use("/", addApiResponseFunctions);
2776
- app.use("/api/", authenticateUser(this.authenticationHandler));
2807
+ app.use("/api/", authenticateRequester(this.authenticationHandler));
2777
2808
  app.use("/restura", this.resturaAuthentication);
2778
2809
  app.put(
2779
2810
  "/restura/v1/schema",
@@ -2794,7 +2825,7 @@ var ResturaEngine = class {
2794
2825
  }
2795
2826
  /**
2796
2827
  * Determines if a given endpoint is public based on the HTTP method and full URL. This
2797
- * is determined on whether the endpoint in the schema has no roles assigned to it.
2828
+ * is determined on whether the endpoint in the schema has no roles or scopes assigned to it.
2798
2829
  *
2799
2830
  * @param method - The HTTP method (e.g., 'GET', 'POST', 'PUT', 'PATCH', 'DELETE').
2800
2831
  * @param fullUrl - The full URL of the endpoint.
@@ -2883,7 +2914,8 @@ var ResturaEngine = class {
2883
2914
  route.path = route.path.startsWith("/") ? route.path : `/${route.path}`;
2884
2915
  route.path = route.path.endsWith("/") ? route.path.slice(0, -1) : route.path;
2885
2916
  const fullUrl = `${baseUrl}${route.path}`;
2886
- if (route.roles.length === 0) this.publicEndpoints[route.method].push(fullUrl);
2917
+ if (route.roles.length === 0 && route.scopes.length === 0)
2918
+ this.publicEndpoints[route.method].push(fullUrl);
2887
2919
  this.resturaRouter[route.method.toLowerCase()](
2888
2920
  route.path,
2889
2921
  // <-- Notice we only use path here since the baseUrl is already added to the router.
@@ -2933,10 +2965,10 @@ var ResturaEngine = class {
2933
2965
  );
2934
2966
  this.generateResturaGlobalTypes(import_path5.default.join(this.resturaConfig.generatedTypesPath, "restura.d.ts"));
2935
2967
  }
2936
- async getSchema(req, res) {
2968
+ async getSchema(_req, res) {
2937
2969
  res.send({ data: this.schema });
2938
2970
  }
2939
- async getSchemaAndTypes(req, res) {
2971
+ async getSchemaAndTypes(_req, res) {
2940
2972
  try {
2941
2973
  const schema = await this.getLatestFileSystemSchema();
2942
2974
  const apiText = await apiGenerator(schema);
@@ -2947,8 +2979,7 @@ var ResturaEngine = class {
2947
2979
  }
2948
2980
  }
2949
2981
  async getMulterFilesIfAny(req, res, routeData) {
2950
- var _a2;
2951
- if (!((_a2 = req.header("content-type")) == null ? void 0 : _a2.includes("multipart/form-data"))) return;
2982
+ if (!req.header("content-type")?.includes("multipart/form-data")) return;
2952
2983
  if (!this.isCustomRoute(routeData)) return;
2953
2984
  if (!routeData.fileUploadType) {
2954
2985
  throw new RsError("BAD_REQUEST", "File upload type not defined for route");
@@ -3011,16 +3042,17 @@ var ResturaEngine = class {
3011
3042
  await customFunction(req, res, routeData);
3012
3043
  }
3013
3044
  async storeFileSystemSchema() {
3014
- const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema), __spreadValues({
3015
- parser: "json"
3016
- }, {
3017
- trailingComma: "none",
3018
- tabWidth: 4,
3019
- useTabs: true,
3020
- endOfLine: "lf",
3021
- printWidth: 120,
3022
- singleQuote: true
3023
- }));
3045
+ const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema), {
3046
+ parser: "json",
3047
+ ...{
3048
+ trailingComma: "none",
3049
+ tabWidth: 4,
3050
+ useTabs: true,
3051
+ endOfLine: "lf",
3052
+ printWidth: 120,
3053
+ singleQuote: true
3054
+ }
3055
+ });
3024
3056
  import_fs4.default.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
3025
3057
  }
3026
3058
  resetPublicEndpoints() {
@@ -3033,9 +3065,13 @@ var ResturaEngine = class {
3033
3065
  };
3034
3066
  }
3035
3067
  validateAuthorization(req, routeData) {
3036
- const role = req.requesterDetails.role;
3037
- if (routeData.roles.length === 0 || !role) return;
3038
- if (!routeData.roles.includes(role)) throw new RsError("FORBIDDEN", "Not authorized to access this endpoint");
3068
+ const requesterRole = req.requesterDetails.role;
3069
+ const requesterScopes = req.requesterDetails.scopes;
3070
+ if (routeData.roles.length === 0 && routeData.scopes.length === 0) return;
3071
+ if (requesterRole && routeData.roles.length > 0 && !routeData.roles.includes(requesterRole))
3072
+ throw new RsError("FORBIDDEN", "Not authorized to access this endpoint");
3073
+ if (requesterScopes.length > 0 && routeData.scopes.length > 0 && !routeData.scopes.some((scope) => requesterScopes.includes(scope)))
3074
+ throw new RsError("FORBIDDEN", "Not authorized to access this endpoint");
3039
3075
  }
3040
3076
  getRouteData(method, baseUrl, path5) {
3041
3077
  const endpoint = this.schema.endpoints.find((item) => {
@@ -3089,6 +3125,9 @@ var PsqlTransaction = class extends PsqlConnection {
3089
3125
  this.connectPromise = this.client.connect();
3090
3126
  this.beginTransactionPromise = this.beginTransaction();
3091
3127
  }
3128
+ client;
3129
+ beginTransactionPromise;
3130
+ connectPromise;
3092
3131
  async close() {
3093
3132
  if (this.client) {
3094
3133
  await this.client.end();