@restura/core 0.1.0-alpha.24 → 0.1.0-alpha.26

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
@@ -90,23 +90,11 @@ var import_internal = require("@restura/internal");
90
90
  var import_winston = __toESM(require("winston"));
91
91
  var import_logform = require("logform");
92
92
 
93
- // src/config.schema.ts
93
+ // src/logger/loggerConfigSchema.ts
94
94
  var import_zod = require("zod");
95
95
  var loggerConfigSchema = import_zod.z.object({
96
96
  level: import_zod.z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
97
97
  });
98
- var _a;
99
- var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
100
- var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
101
- var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
102
- var resturaConfigSchema = import_zod.z.object({
103
- authToken: import_zod.z.string().min(1, "Missing Restura Auth Token"),
104
- sendErrorStackTrace: import_zod.z.boolean().default(false),
105
- schemaFilePath: import_zod.z.string().default(process.cwd() + "/restura.schema.json"),
106
- customApiFolderPath: import_zod.z.string().default(process.cwd() + customApiFolderPath),
107
- generatedTypesPath: import_zod.z.string().default(process.cwd() + "/src/@types"),
108
- fileTempCachePath: import_zod.z.string().optional()
109
- });
110
98
 
111
99
  // src/logger/logger.ts
112
100
  var loggerConfig = import_internal.config.validate("logger", loggerConfigSchema);
@@ -143,7 +131,194 @@ var logger = import_winston.default.createLogger({
143
131
  ]
144
132
  });
145
133
 
146
- // src/restura/errors.ts
134
+ // src/restura/eventManager.ts
135
+ var import_bluebird = __toESM(require("bluebird"));
136
+ var EventManager = class {
137
+ constructor() {
138
+ this.actionHandlers = {
139
+ DATABASE_ROW_DELETE: [],
140
+ DATABASE_ROW_INSERT: [],
141
+ DATABASE_COLUMN_UPDATE: []
142
+ };
143
+ }
144
+ addRowInsertHandler(onInsert, filter) {
145
+ this.actionHandlers.DATABASE_ROW_INSERT.push({
146
+ callback: onInsert,
147
+ filter
148
+ });
149
+ }
150
+ addColumnChangeHandler(onUpdate, filter) {
151
+ this.actionHandlers.DATABASE_COLUMN_UPDATE.push({
152
+ callback: onUpdate,
153
+ filter
154
+ });
155
+ }
156
+ addRowDeleteHandler(onDelete, filter) {
157
+ this.actionHandlers.DATABASE_ROW_DELETE.push({
158
+ callback: onDelete,
159
+ filter
160
+ });
161
+ }
162
+ async fireActionFromDbTrigger(sqlMutationData, result) {
163
+ if (sqlMutationData.mutationType === "INSERT") {
164
+ await this.fireInsertActions(sqlMutationData, result);
165
+ } else if (sqlMutationData.mutationType === "UPDATE") {
166
+ await this.fireUpdateActions(sqlMutationData, result);
167
+ } else if (sqlMutationData.mutationType === "DELETE") {
168
+ await this.fireDeleteActions(sqlMutationData, result);
169
+ }
170
+ }
171
+ async fireInsertActions(data, triggerResult) {
172
+ await import_bluebird.default.map(
173
+ this.actionHandlers.DATABASE_ROW_INSERT,
174
+ ({ callback, filter }) => {
175
+ if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
176
+ const insertData = {
177
+ tableName: triggerResult.table,
178
+ insertId: triggerResult.record.id,
179
+ insertObject: triggerResult.record,
180
+ queryMetadata: data.queryMetadata
181
+ };
182
+ callback(insertData, data.queryMetadata);
183
+ },
184
+ { concurrency: 10 }
185
+ );
186
+ }
187
+ async fireDeleteActions(data, triggerResult) {
188
+ await import_bluebird.default.map(
189
+ this.actionHandlers.DATABASE_ROW_DELETE,
190
+ ({ callback, filter }) => {
191
+ if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
192
+ const deleteData = {
193
+ tableName: triggerResult.table,
194
+ deletedRow: triggerResult.previousRecord,
195
+ queryMetadata: data.queryMetadata
196
+ };
197
+ callback(deleteData, data.queryMetadata);
198
+ },
199
+ { concurrency: 10 }
200
+ );
201
+ }
202
+ async fireUpdateActions(data, triggerResult) {
203
+ await import_bluebird.default.map(
204
+ this.actionHandlers.DATABASE_COLUMN_UPDATE,
205
+ ({ callback, filter }) => {
206
+ if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
207
+ const columnChangeData = {
208
+ tableName: triggerResult.table,
209
+ rowId: triggerResult.record.id,
210
+ newData: triggerResult.record,
211
+ oldData: triggerResult.previousRecord,
212
+ queryMetadata: data.queryMetadata
213
+ };
214
+ callback(columnChangeData, data.queryMetadata);
215
+ },
216
+ { concurrency: 10 }
217
+ );
218
+ }
219
+ hasHandlersForEventType(eventType, filter, triggerResult) {
220
+ if (filter) {
221
+ switch (eventType) {
222
+ case "DATABASE_ROW_INSERT":
223
+ case "DATABASE_ROW_DELETE":
224
+ if (filter.tableName && filter.tableName !== triggerResult.table) return false;
225
+ break;
226
+ case "DATABASE_COLUMN_UPDATE":
227
+ const filterColumnChange = filter;
228
+ if (filterColumnChange.tableName !== filter.tableName) return false;
229
+ if (!filterColumnChange.columns.some((item) => {
230
+ const updatedColumns = Object.keys(
231
+ changedValues(triggerResult.record, triggerResult.previousRecord)
232
+ );
233
+ return updatedColumns.includes(item);
234
+ }))
235
+ return false;
236
+ break;
237
+ }
238
+ }
239
+ return true;
240
+ }
241
+ };
242
+ var eventManager = new EventManager();
243
+ var eventManager_default = eventManager;
244
+ function changedValues(record, previousRecord) {
245
+ const changed = {};
246
+ for (const i in previousRecord) {
247
+ if (previousRecord[i] !== record[i]) {
248
+ if (typeof previousRecord[i] === "object" && typeof record[i] === "object") {
249
+ const nestedChanged = changedValues(record[i], previousRecord[i]);
250
+ if (Object.keys(nestedChanged).length > 0) {
251
+ changed[i] = record[i];
252
+ }
253
+ } else {
254
+ changed[i] = record[i];
255
+ }
256
+ }
257
+ }
258
+ return changed;
259
+ }
260
+
261
+ // src/restura/restura.ts
262
+ var import_core_utils7 = require("@redskytech/core-utils");
263
+ var import_internal4 = require("@restura/internal");
264
+
265
+ // ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
266
+ function _typeof(obj) {
267
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
268
+ _typeof = function _typeof2(obj2) {
269
+ return typeof obj2;
270
+ };
271
+ } else {
272
+ _typeof = function _typeof2(obj2) {
273
+ return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
274
+ };
275
+ }
276
+ return _typeof(obj);
277
+ }
278
+ function boundMethod(target, key, descriptor) {
279
+ var fn = descriptor.value;
280
+ if (typeof fn !== "function") {
281
+ throw new TypeError("@boundMethod decorator can only be applied to methods not: ".concat(_typeof(fn)));
282
+ }
283
+ var definingProperty = false;
284
+ return {
285
+ configurable: true,
286
+ get: function get() {
287
+ if (definingProperty || this === target.prototype || this.hasOwnProperty(key) || typeof fn !== "function") {
288
+ return fn;
289
+ }
290
+ var boundFn = fn.bind(this);
291
+ definingProperty = true;
292
+ Object.defineProperty(this, key, {
293
+ configurable: true,
294
+ get: function get2() {
295
+ return boundFn;
296
+ },
297
+ set: function set(value) {
298
+ fn = value;
299
+ delete this[key];
300
+ }
301
+ });
302
+ definingProperty = false;
303
+ return boundFn;
304
+ },
305
+ set: function set(value) {
306
+ fn = value;
307
+ }
308
+ };
309
+ }
310
+
311
+ // src/restura/restura.ts
312
+ var import_body_parser = __toESM(require("body-parser"));
313
+ var import_compression = __toESM(require("compression"));
314
+ var import_cookie_parser = __toESM(require("cookie-parser"));
315
+ var express = __toESM(require("express"));
316
+ var import_fs4 = __toESM(require("fs"));
317
+ var import_path5 = __toESM(require("path"));
318
+ var import_pg3 = __toESM(require("pg"));
319
+ var prettier3 = __toESM(require("prettier"));
320
+
321
+ // src/restura/RsError.ts
147
322
  var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
