@carbonorm/carbonnode 4.0.0 → 5.0.0
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/README.md +246 -507
- package/dist/api/executors/SqlExecutor.d.ts +6 -0
- package/dist/api/handlers/ExpressHandler.d.ts +2 -1
- package/dist/api/orm/builders/ConditionBuilder.d.ts +2 -0
- package/dist/api/types/ormInterfaces.d.ts +12 -0
- package/dist/api/utils/sqlAllowList.d.ts +2 -0
- package/dist/index.cjs.js +279 -20
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +278 -21
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/scripts/assets/handlebars/C6.test.ts.handlebars +578 -32
- package/scripts/generateRestBindings.cjs +5 -5
- package/scripts/generateRestBindings.ts +5 -5
- package/src/__tests__/fixtures/createTestServer.ts +11 -3
- package/src/__tests__/fixtures/sqlResponses/actor.get.json +13 -0
- package/src/__tests__/fixtures/sqlResponses/sqlAllowList.blocked.json +3 -0
- package/src/__tests__/fixtures/sqlResponses/sqlAllowList.json +3 -0
- package/src/__tests__/sakila-db/C6.js +1 -1
- package/src/__tests__/sakila-db/C6.mysql.cnf +6 -0
- package/src/__tests__/sakila-db/C6.mysqldump.json +1 -0
- package/src/__tests__/sakila-db/C6.mysqldump.sql +720 -0
- package/src/__tests__/sakila-db/C6.sqlAllowList.json +94 -0
- package/src/__tests__/sakila-db/C6.test.ts +578 -32
- package/src/__tests__/sakila-db/C6.ts +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.join.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +12 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.seed.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.fk.current.json +358 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.fk.referenced.json +158 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.get.json +22 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +22 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.address.seed.json +22 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.get.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.join.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.category.seed.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.fk.current.json +158 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.fk.referenced.json +133 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.join.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +12 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.city.seed.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.get.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.join.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.country.seed.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.fk.current.json +283 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.fk.referenced.json +358 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.get.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.join.json +29 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +21 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.seed.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.fk.current.json +383 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.fk.referenced.json +38 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.get.json +23 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +23 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +25 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.film.seed.json +23 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.fk.current.json +158 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.fk.referenced.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.join.json +25 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +12 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +16 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.seed.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.get.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.language.seed.json +13 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.fk.current.json +233 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.fk.referenced.json +233 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.get.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.seed.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.delete.json +10 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.delete.lookup.json +9 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.fk.current.json +233 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.fk.referenced.json +34 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.get.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +15 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +11 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +19 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.seed.json +17 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.fk.current.json +34 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.fk.referenced.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.get.json +21 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.join.json +31 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.staff.seed.json +21 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.fk.current.json +20 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.fk.referenced.json +34 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.get.json +14 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.join.json +24 -0
- package/src/__tests__/sakila-db/sqlResponses/C6.store.seed.json +14 -0
- package/src/__tests__/sakila.generated.test.ts +31 -0
- package/src/__tests__/sqlAllowList.test.ts +135 -0
- package/src/__tests__/sqlBuilders.test.ts +17 -0
- package/src/api/executors/SqlExecutor.ts +156 -0
- package/src/api/handlers/ExpressHandler.ts +10 -1
- package/src/api/orm/builders/ConditionBuilder.ts +27 -7
- package/src/api/types/ormInterfaces.ts +15 -0
- package/src/api/utils/sqlAllowList.ts +54 -0
- package/src/index.ts +1 -0
|
@@ -11,6 +11,11 @@ export declare class SqlExecutor<G extends OrmGenerics> extends Executor<G> {
|
|
|
11
11
|
[key: string]: any;
|
|
12
12
|
}): string;
|
|
13
13
|
private formatValue;
|
|
14
|
+
private stripRequestMetadata;
|
|
15
|
+
private normalizeRequestPayload;
|
|
16
|
+
private extractRequestBody;
|
|
17
|
+
private extractPrimaryKeyValues;
|
|
18
|
+
private broadcastWebsocketIfConfigured;
|
|
14
19
|
runQuery(): Promise<{
|
|
15
20
|
rest: any;
|
|
16
21
|
sql: {
|
|
@@ -26,4 +31,5 @@ export declare class SqlExecutor<G extends OrmGenerics> extends Executor<G> {
|
|
|
26
31
|
values: any;
|
|
27
32
|
};
|
|
28
33
|
}>;
|
|
34
|
+
private validateSqlAllowList;
|
|
29
35
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from "express";
|
|
2
2
|
import { Pool } from "mysql2/promise";
|
|
3
3
|
import { iC6Object } from "../types/ormInterfaces";
|
|
4
|
-
export declare function ExpressHandler({ C6, mysqlPool }: {
|
|
4
|
+
export declare function ExpressHandler({ C6, mysqlPool, sqlAllowListPath, }: {
|
|
5
5
|
C6: iC6Object;
|
|
6
6
|
mysqlPool: Pool;
|
|
7
|
+
sqlAllowListPath?: string;
|
|
7
8
|
}): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -27,6 +27,8 @@ export declare abstract class ConditionBuilder<G extends OrmGenerics> extends Ag
|
|
|
27
27
|
private serializeOperand;
|
|
28
28
|
private isPlainArrayLiteral;
|
|
29
29
|
private isPlainObjectLiteral;
|
|
30
|
+
private resolveColumnDefinition;
|
|
31
|
+
private isJsonColumn;
|
|
30
32
|
protected serializeUpdateValue(value: any, params: any[] | Record<string, any>, contextColumn?: string): string;
|
|
31
33
|
private ensurePlainObject;
|
|
32
34
|
private resolveExistsInnerColumn;
|
|
@@ -135,6 +135,16 @@ export interface iGetC6RestResponse<ResponseDataType extends {
|
|
|
135
135
|
export type DetermineResponseDataType<Method extends iRestMethods, RestTableInterface extends {
|
|
136
136
|
[key: string]: any;
|
|
137
137
|
}, ResponseDataOverrides = {}> = (Method extends 'POST' ? iPostC6RestResponse<RestTableInterface> : Method extends 'GET' ? iGetC6RestResponse<RestTableInterface, ResponseDataOverrides> : Method extends 'PUT' ? iPutC6RestResponse<RestTableInterface> : Method extends 'DELETE' ? iDeleteC6RestResponse<RestTableInterface> : never);
|
|
138
|
+
export type iRestWebsocketPayload = {
|
|
139
|
+
REST: {
|
|
140
|
+
TABLE_NAME: string;
|
|
141
|
+
TABLE_PREFIX: string;
|
|
142
|
+
METHOD: iRestMethods;
|
|
143
|
+
REQUEST: Record<string, any>;
|
|
144
|
+
REQUEST_PRIMARY_KEY: Record<string, any> | null;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
export type tWebsocketBroadcast = (payload: iRestWebsocketPayload) => void | Promise<void>;
|
|
138
148
|
export interface iRest<RestShortTableName extends string = any, RestTableInterface extends Record<string, any> = any, PrimaryKey extends keyof RestTableInterface & string = keyof RestTableInterface & string> {
|
|
139
149
|
C6: iC6Object;
|
|
140
150
|
axios?: AxiosInstance;
|
|
@@ -146,7 +156,9 @@ export interface iRest<RestShortTableName extends string = any, RestTableInterfa
|
|
|
146
156
|
requestMethod: iRestMethods;
|
|
147
157
|
clearCache?: () => void;
|
|
148
158
|
skipPrimaryCheck?: boolean;
|
|
159
|
+
websocketBroadcast?: tWebsocketBroadcast;
|
|
149
160
|
verbose?: boolean;
|
|
161
|
+
sqlAllowListPath?: string;
|
|
150
162
|
}
|
|
151
163
|
export interface iConstraint {
|
|
152
164
|
TABLE: string;
|
package/dist/index.cjs.js
CHANGED
|
@@ -1827,8 +1827,9 @@ var ConditionBuilder = /** @class */ (function (_super) {
|
|
|
1827
1827
|
}
|
|
1828
1828
|
throw new Error('Unsupported operand type in SQL expression.');
|
|
1829
1829
|
};
|
|
1830
|
-
ConditionBuilder.prototype.isPlainArrayLiteral = function (value) {
|
|
1830
|
+
ConditionBuilder.prototype.isPlainArrayLiteral = function (value, allowColumnRefs) {
|
|
1831
1831
|
var _this = this;
|
|
1832
|
+
if (allowColumnRefs === void 0) { allowColumnRefs = false; }
|
|
1832
1833
|
if (!Array.isArray(value))
|
|
1833
1834
|
return false;
|
|
1834
1835
|
return value.every(function (item) {
|
|
@@ -1838,14 +1839,15 @@ var ConditionBuilder = /** @class */ (function (_super) {
|
|
|
1838
1839
|
if (type === 'string' || type === 'number' || type === 'boolean')
|
|
1839
1840
|
return true;
|
|
1840
1841
|
if (Array.isArray(item))
|
|
1841
|
-
return _this.isPlainArrayLiteral(item);
|
|
1842
|
+
return _this.isPlainArrayLiteral(item, allowColumnRefs);
|
|
1842
1843
|
if (item && typeof item === 'object')
|
|
1843
|
-
return _this.isPlainObjectLiteral(item);
|
|
1844
|
+
return _this.isPlainObjectLiteral(item, allowColumnRefs);
|
|
1844
1845
|
return false;
|
|
1845
1846
|
});
|
|
1846
1847
|
};
|
|
1847
|
-
ConditionBuilder.prototype.isPlainObjectLiteral = function (value) {
|
|
1848
|
+
ConditionBuilder.prototype.isPlainObjectLiteral = function (value, allowColumnRefs) {
|
|
1848
1849
|
var _this = this;
|
|
1850
|
+
if (allowColumnRefs === void 0) { allowColumnRefs = false; }
|
|
1849
1851
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
1850
1852
|
return false;
|
|
1851
1853
|
if (value instanceof Date)
|
|
@@ -1864,17 +1866,37 @@ var ConditionBuilder = /** @class */ (function (_super) {
|
|
|
1864
1866
|
})) {
|
|
1865
1867
|
return false;
|
|
1866
1868
|
}
|
|
1867
|
-
if (
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1869
|
+
if (!allowColumnRefs) {
|
|
1870
|
+
if (entries.some(function (_a) {
|
|
1871
|
+
var key = _a[0];
|
|
1872
|
+
return typeof key === 'string' && (_this.isColumnRef(key) || key.includes('.'));
|
|
1873
|
+
})) {
|
|
1874
|
+
return false;
|
|
1875
|
+
}
|
|
1872
1876
|
}
|
|
1873
1877
|
return true;
|
|
1874
1878
|
};
|
|
1879
|
+
ConditionBuilder.prototype.resolveColumnDefinition = function (column) {
|
|
1880
|
+
var _a, _b, _c, _d;
|
|
1881
|
+
if (!column || typeof column !== 'string' || !column.includes('.'))
|
|
1882
|
+
return undefined;
|
|
1883
|
+
var _e = column.split('.', 2), prefix = _e[0], colName = _e[1];
|
|
1884
|
+
var tableName = (_a = this.aliasMap[prefix]) !== null && _a !== void 0 ? _a : prefix;
|
|
1885
|
+
var table = (_c = (_b = this.config.C6) === null || _b === void 0 ? void 0 : _b.TABLES) === null || _c === void 0 ? void 0 : _c[tableName];
|
|
1886
|
+
if (!(table === null || table === void 0 ? void 0 : table.TYPE_VALIDATION))
|
|
1887
|
+
return undefined;
|
|
1888
|
+
return (_d = table.TYPE_VALIDATION[colName]) !== null && _d !== void 0 ? _d : table.TYPE_VALIDATION["".concat(tableName, ".").concat(colName)];
|
|
1889
|
+
};
|
|
1890
|
+
ConditionBuilder.prototype.isJsonColumn = function (column) {
|
|
1891
|
+
var columnDef = this.resolveColumnDefinition(column);
|
|
1892
|
+
var mysqlType = columnDef === null || columnDef === void 0 ? void 0 : columnDef.MYSQL_TYPE;
|
|
1893
|
+
return typeof mysqlType === 'string' && mysqlType.toLowerCase().includes('json');
|
|
1894
|
+
};
|
|
1875
1895
|
ConditionBuilder.prototype.serializeUpdateValue = function (value, params, contextColumn) {
|
|
1876
1896
|
var normalized = value instanceof Map ? Object.fromEntries(value) : value;
|
|
1877
|
-
|
|
1897
|
+
var allowColumnRefs = this.isJsonColumn(contextColumn);
|
|
1898
|
+
if (this.isPlainArrayLiteral(normalized, allowColumnRefs)
|
|
1899
|
+
|| this.isPlainObjectLiteral(normalized, allowColumnRefs)) {
|
|
1878
1900
|
return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
|
|
1879
1901
|
}
|
|
1880
1902
|
var _a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect;
|
|
@@ -2941,6 +2963,63 @@ function normalizeSingularRequest(requestMethod, request, restModel, removedPrim
|
|
|
2941
2963
|
return tslib.__assign(tslib.__assign({}, normalized), { dataInsertMultipleRows: dataInsertMultipleRows, cacheResults: cacheResults, fetchDependencies: fetchDependencies, debug: debug, success: success, error: error });
|
|
2942
2964
|
}
|
|
2943
2965
|
|
|
2966
|
+
var allowListCache = new Map();
|
|
2967
|
+
var normalizeSql = function (sql) {
|
|
2968
|
+
return sql.replace(/\s+/g, " ").trim();
|
|
2969
|
+
};
|
|
2970
|
+
var parseAllowList = function (raw, sourcePath) {
|
|
2971
|
+
var parsed;
|
|
2972
|
+
try {
|
|
2973
|
+
parsed = JSON.parse(raw);
|
|
2974
|
+
}
|
|
2975
|
+
catch (error) {
|
|
2976
|
+
throw new Error("SQL allowlist at ".concat(sourcePath, " is not valid JSON."));
|
|
2977
|
+
}
|
|
2978
|
+
if (!Array.isArray(parsed)) {
|
|
2979
|
+
throw new Error("SQL allowlist at ".concat(sourcePath, " must be a JSON array of strings."));
|
|
2980
|
+
}
|
|
2981
|
+
var sqlEntries = parsed
|
|
2982
|
+
.filter(function (entry) { return typeof entry === "string"; })
|
|
2983
|
+
.map(normalizeSql)
|
|
2984
|
+
.filter(function (entry) { return entry.length > 0; });
|
|
2985
|
+
if (sqlEntries.length !== parsed.length) {
|
|
2986
|
+
throw new Error("SQL allowlist at ".concat(sourcePath, " must contain only string entries."));
|
|
2987
|
+
}
|
|
2988
|
+
return sqlEntries;
|
|
2989
|
+
};
|
|
2990
|
+
var loadSqlAllowList = function (allowListPath) { return tslib.__awaiter(void 0, void 0, void 0, function () {
|
|
2991
|
+
var readFile, raw, sqlEntries, allowList;
|
|
2992
|
+
return tslib.__generator(this, function (_a) {
|
|
2993
|
+
switch (_a.label) {
|
|
2994
|
+
case 0:
|
|
2995
|
+
if (allowListCache.has(allowListPath)) {
|
|
2996
|
+
return [2 /*return*/, allowListCache.get(allowListPath)];
|
|
2997
|
+
}
|
|
2998
|
+
if (!isNode()) {
|
|
2999
|
+
throw new Error("SQL allowlist validation requires a Node runtime.");
|
|
3000
|
+
}
|
|
3001
|
+
return [4 /*yield*/, import('node:fs/promises')];
|
|
3002
|
+
case 1:
|
|
3003
|
+
readFile = (_a.sent()).readFile;
|
|
3004
|
+
_a.label = 2;
|
|
3005
|
+
case 2:
|
|
3006
|
+
_a.trys.push([2, 4, , 5]);
|
|
3007
|
+
return [4 /*yield*/, readFile(allowListPath, "utf-8")];
|
|
3008
|
+
case 3:
|
|
3009
|
+
raw = _a.sent();
|
|
3010
|
+
return [3 /*break*/, 5];
|
|
3011
|
+
case 4:
|
|
3012
|
+
_a.sent();
|
|
3013
|
+
throw new Error("SQL allowlist file not found at ".concat(allowListPath, "."));
|
|
3014
|
+
case 5:
|
|
3015
|
+
sqlEntries = parseAllowList(raw, allowListPath);
|
|
3016
|
+
allowList = new Set(sqlEntries);
|
|
3017
|
+
allowListCache.set(allowListPath, allowList);
|
|
3018
|
+
return [2 /*return*/, allowList];
|
|
3019
|
+
}
|
|
3020
|
+
});
|
|
3021
|
+
}); };
|
|
3022
|
+
|
|
2944
3023
|
var SqlExecutor = /** @class */ (function (_super) {
|
|
2945
3024
|
tslib.__extends(SqlExecutor, _super);
|
|
2946
3025
|
function SqlExecutor() {
|
|
@@ -2973,10 +3052,10 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
2973
3052
|
switch (_a) {
|
|
2974
3053
|
case 'GET': return [3 /*break*/, 1];
|
|
2975
3054
|
case 'POST': return [3 /*break*/, 3];
|
|
2976
|
-
case 'PUT': return [3 /*break*/,
|
|
2977
|
-
case 'DELETE': return [3 /*break*/,
|
|
3055
|
+
case 'PUT': return [3 /*break*/, 6];
|
|
3056
|
+
case 'DELETE': return [3 /*break*/, 9];
|
|
2978
3057
|
}
|
|
2979
|
-
return [3 /*break*/,
|
|
3058
|
+
return [3 /*break*/, 12];
|
|
2980
3059
|
case 1: return [4 /*yield*/, this.runQuery()];
|
|
2981
3060
|
case 2:
|
|
2982
3061
|
rest = _b.sent();
|
|
@@ -2984,16 +3063,25 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
2984
3063
|
case 3: return [4 /*yield*/, this.runQuery()];
|
|
2985
3064
|
case 4:
|
|
2986
3065
|
result = _b.sent();
|
|
3066
|
+
return [4 /*yield*/, this.broadcastWebsocketIfConfigured()];
|
|
3067
|
+
case 5:
|
|
3068
|
+
_b.sent();
|
|
2987
3069
|
return [2 /*return*/, result];
|
|
2988
|
-
case
|
|
2989
|
-
case
|
|
3070
|
+
case 6: return [4 /*yield*/, this.runQuery()];
|
|
3071
|
+
case 7:
|
|
2990
3072
|
result = _b.sent();
|
|
2991
|
-
return [
|
|
2992
|
-
case 7: return [4 /*yield*/, this.runQuery()];
|
|
3073
|
+
return [4 /*yield*/, this.broadcastWebsocketIfConfigured()];
|
|
2993
3074
|
case 8:
|
|
3075
|
+
_b.sent();
|
|
3076
|
+
return [2 /*return*/, result];
|
|
3077
|
+
case 9: return [4 /*yield*/, this.runQuery()];
|
|
3078
|
+
case 10:
|
|
2994
3079
|
result = _b.sent();
|
|
3080
|
+
return [4 /*yield*/, this.broadcastWebsocketIfConfigured()];
|
|
3081
|
+
case 11:
|
|
3082
|
+
_b.sent();
|
|
2995
3083
|
return [2 /*return*/, result];
|
|
2996
|
-
case
|
|
3084
|
+
case 12: throw new Error("Unsupported request method: ".concat(method));
|
|
2997
3085
|
}
|
|
2998
3086
|
});
|
|
2999
3087
|
});
|
|
@@ -3054,6 +3142,149 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
3054
3142
|
return "'".concat(val.toISOString().slice(0, 19).replace('T', ' '), "'");
|
|
3055
3143
|
return "'".concat(JSON.stringify(val), "'");
|
|
3056
3144
|
};
|
|
3145
|
+
SqlExecutor.prototype.stripRequestMetadata = function (source) {
|
|
3146
|
+
var ignoredKeys = new Set([
|
|
3147
|
+
C6Constants.SELECT,
|
|
3148
|
+
C6Constants.UPDATE,
|
|
3149
|
+
C6Constants.DELETE,
|
|
3150
|
+
C6Constants.WHERE,
|
|
3151
|
+
C6Constants.JOIN,
|
|
3152
|
+
C6Constants.PAGINATION,
|
|
3153
|
+
C6Constants.INSERT,
|
|
3154
|
+
C6Constants.REPLACE,
|
|
3155
|
+
"dataInsertMultipleRows",
|
|
3156
|
+
"cacheResults",
|
|
3157
|
+
"fetchDependencies",
|
|
3158
|
+
"debug",
|
|
3159
|
+
"success",
|
|
3160
|
+
"error",
|
|
3161
|
+
]);
|
|
3162
|
+
var filtered = {};
|
|
3163
|
+
for (var _i = 0, _a = Object.entries(source); _i < _a.length; _i++) {
|
|
3164
|
+
var _b = _a[_i], key = _b[0], value = _b[1];
|
|
3165
|
+
if (!ignoredKeys.has(key)) {
|
|
3166
|
+
filtered[key] = value;
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
return filtered;
|
|
3170
|
+
};
|
|
3171
|
+
SqlExecutor.prototype.normalizeRequestPayload = function (source) {
|
|
3172
|
+
var _a;
|
|
3173
|
+
var columns = this.config.restModel.COLUMNS;
|
|
3174
|
+
var validColumns = new Set(Object.values(columns));
|
|
3175
|
+
var normalized = {};
|
|
3176
|
+
for (var _i = 0, _b = Object.entries(source); _i < _b.length; _i++) {
|
|
3177
|
+
var _c = _b[_i], key = _c[0], value = _c[1];
|
|
3178
|
+
var shortKey = (_a = columns[key]) !== null && _a !== void 0 ? _a : (key.includes(".") ? key.split(".").pop() : key);
|
|
3179
|
+
if (validColumns.has(shortKey)) {
|
|
3180
|
+
normalized[shortKey] = value;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
return normalized;
|
|
3184
|
+
};
|
|
3185
|
+
SqlExecutor.prototype.extractRequestBody = function () {
|
|
3186
|
+
var _a, _b;
|
|
3187
|
+
var request = this.request;
|
|
3188
|
+
if (this.config.requestMethod === C6Constants.POST) {
|
|
3189
|
+
if (Array.isArray(request.dataInsertMultipleRows) && request.dataInsertMultipleRows.length > 0) {
|
|
3190
|
+
return request.dataInsertMultipleRows[0];
|
|
3191
|
+
}
|
|
3192
|
+
if (C6Constants.INSERT in request) {
|
|
3193
|
+
return (_a = request[C6Constants.INSERT]) !== null && _a !== void 0 ? _a : {};
|
|
3194
|
+
}
|
|
3195
|
+
if (C6Constants.REPLACE in request) {
|
|
3196
|
+
return (_b = request[C6Constants.REPLACE]) !== null && _b !== void 0 ? _b : {};
|
|
3197
|
+
}
|
|
3198
|
+
return this.stripRequestMetadata(request);
|
|
3199
|
+
}
|
|
3200
|
+
if (this.config.requestMethod === C6Constants.PUT) {
|
|
3201
|
+
if (request[C6Constants.UPDATE] && typeof request[C6Constants.UPDATE] === "object") {
|
|
3202
|
+
return request[C6Constants.UPDATE];
|
|
3203
|
+
}
|
|
3204
|
+
return this.stripRequestMetadata(request);
|
|
3205
|
+
}
|
|
3206
|
+
return {};
|
|
3207
|
+
};
|
|
3208
|
+
SqlExecutor.prototype.extractPrimaryKeyValues = function () {
|
|
3209
|
+
var _a, _b, _c;
|
|
3210
|
+
var request = this.request;
|
|
3211
|
+
var where = request === null || request === void 0 ? void 0 : request[C6Constants.WHERE];
|
|
3212
|
+
var sources = [request, (where && typeof where === "object" && !Array.isArray(where)) ? where : undefined];
|
|
3213
|
+
var columns = this.config.restModel.COLUMNS;
|
|
3214
|
+
var primaryShorts = (_a = this.config.restModel.PRIMARY_SHORT) !== null && _a !== void 0 ? _a : [];
|
|
3215
|
+
var primaryFulls = (_b = this.config.restModel.PRIMARY) !== null && _b !== void 0 ? _b : [];
|
|
3216
|
+
var pkValues = {};
|
|
3217
|
+
var _loop_1 = function (pkShort) {
|
|
3218
|
+
var value = undefined;
|
|
3219
|
+
for (var _d = 0, sources_1 = sources; _d < sources_1.length; _d++) {
|
|
3220
|
+
var source = sources_1[_d];
|
|
3221
|
+
if (source && pkShort in source) {
|
|
3222
|
+
value = source[pkShort];
|
|
3223
|
+
break;
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
if (value === undefined) {
|
|
3227
|
+
var fullKey = (_c = primaryFulls.find(function (key) { return key.endsWith("." + pkShort); })) !== null && _c !== void 0 ? _c : Object.keys(columns).find(function (key) { return columns[key] === pkShort; });
|
|
3228
|
+
if (fullKey) {
|
|
3229
|
+
for (var _e = 0, sources_2 = sources; _e < sources_2.length; _e++) {
|
|
3230
|
+
var source = sources_2[_e];
|
|
3231
|
+
if (source && fullKey in source) {
|
|
3232
|
+
value = source[fullKey];
|
|
3233
|
+
break;
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
if (value !== undefined) {
|
|
3239
|
+
pkValues[pkShort] = value;
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
for (var _i = 0, primaryShorts_1 = primaryShorts; _i < primaryShorts_1.length; _i++) {
|
|
3243
|
+
var pkShort = primaryShorts_1[_i];
|
|
3244
|
+
_loop_1(pkShort);
|
|
3245
|
+
}
|
|
3246
|
+
if (primaryShorts.length > 0 && Object.keys(pkValues).length < primaryShorts.length) {
|
|
3247
|
+
return null;
|
|
3248
|
+
}
|
|
3249
|
+
return Object.keys(pkValues).length > 0 ? pkValues : null;
|
|
3250
|
+
};
|
|
3251
|
+
SqlExecutor.prototype.broadcastWebsocketIfConfigured = function () {
|
|
3252
|
+
return tslib.__awaiter(this, void 0, void 0, function () {
|
|
3253
|
+
var broadcast, payload, error_1;
|
|
3254
|
+
var _a, _b;
|
|
3255
|
+
return tslib.__generator(this, function (_c) {
|
|
3256
|
+
switch (_c.label) {
|
|
3257
|
+
case 0:
|
|
3258
|
+
broadcast = this.config.websocketBroadcast;
|
|
3259
|
+
if (!broadcast || this.config.requestMethod === C6Constants.GET)
|
|
3260
|
+
return [2 /*return*/];
|
|
3261
|
+
payload = {
|
|
3262
|
+
REST: {
|
|
3263
|
+
TABLE_NAME: this.config.restModel.TABLE_NAME,
|
|
3264
|
+
TABLE_PREFIX: (_b = (_a = this.config.C6) === null || _a === void 0 ? void 0 : _a.PREFIX) !== null && _b !== void 0 ? _b : "",
|
|
3265
|
+
METHOD: this.config.requestMethod,
|
|
3266
|
+
REQUEST: this.normalizeRequestPayload(this.extractRequestBody()),
|
|
3267
|
+
REQUEST_PRIMARY_KEY: this.extractPrimaryKeyValues(),
|
|
3268
|
+
},
|
|
3269
|
+
};
|
|
3270
|
+
_c.label = 1;
|
|
3271
|
+
case 1:
|
|
3272
|
+
_c.trys.push([1, 3, , 4]);
|
|
3273
|
+
return [4 /*yield*/, broadcast(payload)];
|
|
3274
|
+
case 2:
|
|
3275
|
+
_c.sent();
|
|
3276
|
+
return [3 /*break*/, 4];
|
|
3277
|
+
case 3:
|
|
3278
|
+
error_1 = _c.sent();
|
|
3279
|
+
if (this.config.verbose) {
|
|
3280
|
+
console.error("[SQL EXECUTOR] websocketBroadcast failed", error_1);
|
|
3281
|
+
}
|
|
3282
|
+
return [3 /*break*/, 4];
|
|
3283
|
+
case 4: return [2 /*return*/];
|
|
3284
|
+
}
|
|
3285
|
+
});
|
|
3286
|
+
});
|
|
3287
|
+
};
|
|
3057
3288
|
SqlExecutor.prototype.runQuery = function () {
|
|
3058
3289
|
return tslib.__awaiter(this, void 0, void 0, function () {
|
|
3059
3290
|
var TABLE_NAME, method, builder, QueryResult, formatted, toUnnamed, _a, sql, values;
|
|
@@ -3085,6 +3316,9 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
3085
3316
|
this.config.verbose && console.log("[SQL EXECUTOR] \uD83E\uDDE0 Formatted ".concat(method.toUpperCase(), " SQL:"), formatted);
|
|
3086
3317
|
toUnnamed = namedPlaceholders();
|
|
3087
3318
|
_a = toUnnamed(QueryResult.sql, QueryResult.params), sql = _a[0], values = _a[1];
|
|
3319
|
+
return [4 /*yield*/, this.validateSqlAllowList(sql)];
|
|
3320
|
+
case 1:
|
|
3321
|
+
_b.sent();
|
|
3088
3322
|
return [4 /*yield*/, this.withConnection(function (conn) { return tslib.__awaiter(_this, void 0, void 0, function () {
|
|
3089
3323
|
var result;
|
|
3090
3324
|
return tslib.__generator(this, function (_a) {
|
|
@@ -3109,7 +3343,29 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
3109
3343
|
}
|
|
3110
3344
|
});
|
|
3111
3345
|
}); })];
|
|
3112
|
-
case
|
|
3346
|
+
case 2: return [2 /*return*/, _b.sent()];
|
|
3347
|
+
}
|
|
3348
|
+
});
|
|
3349
|
+
});
|
|
3350
|
+
};
|
|
3351
|
+
SqlExecutor.prototype.validateSqlAllowList = function (sql) {
|
|
3352
|
+
return tslib.__awaiter(this, void 0, void 0, function () {
|
|
3353
|
+
var allowListPath, allowList, normalized;
|
|
3354
|
+
return tslib.__generator(this, function (_a) {
|
|
3355
|
+
switch (_a.label) {
|
|
3356
|
+
case 0:
|
|
3357
|
+
allowListPath = this.config.sqlAllowListPath;
|
|
3358
|
+
if (!allowListPath) {
|
|
3359
|
+
return [2 /*return*/];
|
|
3360
|
+
}
|
|
3361
|
+
return [4 /*yield*/, loadSqlAllowList(allowListPath)];
|
|
3362
|
+
case 1:
|
|
3363
|
+
allowList = _a.sent();
|
|
3364
|
+
normalized = normalizeSql(sql);
|
|
3365
|
+
if (!allowList.has(normalized)) {
|
|
3366
|
+
throw new Error("SQL statement is not permitted by allowlist (".concat(allowListPath, ")."));
|
|
3367
|
+
}
|
|
3368
|
+
return [2 /*return*/];
|
|
3113
3369
|
}
|
|
3114
3370
|
});
|
|
3115
3371
|
});
|
|
@@ -3126,7 +3382,7 @@ var SqlExecutor$1 = /*#__PURE__*/Object.freeze({
|
|
|
3126
3382
|
// note sure how it would help anyone actually...
|
|
3127
3383
|
function ExpressHandler(_a) {
|
|
3128
3384
|
var _this = this;
|
|
3129
|
-
var C6 = _a.C6, mysqlPool = _a.mysqlPool;
|
|
3385
|
+
var C6 = _a.C6, mysqlPool = _a.mysqlPool, sqlAllowListPath = _a.sqlAllowListPath;
|
|
3130
3386
|
return function (req, res, next) { return tslib.__awaiter(_this, void 0, void 0, function () {
|
|
3131
3387
|
var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1;
|
|
3132
3388
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
@@ -3212,6 +3468,7 @@ function ExpressHandler(_a) {
|
|
|
3212
3468
|
return [4 /*yield*/, restRequest({
|
|
3213
3469
|
C6: C6,
|
|
3214
3470
|
mysqlPool: mysqlPool,
|
|
3471
|
+
sqlAllowListPath: sqlAllowListPath,
|
|
3215
3472
|
requestMethod: method,
|
|
3216
3473
|
restModel: C6.TABLES[table]
|
|
3217
3474
|
})(payload)];
|
|
@@ -3374,7 +3631,9 @@ exports.isLocal = isLocal;
|
|
|
3374
3631
|
exports.isNode = isNode;
|
|
3375
3632
|
exports.isTest = isTest;
|
|
3376
3633
|
exports.isVerbose = isVerbose;
|
|
3634
|
+
exports.loadSqlAllowList = loadSqlAllowList;
|
|
3377
3635
|
exports.normalizeSingularRequest = normalizeSingularRequest;
|
|
3636
|
+
exports.normalizeSql = normalizeSql;
|
|
3378
3637
|
exports.onError = onError;
|
|
3379
3638
|
exports.onSuccess = onSuccess;
|
|
3380
3639
|
exports.removeInvalidKeys = removeInvalidKeys;
|