@restura/core 0.1.0-alpha.23 → 0.1.0-alpha.25

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.mjs CHANGED
@@ -44,23 +44,11 @@ import { config } from "@restura/internal";
44
44
  import winston from "winston";
45
45
  import { format } from "logform";
46
46
 
47
- // src/config.schema.ts
47
+ // src/logger/loggerConfigSchema.ts
48
48
  import { z } from "zod";
49
49
  var loggerConfigSchema = z.object({
50
50
  level: z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
51
51
  });
52
- var _a;
53
- var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
54
- var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
55
- var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
56
- var resturaConfigSchema = z.object({
57
- authToken: z.string().min(1, "Missing Restura Auth Token"),
58
- sendErrorStackTrace: z.boolean().default(false),
59
- schemaFilePath: z.string().default(process.cwd() + "/restura.schema.json"),
60
- customApiFolderPath: z.string().default(process.cwd() + customApiFolderPath),
61
- generatedTypesPath: z.string().default(process.cwd() + "/src/@types"),
62
- fileTempCachePath: z.string().optional()
63
- });
64
52
 
65
53
  // src/logger/logger.ts
66
54
  var loggerConfig = config.validate("logger", loggerConfigSchema);
@@ -97,7 +85,194 @@ var logger = winston.createLogger({
97
85
  ]
98
86
  });
99
87
 
100
- // src/restura/errors.ts
88
+ // src/restura/eventManager.ts
89
+ import Bluebird from "bluebird";
90
+ var EventManager = class {
91
+ constructor() {
92
+ this.actionHandlers = {
93
+ DATABASE_ROW_DELETE: [],
94
+ DATABASE_ROW_INSERT: [],
95
+ DATABASE_COLUMN_UPDATE: []
96
+ };
97
+ }
98
+ addRowInsertHandler(onInsert, filter) {
99
+ this.actionHandlers.DATABASE_ROW_INSERT.push({
100
+ callback: onInsert,
101
+ filter
102
+ });
103
+ }
104
+ addColumnChangeHandler(onUpdate, filter) {
105
+ this.actionHandlers.DATABASE_COLUMN_UPDATE.push({
106
+ callback: onUpdate,
107
+ filter
108
+ });
109
+ }
110
+ addRowDeleteHandler(onDelete, filter) {
111
+ this.actionHandlers.DATABASE_ROW_DELETE.push({
112
+ callback: onDelete,
113
+ filter
114
+ });
115
+ }
116
+ async fireActionFromDbTrigger(sqlMutationData, result) {
117
+ if (sqlMutationData.mutationType === "INSERT") {
118
+ await this.fireInsertActions(sqlMutationData, result);
119
+ } else if (sqlMutationData.mutationType === "UPDATE") {
120
+ await this.fireUpdateActions(sqlMutationData, result);
121
+ } else if (sqlMutationData.mutationType === "DELETE") {
122
+ await this.fireDeleteActions(sqlMutationData, result);
123
+ }
124
+ }
125
+ async fireInsertActions(data, triggerResult) {
126
+ await Bluebird.map(
127
+ this.actionHandlers.DATABASE_ROW_INSERT,
128
+ ({ callback, filter }) => {
129
+ if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
130
+ const insertData = {
131
+ tableName: triggerResult.table,
132
+ insertId: triggerResult.record.id,
133
+ insertObject: triggerResult.record,
134
+ queryMetadata: data.queryMetadata
135
+ };
136
+ callback(insertData, data.queryMetadata);
137
+ },
138
+ { concurrency: 10 }
139
+ );
140
+ }
141
+ async fireDeleteActions(data, triggerResult) {
142
+ await Bluebird.map(
143
+ this.actionHandlers.DATABASE_ROW_DELETE,
144
+ ({ callback, filter }) => {
145
+ if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
146
+ const deleteData = {
147
+ tableName: triggerResult.table,
148
+ deletedRow: triggerResult.previousRecord,
149
+ queryMetadata: data.queryMetadata
150
+ };
151
+ callback(deleteData, data.queryMetadata);
152
+ },
153
+ { concurrency: 10 }
154
+ );
155
+ }
156
+ async fireUpdateActions(data, triggerResult) {
157
+ await Bluebird.map(
158
+ this.actionHandlers.DATABASE_COLUMN_UPDATE,
159
+ ({ callback, filter }) => {
160
+ if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
161
+ const columnChangeData = {
162
+ tableName: triggerResult.table,
163
+ rowId: triggerResult.record.id,
164
+ newData: triggerResult.record,
165
+ oldData: triggerResult.previousRecord,
166
+ queryMetadata: data.queryMetadata
167
+ };
168
+ callback(columnChangeData, data.queryMetadata);
169
+ },
170
+ { concurrency: 10 }
171
+ );
172
+ }
173
+ hasHandlersForEventType(eventType, filter, triggerResult) {
174
+ if (filter) {
175
+ switch (eventType) {
176
+ case "DATABASE_ROW_INSERT":
177
+ case "DATABASE_ROW_DELETE":
178
+ if (filter.tableName && filter.tableName !== triggerResult.table) return false;
179
+ break;
180
+ case "DATABASE_COLUMN_UPDATE":
181
+ const filterColumnChange = filter;
182
+ if (filterColumnChange.tableName !== filter.tableName) return false;
183
+ if (!filterColumnChange.columns.some((item) => {
184
+ const updatedColumns = Object.keys(
185
+ changedValues(triggerResult.record, triggerResult.previousRecord)
186
+ );
187
+ return updatedColumns.includes(item);
188
+ }))
189
+ return false;
190
+ break;
191
+ }
192
+ }
193
+ return true;
194
+ }
195
+ };
196
+ var eventManager = new EventManager();
197
+ var eventManager_default = eventManager;
198
+ function changedValues(record, previousRecord) {
199
+ const changed = {};
200
+ for (const i in previousRecord) {
201
+ if (previousRecord[i] !== record[i]) {
202
+ if (typeof previousRecord[i] === "object" && typeof record[i] === "object") {
203
+ const nestedChanged = changedValues(record[i], previousRecord[i]);
204
+ if (Object.keys(nestedChanged).length > 0) {
205
+ changed[i] = record[i];
206
+ }
207
+ } else {
208
+ changed[i] = record[i];
209
+ }
210
+ }
211
+ }
212
+ return changed;
213
+ }
214
+
215
+ // src/restura/restura.ts
216
+ import { ObjectUtils as ObjectUtils5, StringUtils as StringUtils3 } from "@redskytech/core-utils";
217
+ import { config as config2 } from "@restura/internal";
218
+
219
+ // ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
220
+ function _typeof(obj) {
221
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
222
+ _typeof = function _typeof2(obj2) {
223
+ return typeof obj2;
224
+ };
225
+ } else {
226
+ _typeof = function _typeof2(obj2) {
227
+ return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
228
+ };
229
+ }
230
+ return _typeof(obj);
231
+ }
232
+ function boundMethod(target, key, descriptor) {
233
+ var fn = descriptor.value;
234
+ if (typeof fn !== "function") {
235
+ throw new TypeError("@boundMethod decorator can only be applied to methods not: ".concat(_typeof(fn)));
236
+ }
237
+ var definingProperty = false;
238
+ return {
239
+ configurable: true,
240
+ get: function get() {
241
+ if (definingProperty || this === target.prototype || this.hasOwnProperty(key) || typeof fn !== "function") {
242
+ return fn;
243
+ }
244
+ var boundFn = fn.bind(this);
245
+ definingProperty = true;
246
+ Object.defineProperty(this, key, {
247
+ configurable: true,
248
+ get: function get2() {
249
+ return boundFn;
250
+ },
251
+ set: function set(value) {
252
+ fn = value;
253
+ delete this[key];
254
+ }
255
+ });
256
+ definingProperty = false;
257
+ return boundFn;
258
+ },
259
+ set: function set(value) {
260
+ fn = value;
261
+ }
262
+ };
263
+ }
264
+
265
+ // src/restura/restura.ts
266
+ import bodyParser from "body-parser";
267
+ import compression from "compression";
268
+ import cookieParser from "cookie-parser";
269
+ import * as express from "express";
270
+ import fs4 from "fs";
271
+ import path4 from "path";
272
+ import pg3 from "pg";
273
+ import * as prettier3 from "prettier";
274
+
275
+ // src/restura/RsError.ts
101
276
  var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