148
323
  HtmlStatusCodes2[HtmlStatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
149
324
  HtmlStatusCodes2[HtmlStatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
@@ -167,7 +342,6 @@ var RsError = class _RsError {
167
342
  static htmlStatus(code) {
168
343
  return htmlStatusMap[code];
169
344
  }
170
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
345
  static isRsError(error) {
172
346
  return error instanceof _RsError;
173
347
  }
@@ -209,66 +383,158 @@ var htmlStatusMap = {
209
383
  SCHEMA_ERROR: 500 /* SERVER_ERROR */
210
384
  };
211
385
 
212
- // src/restura/restura.ts
213
- var import_core_utils7 = require("@redskytech/core-utils");
214
- var import_internal4 = require("@restura/internal");
215
-
216
- // ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
217
- function _typeof(obj) {
218
- if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
219
- _typeof = function _typeof2(obj2) {
220
- return typeof obj2;
221
- };
222
- } else {
223
- _typeof = function _typeof2(obj2) {
224
- return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
225
- };
226
- }
227
- return _typeof(obj);
228
- }
229
- function boundMethod(target, key, descriptor) {
230
- var fn = descriptor.value;
231
- if (typeof fn !== "function") {
232
- throw new TypeError("@boundMethod decorator can only be applied to methods not: ".concat(_typeof(fn)));
386
+ // src/restura/compareSchema.ts
387
+ var import_lodash = __toESM(require("lodash.clonedeep"));
388
+ var CompareSchema = class {
389
+ async diffSchema(newSchema, latestSchema, psqlEngine) {
390
+ const endPoints = this.diffEndPoints(newSchema.endpoints[0].routes, latestSchema.endpoints[0].routes);
391
+ const globalParams = this.diffStringArray(newSchema.globalParams, latestSchema.globalParams);
392
+ const roles = this.diffStringArray(newSchema.roles, latestSchema.roles);
393
+ let commands = "";
394
+ if (JSON.stringify(newSchema.database) !== JSON.stringify(latestSchema.database))
395
+ commands = await psqlEngine.diffDatabaseToSchema(newSchema);
396
+ const customTypes = newSchema.customTypes !== latestSchema.customTypes;
397
+ const schemaPreview = { endPoints, globalParams, roles, commands, customTypes };
398
+ return schemaPreview;
233
399
  }
234
- var definingProperty = false;
235
- return {
236
- configurable: true,
237
- get: function get() {
238
- if (definingProperty || this === target.prototype || this.hasOwnProperty(key) || typeof fn !== "function") {
239
- return fn;
400
+ diffStringArray(newArray, originalArray) {
401
+ const stringsDiff = [];
402
+ const originalClone = new Set(originalArray);
403
+ newArray.forEach((item) => {
404
+ const originalIndex = originalClone.has(item);
405
+ if (!originalIndex) {
406
+ stringsDiff.push({
407
+ name: item,
408
+ changeType: "NEW"
409
+ });
410
+ } else {
411
+ originalClone.delete(item);
240
412
  }
241
- var boundFn = fn.bind(this);
242
- definingProperty = true;
243
- Object.defineProperty(this, key, {
244
- configurable: true,
245
- get: function get2() {
246
- return boundFn;
247
- },
248
- set: function set(value) {
249
- fn = value;
250
- delete this[key];
413
+ });
414
+ originalClone.forEach((item) => {
415
+ stringsDiff.push({
416
+ name: item,
417
+ changeType: "DELETED"
418
+ });
419
+ });
420
+ return stringsDiff;
421
+ }
422
+ diffEndPoints(newEndPoints, originalEndpoints) {
423
+ const originalClone = (0, import_lodash.default)(originalEndpoints);
424
+ const diffObj = [];
425
+ newEndPoints.forEach((endPoint) => {
426
+ const { path: path5, method } = endPoint;
427
+ const endPointIndex = originalClone.findIndex((original) => {
428
+ return original.path === endPoint.path && original.method === endPoint.method;
429
+ });
430
+ if (endPointIndex === -1) {
431
+ diffObj.push({
432
+ name: `${method} ${path5}`,
433
+ changeType: "NEW"
434
+ });
435
+ } else {
436
+ const original = originalClone.findIndex((original2) => {
437
+ return this.compareEndPoints(endPoint, original2);
438
+ });
439
+ if (original === -1) {
440
+ diffObj.push({
441
+ name: `${method} ${path5}`,
442
+ changeType: "MODIFIED"
443
+ });
251
444
  }
445
+ originalClone.splice(endPointIndex, 1);
446
+ }
447
+ });
448
+ originalClone.forEach((original) => {
449
+ const { path: path5, method } = original;
450
+ diffObj.push({
451
+ name: `${method} ${path5}`,
452
+ changeType: "DELETED"
252
453
  });
253
- definingProperty = false;
254
- return boundFn;
255
- },
256
- set: function set(value) {
257
- fn = value;
454
+ });
455
+ return diffObj;
456
+ }
457
+ compareEndPoints(endPoint1, endPoint2) {
458
+ return JSON.stringify(endPoint1) === JSON.stringify(endPoint2);
459
+ }
460
+ };
461
+ __decorateClass([
462
+ boundMethod
463
+ ], CompareSchema.prototype, "diffSchema", 1);
464
+ __decorateClass([
465
+ boundMethod
466
+ ], CompareSchema.prototype, "diffStringArray", 1);
467
+ __decorateClass([
468
+ boundMethod
469
+ ], CompareSchema.prototype, "diffEndPoints", 1);
470
+ __decorateClass([
471
+ boundMethod
472
+ ], CompareSchema.prototype, "compareEndPoints", 1);
473
+ var compareSchema = new CompareSchema();
474
+ var compareSchema_default = compareSchema;
475
+
476
+ // src/restura/customApiFactory.ts
477
+ var import_internal2 = require("@restura/internal");
478
+ var import_bluebird2 = __toESM(require("bluebird"));
479
+ var import_fs = __toESM(require("fs"));
480
+ var import_path = __toESM(require("path"));
481
+ var CustomApiFactory = class {
482
+ constructor() {
483
+ this.customApis = {};
484
+ }
485
+ async loadApiFiles(baseFolderPath) {
486
+ const apiVersions = ["v1"];
487
+ for (const apiVersion of apiVersions) {
488
+ const apiVersionFolderPath = import_path.default.join(baseFolderPath, apiVersion);
489
+ const directoryExists = await import_internal2.fileUtils.existDir(apiVersionFolderPath);
490
+ if (!directoryExists) continue;
491
+ await this.addDirectory(apiVersionFolderPath, apiVersion);
258
492
  }
259
- };
260
- }
493
+ }
494
+ getCustomApi(customApiName) {
495
+ return this.customApis[customApiName];
496
+ }
497
+ async addDirectory(directoryPath, apiVersion) {
498
+ var _a2;
499
+ const entries = await import_fs.default.promises.readdir(directoryPath, {
500
+ withFileTypes: true
501
+ });
502
+ const isTsx2 = (_a2 = process.argv[1]) == null ? void 0 : _a2.endsWith(".ts");
503
+ const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
504
+ const extension = isTsx2 || isTsNode2 ? "ts" : "js";
505
+ const shouldEndWith = `.api.${apiVersion}.${extension}`;
506
+ await import_bluebird2.default.map(entries, async (entry) => {
507
+ if (entry.isFile()) {
508
+ if (entry.name.endsWith(shouldEndWith) === false) return;
509
+ try {
510
+ const importPath = `${import_path.default.join(directoryPath, entry.name)}`;
511
+ const ApiImport = await import(importPath);
512
+ const customApiClass = new ApiImport.default();
513
+ logger.info(`Registering custom API: ${ApiImport.default.name}`);
514
+ this.bindMethodsToInstance(customApiClass);
515
+ this.customApis[ApiImport.default.name] = customApiClass;
516
+ } catch (e) {
517
+ console.error(e);
518
+ }
519
+ }
520
+ });
521
+ }
522
+ bindMethodsToInstance(instance) {
523
+ const proto = Object.getPrototypeOf(instance);
524
+ Object.getOwnPropertyNames(proto).forEach((key) => {
525
+ const property = instance[key];
526
+ if (typeof property === "function" && key !== "constructor") {
527
+ instance[key] = property.bind(instance);
528
+ }
529
+ });
530
+ }
531
+ };
532
+ var customApiFactory = new CustomApiFactory();
533
+ var customApiFactory_default = customApiFactory;
261
534
 
262
- // src/restura/restura.ts
263
- var import_body_parser = __toESM(require("body-parser"));
264
- var import_compression = __toESM(require("compression"));
265
- var import_cookie_parser = __toESM(require("cookie-parser"));
266
- var import_crypto2 = require("crypto");
267
- var express = __toESM(require("express"));
268
- var import_fs4 = __toESM(require("fs"));
269
- var import_path5 = __toESM(require("path"));
270
- var import_pg3 = __toESM(require("pg"));
271
- var prettier3 = __toESM(require("prettier"));
535
+ // src/restura/generators/apiGenerator.ts
536
+ var import_core_utils = require("@redskytech/core-utils");
537
+ var import_prettier = __toESM(require("prettier"));
272
538
 
273
539
  // src/restura/sql/SqlUtils.ts
274
540
  var SqlUtils = class _SqlUtils {
@@ -296,7 +562,7 @@ var SqlUtils = class _SqlUtils {
296
562
  }
297
563
  };
298
564
 
299
- // src/restura/ResponseValidator.ts
565
+ // src/restura/validators/ResponseValidator.ts
300
566
  var ResponseValidator = class _ResponseValidator {
301
567
  constructor(schema) {
302
568
  this.database = schema.database;
@@ -445,9 +711,7 @@ var ResponseValidator = class _ResponseValidator {
445
711
  }
446
712
  };
447
713
 
448
- // src/restura/apiGenerator.ts
449
- var import_core_utils = require("@redskytech/core-utils");
450
- var import_prettier = __toESM(require("prettier"));
714
+ // src/restura/generators/apiGenerator.ts
451
715
  var ApiTree = class _ApiTree {
452
716
  constructor(namespace, database) {
453
717
  this.database = database;
@@ -594,108 +858,49 @@ var ApiTree = class _ApiTree {
594
858
  return {
595
859
  responseType: SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value),
596
860
  isNullable: column.roles.length > 0 || column.isNullable
597
- };
598
- }
599
- };
600
- function pathToNamespaces(path5) {
601
- return path5.split("/").map((e) => import_core_utils.StringUtils.toPascalCasing(e)).filter((e) => e);
602
- }
603
- function apiGenerator(schema, schemaHash) {
604
- let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
605
- const rootNamespace = ApiTree.createRootNode(schema.database);
606
- for (const endpoint of schema.endpoints) {
607
- const endpointNamespaces = pathToNamespaces(endpoint.baseUrl);
608
- rootNamespace.addData(endpointNamespaces, endpoint);
609
- for (const route of endpoint.routes) {
610
- const fullNamespace = [...endpointNamespaces, ...pathToNamespaces(route.path)];
611
- rootNamespace.addData(fullNamespace, route);
612
- }
613
- }
614
- apiString += rootNamespace.createApiModels();
615
- if (schema.customTypes.length > 0) {
616
- apiString += `
617
-
618
- declare namespace CustomTypes {
619
- ${schema.customTypes}
620
- }`;
621
- }
622
- return import_prettier.default.format(apiString, __spreadValues({
623
- parser: "typescript"
624
- }, {
625
- trailingComma: "none",
626
- tabWidth: 4,
627
- useTabs: true,
628
- endOfLine: "lf",
629
- printWidth: 120,
630
- singleQuote: true
631
- }));
632
- }
633
-
634
- // src/restura/customApiFactory.ts
635
- var import_fs = __toESM(require("fs"));
636
- var import_path = __toESM(require("path"));
637
- var import_bluebird = __toESM(require("bluebird"));
638
- var import_internal2 = require("@restura/internal");
639
- var CustomApiFactory = class {
640
- constructor() {
641
- this.customApis = {};
642
- }
643
- async loadApiFiles(baseFolderPath) {
644
- const apiVersions = ["v1"];
645
- for (const apiVersion of apiVersions) {
646
- const apiVersionFolderPath = import_path.default.join(baseFolderPath, apiVersion);
647
- const directoryExists = await import_internal2.fileUtils.existDir(apiVersionFolderPath);
648
- if (!directoryExists) continue;
649
- await this.addDirectory(apiVersionFolderPath, apiVersion);
650
- }
651
- }
652
- getCustomApi(customApiName) {
653
- return this.customApis[customApiName];
654
- }
655
- async addDirectory(directoryPath, apiVersion) {
656
- var _a2;
657
- const entries = await import_fs.default.promises.readdir(directoryPath, {
658
- withFileTypes: true
659
- });
660
- const isTsx2 = (_a2 = process.argv[1]) == null ? void 0 : _a2.endsWith(".ts");
661
- const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
662
- const extension = isTsx2 || isTsNode2 ? "ts" : "js";
663
- const shouldEndWith = `.api.${apiVersion}.${extension}`;
664
- await import_bluebird.default.map(entries, async (entry) => {
665
- if (entry.isFile()) {
666
- if (entry.name.endsWith(shouldEndWith) === false) return;
667
- try {
668
- const importPath = `${import_path.default.join(directoryPath, entry.name)}`;
669
- const ApiImport = await import(importPath);
670
- const customApiClass = new ApiImport.default();
671
- logger.info(`Registering custom API: ${ApiImport.default.name}`);
672
- this.bindMethodsToInstance(customApiClass);
673
- this.customApis[ApiImport.default.name] = customApiClass;
674
- } catch (e) {
675
- console.error(e);
676
- }
677
- }
678
- });
679
- }
680
- bindMethodsToInstance(instance) {
681
- const proto = Object.getPrototypeOf(instance);
682
- Object.getOwnPropertyNames(proto).forEach((key) => {
683
- const property = instance[key];
684
- if (typeof property === "function" && key !== "constructor") {
685
- instance[key] = property.bind(instance);
686
- }
687
- });
861
+ };
688
862
  }
689
863
  };
690
- var customApiFactory = new CustomApiFactory();
691
- var customApiFactory_default = customApiFactory;
864
+ function pathToNamespaces(path5) {
865
+ return path5.split("/").map((e) => import_core_utils.StringUtils.toPascalCasing(e)).filter((e) => e);
866
+ }
867
+ function apiGenerator(schema) {
868
+ let apiString = `/** Auto generated file. DO NOT MODIFY **/
869
+ `;
870
+ const rootNamespace = ApiTree.createRootNode(schema.database);
871
+ for (const endpoint of schema.endpoints) {
872
+ const endpointNamespaces = pathToNamespaces(endpoint.baseUrl);
873
+ rootNamespace.addData(endpointNamespaces, endpoint);
874
+ for (const route of endpoint.routes) {
875
+ const fullNamespace = [...endpointNamespaces, ...pathToNamespaces(route.path)];
876
+ rootNamespace.addData(fullNamespace, route);
877
+ }
878
+ }
879
+ apiString += rootNamespace.createApiModels();
880
+ if (schema.customTypes.length > 0) {
881
+ apiString += `
882
+
883
+ declare namespace CustomTypes {
884
+ ${schema.customTypes}
885
+ }`;
886
+ }
887
+ return import_prettier.default.format(apiString, __spreadValues({
888
+ parser: "typescript"
889
+ }, {
890
+ trailingComma: "none",
891
+ tabWidth: 4,
892
+ useTabs: true,
893
+ endOfLine: "lf",
894
+ printWidth: 120,
895
+ singleQuote: true
896
+ }));
897
+ }
692
898
 
693
- // src/restura/customTypeValidationGenerator.ts
899
+ // src/restura/generators/customTypeValidationGenerator.ts
694
900
  var import_fs2 = __toESM(require("fs"));
695
- var TJS = __toESM(require("typescript-json-schema"));
696
901
  var import_path2 = __toESM(require("path"));
697
902
  var import_tmp = __toESM(require("tmp"));
698
- var process2 = __toESM(require("process"));
903
+ var TJS = __toESM(require("typescript-json-schema"));
699
904
  function customTypeValidationGenerator(currentSchema) {
700
905
  const schemaObject = {};
701
906
  const customInterfaceNames = currentSchema.customTypes.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
@@ -705,13 +910,14 @@ function customTypeValidationGenerator(currentSchema) {
705
910
  const compilerOptions = {
706
911
  strictNullChecks: true,
707
912
  skipLibCheck: true
913
+ // Needed if we are processing ES modules
708
914
  };
709
915
  const program = TJS.getProgramFromFiles(
710
916
  [
711
917
  (0, import_path2.resolve)(temporaryFile.name),
712
- // find a way to remove
713
- import_path2.default.join(process2.cwd(), "src/@types/models.d.ts"),
714
- import_path2.default.join(process2.cwd(), "src/@types/api.d.ts")
918
+ import_path2.default.join(restura.resturaConfig.generatedTypesPath, "restura.d.ts"),
919
+ import_path2.default.join(restura.resturaConfig.generatedTypesPath, "models.d.ts"),
920
+ import_path2.default.join(restura.resturaConfig.generatedTypesPath, "api.d.ts")
715
921
  ],
716
922
  compilerOptions
717
923
  );
@@ -725,6 +931,61 @@ function customTypeValidationGenerator(currentSchema) {
725
931
  return schemaObject;
726
932
  }
727
933
 
934
+ // src/restura/generators/modelGenerator.ts
935
+ var import_core_utils2 = require("@redskytech/core-utils");
936
+ var import_prettier2 = __toESM(require("prettier"));
937
+ function modelGenerator(schema) {
938
+ let modelString = `/** Auto generated file. DO NOT MODIFY **/
939
+
940
+ `;
941
+ modelString += `declare namespace Model {
942
+ `;
943
+ for (const table of schema.database) {
944
+ modelString += convertTable(table);
945
+ }
946
+ modelString += `}`;
947
+ return import_prettier2.default.format(modelString, __spreadValues({
948
+ parser: "typescript"
949
+ }, {
950
+ trailingComma: "none",
951
+ tabWidth: 4,
952
+ useTabs: true,
953
+ endOfLine: "lf",
954
+ printWidth: 120,
955
+ singleQuote: true
956
+ }));
957
+ }
958
+ function convertTable(table) {
959
+ let modelString = ` export interface ${import_core_utils2.StringUtils.capitalizeFirst(table.name)} {
960
+ `;
961
+ for (const column of table.columns) {
962
+ modelString += ` ${column.name}${column.isNullable ? "?" : ""}: ${SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value)};
963
+ `;
964
+ }
965
+ modelString += ` }
966
+ `;
967
+ return modelString;
968
+ }
969
+
970
+ // src/restura/generators/resturaGlobalTypesGenerator.ts
971
+ function resturaGlobalTypesGenerator() {
972
+ return `/** Auto generated file. DO NOT MODIFY **/
973
+ /** This file contains types that may be used in the CustomTypes of Restura **/
974
+ /** For example export interface MyPagedQuery extends Restura.PageQuery { } **/
975
+
976
+ declare namespace Restura {
977
+ export type StandardOrderTypes = 'ASC' | 'DESC' | 'RAND' | 'NONE';
978
+ export interface PageQuery {
979
+ page?: number;
980
+ perPage?: number;
981
+ sortBy?: string;
982
+ sortOrder?: StandardOrderTypes;
983
+ filter?: string;
984
+ }
985
+ }
986
+ `;
987
+ }
988
+
728
989
  // src/restura/middleware/addApiResponseFunctions.ts
729
990
  function addApiResponseFunctions(req, res, next) {
730
991
  res.sendData = function(data, statusCode = 200) {
@@ -763,10 +1024,35 @@ function authenticateUser(applicationAuthenticateHandler) {
763
1024
  };
764
1025
  }
765
1026
 
766
- // src/restura/restura.schema.ts
1027
+ // src/restura/middleware/getMulterUpload.ts
1028
+ var import_multer = __toESM(require("multer"));
1029
+ var os = __toESM(require("os"));
1030
+ var import_path3 = require("path");
1031
+ var OneHundredMB = 100 * 1024 * 1024;
1032
+ var commonUpload = null;
1033
+ var getMulterUpload = (directory) => {
1034
+ if (commonUpload) return commonUpload;
1035
+ const storage = import_multer.default.diskStorage({
1036
+ destination: directory || os.tmpdir(),
1037
+ filename: function(request, file, cb) {
1038
+ const extension = (0, import_path3.extname)(file.originalname);
1039
+ const uniqueName = Date.now() + "-" + Math.round(Math.random() * 1e3);
1040
+ cb(null, `${uniqueName}${extension}`);
1041
+ }
1042
+ });
1043
+ commonUpload = (0, import_multer.default)({
1044
+ storage,
1045
+ limits: {
1046
+ fileSize: OneHundredMB
1047
+ }
1048
+ });
1049
+ return commonUpload;
1050
+ };
1051
+
1052
+ // src/restura/schemas/resturaSchema.ts
767
1053
  var import_zod3 = require("zod");
768
1054
 
769
- // src/restura/types/validation.types.ts
1055
+ // src/restura/schemas/validatorDataSchema.ts
770
1056
  var import_zod2 = require("zod");
771
1057
  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())]);
772
1058
  var validatorDataSchema = import_zod2.z.object({
@@ -774,7 +1060,7 @@ var validatorDataSchema = import_zod2.z.object({
774
1060
  value: validatorDataSchemeValue
775
1061
  }).strict();
776
1062
 
777
- // src/restura/restura.schema.ts
1063
+ // src/restura/schemas/resturaSchema.ts
778
1064
  var orderBySchema = import_zod3.z.object({
779
1065
  columnName: import_zod3.z.string(),
780
1066
  order: import_zod3.z.enum(["ASC", "DESC"]),
@@ -1021,7 +1307,7 @@ var endpointDataSchema = import_zod3.z.object({
1021
1307
  baseUrl: import_zod3.z.string(),
1022
1308
  routes: import_zod3.z.array(import_zod3.z.union([standardRouteSchema, customRouteSchema]))
1023
1309
  }).strict();
1024
- var resturaZodSchema = import_zod3.z.object({
1310
+ var resturaSchema = import_zod3.z.object({
1025
1311
  database: import_zod3.z.array(tableDataSchema),
1026
1312
  endpoints: import_zod3.z.array(endpointDataSchema),
1027
1313
  globalParams: import_zod3.z.array(import_zod3.z.string()),
@@ -1030,7 +1316,7 @@ var resturaZodSchema = import_zod3.z.object({
1030
1316
  }).strict();
1031
1317
  async function isSchemaValid(schemaToCheck) {
1032
1318
  try {
1033
- resturaZodSchema.parse(schemaToCheck);
1319
+ resturaSchema.parse(schemaToCheck);
1034
1320
  return true;
1035
1321
  } catch (error) {
1036
1322
  logger.error(error);
@@ -1038,8 +1324,8 @@ async function isSchemaValid(schemaToCheck) {
1038
1324
  }
1039
1325
  }
1040
1326
 
1041
- // src/restura/validateRequestParams.ts
1042
- var import_core_utils2 = require("@redskytech/core-utils");
1327
+ // src/restura/validators/requestValidator.ts
1328
+ var import_core_utils3 = require("@redskytech/core-utils");
1043
1329
  var import_jsonschema = __toESM(require("jsonschema"));
1044
1330
  var import_zod4 = require("zod");
1045
1331
 
@@ -1055,8 +1341,8 @@ function addQuotesToStrings(variable) {
1055
1341
  }
1056
1342
  }
1057
1343
 
1058
- // src/restura/validateRequestParams.ts
1059
- function validateRequestParams(req, routeData, validationSchema) {
1344
+ // src/restura/validators/requestValidator.ts
1345
+ function requestValidator(req, routeData, validationSchema) {
1060
1346
  const requestData = getRequestData(req);
1061
1347
  req.data = requestData;
1062
1348
  if (routeData.request === void 0) {
@@ -1172,238 +1458,92 @@ function performMinCheck(requestValue, validator, requestParamName) {
1172
1458
  function performMaxCheck(requestValue, validator, requestParamName) {
1173
1459
  expectOnlyNumbers(requestValue, validator, requestParamName);
1174
1460
  if (requestValue > validator.value)
1175
- throw new RsError(
1176
- "BAD_REQUEST",
1177
- `Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
1178
- );
1179
- }
1180
- function performOneOfCheck(requestValue, validator, requestParamName) {
1181
- if (!import_core_utils2.ObjectUtils.isArrayWithData(validator.value))
1182
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
1183
- if (typeof requestValue === "object")
1184
- throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
1185
- if (!validator.value.includes(requestValue))
1186
- throw new RsError(
1187
- "BAD_REQUEST",
1188
- `Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
1189
- );
1190
- }
1191
- function isValueNumber(value) {
1192
- return !isNaN(Number(value));
1193
- }
1194
- function getRequestData(req) {
1195
- let body = "";
1196
- if (req.method === "GET" || req.method === "DELETE") {
1197
- body = "query";
1198
- } else {
1199
- body = "body";
1200
- }
1201
- const bodyData = req[body];
1202
- if (bodyData && body === "query") {
1203
- for (const attr in bodyData) {
1204
- if (bodyData[attr] instanceof Array) {
1205
- const attrList = [];
1206
- for (const value of bodyData[attr]) {
1207
- if (isNaN(Number(value))) continue;
1208
- attrList.push(Number(value));
1209
- }
1210
- if (import_core_utils2.ObjectUtils.isArrayWithData(attrList)) {
1211
- bodyData[attr] = attrList;
1212
- }
1213
- } else {
1214
- bodyData[attr] = import_core_utils2.ObjectUtils.safeParse(bodyData[attr]);
1215
- if (isNaN(Number(bodyData[attr]))) continue;
1216
- bodyData[attr] = Number(bodyData[attr]);
1217
- }
1218
- }
1219
- }
1220
- return bodyData;
1221
- }
1222
-
1223
- // src/restura/middleware/schemaValidation.ts
1224
- async function schemaValidation(req, res, next) {
1225
- req.data = getRequestData(req);
1226
- try {
1227
- resturaZodSchema.parse(req.data);
1228
- next();
1229
- } catch (error) {
1230
- logger.error(error);
1231
- res.sendError("BAD_REQUEST", error, 400 /* BAD_REQUEST */);
1232
- }
1233
- }
1234
-
1235
- // src/restura/modelGenerator.ts
1236
- var import_core_utils3 = require("@redskytech/core-utils");
1237
- var import_prettier2 = __toESM(require("prettier"));
1238
- function modelGenerator(schema, schemaHash) {
1239
- let modelString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/
1240
- `;
1241
- modelString += `declare namespace Model {
1242
- `;
1243
- for (const table of schema.database) {
1244
- modelString += convertTable(table);
1245
- }
1246
- modelString += `}`;
1247
- return import_prettier2.default.format(modelString, __spreadValues({
1248
- parser: "typescript"
1249
- }, {
1250
- trailingComma: "none",
1251
- tabWidth: 4,
1252
- useTabs: true,
1253
- endOfLine: "lf",
1254
- printWidth: 120,
1255
- singleQuote: true
1256
- }));
1257
- }
1258
- function convertTable(table) {
1259
- let modelString = ` export interface ${import_core_utils3.StringUtils.capitalizeFirst(table.name)} {
1260
- `;
1261
- for (const column of table.columns) {
1262
- modelString += ` ${column.name}${column.isNullable ? "?" : ""}: ${SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value)};
1263
- `;
1264
- }
1265
- modelString += ` }
1266
- `;
1267
- return modelString;
1268
- }
1269
-
1270
- // src/restura/sql/PsqlEngine.ts
1271
- var import_core_utils5 = require("@redskytech/core-utils");
1272
- var import_pg_diff_sync = __toESM(require("@wmfs/pg-diff-sync"));
1273
- var import_pg_info = __toESM(require("@wmfs/pg-info"));
1274
- var import_pg2 = __toESM(require("pg"));
1275
-
1276
- // src/restura/eventManager.ts
1277
- var import_bluebird2 = __toESM(require("bluebird"));
1278
- var EventManager = class {
1279
- constructor() {
1280
- this.actionHandlers = {
1281
- DATABASE_ROW_DELETE: [],
1282
- DATABASE_ROW_INSERT: [],
1283
- DATABASE_COLUMN_UPDATE: []
1284
- };
1285
- }
1286
- addRowInsertHandler(onInsert, filter) {
1287
- this.actionHandlers.DATABASE_ROW_INSERT.push({
1288
- callback: onInsert,
1289
- filter
1290
- });
1291
- }
1292
- addColumnChangeHandler(onUpdate, filter) {
1293
- this.actionHandlers.DATABASE_COLUMN_UPDATE.push({
1294
- callback: onUpdate,
1295
- filter
1296
- });
1297
- }
1298
- addRowDeleteHandler(onDelete, filter) {
1299
- this.actionHandlers.DATABASE_ROW_DELETE.push({
1300
- callback: onDelete,
1301
- filter
1302
- });
1303
- }
1304
- async fireActionFromDbTrigger(sqlMutationData, result) {
1305
- if (sqlMutationData.mutationType === "INSERT") {
1306
- await this.fireInsertActions(sqlMutationData, result);
1307
- } else if (sqlMutationData.mutationType === "UPDATE") {
1308
- await this.fireUpdateActions(sqlMutationData, result);
1309
- } else if (sqlMutationData.mutationType === "DELETE") {
1310
- await this.fireDeleteActions(sqlMutationData, result);
1311
- }
1312
- }
1313
- async fireInsertActions(data, triggerResult) {
1314
- await import_bluebird2.default.map(
1315
- this.actionHandlers.DATABASE_ROW_INSERT,
1316
- ({ callback, filter }) => {
1317
- if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
1318
- const insertData = {
1319
- tableName: triggerResult.table,
1320
- insertId: triggerResult.record.id,
1321
- insertObject: triggerResult.record,
1322
- queryMetadata: data.queryMetadata
1323
- };
1324
- callback(insertData, data.queryMetadata);
1325
- },
1326
- { concurrency: 10 }
1327
- );
1328
- }
1329
- async fireDeleteActions(data, triggerResult) {
1330
- await import_bluebird2.default.map(
1331
- this.actionHandlers.DATABASE_ROW_DELETE,
1332
- ({ callback, filter }) => {
1333
- if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
1334
- const deleteData = {
1335
- tableName: triggerResult.table,
1336
- deletedRow: triggerResult.previousRecord,
1337
- queryMetadata: data.queryMetadata
1338
- };
1339
- callback(deleteData, data.queryMetadata);
1340
- },
1341
- { concurrency: 10 }
1461
+ throw new RsError(
1462
+ "BAD_REQUEST",
1463
+ `Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
1342
1464
  );
1343
- }
1344
- async fireUpdateActions(data, triggerResult) {
1345
- await import_bluebird2.default.map(
1346
- this.actionHandlers.DATABASE_COLUMN_UPDATE,
1347
- ({ callback, filter }) => {
1348
- if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
1349
- const columnChangeData = {
1350
- tableName: triggerResult.table,
1351
- rowId: triggerResult.record.id,
1352
- newData: triggerResult.record,
1353
- oldData: triggerResult.previousRecord,
1354
- queryMetadata: data.queryMetadata
1355
- };
1356
- callback(columnChangeData, data.queryMetadata);
1357
- },
1358
- { concurrency: 10 }
1465
+ }
1466
+ function performOneOfCheck(requestValue, validator, requestParamName) {
1467
+ if (!import_core_utils3.ObjectUtils.isArrayWithData(validator.value))
1468
+ throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
1469
+ if (typeof requestValue === "object")
1470
+ throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
1471
+ if (!validator.value.includes(requestValue))
1472
+ throw new RsError(
1473
+ "BAD_REQUEST",
1474
+ `Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
1359
1475
  );
1476
+ }
1477
+ function isValueNumber(value) {
1478
+ return !isNaN(Number(value));
1479
+ }
1480
+ function getRequestData(req) {
1481
+ let body = "";
1482
+ if (req.method === "GET" || req.method === "DELETE") {
1483
+ body = "query";
1484
+ } else {
1485
+ body = "body";
1360
1486
  }
1361
- hasHandlersForEventType(eventType, filter, triggerResult) {
1362
- if (filter) {
1363
- switch (eventType) {
1364
- case "DATABASE_ROW_INSERT":
1365
- case "DATABASE_ROW_DELETE":
1366
- if (filter.tableName && filter.tableName !== triggerResult.table) return false;
1367
- break;
1368
- case "DATABASE_COLUMN_UPDATE":
1369
- const filterColumnChange = filter;
1370
- if (filterColumnChange.tableName !== filter.tableName) return false;
1371
- if (!filterColumnChange.columns.some((item) => {
1372
- const updatedColumns = Object.keys(
1373
- changedValues(triggerResult.record, triggerResult.previousRecord)
1374
- );
1375
- return updatedColumns.includes(item);
1376
- }))
1377
- return false;
1378
- break;
1379
- }
1380
- }
1381
- return true;
1382
- }
1383
- };
1384
- var eventManager = new EventManager();
1385
- var eventManager_default = eventManager;
1386
- function changedValues(record, previousRecord) {
1387
- const changed = {};
1388
- for (const i in previousRecord) {
1389
- if (previousRecord[i] !== record[i]) {
1390
- if (typeof previousRecord[i] === "object" && typeof record[i] === "object") {
1391
- const nestedChanged = changedValues(record[i], previousRecord[i]);
1392
- if (Object.keys(nestedChanged).length > 0) {
1393
- changed[i] = record[i];
1487
+ const bodyData = req[body];
1488
+ if (bodyData && body === "query") {
1489
+ for (const attr in bodyData) {
1490
+ if (bodyData[attr] instanceof Array) {
1491
+ const attrList = [];
1492
+ for (const value of bodyData[attr]) {
1493
+ if (isNaN(Number(value))) continue;
1494
+ attrList.push(Number(value));
1495
+ }
1496
+ if (import_core_utils3.ObjectUtils.isArrayWithData(attrList)) {
1497
+ bodyData[attr] = attrList;
1394
1498
  }
1395
1499
  } else {
1396
- changed[i] = record[i];
1500
+ bodyData[attr] = import_core_utils3.ObjectUtils.safeParse(bodyData[attr]);
1501
+ if (isNaN(Number(bodyData[attr]))) continue;
1502
+ bodyData[attr] = Number(bodyData[attr]);
1397
1503
  }
1398
1504
  }
1399
1505
  }
1400
- return changed;
1506
+ return bodyData;
1507
+ }
1508
+
1509
+ // src/restura/middleware/schemaValidation.ts
1510
+ async function schemaValidation(req, res, next) {
1511
+ req.data = getRequestData(req);
1512
+ try {
1513
+ resturaSchema.parse(req.data);
1514
+ next();
1515
+ } catch (error) {
1516
+ logger.error(error);
1517
+ res.sendError("BAD_REQUEST", error, 400 /* BAD_REQUEST */);
1518
+ }
1401
1519
  }
1402
1520
 
1521
+ // src/restura/schemas/resturaConfigSchema.ts
1522
+ var import_zod5 = require("zod");
1523
+ var _a;
1524
+ var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
1525
+ var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
1526
+ var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
1527
+ var resturaConfigSchema = import_zod5.z.object({
1528
+ authToken: import_zod5.z.string().min(1, "Missing Restura Auth Token"),
1529
+ sendErrorStackTrace: import_zod5.z.boolean().default(false),
1530
+ schemaFilePath: import_zod5.z.string().default(process.cwd() + "/restura.schema.json"),
1531
+ customApiFolderPath: import_zod5.z.string().default(process.cwd() + customApiFolderPath),
1532
+ generatedTypesPath: import_zod5.z.string().default(process.cwd() + "/src/@types"),
1533
+ fileTempCachePath: import_zod5.z.string().optional()
1534
+ });
1535
+
1536
+ // src/restura/sql/PsqlEngine.ts
1537
+ var import_core_utils5 = require("@redskytech/core-utils");
1538
+ var import_pg_diff_sync = __toESM(require("@wmfs/pg-diff-sync"));
1539
+ var import_pg_info = __toESM(require("@wmfs/pg-info"));
1540
+ var import_pg2 = __toESM(require("pg"));
1541
+
1403
1542
  // src/restura/sql/PsqlPool.ts
1404
1543
  var import_pg = __toESM(require("pg"));
1405
1544
 
1406
1545
  // src/restura/sql/PsqlConnection.ts
1546
+ var import_crypto = __toESM(require("crypto"));
1407
1547
  var import_pg_format2 = __toESM(require("pg-format"));
1408
1548
 
1409
1549
  // src/restura/sql/PsqlUtils.ts
@@ -1414,7 +1554,22 @@ function escapeColumnName(columnName) {
1414
1554
  }
1415
1555
  function questionMarksToOrderedParams(query) {
1416
1556
  let count = 1;
1417
- return query.replace(/'\?'|\?/g, () => `$${count++}`);
1557
+ let inSingleQuote = false;
1558
+ let inDoubleQuote = false;
1559
+ return query.replace(/('|"|\?)/g, (char) => {
1560
+ if (char === "'") {
1561
+ inSingleQuote = !inSingleQuote && !inDoubleQuote;
1562
+ return char;
1563
+ }
1564
+ if (char === '"') {
1565
+ inDoubleQuote = !inDoubleQuote && !inSingleQuote;
1566
+ return char;
1567
+ }
1568
+ if (char === "?" && !inSingleQuote && !inDoubleQuote) {
1569
+ return `$${count++}`;
1570
+ }
1571
+ return char;
1572
+ });
1418
1573
  }
1419
1574
  function insertObjectQuery(table, obj) {
1420
1575
  const keys = Object.keys(obj);
@@ -1456,7 +1611,6 @@ function SQL(strings, ...values) {
1456
1611
  }
1457
1612
 
1458
1613
  // src/restura/sql/PsqlConnection.ts
1459
- var import_crypto = __toESM(require("crypto"));
1460
1614
  var PsqlConnection = class {
1461
1615
  constructor() {
1462
1616
  this.instanceId = import_crypto.default.randomUUID();
@@ -2308,121 +2462,6 @@ function schemaToPsqlType(column) {
2308
2462
  return column.type;
2309
2463
  }
2310
2464
 
2311
- // src/restura/compareSchema.ts
2312
- var import_lodash = __toESM(require("lodash.clonedeep"));
2313
- var CompareSchema = class {
2314
- async diffSchema(newSchema, latestSchema, psqlEngine) {
2315
- const endPoints = this.diffEndPoints(newSchema.endpoints[0].routes, latestSchema.endpoints[0].routes);
2316
- const globalParams = this.diffStringArray(newSchema.globalParams, latestSchema.globalParams);
2317
- const roles = this.diffStringArray(newSchema.roles, latestSchema.roles);
2318
- let commands = "";
2319
- if (JSON.stringify(newSchema.database) !== JSON.stringify(latestSchema.database))
2320
- commands = await psqlEngine.diffDatabaseToSchema(newSchema);
2321
- const customTypes = newSchema.customTypes !== latestSchema.customTypes;
2322
- const schemaPreview = { endPoints, globalParams, roles, commands, customTypes };
2323
- return schemaPreview;
2324
- }
2325
- diffStringArray(newArray, originalArray) {
2326
- const stringsDiff = [];
2327
- const originalClone = new Set(originalArray);
2328
- newArray.forEach((item) => {
2329
- const originalIndex = originalClone.has(item);
2330
- if (!originalIndex) {
2331
- stringsDiff.push({
2332
- name: item,
2333
- changeType: "NEW"
2334
- });
2335
- } else {
2336
- originalClone.delete(item);
2337
- }
2338
- });
2339
- originalClone.forEach((item) => {
2340
- stringsDiff.push({
2341
- name: item,
2342
- changeType: "DELETED"
2343
- });
2344
- });
2345
- return stringsDiff;
2346
- }
2347
- diffEndPoints(newEndPoints, originalEndpoints) {
2348
- const originalClone = (0, import_lodash.default)(originalEndpoints);
2349
- const diffObj = [];
2350
- newEndPoints.forEach((endPoint) => {
2351
- const { path: path5, method } = endPoint;
2352
- const endPointIndex = originalClone.findIndex((original) => {
2353
- return original.path === endPoint.path && original.method === endPoint.method;
2354
- });
2355
- if (endPointIndex === -1) {
2356
- diffObj.push({
2357
- name: `${method} ${path5}`,
2358
- changeType: "NEW"
2359
- });
2360
- } else {
2361
- const original = originalClone.findIndex((original2) => {
2362
- return this.compareEndPoints(endPoint, original2);
2363
- });
2364
- if (original === -1) {
2365
- diffObj.push({
2366
- name: `${method} ${path5}`,
2367
- changeType: "MODIFIED"
2368
- });
2369
- }
2370
- originalClone.splice(endPointIndex, 1);
2371
- }
2372
- });
2373
- originalClone.forEach((original) => {
2374
- const { path: path5, method } = original;
2375
- diffObj.push({
2376
- name: `${method} ${path5}`,
2377
- changeType: "DELETED"
2378
- });
2379
- });
2380
- return diffObj;
2381
- }
2382
- compareEndPoints(endPoint1, endPoint2) {
2383
- return JSON.stringify(endPoint1) === JSON.stringify(endPoint2);
2384
- }
2385
- };
2386
- __decorateClass([
2387
- boundMethod
2388
- ], CompareSchema.prototype, "diffSchema", 1);
2389
- __decorateClass([
2390
- boundMethod
2391
- ], CompareSchema.prototype, "diffStringArray", 1);
2392
- __decorateClass([
2393
- boundMethod
2394
- ], CompareSchema.prototype, "diffEndPoints", 1);
2395
- __decorateClass([
2396
- boundMethod
2397
- ], CompareSchema.prototype, "compareEndPoints", 1);
2398
- var compareSchema = new CompareSchema();
2399
- var compareSchema_default = compareSchema;
2400
-
2401
- // src/restura/middleware/getMulterUploadSingleton.ts
2402
- var import_multer = __toESM(require("multer"));
2403
- var import_path3 = require("path");
2404
- var os = __toESM(require("os"));
2405
- var OneHundredMB = 100 * 1024 * 1024;
2406
- var commonUpload = null;
2407
- var getMulterUploadSingleton = (directory) => {
2408
- if (commonUpload) return commonUpload;
2409
- const storage = import_multer.default.diskStorage({
2410
- destination: directory || os.tmpdir(),
2411
- filename: function(request, file, cb) {
2412
- const extension = (0, import_path3.extname)(file.originalname);
2413
- const uniqueName = Date.now() + "-" + Math.round(Math.random() * 1e3);
2414
- cb(null, `${uniqueName}${extension}`);
2415
- }
2416
- });
2417
- commonUpload = (0, import_multer.default)({
2418
- storage,
2419
- limits: {
2420
- fileSize: OneHundredMB
2421
- }
2422
- });
2423
- return commonUpload;
2424
- };
2425
-
2426
2465
  // src/restura/utils/TempCache.ts
2427
2466
  var import_fs3 = __toESM(require("fs"));
2428
2467
  var import_path4 = __toESM(require("path"));
@@ -2475,7 +2514,7 @@ var ResturaEngine = class {
2475
2514
  */
2476
2515
  async init(app, authenticationHandler, psqlConnectionPool) {
2477
2516
  this.resturaConfig = import_internal4.config.validate("restura", resturaConfigSchema);
2478
- this.multerCommonUpload = getMulterUploadSingleton(this.resturaConfig.fileTempCachePath);
2517
+ this.multerCommonUpload = getMulterUpload(this.resturaConfig.fileTempCachePath);
2479
2518
  new TempCache(this.resturaConfig.fileTempCachePath);
2480
2519
  this.psqlConnectionPool = psqlConnectionPool;
2481
2520
  this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true);
@@ -2543,10 +2582,7 @@ var ResturaEngine = class {
2543
2582
  * @returns A promise that resolves when the API has been successfully generated and written to the output file.
2544
2583
  */
2545
2584
  async generateApiFromSchema(outputFile, providedSchema) {
2546
- import_fs4.default.writeFileSync(
2547
- outputFile,
2548
- await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2549
- );
2585
+ import_fs4.default.writeFileSync(outputFile, await apiGenerator(providedSchema));
2550
2586
  }
2551
2587
  /**
2552
2588
  * Generates a model from the provided schema and writes it to the specified output file.
@@ -2556,10 +2592,15 @@ var ResturaEngine = class {
2556
2592
  * @returns A promise that resolves when the model has been successfully written to the output file.
2557
2593
  */
2558
2594
  async generateModelFromSchema(outputFile, providedSchema) {
2559
- import_fs4.default.writeFileSync(
2560
- outputFile,
2561
- await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2562
- );
2595
+ import_fs4.default.writeFileSync(outputFile, await modelGenerator(providedSchema));
2596
+ }
2597
+ /**
2598
+ * Generates the ambient module declaration for Restura global types and writes it to the specified output file.
2599
+ * These types are used sometimes in the CustomTypes
2600
+ * @param outputFile
2601
+ */
2602
+ generateResturaGlobalTypes(outputFile) {
2603
+ import_fs4.default.writeFileSync(outputFile, resturaGlobalTypesGenerator());
2563
2604
  }
2564
2605
  /**
2565
2606
  * Retrieves the latest file system schema for Restura.
@@ -2581,28 +2622,6 @@ var ResturaEngine = class {
2581
2622
  }
2582
2623
  return schema;
2583
2624
  }
2584
- /**
2585
- * Asynchronously generates and retrieves hashes for the provided schema and related generated files.
2586
- *
2587
- * @param providedSchema - The schema for which hashes need to be generated.
2588
- * @returns A promise that resolves to an object containing:
2589
- * - `schemaHash`: The hash of the provided schema.
2590
- * - `apiCreatedSchemaHash`: The hash extracted from the generated `api.d.ts` file.
2591
- * - `modelCreatedSchemaHash`: The hash extracted from the generated `models.d.ts` file.
2592
- */
2593
- async getHashes(providedSchema) {
2594
- var _a2, _b, _c, _d;
2595
- const schemaHash = await this.generateHashForSchema(providedSchema);
2596
- const apiFile = import_fs4.default.readFileSync(import_path5.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2597
- const apiCreatedSchemaHash = (_b = (_a2 = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a2[1]) != null ? _b : "";
2598
- const modelFile = import_fs4.default.readFileSync(import_path5.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2599
- const modelCreatedSchemaHash = (_d = (_c = modelFile.toString().match(/\((.*)\)/)) == null ? void 0 : _c[1]) != null ? _d : "";
2600
- return {
2601
- schemaHash,
2602
- apiCreatedSchemaHash,
2603
- modelCreatedSchemaHash
2604
- };
2605
- }
2606
2625
  async reloadEndpoints() {
2607
2626
  this.schema = await this.getLatestFileSystemSchema();
2608
2627
  this.customTypeValidation = customTypeValidationGenerator(this.schema);
@@ -2634,27 +2653,7 @@ var ResturaEngine = class {
2634
2653
  if (!import_fs4.default.existsSync(this.resturaConfig.generatedTypesPath)) {
2635
2654
  import_fs4.default.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
2636
2655
  }
2637
- const hasApiFile = import_fs4.default.existsSync(import_path5.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2638
- const hasModelsFile = import_fs4.default.existsSync(import_path5.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2639
- if (!hasApiFile) {
2640
- await this.generateApiFromSchema(import_path5.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2641
- }
2642
- if (!hasModelsFile) {
2643
- await this.generateModelFromSchema(
2644
- import_path5.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2645
- this.schema
2646
- );
2647
- }
2648
- const hashes = await this.getHashes(this.schema);
2649
- if (hashes.schemaHash !== hashes.apiCreatedSchemaHash) {
2650
- await this.generateApiFromSchema(import_path5.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2651
- }
2652
- if (hashes.schemaHash !== hashes.modelCreatedSchemaHash) {
2653
- await this.generateModelFromSchema(
2654
- import_path5.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2655
- this.schema
2656
- );
2657
- }
2656
+ this.updateTypes();
2658
2657
  }
2659
2658
  resturaAuthentication(req, res, next) {
2660
2659
  if (req.headers["x-auth-token"] !== this.resturaConfig.authToken) res.status(401).send("Unauthorized");
@@ -2686,6 +2685,7 @@ var ResturaEngine = class {
2686
2685
  import_path5.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2687
2686
  this.schema
2688
2687
  );
2688
+ this.generateResturaGlobalTypes(import_path5.default.join(this.resturaConfig.generatedTypesPath, "restura.d.ts"));
2689
2689
  }
2690
2690
  async getSchema(req, res) {
2691
2691
  res.send({ data: this.schema });
@@ -2693,9 +2693,8 @@ var ResturaEngine = class {
2693
2693
  async getSchemaAndTypes(req, res) {
2694
2694
  try {
2695
2695
  const schema = await this.getLatestFileSystemSchema();
2696
- const schemaHash = await this.generateHashForSchema(schema);
2697
- const apiText = await apiGenerator(schema, schemaHash);
2698
- const modelsText = await modelGenerator(schema, schemaHash);
2696
+ const apiText = await apiGenerator(schema);
2697
+ const modelsText = await modelGenerator(schema);
2699
2698
  res.send({ schema, api: apiText, models: modelsText });
2700
2699
  } catch (err) {
2701
2700
  res.status(400).send({ error: err });
@@ -2725,7 +2724,7 @@ var ResturaEngine = class {
2725
2724
  const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
2726
2725
  this.validateAuthorization(req, routeData);
2727
2726
  await this.getMulterFilesIfAny(req, res, routeData);
2728
- validateRequestParams(req, routeData, this.customTypeValidation);
2727
+ requestValidator(req, routeData, this.customTypeValidation);
2729
2728
  if (this.isCustomRoute(routeData)) {
2730
2729
  await this.runCustomRouteLogic(req, res, routeData);
2731
2730
  return;
@@ -2765,19 +2764,6 @@ var ResturaEngine = class {
2765
2764
  throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented ${functionName}`);
2766
2765
  await customFunction(req, res, routeData);
2767
2766
  }
2768
- async generateHashForSchema(providedSchema) {
2769
- const schemaPrettyStr = await prettier3.format(JSON.stringify(providedSchema), __spreadValues({
2770
- parser: "json"
2771
- }, {
2772
- trailingComma: "none",
2773
- tabWidth: 4,
2774
- useTabs: true,
2775
- endOfLine: "lf",
2776
- printWidth: 120,
2777
- singleQuote: true
2778
- }));
2779
- return (0, import_crypto2.createHash)("sha256").update(schemaPrettyStr).digest("hex");
2780
- }
2781
2767
  async storeFileSystemSchema() {
2782
2768
  const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema), __spreadValues({
2783
2769
  parser: "json"