@carbonorm/carbonnode 3.0.13 → 3.1.2

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.
Files changed (49) hide show
  1. package/dist/api/C6Constants.d.ts +184 -0
  2. package/dist/api/builders/queryHelpers.d.ts +4 -0
  3. package/dist/api/builders/sqlBuilder.d.ts +17 -5
  4. package/dist/api/executors/Executor.d.ts +9 -15
  5. package/dist/api/executors/HttpExecutor.d.ts +7 -14
  6. package/dist/api/executors/SqlExecutor.d.ts +43 -14
  7. package/dist/api/orm/SqlBuilder.d.ts +17 -0
  8. package/dist/api/orm/builders/AggregateBuilder.d.ts +5 -0
  9. package/dist/api/orm/builders/ConditionBuilder.d.ts +11 -0
  10. package/dist/api/orm/builders/JoinBuilder.d.ts +5 -0
  11. package/dist/api/orm/builders/PaginationBuilder.d.ts +5 -0
  12. package/dist/api/orm/queries/DeleteQueryBuilder.d.ts +6 -0
  13. package/dist/api/orm/queries/SelectQueryBuilder.d.ts +6 -0
  14. package/dist/api/orm/queries/UpdateQueryBuilder.d.ts +6 -0
  15. package/dist/api/orm/queryHelpers.d.ts +4 -0
  16. package/dist/api/orm/utils/sqlUtils.d.ts +7 -0
  17. package/dist/api/restOrm.d.ts +3 -10
  18. package/dist/api/restRequest.d.ts +3 -10
  19. package/dist/api/types/ormGenerics.d.ts +13 -0
  20. package/dist/api/types/ormInterfaces.d.ts +23 -40
  21. package/dist/index.cjs.js +444 -249
  22. package/dist/index.cjs.js.map +1 -1
  23. package/dist/index.d.ts +10 -1
  24. package/dist/index.esm.js +432 -249
  25. package/dist/index.esm.js.map +1 -1
  26. package/package.json +7 -2
  27. package/scripts/assets/handlebars/C6.ts.handlebars +2 -4
  28. package/scripts/generateRestBindings.cjs +1 -1
  29. package/scripts/generateRestBindings.ts +1 -1
  30. package/src/api/C6Constants.ts +38 -2
  31. package/src/api/executors/Executor.ts +18 -36
  32. package/src/api/executors/HttpExecutor.ts +46 -59
  33. package/src/api/executors/SqlExecutor.ts +89 -58
  34. package/src/api/handlers/ExpressHandler.ts +3 -2
  35. package/src/api/orm/builders/AggregateBuilder.ts +38 -0
  36. package/src/api/orm/builders/ConditionBuilder.ts +113 -0
  37. package/src/api/orm/builders/JoinBuilder.ts +25 -0
  38. package/src/api/orm/builders/PaginationBuilder.ts +28 -0
  39. package/src/api/orm/queries/DeleteQueryBuilder.ts +28 -0
  40. package/src/api/orm/queries/SelectQueryBuilder.ts +49 -0
  41. package/src/api/orm/queries/UpdateQueryBuilder.ts +42 -0
  42. package/src/api/orm/queryHelpers.ts +18 -0
  43. package/src/api/orm/utils/sqlUtils.ts +24 -0
  44. package/src/api/restOrm.ts +4 -14
  45. package/src/api/restRequest.ts +16 -34
  46. package/src/api/types/ormGenerics.ts +18 -0
  47. package/src/api/types/ormInterfaces.ts +28 -43
  48. package/src/index.ts +10 -1
  49. package/src/api/builders/sqlBuilder.ts +0 -223
@@ -5,6 +5,7 @@ import isTest from "../../variables/isTest";
5
5
  import isVerbose from "../../variables/isVerbose";
6
6
  import convertForRequestBody from "../convertForRequestBody";
7
7
  import {eFetchDependencies} from "../types/dynamicFetching";
