@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.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;
@@ -548,108 +812,49 @@ var ApiTree = class _ApiTree {
548
812
  return {
549
813
  responseType: SqlUtils.convertDatabaseTypeToTypescript(column.type, column.value),
550
814
  isNullable: column.roles.length > 0 || column.isNullable
551
- };
552
- }
553
- };
554
- function pathToNamespaces(path5) {
555
- return path5.split("/").map((e) => StringUtils.toPascalCasing(e)).filter((e) => e);
556
- }
557
- function apiGenerator(schema, schemaHash) {
558
- let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
559
- const rootNamespace = ApiTree.createRootNode(schema.database);
560
- for (const endpoint of schema.endpoints) {
561
- const endpointNamespaces = pathToNamespaces(endpoint.baseUrl);
562
- rootNamespace.addData(endpointNamespaces, endpoint);
563
- for (const route of endpoint.routes) {
564
- const fullNamespace = [...endpointNamespaces, ...pathToNamespaces(route.path)];
565
- rootNamespace.addData(fullNamespace, route);
566
- }
567
- }
568
- apiString += rootNamespace.createApiModels();
569
- if (schema.customTypes.length > 0) {
570
- apiString += `
571
-
572
- declare namespace CustomTypes {
573
- ${schema.customTypes}
574
- }`;
575
- }
576
- return prettier.format(apiString, __spreadValues({
577
- parser: "typescript"
578
- }, {
579
- trailingComma: "none",
580
- tabWidth: 4,
581
- useTabs: true,
582
- endOfLine: "lf",
583
- printWidth: 120,
584
- singleQuote: true
585
- }));
586
- }
587
-
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
- });
815
+ };
642
816
  }
643
817
  };