102
277
  HtmlStatusCodes2[HtmlStatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
103
278
  HtmlStatusCodes2[HtmlStatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
@@ -121,7 +296,6 @@ var RsError = class _RsError {
121
296
  static htmlStatus(code) {
122
297
  return htmlStatusMap[code];
123
298
  }
124
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
299
  static isRsError(error) {
126
300
  return error instanceof _RsError;
127
301
  }
@@ -163,66 +337,158 @@ var htmlStatusMap = {
163
337
  SCHEMA_ERROR: 500 /* SERVER_ERROR */
164
338
  };
165
339
 
166
- // src/restura/restura.ts
167
- import { ObjectUtils as ObjectUtils5, StringUtils as StringUtils3 } from "@redskytech/core-utils";
168
- import { config as config2 } from "@restura/internal";
169
-
170
- // ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
171
- function _typeof(obj) {
172
- if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
173
- _typeof = function _typeof2(obj2) {
174
- return typeof obj2;
175
- };
176
- } else {
177
- _typeof = function _typeof2(obj2) {
178
- return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
179
- };
180
- }
181
- return _typeof(obj);
182
- }
183
- function boundMethod(target, key, descriptor) {
184
- var fn = descriptor.value;
185
- if (typeof fn !== "function") {
186
- throw new TypeError("@boundMethod decorator can only be applied to methods not: ".concat(_typeof(fn)));
340
+ // src/restura/compareSchema.ts
341
+ import cloneDeep from "lodash.clonedeep";
342
+ var CompareSchema = class {
343
+ async diffSchema(newSchema, latestSchema, psqlEngine) {
344
+ const endPoints = this.diffEndPoints(newSchema.endpoints[0].routes, latestSchema.endpoints[0].routes);
345
+ const globalParams = this.diffStringArray(newSchema.globalParams, latestSchema.globalParams);
346
+ const roles = this.diffStringArray(newSchema.roles, latestSchema.roles);
347
+ let commands = "";
348
+ if (JSON.stringify(newSchema.database) !== JSON.stringify(latestSchema.database))
349
+ commands = await psqlEngine.diffDatabaseToSchema(newSchema);
350
+ const customTypes = newSchema.customTypes !== latestSchema.customTypes;
351
+ const schemaPreview = { endPoints, globalParams, roles, commands, customTypes };
352
+ return schemaPreview;
187
353
  }
188
- var definingProperty = false;
189
- return {
190
- configurable: true,
191
- get: function get() {
192
- if (definingProperty || this === target.prototype || this.hasOwnProperty(key) || typeof fn !== "function") {
193
- return fn;
354
+ diffStringArray(newArray, originalArray) {
355
+ const stringsDiff = [];
356
+ const originalClone = new Set(originalArray);
357
+ newArray.forEach((item) => {
358
+ const originalIndex = originalClone.has(item);
359
+ if (!originalIndex) {
360
+ stringsDiff.push({
361
+ name: item,
362
+ changeType: "NEW"
363
+ });
364
+ } else {
365
+ originalClone.delete(item);
194
366
  }
195
- var boundFn = fn.bind(this);
196
- definingProperty = true;
197
- Object.defineProperty(this, key, {
198
- configurable: true,
199
- get: function get2() {
200
- return boundFn;
201
- },
202
- set: function set(value) {
203
- fn = value;
204
- delete this[key];
367
+ });
368
+ originalClone.forEach((item) => {
369
+ stringsDiff.push({
370
+ name: item,
371
+ changeType: "DELETED"
372
+ });
373
+ });
374
+ return stringsDiff;
375
+ }
376
+ diffEndPoints(newEndPoints, originalEndpoints) {
377
+ const originalClone = cloneDeep(originalEndpoints);
378
+ const diffObj = [];
379
+ newEndPoints.forEach((endPoint) => {
380
+ const { path: path5, method } = endPoint;
381
+ const endPointIndex = originalClone.findIndex((original) => {
382
+ return original.path === endPoint.path && original.method === endPoint.method;
383
+ });
384
+ if (endPointIndex === -1) {
385
+ diffObj.push({
386
+ name: `${method} ${path5}`,
387
+ changeType: "NEW"
388
+ });
389
+ } else {
390
+ const original = originalClone.findIndex((original2) => {
391
+ return this.compareEndPoints(endPoint, original2);
392
+ });
393
+ if (original === -1) {
394
+ diffObj.push({
395
+ name: `${method} ${path5}`,
396
+ changeType: "MODIFIED"
397
+ });
205
398
  }
399
+ originalClone.splice(endPointIndex, 1);
400
+ }
401
+ });
402
+ originalClone.forEach((original) => {
403
+ const { path: path5, method } = original;
404
+ diffObj.push({
405
+ name: `${method} ${path5}`,
406
+ changeType: "DELETED"
206
407
  });
207
- definingProperty = false;
208
- return boundFn;
209
- },
210
- set: function set(value) {
211
- fn = value;
408
+ });
409
+ return diffObj;
410
+ }
411
+ compareEndPoints(endPoint1, endPoint2) {
412
+ return JSON.stringify(endPoint1) === JSON.stringify(endPoint2);
413
+ }
414
+ };
415
+ __decorateClass([
416
+ boundMethod
417
+ ], CompareSchema.prototype, "diffSchema", 1);
418
+ __decorateClass([
419
+ boundMethod
420
+ ], CompareSchema.prototype, "diffStringArray", 1);
421
+ __decorateClass([
422
+ boundMethod
423
+ ], CompareSchema.prototype, "diffEndPoints", 1);
424
+ __decorateClass([
425
+ boundMethod
426
+ ], CompareSchema.prototype, "compareEndPoints", 1);
427
+ var compareSchema = new CompareSchema();
428
+ var compareSchema_default = compareSchema;
429
+
430
+ // src/restura/customApiFactory.ts
431
+ import { fileUtils } from "@restura/internal";
432
+ import Bluebird2 from "bluebird";
433
+ import fs from "fs";
434
+ import path from "path";
435
+ var CustomApiFactory = class {
436
+ constructor() {
437
+ this.customApis = {};
438
+ }
439
+ async loadApiFiles(baseFolderPath) {
440
+ const apiVersions = ["v1"];
441
+ for (const apiVersion of apiVersions) {
442
+ const apiVersionFolderPath = path.join(baseFolderPath, apiVersion);
443
+ const directoryExists = await fileUtils.existDir(apiVersionFolderPath);
444
+ if (!directoryExists) continue;
445
+ await this.addDirectory(apiVersionFolderPath, apiVersion);
212
446
  }
213
- };
214
- }
447
+ }
448
+ getCustomApi(customApiName) {
449
+ return this.customApis[customApiName];
450
+ }
451
+ async addDirectory(directoryPath, apiVersion) {
452
+ var _a2;
453
+ const entries = await fs.promises.readdir(directoryPath, {
454
+ withFileTypes: true
455
+ });
456
+ const isTsx2 = (_a2 = process.argv[1]) == null ? void 0 : _a2.endsWith(".ts");
457
+ const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
458
+ const extension = isTsx2 || isTsNode2 ? "ts" : "js";
459
+ const shouldEndWith = `.api.${apiVersion}.${extension}`;
460
+ await Bluebird2.map(entries, async (entry) => {
461
+ if (entry.isFile()) {
462
+ if (entry.name.endsWith(shouldEndWith) === false) return;
463
+ try {
464
+ const importPath = `${path.join(directoryPath, entry.name)}`;
465
+ const ApiImport = await import(importPath);
466
+ const customApiClass = new ApiImport.default();
467
+ logger.info(`Registering custom API: ${ApiImport.default.name}`);
468
+ this.bindMethodsToInstance(customApiClass);
469
+ this.customApis[ApiImport.default.name] = customApiClass;
470
+ } catch (e) {
471
+ console.error(e);
472
+ }
473
+ }
474
+ });
475
+ }
476
+ bindMethodsToInstance(instance) {
477
+ const proto = Object.getPrototypeOf(instance);
478
+ Object.getOwnPropertyNames(proto).forEach((key) => {
479
+ const property = instance[key];
480
+ if (typeof property === "function" && key !== "constructor") {
481
+ instance[key] = property.bind(instance);
482
+ }
483
+ });
484
+ }
485
+ };
486
+ var customApiFactory = new CustomApiFactory();
487
+ var customApiFactory_default = customApiFactory;
215
488
 
216
- // src/restura/restura.ts
217
- import bodyParser from "body-parser";
218
- import compression from "compression";
219
- import cookieParser from "cookie-parser";
220
- import { createHash } from "crypto";
221
- import * as express from "express";
222
- import fs4 from "fs";
223
- import path4 from "path";
224
- import pg3 from "pg";
225
- import * as prettier3 from "prettier";
489
+ // src/restura/generators/apiGenerator.ts
490
+ import { ObjectUtils, StringUtils } from "@redskytech/core-utils";
491
+ import prettier from "prettier";
226
492
 
227
493
  // src/restura/sql/SqlUtils.ts
228
494
  var SqlUtils = class _SqlUtils {
@@ -250,7 +516,7 @@ var SqlUtils = class _SqlUtils {
250
516
  }
251
517
  };
252
518
 
253
- // src/restura/ResponseValidator.ts
519
+ // src/restura/validators/ResponseValidator.ts
254
520
  var ResponseValidator = class _ResponseValidator {
255
521
  constructor(schema) {
256
522
  this.database = schema.database;
@@ -399,9 +665,7 @@ var ResponseValidator = class _ResponseValidator {
399
665
  }
400
666
  };
401
667
 
402
- // src/restura/apiGenerator.ts
403
- import { ObjectUtils, StringUtils } from "@redskytech/core-utils";
404
- import prettier from "prettier";
668
+ // src/restura/generators/apiGenerator.ts
405
669
  var ApiTree = class _ApiTree {
406
670
  constructor(namespace, database) {
407
671
  this.database = database;
@@ -554,8 +818,9 @@ var ApiTree = class _ApiTree {
554
818
  function pathToNamespaces(path5) {
555
819
  return path5.split("/").map((e) => StringUtils.toPascalCasing(e)).filter((e) => e);
556
820
  }
557
- function apiGenerator(schema, schemaHash) {
558
- let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
821
+ function apiGenerator(schema) {
822
+ let apiString = `/** Auto generated file. DO NOT MODIFY **/
823
+ `;
559
824
  const rootNamespace = ApiTree.createRootNode(schema.database);
560
825
  for (const endpoint of schema.endpoints) {
561
826
  const endpointNamespaces = pathToNamespaces(endpoint.baseUrl);
@@ -585,71 +850,11 @@ function apiGenerator(schema, schemaHash) {
585
850
  }));
586
851
  }
587
852
 
588
- // src/restura/customApiFactory.ts
589
- import fs from "fs";
590
- import path from "path";
591
- import Bluebird from "bluebird";
592
- import { fileUtils } from "@restura/internal";
593
- var CustomApiFactory = class {
594
- constructor() {
595
- this.customApis = {};
596
- }
597
- async loadApiFiles(baseFolderPath) {
598
- const apiVersions = ["v1"];
599
- for (const apiVersion of apiVersions) {
600
- const apiVersionFolderPath = path.join(baseFolderPath, apiVersion);
601
- const directoryExists = await fileUtils.existDir(apiVersionFolderPath);
602
- if (!directoryExists) continue;
603
- await this.addDirectory(apiVersionFolderPath, apiVersion);
604
- }
605
- }
606
- getCustomApi(customApiName) {
607
- return this.customApis[customApiName];
608
- }
609
- async addDirectory(directoryPath, apiVersion) {
610
- var _a2;
611
- const entries = await fs.promises.readdir(directoryPath, {
612
- withFileTypes: true
613
- });
614
- const isTsx2 = (_a2 = process.argv[1]) == null ? void 0 : _a2.endsWith(".ts");
615
- const isTsNode2 = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
616
- const extension = isTsx2 || isTsNode2 ? "ts" : "js";
617
- const shouldEndWith = `.api.${apiVersion}.${extension}`;
618
- await Bluebird.map(entries, async (entry) => {
619
- if (entry.isFile()) {
620
- if (entry.name.endsWith(shouldEndWith) === false) return;
621
- try {
622
- const importPath = `${path.join(directoryPath, entry.name)}`;
623
- const ApiImport = await import(importPath);
624
- const customApiClass = new ApiImport.default();
625
- logger.info(`Registering custom API: ${ApiImport.default.name}`);
626
- this.bindMethodsToInstance(customApiClass);
627
- this.customApis[ApiImport.default.name] = customApiClass;
628
- } catch (e) {
629
- console.error(e);
630
- }
631
- }
632
- });
633
- }
634
- bindMethodsToInstance(instance) {
635
- const proto = Object.getPrototypeOf(instance);
636
- Object.getOwnPropertyNames(proto).forEach((key) => {
637
- const property = instance[key];
638
- if (typeof property === "function" && key !== "constructor") {
639
- instance[key] = property.bind(instance);
640
- }
641
- });
642
- }
643
- };
644
- var customApiFactory = new CustomApiFactory();
645
- var customApiFactory_default = customApiFactory;
646
-
647
- // src/restura/customTypeValidationGenerator.ts
853
+ // src/restura/generators/customTypeValidationGenerator.ts
648
854
  import fs2 from "fs";
649
- import * as TJS from "typescript-json-schema";
650
855
  import path2, { resolve } from "path";
651
856
  import tmp from "tmp";
652
- import * as process2 from "process";
857
+ import * as TJS from "typescript-json-schema";
653
858
  function customTypeValidationGenerator(currentSchema) {
654
859
  const schemaObject = {};
655
860
  const customInterfaceNames = currentSchema.customTypes.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
@@ -659,13 +864,14 @@ function customTypeValidationGenerator(currentSchema) {
659
864
  const compilerOptions = {
660
865
  strictNullChecks: true,
661
866
  skipLibCheck: true
867
+ // Needed if we are processing ES modules
662
868
  };
663
869
  const program = TJS.getProgramFromFiles(
664
870
  [
665
871
  resolve(temporaryFile.name),
666
- // find a way to remove
667
- path2.join(process2.cwd(), "src/@types/models.d.ts"),
668
- path2.join(process2.cwd(), "src/@types/api.d.ts")
872
+ path2.join(restura.resturaConfig.generatedTypesPath, "restura.d.ts"),
873
+ path2.join(restura.resturaConfig.generatedTypesPath, "models.d.ts"),
874
+ path2.join(restura.resturaConfig.generatedTypesPath, "api.d.ts")
669
875
  ],
670
876
  compilerOptions
671
877
  );
@@ -679,6 +885,61 @@ function customTypeValidationGenerator(currentSchema) {
679
885
  return schemaObject;
680
886
  }
681
887
 
888
+ // src/restura/generators/modelGenerator.ts
889
+ import { StringUtils as StringUtils2 } from "@redskytech/core-utils";
890
+ import prettier2 from "prettier";
891
+ function modelGenerator(schema) {
892
+ let modelString = `/** Auto generated file. DO NOT MODIFY **/
893
+
894
+ `;
895
+ modelString += `declare namespace Model {
896
+ `;
897
+ for (const table of schema.database) {
898
+ modelString += convertTable(table);
899
+ }
900
+ modelString += `}`;
901
+ return prettier2.format(modelString, __spreadValues({
902
+ parser: "typescript"
903
+ }, {
904
+ trailingComma: "none",
905
+ tabWidth: 4,
906
+ useTabs: true,
907
+ endOfLine: "lf",
908
+ printWidth: 120,
909
+ singleQuote: true
910
+ }));
911
+ }
912
+ function convertTable(table) {
913
+ let modelString = ` export interface ${StringUtils2.capitalizeFirst(table.name)} {
914
+ `;
915
+ for (const column of table.columns) {
916
+ modelString += ` ${column.name}${column.isNullable ? "?" : ""}: ${SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value)};
917
+ `;
918
+ }
919
+ modelString += ` }
920
+ `;
921
+ return modelString;
922
+ }
923
+
924
+ // src/restura/generators/resturaGlobalTypesGenerator.ts
925
+ function resturaGlobalTypesGenerator() {
926
+ return `/** Auto generated file. DO NOT MODIFY **/
927
+ /** This file contains types that may be used in the CustomTypes of Restura **/
928
+ /** For example export interface MyPagedQuery extends Restura.PageQuery { } **/
929
+
930
+ declare namespace Restura {
931
+ export type StandardOrderTypes = 'ASC' | 'DESC' | 'RAND' | 'NONE';
932
+ export interface PageQuery {
933
+ page?: number;
934
+ perPage?: number;
935
+ sortBy?: string;
936
+ sortOrder?: StandardOrderTypes;
937
+ filter?: string;
938
+ }
939
+ }
940
+ `;
941
+ }
942
+
682
943
  // src/restura/middleware/addApiResponseFunctions.ts
683
944
  function addApiResponseFunctions(req, res, next) {
684
945
  res.sendData = function(data, statusCode = 200) {
@@ -717,10 +978,35 @@ function authenticateUser(applicationAuthenticateHandler) {
717
978
  };
718
979
  }
719
980
 
720
- // src/restura/restura.schema.ts
981
+ // src/restura/middleware/getMulterUpload.ts
982
+ import multer from "multer";
983
+ import * as os from "os";
984
+ import { extname } from "path";
985
+ var OneHundredMB = 100 * 1024 * 1024;
986
+ var commonUpload = null;
987
+ var getMulterUpload = (directory) => {
988
+ if (commonUpload) return commonUpload;
989
+ const storage = multer.diskStorage({
990
+ destination: directory || os.tmpdir(),
991
+ filename: function(request, file, cb) {
992
+ const extension = extname(file.originalname);
993
+ const uniqueName = Date.now() + "-" + Math.round(Math.random() * 1e3);
994
+ cb(null, `${uniqueName}${extension}`);
995
+ }
996
+ });
997
+ commonUpload = multer({
998
+ storage,
999
+ limits: {
1000
+ fileSize: OneHundredMB
1001
+ }
1002
+ });
1003
+ return commonUpload;
1004
+ };
1005
+
1006
+ // src/restura/schemas/resturaSchema.ts
721
1007
  import { z as z3 } from "zod";
722
1008
 
723
- // src/restura/types/validation.types.ts
1009
+ // src/restura/schemas/validatorDataSchema.ts
724
1010
  import { z as z2 } from "zod";
725
1011
  var validatorDataSchemeValue = z2.union([z2.string(), z2.array(z2.string()), z2.number(), z2.array(z2.number())]);
726
1012
  var validatorDataSchema = z2.object({
@@ -728,7 +1014,7 @@ var validatorDataSchema = z2.object({
728
1014
  value: validatorDataSchemeValue
729
1015
  }).strict();
730
1016
 
731
- // src/restura/restura.schema.ts
1017
+ // src/restura/schemas/resturaSchema.ts
732
1018
  var orderBySchema = z3.object({
733
1019
  columnName: z3.string(),
734
1020
  order: z3.enum(["ASC", "DESC"]),
@@ -975,7 +1261,7 @@ var endpointDataSchema = z3.object({
975
1261
  baseUrl: z3.string(),
976
1262
  routes: z3.array(z3.union([standardRouteSchema, customRouteSchema]))
977
1263
  }).strict();
978
- var resturaZodSchema = z3.object({
1264
+ var resturaSchema = z3.object({
979
1265
  database: z3.array(tableDataSchema),
980
1266
  endpoints: z3.array(endpointDataSchema),
981
1267
  globalParams: z3.array(z3.string()),
@@ -984,7 +1270,7 @@ var resturaZodSchema = z3.object({
984
1270
  }).strict();
985
1271
  async function isSchemaValid(schemaToCheck) {
986
1272
  try {
987
- resturaZodSchema.parse(schemaToCheck);
1273
+ resturaSchema.parse(schemaToCheck);
988
1274
  return true;
989
1275
  } catch (error) {
990
1276
  logger.error(error);
@@ -992,7 +1278,7 @@ async function isSchemaValid(schemaToCheck) {
992
1278
  }
993
1279
  }
994
1280
 
995
- // src/restura/validateRequestParams.ts
1281
+ // src/restura/validators/requestValidator.ts
996
1282
  import { ObjectUtils as ObjectUtils2 } from "@redskytech/core-utils";
997
1283
  import jsonschema from "jsonschema";
998
1284
  import { z as z4 } from "zod";
@@ -1009,8 +1295,8 @@ function addQuotesToStrings(variable) {
1009
1295
  }
1010
1296
  }
1011
1297
 
1012
- // src/restura/validateRequestParams.ts
1013
- function validateRequestParams(req, routeData, validationSchema) {
1298
+ // src/restura/validators/requestValidator.ts
1299
+ function requestValidator(req, routeData, validationSchema) {
1014
1300
  const requestData = getRequestData(req);
1015
1301
  req.data = requestData;
1016
1302
  if (routeData.request === void 0) {
@@ -1178,7 +1464,7 @@ function getRequestData(req) {
1178
1464
  async function schemaValidation(req, res, next) {
1179
1465
  req.data = getRequestData(req);
1180
1466
  try {
1181
- resturaZodSchema.parse(req.data);
1467
+ resturaSchema.parse(req.data);
1182
1468
  next();
1183
1469
  } catch (error) {
1184
1470
  logger.error(error);
@@ -1186,40 +1472,20 @@ async function schemaValidation(req, res, next) {
1186
1472
  }
1187
1473
  }
1188
1474
 
1189
- // src/restura/modelGenerator.ts
1190
- import { StringUtils as StringUtils2 } from "@redskytech/core-utils";
1191
- import prettier2 from "prettier";
1192
- function modelGenerator(schema, schemaHash) {
1193
- let modelString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/
1194
- `;
1195
- modelString += `declare namespace Model {
1196
- `;
1197
- for (const table of schema.database) {
1198
- modelString += convertTable(table);
1199
- }
1200
- modelString += `}`;
1201
- return prettier2.format(modelString, __spreadValues({
1202
- parser: "typescript"
1203
- }, {
1204
- trailingComma: "none",
1205
- tabWidth: 4,
1206
- useTabs: true,
1207
- endOfLine: "lf",
1208
- printWidth: 120,
1209
- singleQuote: true
1210
- }));
1211
- }
1212
- function convertTable(table) {
1213
- let modelString = ` export interface ${StringUtils2.capitalizeFirst(table.name)} {
1214
- `;
1215
- for (const column of table.columns) {
1216
- modelString += ` ${column.name}${column.isNullable ? "?" : ""}: ${SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value)};
1217
- `;
1218
- }
1219
- modelString += ` }
1220
- `;
1221
- return modelString;
1222
- }
1475
+ // src/restura/schemas/resturaConfigSchema.ts
1476
+ import { z as z5 } from "zod";
1477
+ var _a;
1478
+ var isTsx = (_a = process.argv[1]) == null ? void 0 : _a.endsWith(".ts");
1479
+ var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
1480
+ var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
1481
+ var resturaConfigSchema = z5.object({
1482
+ authToken: z5.string().min(1, "Missing Restura Auth Token"),
1483
+ sendErrorStackTrace: z5.boolean().default(false),
1484
+ schemaFilePath: z5.string().default(process.cwd() + "/restura.schema.json"),
1485
+ customApiFolderPath: z5.string().default(process.cwd() + customApiFolderPath),
1486
+ generatedTypesPath: z5.string().default(process.cwd() + "/src/@types"),
1487
+ fileTempCachePath: z5.string().optional()
1488
+ });
1223
1489
 
1224
1490
  // src/restura/sql/PsqlEngine.ts
1225
1491
  import { ObjectUtils as ObjectUtils4 } from "@redskytech/core-utils";
@@ -1231,6 +1497,7 @@ import pg2 from "pg";
1231
1497
  import pg from "pg";
1232
1498
 
1233
1499
  // src/restura/sql/PsqlConnection.ts
1500
+ import crypto from "crypto";
1234
1501
  import format3 from "pg-format";
1235
1502
 
1236
1503
  // src/restura/sql/PsqlUtils.ts
@@ -1283,7 +1550,6 @@ function SQL(strings, ...values) {
1283
1550
  }
1284
1551
 
1285
1552
  // src/restura/sql/PsqlConnection.ts
1286
- import crypto from "crypto";
1287
1553
  var PsqlConnection = class {
1288
1554
  constructor() {
1289
1555
  this.instanceId = crypto.randomUUID();
@@ -1471,233 +1737,106 @@ var SqlEngine = class {
1471
1737
 
1472
1738
  // src/restura/sql/filterPsqlParser.ts
1473
1739
  import peg from "pegjs";
1474
- var filterSqlGrammar = `
1475
- {
1476
- // ported from pg-format but intentionally will add double quotes to every column
1477
- function quoteSqlIdentity(value) {
1478
- if (value === undefined || value === null) {
1479
- throw new Error('SQL identifier cannot be null or undefined');
1480
- } else if (value === false) {
1481
- return '"f"';
1482
- } else if (value === true) {
1483
- return '"t"';
1484
- } else if (value instanceof Date) {
1485
- // return '"' + formatDate(value.toISOString()) + '"';
1486
- } else if (value instanceof Buffer) {
1487
- throw new Error('SQL identifier cannot be a buffer');
1488
- } else if (Array.isArray(value) === true) {
1489
- var temp = [];
1490
- for (var i = 0; i < value.length; i++) {
1491
- if (Array.isArray(value[i]) === true) {
1492
- throw new Error('Nested array to grouped list conversion is not supported for SQL identifier');
1493
- } else {
1494
- // temp.push(quoteIdent(value[i]));
1495
- }
1496
- }
1497
- return temp.toString();
1498
- } else if (value === Object(value)) {
1499
- throw new Error('SQL identifier cannot be an object');
1500
- }
1501
-
1502
- var ident = value.toString().slice(0); // create copy
1503
-
1504
- // do not quote a valid, unquoted identifier
1505
- // if (/^[a-z_][a-z0-9_$]*$/.test(ident) === true && isReserved(ident) === false) {
1506
- // return ident;
1507
- // }
1508
-
1509
- var quoted = '"';
1510
-
1511
- for (var i = 0; i < ident.length; i++) {
1512
- var c = ident[i];
1513
- if (c === '"') {
1514
- quoted += c + c;
1515
- } else {
1516
- quoted += c;
1517
- }
1518
- }
1519
-
1520
- quoted += '"';
1521
-
1522
- return quoted;
1523
- };
1524
- }
1525
-
1526
- start = expressionList
1527
-
1528
- _ = [ \\t\\r\\n]* // Matches spaces, tabs, and line breaks
1529
-
1530
- expressionList =
1531
- leftExpression:expression _ operator:operator _ rightExpression:expressionList
1532
- { return \`\${leftExpression} \${operator} \${rightExpression}\`;}
1533
- / expression
1534
-
1535
- expression =
1536
- negate:negate? _ "(" _ "column" _ ":" column:column _ ","? _ value:value? ","? _ type:type? _ ")"_
1537
- {return \`\${negate? " NOT " : ""}(\${type? type(column, value) : \`\${column} = \${format.literal(value)}\`})\`;}
1538
- /
1539
- negate:negate?"("expression:expressionList")" { return \`\${negate? " NOT " : ""}(\${expression})\`; }
1540
-
1541
- negate = "!"
1542
-
1543
- operator = "and"i / "or"i
1544
-
1545
-
1546
- column = left:text "." right:text { return \`\${quoteSqlIdentity(left)}.\${quoteSqlIdentity(right)}\`; }
1547
- /
1548
- text:text { return quoteSqlIdentity(text); }
1549
-
1550
-
1551
- text = text:[a-z0-9 \\t\\r\\n\\-_:@]i+ { return text.join(""); }
1552
-
1553
- type = "type" _ ":" _ type:typeString { return type; }
1554
- typeString = text:"startsWith" { return function(column, value) { return \`\${column} ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } } /
1555
- text:"endsWith" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } } /
1556
- text:"contains" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } } /
1557
- text:"exact" { return function(column, value) { return \`\${column} = '\${format.literal(value).slice(1,-1)}'\`; } } /
1558
- text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${format.literal(value).slice(1,-1)}'\`; } } /
1559
- text:"greaterThan" { return function(column, value) { return \`\${column} > '\${format.literal(value).slice(1,-1)}'\`; } } /
1560
- text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${format.literal(value).slice(1,-1)}'\`; } } /
1561
- text:"lessThan" { return function(column, value) { return \`\${column} < '\${format.literal(value).slice(1,-1)}'\`; } } /
1562
- text:"isNull" { return function(column, value) { return \`isNull(\${column})\`; } }
1563
-
1564
- value = "value" _ ":" value:text { return value; }
1565
-
1566
-
1567
- `;
1568
- var filterPsqlParser = peg.generate(filterSqlGrammar, {
1569
- format: "commonjs",
1570
- dependencies: { format: "pg-format" }
1571
- });
1572
- var filterPsqlParser_default = filterPsqlParser;
1573
-
1574
- // src/restura/eventManager.ts
1575
- import Bluebird2 from "bluebird";
1576
- var EventManager = class {
1577
- constructor() {
1578
- this.actionHandlers = {
1579
- DATABASE_ROW_DELETE: [],
1580
- DATABASE_ROW_INSERT: [],
1581
- DATABASE_COLUMN_UPDATE: []
1582
- };
1583
- }
1584
- addRowInsertHandler(onInsert, filter) {
1585
- this.actionHandlers.DATABASE_ROW_INSERT.push({
1586
- callback: onInsert,
1587
- filter
1588
- });
1589
- }
1590
- addColumnChangeHandler(onUpdate, filter) {
1591
- this.actionHandlers.DATABASE_COLUMN_UPDATE.push({
1592
- callback: onUpdate,
1593
- filter
1594
- });
1595
- }
1596
- addRowDeleteHandler(onDelete, filter) {
1597
- this.actionHandlers.DATABASE_ROW_DELETE.push({
1598
- callback: onDelete,
1599
- filter
1600
- });
1601
- }
1602
- async fireActionFromDbTrigger(sqlMutationData, result) {
1603
- if (sqlMutationData.mutationType === "INSERT") {
1604
- await this.fireInsertActions(sqlMutationData, result);
1605
- } else if (sqlMutationData.mutationType === "UPDATE") {
1606
- await this.fireUpdateActions(sqlMutationData, result);
1607
- } else if (sqlMutationData.mutationType === "DELETE") {
1608
- await this.fireDeleteActions(sqlMutationData, result);
1609
- }
1610
- }
1611
- async fireInsertActions(data, triggerResult) {
1612
- await Bluebird2.map(
1613
- this.actionHandlers.DATABASE_ROW_INSERT,
1614
- ({ callback, filter }) => {
1615
- if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
1616
- const insertData = {
1617
- tableName: triggerResult.table,
1618
- insertId: triggerResult.record.id,
1619
- insertObject: triggerResult.record,
1620
- queryMetadata: data.queryMetadata
1621
- };
1622
- callback(insertData, data.queryMetadata);
1623
- },
1624
- { concurrency: 10 }
1625
- );
1626
- }
1627
- async fireDeleteActions(data, triggerResult) {
1628
- await Bluebird2.map(
1629
- this.actionHandlers.DATABASE_ROW_DELETE,
1630
- ({ callback, filter }) => {
1631
- if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
1632
- const deleteData = {
1633
- tableName: triggerResult.table,
1634
- deletedRow: triggerResult.previousRecord,
1635
- queryMetadata: data.queryMetadata
1636
- };
1637
- callback(deleteData, data.queryMetadata);
1638
- },
1639
- { concurrency: 10 }
1640
- );
1641
- }
1642
- async fireUpdateActions(data, triggerResult) {
1643
- await Bluebird2.map(
1644
- this.actionHandlers.DATABASE_COLUMN_UPDATE,
1645
- ({ callback, filter }) => {
1646
- if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
1647
- const columnChangeData = {
1648
- tableName: triggerResult.table,
1649
- rowId: triggerResult.record.id,
1650
- newData: triggerResult.record,
1651
- oldData: triggerResult.previousRecord,
1652
- queryMetadata: data.queryMetadata
1653
- };
1654
- callback(columnChangeData, data.queryMetadata);
1655
- },
1656
- { concurrency: 10 }
1657
- );
1658
- }
1659
- hasHandlersForEventType(eventType, filter, triggerResult) {
1660
- if (filter) {
1661
- switch (eventType) {
1662
- case "DATABASE_ROW_INSERT":
1663
- case "DATABASE_ROW_DELETE":
1664
- if (filter.tableName && filter.tableName !== triggerResult.table) return false;
1665
- break;
1666
- case "DATABASE_COLUMN_UPDATE":
1667
- const filterColumnChange = filter;
1668
- if (filterColumnChange.tableName !== filter.tableName) return false;
1669
- if (!filterColumnChange.columns.some((item) => {
1670
- const updatedColumns = Object.keys(
1671
- changedValues(triggerResult.record, triggerResult.previousRecord)
1672
- );
1673
- return updatedColumns.includes(item);
1674
- }))
1675
- return false;
1676
- break;
1677
- }
1740
+ var filterSqlGrammar = `
1741
+ {
1742
+ // ported from pg-format but intentionally will add double quotes to every column
1743
+ function quoteSqlIdentity(value) {
1744
+ if (value === undefined || value === null) {
1745
+ throw new Error('SQL identifier cannot be null or undefined');
1746
+ } else if (value === false) {
1747
+ return '"f"';
1748
+ } else if (value === true) {
1749
+ return '"t"';
1750
+ } else if (value instanceof Date) {
1751
+ // return '"' + formatDate(value.toISOString()) + '"';
1752
+ } else if (value instanceof Buffer) {
1753
+ throw new Error('SQL identifier cannot be a buffer');
1754
+ } else if (Array.isArray(value) === true) {
1755
+ var temp = [];
1756
+ for (var i = 0; i < value.length; i++) {
1757
+ if (Array.isArray(value[i]) === true) {
1758
+ throw new Error('Nested array to grouped list conversion is not supported for SQL identifier');
1759
+ } else {
1760
+ // temp.push(quoteIdent(value[i]));
1761
+ }
1762
+ }
1763
+ return temp.toString();
1764
+ } else if (value === Object(value)) {
1765
+ throw new Error('SQL identifier cannot be an object');
1678
1766
  }
1679
- return true;
1680
- }
1681
- };
1682
- var eventManager = new EventManager();
1683
- var eventManager_default = eventManager;
1684
- function changedValues(record, previousRecord) {
1685
- const changed = {};
1686
- for (const i in previousRecord) {
1687
- if (previousRecord[i] !== record[i]) {
1688
- if (typeof previousRecord[i] === "object" && typeof record[i] === "object") {
1689
- const nestedChanged = changedValues(record[i], previousRecord[i]);
1690
- if (Object.keys(nestedChanged).length > 0) {
1691
- changed[i] = record[i];
1767
+
1768
+ var ident = value.toString().slice(0); // create copy
1769
+
1770
+ // do not quote a valid, unquoted identifier
1771
+ // if (/^[a-z_][a-z0-9_$]*$/.test(ident) === true && isReserved(ident) === false) {
1772
+ // return ident;
1773
+ // }
1774
+
1775
+ var quoted = '"';
1776
+
1777
+ for (var i = 0; i < ident.length; i++) {
1778
+ var c = ident[i];
1779
+ if (c === '"') {
1780
+ quoted += c + c;
1781
+ } else {
1782
+ quoted += c;
1692
1783
  }
1693
- } else {
1694
- changed[i] = record[i];
1695
- }
1696
1784
  }
1697
- }
1698
- return changed;
1785
+
1786
+ quoted += '"';
1787
+
1788
+ return quoted;
1789
+ };
1699
1790
  }
1700
1791
 
1792
+ start = expressionList
1793
+
1794
+ _ = [ \\t\\r\\n]* // Matches spaces, tabs, and line breaks
1795
+
1796
+ expressionList =
1797
+ leftExpression:expression _ operator:operator _ rightExpression:expressionList
1798
+ { return \`\${leftExpression} \${operator} \${rightExpression}\`;}
1799
+ / expression
1800
+
1801
+ expression =
1802
+ negate:negate? _ "(" _ "column" _ ":" column:column _ ","? _ value:value? ","? _ type:type? _ ")"_
1803
+ {return \`\${negate? " NOT " : ""}(\${type? type(column, value) : \`\${column} = \${format.literal(value)}\`})\`;}
1804
+ /
1805
+ negate:negate?"("expression:expressionList")" { return \`\${negate? " NOT " : ""}(\${expression})\`; }
1806
+
1807
+ negate = "!"
1808
+
1809
+ operator = "and"i / "or"i
1810
+
1811
+
1812
+ column = left:text "." right:text { return \`\${quoteSqlIdentity(left)}.\${quoteSqlIdentity(right)}\`; }
1813
+ /
1814
+ text:text { return quoteSqlIdentity(text); }
1815
+
1816
+
1817
+ text = text:[a-z0-9 \\t\\r\\n\\-_:@]i+ { return text.join(""); }
1818
+
1819
+ type = "type" _ ":" _ type:typeString { return type; }
1820
+ typeString = text:"startsWith" { return function(column, value) { return \`\${column} ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } } /
1821
+ text:"endsWith" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } } /
1822
+ text:"contains" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } } /
1823
+ text:"exact" { return function(column, value) { return \`\${column} = '\${format.literal(value).slice(1,-1)}'\`; } } /
1824
+ text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${format.literal(value).slice(1,-1)}'\`; } } /
1825
+ text:"greaterThan" { return function(column, value) { return \`\${column} > '\${format.literal(value).slice(1,-1)}'\`; } } /
1826
+ text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${format.literal(value).slice(1,-1)}'\`; } } /
1827
+ text:"lessThan" { return function(column, value) { return \`\${column} < '\${format.literal(value).slice(1,-1)}'\`; } } /
1828
+ text:"isNull" { return function(column, value) { return \`isNull(\${column})\`; } }
1829
+
1830
+ value = "value" _ ":" value:text { return value; }
1831
+
1832
+
1833
+ `;
1834
+ var filterPsqlParser = peg.generate(filterSqlGrammar, {
1835
+ format: "commonjs",
1836
+ dependencies: { format: "pg-format" }
1837
+ });
1838
+ var filterPsqlParser_default = filterPsqlParser;
1839
+
1701
1840
  // src/restura/sql/PsqlEngine.ts
1702
1841
  var { Client } = pg2;
1703
1842
  var systemUser = {
@@ -1726,7 +1865,7 @@ var PsqlEngine = class extends SqlEngine {
1726
1865
  database: this.psqlConnectionPool.poolConfig.database,
1727
1866
  password: this.psqlConnectionPool.poolConfig.password,
1728
1867
  port: this.psqlConnectionPool.poolConfig.port,
1729
- connectionTimeoutMillis: 2e3
1868
+ connectionTimeoutMillis: this.psqlConnectionPool.poolConfig.connectionTimeoutMillis
1730
1869
  });
1731
1870
  await this.triggerClient.connect();
1732
1871
  const promises = [];
@@ -2262,121 +2401,6 @@ function schemaToPsqlType(column) {
2262
2401
  return column.type;
2263
2402
  }
2264
2403
 
2265
- // src/restura/compareSchema.ts
2266
- import cloneDeep from "lodash.clonedeep";
2267
- var CompareSchema = class {
2268
- async diffSchema(newSchema, latestSchema, psqlEngine) {
2269
- const endPoints = this.diffEndPoints(newSchema.endpoints[0].routes, latestSchema.endpoints[0].routes);
2270
- const globalParams = this.diffStringArray(newSchema.globalParams, latestSchema.globalParams);
2271
- const roles = this.diffStringArray(newSchema.roles, latestSchema.roles);
2272
- let commands = "";
2273
- if (JSON.stringify(newSchema.database) !== JSON.stringify(latestSchema.database))
2274
- commands = await psqlEngine.diffDatabaseToSchema(newSchema);
2275
- const customTypes = newSchema.customTypes !== latestSchema.customTypes;
2276
- const schemaPreview = { endPoints, globalParams, roles, commands, customTypes };
2277
- return schemaPreview;
2278
- }
2279
- diffStringArray(newArray, originalArray) {
2280
- const stringsDiff = [];
2281
- const originalClone = new Set(originalArray);
2282
- newArray.forEach((item) => {
2283
- const originalIndex = originalClone.has(item);
2284
- if (!originalIndex) {
2285
- stringsDiff.push({
2286
- name: item,
2287
- changeType: "NEW"
2288
- });
2289
- } else {
2290
- originalClone.delete(item);
2291
- }
2292
- });
2293
- originalClone.forEach((item) => {
2294
- stringsDiff.push({
2295
- name: item,
2296
- changeType: "DELETED"
2297
- });
2298
- });
2299
- return stringsDiff;
2300
- }
2301
- diffEndPoints(newEndPoints, originalEndpoints) {
2302
- const originalClone = cloneDeep(originalEndpoints);
2303
- const diffObj = [];
2304
- newEndPoints.forEach((endPoint) => {
2305
- const { path: path5, method } = endPoint;
2306
- const endPointIndex = originalClone.findIndex((original) => {
2307
- return original.path === endPoint.path && original.method === endPoint.method;
2308
- });
2309
- if (endPointIndex === -1) {
2310
- diffObj.push({
2311
- name: `${method} ${path5}`,
2312
- changeType: "NEW"
2313
- });
2314
- } else {
2315
- const original = originalClone.findIndex((original2) => {
2316
- return this.compareEndPoints(endPoint, original2);
2317
- });
2318
- if (original === -1) {
2319
- diffObj.push({
2320
- name: `${method} ${path5}`,
2321
- changeType: "MODIFIED"
2322
- });
2323
- }
2324
- originalClone.splice(endPointIndex, 1);
2325
- }
2326
- });
2327
- originalClone.forEach((original) => {
2328
- const { path: path5, method } = original;
2329
- diffObj.push({
2330
- name: `${method} ${path5}`,
2331
- changeType: "DELETED"
2332
- });
2333
- });
2334
- return diffObj;
2335
- }
2336
- compareEndPoints(endPoint1, endPoint2) {
2337
- return JSON.stringify(endPoint1) === JSON.stringify(endPoint2);
2338
- }
2339
- };
2340
- __decorateClass([
2341
- boundMethod
2342
- ], CompareSchema.prototype, "diffSchema", 1);
2343
- __decorateClass([
2344
- boundMethod
2345
- ], CompareSchema.prototype, "diffStringArray", 1);
2346
- __decorateClass([
2347
- boundMethod
2348
- ], CompareSchema.prototype, "diffEndPoints", 1);
2349
- __decorateClass([
2350
- boundMethod
2351
- ], CompareSchema.prototype, "compareEndPoints", 1);
2352
- var compareSchema = new CompareSchema();
2353
- var compareSchema_default = compareSchema;
2354
-
2355
- // src/restura/middleware/getMulterUploadSingleton.ts
2356
- import multer from "multer";
2357
- import { extname } from "path";
2358
- import * as os from "os";
2359
- var OneHundredMB = 100 * 1024 * 1024;
2360
- var commonUpload = null;
2361
- var getMulterUploadSingleton = (directory) => {
2362
- if (commonUpload) return commonUpload;
2363
- const storage = multer.diskStorage({
2364
- destination: directory || os.tmpdir(),
2365
- filename: function(request, file, cb) {
2366
- const extension = extname(file.originalname);
2367
- const uniqueName = Date.now() + "-" + Math.round(Math.random() * 1e3);
2368
- cb(null, `${uniqueName}${extension}`);
2369
- }
2370
- });
2371
- commonUpload = multer({
2372
- storage,
2373
- limits: {
2374
- fileSize: OneHundredMB
2375
- }
2376
- });
2377
- return commonUpload;
2378
- };
2379
-
2380
2404
  // src/restura/utils/TempCache.ts
2381
2405
  import fs3 from "fs";
2382
2406
  import path3 from "path";
@@ -2429,7 +2453,7 @@ var ResturaEngine = class {
2429
2453
  */
2430
2454
  async init(app, authenticationHandler, psqlConnectionPool) {
2431
2455
  this.resturaConfig = config2.validate("restura", resturaConfigSchema);
2432
- this.multerCommonUpload = getMulterUploadSingleton(this.resturaConfig.fileTempCachePath);
2456
+ this.multerCommonUpload = getMulterUpload(this.resturaConfig.fileTempCachePath);
2433
2457
  new TempCache(this.resturaConfig.fileTempCachePath);
2434
2458
  this.psqlConnectionPool = psqlConnectionPool;
2435
2459
  this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true);
@@ -2497,10 +2521,7 @@ var ResturaEngine = class {
2497
2521
  * @returns A promise that resolves when the API has been successfully generated and written to the output file.
2498
2522
  */
2499
2523
  async generateApiFromSchema(outputFile, providedSchema) {
2500
- fs4.writeFileSync(
2501
- outputFile,
2502
- await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2503
- );
2524
+ fs4.writeFileSync(outputFile, await apiGenerator(providedSchema));
2504
2525
  }
2505
2526
  /**
2506
2527
  * Generates a model from the provided schema and writes it to the specified output file.
@@ -2510,10 +2531,15 @@ var ResturaEngine = class {
2510
2531
  * @returns A promise that resolves when the model has been successfully written to the output file.
2511
2532
  */
2512
2533
  async generateModelFromSchema(outputFile, providedSchema) {
2513
- fs4.writeFileSync(
2514
- outputFile,
2515
- await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2516
- );
2534
+ fs4.writeFileSync(outputFile, await modelGenerator(providedSchema));
2535
+ }
2536
+ /**
2537
+ * Generates the ambient module declaration for Restura global types and writes it to the specified output file.
2538
+ * These types are used sometimes in the CustomTypes
2539
+ * @param outputFile
2540
+ */
2541
+ generateResturaGlobalTypes(outputFile) {
2542
+ fs4.writeFileSync(outputFile, resturaGlobalTypesGenerator());
2517
2543
  }
2518
2544
  /**
2519
2545
  * Retrieves the latest file system schema for Restura.
@@ -2535,28 +2561,6 @@ var ResturaEngine = class {
2535
2561
  }
2536
2562
  return schema;
2537
2563
  }
2538
- /**
2539
- * Asynchronously generates and retrieves hashes for the provided schema and related generated files.
2540
- *
2541
- * @param providedSchema - The schema for which hashes need to be generated.
2542
- * @returns A promise that resolves to an object containing:
2543
- * - `schemaHash`: The hash of the provided schema.
2544
- * - `apiCreatedSchemaHash`: The hash extracted from the generated `api.d.ts` file.
2545
- * - `modelCreatedSchemaHash`: The hash extracted from the generated `models.d.ts` file.
2546
- */
2547
- async getHashes(providedSchema) {
2548
- var _a2, _b, _c, _d;
2549
- const schemaHash = await this.generateHashForSchema(providedSchema);
2550
- const apiFile = fs4.readFileSync(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2551
- const apiCreatedSchemaHash = (_b = (_a2 = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a2[1]) != null ? _b : "";
2552
- const modelFile = fs4.readFileSync(path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2553
- const modelCreatedSchemaHash = (_d = (_c = modelFile.toString().match(/\((.*)\)/)) == null ? void 0 : _c[1]) != null ? _d : "";
2554
- return {
2555
- schemaHash,
2556
- apiCreatedSchemaHash,
2557
- modelCreatedSchemaHash
2558
- };
2559
- }
2560
2564
  async reloadEndpoints() {
2561
2565
  this.schema = await this.getLatestFileSystemSchema();
2562
2566
  this.customTypeValidation = customTypeValidationGenerator(this.schema);
@@ -2588,27 +2592,7 @@ var ResturaEngine = class {
2588
2592
  if (!fs4.existsSync(this.resturaConfig.generatedTypesPath)) {
2589
2593
  fs4.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
2590
2594
  }
2591
- const hasApiFile = fs4.existsSync(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
2592
- const hasModelsFile = fs4.existsSync(path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
2593
- if (!hasApiFile) {
2594
- await this.generateApiFromSchema(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2595
- }
2596
- if (!hasModelsFile) {
2597
- await this.generateModelFromSchema(
2598
- path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2599
- this.schema
2600
- );
2601
- }
2602
- const hashes = await this.getHashes(this.schema);
2603
- if (hashes.schemaHash !== hashes.apiCreatedSchemaHash) {
2604
- await this.generateApiFromSchema(path4.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2605
- }
2606
- if (hashes.schemaHash !== hashes.modelCreatedSchemaHash) {
2607
- await this.generateModelFromSchema(
2608
- path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2609
- this.schema
2610
- );
2611
- }
2595
+ this.updateTypes();
2612
2596
  }
2613
2597
  resturaAuthentication(req, res, next) {
2614
2598
  if (req.headers["x-auth-token"] !== this.resturaConfig.authToken) res.status(401).send("Unauthorized");
@@ -2640,6 +2624,7 @@ var ResturaEngine = class {
2640
2624
  path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2641
2625
  this.schema
2642
2626
  );
2627
+ this.generateResturaGlobalTypes(path4.join(this.resturaConfig.generatedTypesPath, "restura.d.ts"));
2643
2628
  }
2644
2629
  async getSchema(req, res) {
2645
2630
  res.send({ data: this.schema });
@@ -2647,9 +2632,8 @@ var ResturaEngine = class {
2647
2632
  async getSchemaAndTypes(req, res) {
2648
2633
  try {
2649
2634
  const schema = await this.getLatestFileSystemSchema();
2650
- const schemaHash = await this.generateHashForSchema(schema);
2651
- const apiText = await apiGenerator(schema, schemaHash);
2652
- const modelsText = await modelGenerator(schema, schemaHash);
2635
+ const apiText = await apiGenerator(schema);
2636
+ const modelsText = await modelGenerator(schema);
2653
2637
  res.send({ schema, api: apiText, models: modelsText });
2654
2638
  } catch (err) {
2655
2639
  res.status(400).send({ error: err });
@@ -2679,7 +2663,7 @@ var ResturaEngine = class {
2679
2663
  const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
2680
2664
  this.validateAuthorization(req, routeData);
2681
2665
  await this.getMulterFilesIfAny(req, res, routeData);
2682
- validateRequestParams(req, routeData, this.customTypeValidation);
2666
+ requestValidator(req, routeData, this.customTypeValidation);
2683
2667
  if (this.isCustomRoute(routeData)) {
2684
2668
  await this.runCustomRouteLogic(req, res, routeData);
2685
2669
  return;
@@ -2719,19 +2703,6 @@ var ResturaEngine = class {
2719
2703
  throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented ${functionName}`);
2720
2704
  await customFunction(req, res, routeData);
2721
2705
  }
2722
- async generateHashForSchema(providedSchema) {
2723
- const schemaPrettyStr = await prettier3.format(JSON.stringify(providedSchema), __spreadValues({
2724
- parser: "json"
2725
- }, {
2726
- trailingComma: "none",
2727
- tabWidth: 4,
2728
- useTabs: true,
2729
- endOfLine: "lf",
2730
- printWidth: 120,
2731
- singleQuote: true
2732
- }));
2733
- return createHash("sha256").update(schemaPrettyStr).digest("hex");
2734
- }
2735
2706
  async storeFileSystemSchema() {
2736
2707
  const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema), __spreadValues({
2737
2708
  parser: "json"