8
+ import {OrmGenerics} from "../types/ormGenerics";
8
9
  import {
9
10
  apiReturn,
10
11
  DELETE, DetermineResponseDataType,
@@ -12,7 +13,6 @@ import {
12
13
  iCacheAPI,
13
14
  iConstraint,
14
15
  iGetC6RestResponse,
15
- iRestMethods,
16
16
  POST,
17
17
  PUT, RequestQueryBody
18
18
  } from "../types/ormInterfaces";
@@ -23,36 +23,24 @@ import {Executor} from "./Executor";
23
23
  import {toastOptions, toastOptionsDevs} from "variables/toastOptions";
24
24
 
25
25
  export class HttpExecutor<
26
- RequestMethod extends iRestMethods,
27
- RestShortTableName extends string = any,
28
- RestTableInterface extends { [key: string]: any } = any,
29
- PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
30
- CustomAndRequiredFields extends { [key: string]: any } = any,
31
- RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
26
+ G extends OrmGenerics
32
27
  >
33
- extends Executor<
34
- RequestMethod,
35
- RestShortTableName,
36
- RestTableInterface,
37
- PrimaryKey,
38
- CustomAndRequiredFields,
39
- RequestTableOverrides
40
- > {
28
+ extends Executor<G> {
41
29
 
42
30
  public putState(
43
- response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
31
+ response: AxiosResponse<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>,
44
32
  request: RequestQueryBody<
45
- RequestMethod,
46
- RestTableInterface,
47
- CustomAndRequiredFields,
48
- RequestTableOverrides
33
+ G['RequestMethod'],
34
+ G['RestTableInterface'],
35
+ G['CustomAndRequiredFields'],
36
+ G['RequestTableOverrides']
49
37
  >,
50
38
  callback: () => void
51
39
  ) {
52
- this.config.reactBootstrap?.updateRestfulObjectArrays<RestTableInterface>({
40
+ this.config.reactBootstrap?.updateRestfulObjectArrays<G['RestTableInterface']>({
53
41
  callback,
54
42
  dataOrCallback: [
55
- removeInvalidKeys<RestTableInterface>({
43
+ removeInvalidKeys<G['RestTableInterface']>({
56
44
  ...request,
57
45
  ...response?.data?.rest,
58
46
  }, this.config.C6.TABLES)
@@ -63,12 +51,12 @@ export class HttpExecutor<
63
51
  }
64
52
 
65
53
  public postState(
66
- response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
54
+ response: AxiosResponse<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>,
67
55
  request: RequestQueryBody<
68
- RequestMethod,
69
- RestTableInterface,
70
- CustomAndRequiredFields,
71
- RequestTableOverrides
56
+ G['RequestMethod'],
57
+ G['RestTableInterface'],
58
+ G['CustomAndRequiredFields'],
59
+ G['RequestTableOverrides']
72
60
  >,
73
61
  callback: () => void
74
62
  ) {
@@ -87,49 +75,49 @@ export class HttpExecutor<
87
75
 
88
76
  }
89
77
 
90
- this.config.reactBootstrap?.updateRestfulObjectArrays<RestTableInterface>({
78
+ this.config.reactBootstrap?.updateRestfulObjectArrays<G['RestTableInterface']>({
91
79
  callback,
92
80
  dataOrCallback: undefined !== request.dataInsertMultipleRows
93
81
  ? request.dataInsertMultipleRows.map((request, index) => {
94
- return removeInvalidKeys<RestTableInterface>({
82
+ return removeInvalidKeys<G['RestTableInterface']>({
95
83
  ...request,
96
84
  ...(index === 0 ? response?.data?.rest : {}),
97
85
  }, this.config.C6.TABLES)
98
86
  })
99
87
  : [
100
- removeInvalidKeys<RestTableInterface>({
88
+ removeInvalidKeys<G['RestTableInterface']>({
101
89
  ...request,
102
90
  ...response?.data?.rest,
103
91
  }, this.config.C6.TABLES)
104
92
  ],
105
93
  stateKey: this.config.restModel.TABLE_NAME,
106
- uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[]
94
+ uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof G['RestTableInterface'])[]
107
95
  })
108
96
  }
109
97
 
110
98
  public deleteState(
111
- _response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
99
+ _response: AxiosResponse<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>,
112
100
  request: RequestQueryBody<
113
- RequestMethod,
114
- RestTableInterface,
115
- CustomAndRequiredFields,
116
- RequestTableOverrides
101
+ G['RequestMethod'],
102
+ G['RestTableInterface'],
103
+ G['CustomAndRequiredFields'],
104
+ G['RequestTableOverrides']
117
105
  >,
118
106
  callback: () => void
119
107
  ) {
120
- this.config.reactBootstrap?.deleteRestfulObjectArrays<RestTableInterface>({
108
+ this.config.reactBootstrap?.deleteRestfulObjectArrays<G['RestTableInterface']>({
121
109
  callback,
122
110
  dataOrCallback: [
123
- request as unknown as RestTableInterface,
111
+ request as unknown as G['RestTableInterface'],
124
112
  ],
125
113
  stateKey: this.config.restModel.TABLE_NAME,
126
- uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[]
114
+ uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof G['RestTableInterface'])[]
127
115
  })
128
116
  }
129
117
 
130
- public async execute(): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> {
118
+ public async execute(): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> {
131
119
 
132
- type ResponseDataType = DetermineResponseDataType<RequestMethod, RestTableInterface>;
120
+ type ResponseDataType = DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>;
133
121
 
134
122
  const {
135
123
  C6,
@@ -151,7 +139,7 @@ export class HttpExecutor<
151
139
  });
152
140
 
153
141
 
154
- const tableName = restModel.TABLE_NAME;
142
+ const tableName = restModel.TABLE_NAME as string;
155
143
 
156
144
  const fullTableList = Array.isArray(tableName) ? tableName : [tableName];
157
145
 
@@ -218,7 +206,7 @@ export class HttpExecutor<
218
206
  }
219
207
 
220
208
  // this could return itself with a new page number, or undefined if the end is reached
221
- const apiRequest = async (): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> => {
209
+ const apiRequest = async (): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> => {
222
210
 
223
211
  const {
224
212
  debug,
@@ -261,10 +249,10 @@ export class HttpExecutor<
261
249
  if (undefined === query || null === query) {
262
250
 
263
251
  query = {} as RequestQueryBody<
264
- RequestMethod,
265
- RestTableInterface,
266
- CustomAndRequiredFields,
267
- RequestTableOverrides
252
+ G['RequestMethod'],
253
+ G['RestTableInterface'],
254
+ G['CustomAndRequiredFields'],
255
+ G['RequestTableOverrides']
268
256
  >
269
257
 
270
258
  }
@@ -351,7 +339,7 @@ export class HttpExecutor<
351
339
 
352
340
  let addBackPK: (() => void) | undefined;
353
341
 
354
- let apiResponse: RestTableInterface[PrimaryKey] | string | boolean | number | undefined;
342
+ let apiResponse: G['RestTableInterface'][G['PrimaryKey']] | string | boolean | number | undefined;
355
343
 
356
344
  let returnGetNextPageFunction = false;
357
345
 
@@ -427,10 +415,10 @@ export class HttpExecutor<
427
415
 
428
416
  addBackPK = () => {
429
417
  query ??= {} as RequestQueryBody<
430
- RequestMethod,
431
- RestTableInterface,
432
- CustomAndRequiredFields,
433
- RequestTableOverrides
418
+ G['RequestMethod'],
419
+ G['RestTableInterface'],
420
+ G['CustomAndRequiredFields'],
421
+ G['RequestTableOverrides']
434
422
  >;
435
423
  query[primaryKey] = removedPkValue;
436
424
  }
@@ -470,10 +458,10 @@ export class HttpExecutor<
470
458
  ...(() => {
471
459
  const convert = (data: any) =>
472
460
  convertForRequestBody<
473
- RequestMethod,
474
- RestTableInterface,
475
- CustomAndRequiredFields,
476
- RequestTableOverrides
461
+ G['RequestMethod'],
462
+ G['RestTableInterface'],
463
+ G['CustomAndRequiredFields'],
464
+ G['RequestTableOverrides']
477
465
  >(
478
466
  data,
479
467
  fullTableList,
@@ -574,7 +562,6 @@ export class HttpExecutor<
574
562
  if (false === apiResponse) {
575
563
 
576
564
 
577
-
578
565
  if (debug && isLocal()) {
579
566
 
580
567
  toast.warning("DEVS: TestRestfulResponse returned false for (" + operatingTable + ").", toastOptionsDevs);
@@ -596,10 +583,10 @@ export class HttpExecutor<
596
583
  if (undefined !== reactBootstrap && response) {
597
584
  switch (requestMethod) {
598
585
  case GET:
599
- reactBootstrap.updateRestfulObjectArrays<RestTableInterface>({
586
+ reactBootstrap.updateRestfulObjectArrays<G['RestTableInterface']>({
600
587
  dataOrCallback: Array.isArray(response.data.rest) ? response.data.rest : [response.data.rest],
601
588
  stateKey: this.config.restModel.TABLE_NAME,
602
- uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[],
589
+ uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof G['RestTableInterface'])[],
603
590
  callback
604
591
  })
605
592
  break;
@@ -1,32 +1,23 @@
1
- import { SqlBuilder } from "api/builders/sqlBuilder";
1
+ import {SelectQueryBuilder} from "../orm/queries/SelectQueryBuilder";
2
+ import {OrmGenerics} from "../types/ormGenerics";
2
3
  import {
3
4
  apiReturn,
4
5
  DetermineResponseDataType,
5
6
  iPostC6RestResponse,
6
7
  iPutC6RestResponse,
7
- iDeleteC6RestResponse,
8
- iRestMethods
8
+ iDeleteC6RestResponse
9
9
  } from "../types/ormInterfaces";
10
- import { PoolConnection, RowDataPacket, ResultSetHeader } from 'mysql2/promise';
10
+ import namedPlaceholders from 'named-placeholders';
11
+ import {PoolConnection, RowDataPacket, ResultSetHeader} from 'mysql2/promise';
12
+ import {Buffer} from 'buffer';
13
+ import {Executor} from "./Executor";
11
14
 
12
15
  export class SqlExecutor<
13
- RequestMethod extends iRestMethods,
14
- RestShortTableName extends string = any,
15
- RestTableInterface extends Record<string, any> = any,
16
- PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
17
- CustomAndRequiredFields extends Record<string, any> = any,
18
- RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
19
- > extends SqlBuilder<
20
- RequestMethod,
21
- RestShortTableName,
22
- RestTableInterface,
23
- PrimaryKey,
24
- CustomAndRequiredFields,
25
- RequestTableOverrides
26
- > {
27
-
28
- async execute(): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> {
29
- const { TABLE_NAME } = this.config.restModel;
16
+ G extends OrmGenerics
17
+ > extends Executor<G>{
18
+
19
+ async execute(): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> {
20
+ const {TABLE_NAME} = this.config.restModel;
30
21
  const method = this.config.requestMethod;
31
22
 
32
23
  console.log(`[SQL EXECUTOR] ▶️ Executing ${method} on table "${TABLE_NAME}"`);
@@ -36,25 +27,25 @@ export class SqlExecutor<
36
27
  case 'GET': {
37
28
  const rest = await this.select(TABLE_NAME, undefined, this.request);
38
29
  console.log(`[SQL EXECUTOR] ✅ GET result:`, rest);
39
- return { rest } as apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>;
30
+ return rest as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
40
31
  }
41
32
 
42
33
  case 'POST': {
43
34
  const result = await this.insert(TABLE_NAME, this.request);
44
35
  console.log(`[SQL EXECUTOR] ✅ POST result:`, result);
45
- const created: iPostC6RestResponse = { rest: result, created: true };
46
- return created as apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>;
36
+ const created: iPostC6RestResponse = {rest: result, created: true};
37
+ return created as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
47
38
  }
48
39
 
49
40
  case 'PUT': {
50
41
  const result = await this.update(TABLE_NAME, [], this.request);
51
42
  console.log(`[SQL EXECUTOR] ✅ PUT result:`, result);
52
43
  const updated: iPutC6RestResponse = {
53
- rest: result,
44
+ ...result,
54
45
  updated: true,
55
- rowCount: (result as ResultSetHeader).affectedRows
46
+ rowCount: result.rest.affectedRows
56
47
  };
57
- return updated as apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>;
48
+ return updated as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
58
49
  }
59
50
 
60
51
  case 'DELETE': {
@@ -63,9 +54,9 @@ export class SqlExecutor<
63
54
  const deleted: iDeleteC6RestResponse = {
64
55
  rest: result,
65
56
  deleted: true,
66
- rowCount: (result as ResultSetHeader).affectedRows
57
+ rowCount: result.rest.affectedRows
67
58
  };
68
- return deleted as apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>;
59
+ return deleted as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
69
60
  }
70
61
 
71
62
  default:
@@ -85,40 +76,58 @@ export class SqlExecutor<
85
76
  }
86
77
  }
87
78
 
88
- async select<TName extends string>(table: TName, primary: string | undefined, args: any) {
89
- const sql = this.buildSelectQuery<TName>(table, primary, args);
90
- console.log(`[SQL EXECUTOR] 🧠 Generated SELECT SQL:`, sql);
91
- const formatted = this.formatSQLWithParams(sql.sql, sql.params);
92
- console.log(`[SQL EXECUTOR] 🧠 Formatted SELECT SQL:`, formatted);
79
+ public serialize = (row: any) => Object.fromEntries(Object.entries(row).map(
80
+ ([k, v]) => [k, Buffer.isBuffer(v) ? v.toString('hex').toUpperCase() : v]
81
+ ));
93
82
 
83
+ async select(table: G['RestShortTableName'], primary: string | undefined, args: any) {
84
+ const QueryResult = (new SelectQueryBuilder(this.config, this.request)).build(table, args, primary);
85
+ console.log(`[SQL EXECUTOR] 🧠 Generated SELECT SQL:`, QueryResult);
86
+ const formatted = this.formatSQLWithParams(QueryResult.sql, QueryResult.params);
87
+ console.log(`[SQL EXECUTOR] 🧠 Formatted SELECT SQL:`, formatted);
88
+ const toUnnamed = namedPlaceholders();
89
+ const [sql, values] = toUnnamed(QueryResult.sql, QueryResult.params);
94
90
  return await this.withConnection(async (conn) => {
95
- const [rows] = await conn.query<RowDataPacket[]>(sql.sql, sql.params);
91
+ const [rows] = await conn.query<RowDataPacket[]>(sql, values);
96
92
  console.log(`[SQL EXECUTOR] 📦 Rows fetched:`, rows);
97
- return rows;
93
+ return {
94
+ rest: rows.map(this.serialize),
95
+ sql: {
96
+ sql, values
97
+ }
98
+ };
98
99
  });
99
100
  }
100
101
 
101
- async insert<TName extends string>(table: TName, data: Record<string, any>) {
102
+ async insert(table: G['RestShortTableName'], data: Record<string, any>) {
102
103
  const keys = Object.keys(data);
103
104
  const values = keys.map(k => data[k]);
104
105
  const placeholders = keys.map(() => '?').join(', ');
105
- const sql = `INSERT INTO \`${table}\` (${keys.join(', ')}) VALUES (${placeholders})`;
106
+ const sql = `INSERT INTO \`${table}\` (${keys.join(', ')})
107
+ VALUES (${placeholders})`;
106
108
 
107
109
  console.log(`[SQL EXECUTOR] 🧠 Generated INSERT SQL:`, sql);
108
110
  console.log(`[SQL EXECUTOR] 🔢 Values:`, values);
109
111
 
110
112
  return await this.withConnection(async (conn) => {
111
113
  const [result] = await conn.execute<ResultSetHeader>(sql, values);
112
- return result;
114
+ return {
115
+ rest: result,
116
+ sql: {
117
+ sql, placeholders
118
+ }
119
+ };
113
120
  });
114
121
  }
115
122
 
116
- async update<TName extends string>(table: TName, primary: string[], data: Record<string, any>) {
123
+ async update(table: G['RestShortTableName'], primary: string[], data: Record<string, any>) {
117
124
  if (!primary?.length) throw new Error('Primary key is required for update');
118
125
  const keys = Object.keys(data);
119
126
  const values = keys.map(k => data[k]);
120
127
  const updates = keys.map(k => `\`${k}\` = ?`).join(', ');
121
- const sql = `UPDATE \`${table}\` SET ${updates} WHERE \`${primary[0]}\` = ?`;
128
+ const sql = `UPDATE \`${table}\`
129
+ SET ${updates}
130
+ WHERE \`${primary[0]}\` = ?`;
122
131
  values.push(data[primary[0]]);
123
132
 
124
133
  console.log(`[SQL EXECUTOR] 🧠 Generated UPDATE SQL:`, sql);
@@ -126,38 +135,60 @@ export class SqlExecutor<
126
135
 
127
136
  return await this.withConnection(async (conn) => {
128
137
  const [result] = await conn.execute<ResultSetHeader>(sql, values);
129
- return result;
138
+ return {
139
+ rest:result,
140
+ sql: {
141
+ sql, values
142
+ }
143
+ };
130
144
  });
131
145
  }
132
146
 
133
- async delete<TName extends string>(table: TName, primary: string[], args: Record<string, any>) {
147
+ async delete(table: G['RestShortTableName'], primary: string[], args: Record<string, any>) {
134
148
  const key = primary?.[0];
135
149
  if (!key || !args?.[key]) throw new Error('Primary key and value required for delete');
136
- const sql = `DELETE FROM \`${table}\` WHERE \`${key}\` = ?`;
150
+ const sql = `DELETE
151
+ FROM \`${table}\`
152
+ WHERE \`${key}\` = ?`;
137
153
 
138
154
  console.log(`[SQL EXECUTOR] 🧠 Generated DELETE SQL:`, sql);
139
155
  console.log(`[SQL EXECUTOR] 🔢 Value:`, args[key]);
140
156
 
141
157
  return await this.withConnection(async (conn) => {
142
158
  const [result] = await conn.execute<ResultSetHeader>(sql, [args[key]]);
143
- return result;
159
+ return {
160
+ rest: result,
161
+ sql: {
162
+ sql, args
163
+ }
164
+ };
144
165
  });
145
166
  }
146
167
 
168
+ public formatSQLWithParams(sql: string, params: any[] | { [key: string]: any }): string {
169
+ if (Array.isArray(params)) {
170
+ let index = 0;
171
+ return sql.replace(/\?/g, () => {
172
+ if (index >= params.length) return '?';
173
+ const val = params[index++];
174
+ return this.formatValue(val);
175
+ });
176
+ } else {
177
+ return sql.replace(/:([a-zA-Z0-9_]+)/g, (_, key) => {
178
+ const val = params[key];
179
+ return this.formatValue(val);
180
+ });
181
+ }
182
+ }
147
183
 
148
- public formatSQLWithParams(sql: string, params: any[]): string {
149
- let index = 0;
150
-
151
- return sql.replace(/\?/g, () => {
152
- if (index >= params.length) return '?'; // fallback if params are missing
153
- const val = params[index++];
154
- if (val === null || val === undefined) return 'NULL';
155
- if (Buffer.isBuffer(val)) return `UNHEX('${val.toString('hex')}')`;
156
- if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`;
157
- if (typeof val === 'number') return val.toString();
158
- if (val instanceof Date) return `'${val.toISOString().slice(0, 19).replace('T', ' ')}'`;
159
- return `'${JSON.stringify(val)}'`;
160
- });
184
+ private formatValue(val: any): string {
185
+ if (val === null || val === undefined) return 'NULL';
186
+ if (Buffer.isBuffer(val)) return `UNHEX('${val.toString('hex')}')`;
187
+ if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`;
188
+ if (typeof val === 'number') return val.toString();
189
+ if (val instanceof Date) return `'${val.toISOString().slice(0, 19).replace('T', ' ')}'`;
190
+ return `'${JSON.stringify(val)}'`;
161
191
  }
162
192
 
193
+
163
194
  }
@@ -38,6 +38,7 @@ export function ExpressHandler({C6, mysqlPool}: { C6: iC6Object, mysqlPool: Pool
38
38
  res.status(405).json({error: `Method ${method} not allowed`});
39
39
  return;
40
40
  }
41
+
41
42
  const response = await restRequest({
42
43
  C6,
43
44
  mysqlPool,
@@ -45,10 +46,10 @@ export function ExpressHandler({C6, mysqlPool}: { C6: iC6Object, mysqlPool: Pool
45
46
  restModel: C6.TABLES[table]
46
47
  })(payload);
47
48
 
48
- console.log('response', JSON.stringify(response));
49
-
50
49
  res.status(200).json({success: true, ...response});
50
+
51
51
  } catch (err) {
52
+ res.status(500).json({success: false, error: err});
52
53
  next(err);
53
54
  }
54
55
  };
@@ -0,0 +1,38 @@
1
+ import isVerbose from "../../../variables/isVerbose";
2
+ import {Executor} from "../../executors/Executor";
3
+ import {OrmGenerics} from "../../types/ormGenerics";
4
+
5
+ export abstract class AggregateBuilder<G extends OrmGenerics> extends Executor<G>{
6
+ buildAggregateField(field: string | any[]): string {
7
+ if (typeof field === 'string') {
8
+ return field;
9
+ }
10
+
11
+ if (!Array.isArray(field) || field.length === 0) {
12
+ throw new Error('Invalid SELECT field entry');
13
+ }
14
+
15
+ let [fn, ...args] = field;
16
+ let alias: string | undefined;
17
+
18
+ if (args.length >= 2 && String(args[args.length - 2]).toUpperCase() === 'AS') {
19
+ alias = String(args.pop());
20
+ args.pop();
21
+ }
22
+
23
+ const F = String(fn).toUpperCase();
24
+ const argList = args.map(arg =>
25
+ Array.isArray(arg) ? this.buildAggregateField(arg) : arg
26
+ ).join(', ');
27
+
28
+ let expr = `${F}(${argList})`;
29
+
30
+ if (alias) {
31
+ expr += ` AS ${alias}`;
32
+ }
33
+
34
+ isVerbose() && console.log(`[SELECT] ${expr}`);
35
+
36
+ return expr;
37
+ }
38
+ }
@@ -0,0 +1,113 @@
1
+ import {C6Constants} from "api/C6Constants";
2
+ import isVerbose from "../../../variables/isVerbose";
3
+ import {OrmGenerics} from "../../types/ormGenerics";
4
+ import {apiReturn, DetermineResponseDataType} from "../../types/ormInterfaces";
5
+ import {convertHexIfBinary} from "../utils/sqlUtils";
6
+ import {AggregateBuilder} from "./AggregateBuilder";
7
+
8
+ export class ConditionBuilder<
9
+ G extends OrmGenerics
10
+ > extends AggregateBuilder<G> {
11
+ execute(): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> {
12
+ throw new Error("Method not implemented.");
13
+ }
14
+
15
+ private readonly OPERATORS = new Set([
16
+ '=', '!=', '<', '<=', '>', '>=',
17
+ 'LIKE', 'NOT LIKE', 'IN', 'NOT IN',
18
+ 'IS', 'IS NOT', 'BETWEEN', 'NOT BETWEEN',
19
+ C6Constants.MATCH_AGAINST
20
+ ]);
21
+
22
+ private validateOperator(op: string) {
23
+ if (!this.OPERATORS.has(op)) {
24
+ throw new Error(`Invalid or unsupported SQL operator detected: '${op}'`);
25
+ }
26
+ }
27
+
28
+ private addParam(
29
+ params: any[] | Record<string, any>,
30
+ column: string,
31
+ value: any
32
+ ): string {
33
+ const columnDef = this.config.C6[column.split('.')[0]]?.TYPE_VALIDATION?.[column];
34
+ const val = convertHexIfBinary(column, value, columnDef);
35
+
36
+ if (this.useNamedParams) {
37
+ const key = `param${Object.keys(params).length}`;
38
+ (params as Record<string, any>)[key] = val;
39
+ return `:${key}`;
40
+ } else {
41
+ (params as any[]).push(val);
42
+ return '?';
43
+ }
44
+ }
45
+
46
+ buildBooleanJoinedConditions(
47
+ set: any,
48
+ andMode: boolean = true,
49
+ params: any[] | Record<string, any> = []
50
+ ): string {
51
+ const booleanOperator = andMode ? 'AND' : 'OR';
52
+
53
+ const addCondition = (column: string, op: string, value: any): string => {
54
+ this.validateOperator(op);
55
+
56
+
57
+ if (op === C6Constants.MATCH_AGAINST && Array.isArray(value)) {
58
+ const [search, mode] = value;
59
+ const paramName = this.useNamedParams ? `param${Object.keys(params).length}` : null;
60
+ if (this.useNamedParams) {
61
+ params[paramName!] = search;
62
+ } else {
63
+ params.push(search);
64
+ }
65
+
66
+ let againstClause: string;
67
+
68
+ switch ((mode || '').toUpperCase()) {
69
+ case 'BOOLEAN':
70
+ againstClause = this.useNamedParams ? `AGAINST(:${paramName} IN BOOLEAN MODE)` : `AGAINST(? IN BOOLEAN MODE)`;
71
+ break;
72
+ case 'WITH QUERY EXPANSION':
73
+ againstClause = this.useNamedParams ? `AGAINST(:${paramName} WITH QUERY EXPANSION)` : `AGAINST(? WITH QUERY EXPANSION)`;
74
+ break;
75
+ default: // NATURAL or undefined
76
+ againstClause = this.useNamedParams ? `AGAINST(:${paramName})` : `AGAINST(?)`;
77
+ break;
78
+ }
79
+
80
+ const matchClause = `(MATCH(${column}) ${againstClause})`;
81
+ isVerbose() && console.log(`[MATCH_AGAINST] ${matchClause}`);
82
+ return matchClause;
83
+ }
84
+
85
+ // handle other operators
86
+ return `( ${column} ${op} ${this.addParam(params, column, value)} )`;
87
+ };
88
+
89
+ const parts: string[] = [];
90
+
91
+ if (typeof set === 'object' && !Array.isArray(set)) {
92
+ for (const [key, value] of Object.entries(set)) {
93
+ if (typeof value === 'object' && value !== null && Object.keys(value).length === 1) {
94
+ const [op, val] = Object.entries(value)[0];
95
+ parts.push(addCondition(key, op, val));
96
+ } else {
97
+ parts.push(addCondition(key, '=', value));
98
+ }
99
+ }
100
+ }
101
+
102
+ const clause = parts.join(` ${booleanOperator} `);
103
+ return clause ? `(${clause})` : '';
104
+ }
105
+
106
+ buildWhereClause(whereArg: any, params: any[] | Record<string, any>): string {
107
+ const clause = this.buildBooleanJoinedConditions(whereArg, true, params);
108
+ if (!clause) return '';
109
+ const trimmed = clause.replace(/^\((.*)\)$/, '$1');
110
+ isVerbose() && console.log(`[WHERE] ${trimmed}`);
111
+ return ` WHERE ${trimmed}`;
112
+ }
113
+ }