644
- var customApiFactory = new CustomApiFactory();
645
- var customApiFactory_default = customApiFactory;
818
+ function pathToNamespaces(path5) {
819
+ return path5.split("/").map((e) => StringUtils.toPascalCasing(e)).filter((e) => e);
820
+ }
821
+ function apiGenerator(schema) {
822
+ let apiString = `/** Auto generated file. DO NOT MODIFY **/
823
+ `;
824
+ const rootNamespace = ApiTree.createRootNode(schema.database);
825
+ for (const endpoint of schema.endpoints) {
826
+ const endpointNamespaces = pathToNamespaces(endpoint.baseUrl);
827
+ rootNamespace.addData(endpointNamespaces, endpoint);
828
+ for (const route of endpoint.routes) {
829
+ const fullNamespace = [...endpointNamespaces, ...pathToNamespaces(route.path)];
830
+ rootNamespace.addData(fullNamespace, route);
831
+ }
832
+ }
833
+ apiString += rootNamespace.createApiModels();
834
+ if (schema.customTypes.length > 0) {
835
+ apiString += `
836
+
837
+ declare namespace CustomTypes {
838
+ ${schema.customTypes}
839
+ }`;
840
+ }
841
+ return prettier.format(apiString, __spreadValues({
842
+ parser: "typescript"
843
+ }, {
844
+ trailingComma: "none",
845
+ tabWidth: 4,
846
+ useTabs: true,
847
+ endOfLine: "lf",
848
+ printWidth: 120,
849
+ singleQuote: true
850
+ }));
851
+ }
646
852
 
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) {
@@ -1130,234 +1416,88 @@ function performMaxCheck(requestValue, validator, requestParamName) {
1130
1416
  "BAD_REQUEST",
1131
1417
  `Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
1132
1418
  );
1133
- }
1134
- function performOneOfCheck(requestValue, validator, requestParamName) {
1135
- if (!ObjectUtils2.isArrayWithData(validator.value))
1136
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
1137
- if (typeof requestValue === "object")
1138
- throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
1139
- if (!validator.value.includes(requestValue))
1140
- throw new RsError(
1141
- "BAD_REQUEST",
1142
- `Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
1143
- );
1144
- }
1145
- function isValueNumber(value) {
1146
- return !isNaN(Number(value));
1147
- }
1148
- function getRequestData(req) {
1149
- let body = "";
1150
- if (req.method === "GET" || req.method === "DELETE") {
1151
- body = "query";
1152
- } else {
1153
- body = "body";
1154
- }
1155
- const bodyData = req[body];
1156
- if (bodyData && body === "query") {
1157
- for (const attr in bodyData) {
1158
- if (bodyData[attr] instanceof Array) {
1159
- const attrList = [];
1160
- for (const value of bodyData[attr]) {
1161
- if (isNaN(Number(value))) continue;
1162
- attrList.push(Number(value));
1163
- }
1164
- if (ObjectUtils2.isArrayWithData(attrList)) {
1165
- bodyData[attr] = attrList;
1166
- }
1167
- } else {
1168
- bodyData[attr] = ObjectUtils2.safeParse(bodyData[attr]);
1169
- if (isNaN(Number(bodyData[attr]))) continue;
1170
- bodyData[attr] = Number(bodyData[attr]);
1171
- }
1172
- }
1173
- }
1174
- return bodyData;
1175
- }
1176
-
1177
- // src/restura/middleware/schemaValidation.ts
1178
- async function schemaValidation(req, res, next) {
1179
- req.data = getRequestData(req);
1180
- try {
1181
- resturaZodSchema.parse(req.data);
1182
- next();
1183
- } catch (error) {
1184
- logger.error(error);
1185
- res.sendError("BAD_REQUEST", error, 400 /* BAD_REQUEST */);
1186
- }
1187
- }
1188
-
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
- }
1223
-
1224
- // src/restura/sql/PsqlEngine.ts
1225
- import { ObjectUtils as ObjectUtils4 } from "@redskytech/core-utils";
1226
- import getDiff from "@wmfs/pg-diff-sync";
1227
- import pgInfo from "@wmfs/pg-info";
1228
- import pg2 from "pg";
1229
-
1230
- // src/restura/eventManager.ts
1231
- import Bluebird2 from "bluebird";
1232
- var EventManager = class {
1233
- constructor() {
1234
- this.actionHandlers = {
1235
- DATABASE_ROW_DELETE: [],
1236
- DATABASE_ROW_INSERT: [],
1237
- DATABASE_COLUMN_UPDATE: []
1238
- };
1239
- }
1240
- addRowInsertHandler(onInsert, filter) {
1241
- this.actionHandlers.DATABASE_ROW_INSERT.push({
1242
- callback: onInsert,
1243
- filter
1244
- });
1245
- }
1246
- addColumnChangeHandler(onUpdate, filter) {
1247
- this.actionHandlers.DATABASE_COLUMN_UPDATE.push({
1248
- callback: onUpdate,
1249
- filter
1250
- });
1251
- }
1252
- addRowDeleteHandler(onDelete, filter) {
1253
- this.actionHandlers.DATABASE_ROW_DELETE.push({
1254
- callback: onDelete,
1255
- filter
1256
- });
1257
- }
1258
- async fireActionFromDbTrigger(sqlMutationData, result) {
1259
- if (sqlMutationData.mutationType === "INSERT") {
1260
- await this.fireInsertActions(sqlMutationData, result);
1261
- } else if (sqlMutationData.mutationType === "UPDATE") {
1262
- await this.fireUpdateActions(sqlMutationData, result);
1263
- } else if (sqlMutationData.mutationType === "DELETE") {
1264
- await this.fireDeleteActions(sqlMutationData, result);
1265
- }
1266
- }
1267
- async fireInsertActions(data, triggerResult) {
1268
- await Bluebird2.map(
1269
- this.actionHandlers.DATABASE_ROW_INSERT,
1270
- ({ callback, filter }) => {
1271
- if (!this.hasHandlersForEventType("DATABASE_ROW_INSERT", filter, triggerResult)) return;
1272
- const insertData = {
1273
- tableName: triggerResult.table,
1274
- insertId: triggerResult.record.id,
1275
- insertObject: triggerResult.record,
1276
- queryMetadata: data.queryMetadata
1277
- };
1278
- callback(insertData, data.queryMetadata);
1279
- },
1280
- { concurrency: 10 }
1281
- );
1282
- }
1283
- async fireDeleteActions(data, triggerResult) {
1284
- await Bluebird2.map(
1285
- this.actionHandlers.DATABASE_ROW_DELETE,
1286
- ({ callback, filter }) => {
1287
- if (!this.hasHandlersForEventType("DATABASE_ROW_DELETE", filter, triggerResult)) return;
1288
- const deleteData = {
1289
- tableName: triggerResult.table,
1290
- deletedRow: triggerResult.previousRecord,
1291
- queryMetadata: data.queryMetadata
1292
- };
1293
- callback(deleteData, data.queryMetadata);
1294
- },
1295
- { concurrency: 10 }
1296
- );
1297
- }
1298
- async fireUpdateActions(data, triggerResult) {
1299
- await Bluebird2.map(
1300
- this.actionHandlers.DATABASE_COLUMN_UPDATE,
1301
- ({ callback, filter }) => {
1302
- if (!this.hasHandlersForEventType("DATABASE_COLUMN_UPDATE", filter, triggerResult)) return;
1303
- const columnChangeData = {
1304
- tableName: triggerResult.table,
1305
- rowId: triggerResult.record.id,
1306
- newData: triggerResult.record,
1307
- oldData: triggerResult.previousRecord,
1308
- queryMetadata: data.queryMetadata
1309
- };
1310
- callback(columnChangeData, data.queryMetadata);
1311
- },
1312
- { concurrency: 10 }
1419
+ }
1420
+ function performOneOfCheck(requestValue, validator, requestParamName) {
1421
+ if (!ObjectUtils2.isArrayWithData(validator.value))
1422
+ throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
1423
+ if (typeof requestValue === "object")
1424
+ throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
1425
+ if (!validator.value.includes(requestValue))
1426
+ throw new RsError(
1427
+ "BAD_REQUEST",
1428
+ `Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
1313
1429
  );
1430
+ }
1431
+ function isValueNumber(value) {
1432
+ return !isNaN(Number(value));
1433
+ }
1434
+ function getRequestData(req) {
1435
+ let body = "";
1436
+ if (req.method === "GET" || req.method === "DELETE") {
1437
+ body = "query";
1438
+ } else {
1439
+ body = "body";
1314
1440
  }
1315
- hasHandlersForEventType(eventType, filter, triggerResult) {
1316
- if (filter) {
1317
- switch (eventType) {
1318
- case "DATABASE_ROW_INSERT":
1319
- case "DATABASE_ROW_DELETE":
1320
- if (filter.tableName && filter.tableName !== triggerResult.table) return false;
1321
- break;
1322
- case "DATABASE_COLUMN_UPDATE":
1323
- const filterColumnChange = filter;
1324
- if (filterColumnChange.tableName !== filter.tableName) return false;
1325
- if (!filterColumnChange.columns.some((item) => {
1326
- const updatedColumns = Object.keys(
1327
- changedValues(triggerResult.record, triggerResult.previousRecord)
1328
- );
1329
- return updatedColumns.includes(item);
1330
- }))
1331
- return false;
1332
- break;
1333
- }
1334
- }
1335
- return true;
1336
- }
1337
- };
1338
- var eventManager = new EventManager();
1339
- var eventManager_default = eventManager;
1340
- function changedValues(record, previousRecord) {
1341
- const changed = {};
1342
- for (const i in previousRecord) {
1343
- if (previousRecord[i] !== record[i]) {
1344
- if (typeof previousRecord[i] === "object" && typeof record[i] === "object") {
1345
- const nestedChanged = changedValues(record[i], previousRecord[i]);
1346
- if (Object.keys(nestedChanged).length > 0) {
1347
- changed[i] = record[i];
1441
+ const bodyData = req[body];
1442
+ if (bodyData && body === "query") {
1443
+ for (const attr in bodyData) {
1444
+ if (bodyData[attr] instanceof Array) {
1445
+ const attrList = [];
1446
+ for (const value of bodyData[attr]) {
1447
+ if (isNaN(Number(value))) continue;
1448
+ attrList.push(Number(value));
1449
+ }
1450
+ if (ObjectUtils2.isArrayWithData(attrList)) {
1451
+ bodyData[attr] = attrList;
1348
1452
  }
1349
1453
  } else {
1350
- changed[i] = record[i];
1454
+ bodyData[attr] = ObjectUtils2.safeParse(bodyData[attr]);
1455
+ if (isNaN(Number(bodyData[attr]))) continue;
1456
+ bodyData[attr] = Number(bodyData[attr]);
1351
1457
  }
1352
1458
  }
1353
1459
  }
1354
- return changed;
1460
+ return bodyData;
1461
+ }
1462
+
1463
+ // src/restura/middleware/schemaValidation.ts
1464
+ async function schemaValidation(req, res, next) {
1465
+ req.data = getRequestData(req);
1466
+ try {
1467
+ resturaSchema.parse(req.data);
1468
+ next();
1469
+ } catch (error) {
1470
+ logger.error(error);
1471
+ res.sendError("BAD_REQUEST", error, 400 /* BAD_REQUEST */);
1472
+ }
1355
1473
  }
1356
1474
 
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
+ });
1489
+
1490
+ // src/restura/sql/PsqlEngine.ts
1491
+ import { ObjectUtils as ObjectUtils4 } from "@redskytech/core-utils";
1492
+ import getDiff from "@wmfs/pg-diff-sync";
1493
+ import pgInfo from "@wmfs/pg-info";
1494
+ import pg2 from "pg";
1495
+
1357
1496
  // src/restura/sql/PsqlPool.ts
1358
1497
  import pg from "pg";
1359
1498
 
1360
1499
  // src/restura/sql/PsqlConnection.ts
1500
+ import crypto from "crypto";
1361
1501
  import format3 from "pg-format";
1362
1502
 
1363
1503
  // src/restura/sql/PsqlUtils.ts
@@ -1368,7 +1508,22 @@ function escapeColumnName(columnName) {
1368
1508
  }
1369
1509
  function questionMarksToOrderedParams(query) {
1370
1510
  let count = 1;
1371
- return query.replace(/'\?'|\?/g, () => `$${count++}`);
1511
+ let inSingleQuote = false;
1512
+ let inDoubleQuote = false;
1513
+ return query.replace(/('|"|\?)/g, (char) => {
1514
+ if (char === "'") {
1515
+ inSingleQuote = !inSingleQuote && !inDoubleQuote;
1516
+ return char;
1517
+ }
1518
+ if (char === '"') {
1519
+ inDoubleQuote = !inDoubleQuote && !inSingleQuote;
1520
+ return char;
1521
+ }
1522
+ if (char === "?" && !inSingleQuote && !inDoubleQuote) {
1523
+ return `$${count++}`;
1524
+ }
1525
+ return char;
1526
+ });
1372
1527
  }
1373
1528
  function insertObjectQuery(table, obj) {
1374
1529
  const keys = Object.keys(obj);
@@ -1410,7 +1565,6 @@ function SQL(strings, ...values) {
1410
1565
  }
1411
1566
 
1412
1567
  // src/restura/sql/PsqlConnection.ts
1413
- import crypto from "crypto";
1414
1568
  var PsqlConnection = class {
1415
1569
  constructor() {
1416
1570
  this.instanceId = crypto.randomUUID();
@@ -2262,121 +2416,6 @@ function schemaToPsqlType(column) {
2262
2416
  return column.type;
2263
2417
  }
2264
2418
 
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
2419
  // src/restura/utils/TempCache.ts
2381
2420
  import fs3 from "fs";
2382
2421
  import path3 from "path";
@@ -2429,7 +2468,7 @@ var ResturaEngine = class {
2429
2468
  */
2430
2469
  async init(app, authenticationHandler, psqlConnectionPool) {
2431
2470
  this.resturaConfig = config2.validate("restura", resturaConfigSchema);
2432
- this.multerCommonUpload = getMulterUploadSingleton(this.resturaConfig.fileTempCachePath);
2471
+ this.multerCommonUpload = getMulterUpload(this.resturaConfig.fileTempCachePath);
2433
2472
  new TempCache(this.resturaConfig.fileTempCachePath);
2434
2473
  this.psqlConnectionPool = psqlConnectionPool;
2435
2474
  this.psqlEngine = new PsqlEngine(this.psqlConnectionPool, true);
@@ -2497,10 +2536,7 @@ var ResturaEngine = class {
2497
2536
  * @returns A promise that resolves when the API has been successfully generated and written to the output file.
2498
2537
  */
2499
2538
  async generateApiFromSchema(outputFile, providedSchema) {
2500
- fs4.writeFileSync(
2501
- outputFile,
2502
- await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2503
- );
2539
+ fs4.writeFileSync(outputFile, await apiGenerator(providedSchema));
2504
2540
  }
2505
2541
  /**
2506
2542
  * Generates a model from the provided schema and writes it to the specified output file.
@@ -2510,10 +2546,15 @@ var ResturaEngine = class {
2510
2546
  * @returns A promise that resolves when the model has been successfully written to the output file.
2511
2547
  */
2512
2548
  async generateModelFromSchema(outputFile, providedSchema) {
2513
- fs4.writeFileSync(
2514
- outputFile,
2515
- await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
2516
- );
2549
+ fs4.writeFileSync(outputFile, await modelGenerator(providedSchema));
2550
+ }
2551
+ /**
2552
+ * Generates the ambient module declaration for Restura global types and writes it to the specified output file.
2553
+ * These types are used sometimes in the CustomTypes
2554
+ * @param outputFile
2555
+ */
2556
+ generateResturaGlobalTypes(outputFile) {
2557
+ fs4.writeFileSync(outputFile, resturaGlobalTypesGenerator());
2517
2558
  }
2518
2559
  /**
2519
2560
  * Retrieves the latest file system schema for Restura.
@@ -2535,28 +2576,6 @@ var ResturaEngine = class {
2535
2576
  }
2536
2577
  return schema;
2537
2578
  }
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
2579
  async reloadEndpoints() {
2561
2580
  this.schema = await this.getLatestFileSystemSchema();
2562
2581
  this.customTypeValidation = customTypeValidationGenerator(this.schema);
@@ -2588,27 +2607,7 @@ var ResturaEngine = class {
2588
2607
  if (!fs4.existsSync(this.resturaConfig.generatedTypesPath)) {
2589
2608
  fs4.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
2590
2609
  }
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
- }
2610
+ this.updateTypes();
2612
2611
  }
2613
2612
  resturaAuthentication(req, res, next) {
2614
2613
  if (req.headers["x-auth-token"] !== this.resturaConfig.authToken) res.status(401).send("Unauthorized");
@@ -2640,6 +2639,7 @@ var ResturaEngine = class {
2640
2639
  path4.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2641
2640
  this.schema
2642
2641
  );
2642
+ this.generateResturaGlobalTypes(path4.join(this.resturaConfig.generatedTypesPath, "restura.d.ts"));
2643
2643
  }
2644
2644
  async getSchema(req, res) {
2645
2645
  res.send({ data: this.schema });
@@ -2647,9 +2647,8 @@ var ResturaEngine = class {
2647
2647
  async getSchemaAndTypes(req, res) {
2648
2648
  try {
2649
2649
  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);
2650
+ const apiText = await apiGenerator(schema);
2651
+ const modelsText = await modelGenerator(schema);
2653
2652
  res.send({ schema, api: apiText, models: modelsText });
2654
2653
  } catch (err) {
2655
2654
  res.status(400).send({ error: err });
@@ -2679,7 +2678,7 @@ var ResturaEngine = class {
2679
2678
  const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
2680
2679
  this.validateAuthorization(req, routeData);
2681
2680
  await this.getMulterFilesIfAny(req, res, routeData);
2682
- validateRequestParams(req, routeData, this.customTypeValidation);
2681
+ requestValidator(req, routeData, this.customTypeValidation);
2683
2682
  if (this.isCustomRoute(routeData)) {
2684
2683
  await this.runCustomRouteLogic(req, res, routeData);
2685
2684
  return;
@@ -2719,19 +2718,6 @@ var ResturaEngine = class {
2719
2718
  throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented ${functionName}`);
2720
2719
  await customFunction(req, res, routeData);
2721
2720
  }
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
2721
  async storeFileSystemSchema() {
2736
2722
  const schemaPrettyStr = await prettier3.format(JSON.stringify(this.schema), __spreadValues({
2737
2723
  parser: "json"