@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
@@ -1,223 +0,0 @@
1
- import {C6Constants} from "api/C6Constants";
2
- import isVerbose from "../../variables/isVerbose";
3
- import {Executor} from "../executors/Executor";
4
- import {iRestMethods} from "../types/ormInterfaces";
5
-
6
- interface QueryResult {
7
- sql: string;
8
- params: any[];
9
- }
10
-
11
- export abstract class SqlBuilder<
12
- RequestMethod extends iRestMethods,
13
- RestShortTableName extends string = any,
14
- RestTableInterface extends Record<string, any> = any,
15
- PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
16
- CustomAndRequiredFields extends { [key: string]: any } = any,
17
- RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
18
- > extends Executor<
19
- RequestMethod,
20
- RestShortTableName,
21
- RestTableInterface,
22
- PrimaryKey,
23
- CustomAndRequiredFields,
24
- RequestTableOverrides
25
- > {
26
- /** Generate nested WHERE/JOIN conditions with parameter binding */
27
- protected buildBooleanJoinedConditions(
28
- set: any,
29
- andMode = true,
30
- params: any[] = []
31
- ): string {
32
- const booleanOperator = andMode ? 'AND' : 'OR';
33
- const OPERATORS = ['=', '!=', '<', '<=', '>', '>=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'IS', 'IS NOT'];
34
-
35
- const isAggregateArray = (value: any) =>
36
- Array.isArray(value) && typeof value[0] === 'string' && OPERATORS.includes(value[0]);
37
- const isNumericKeyed = (obj: any) =>
38
- Array.isArray(obj) && Object.keys(obj).every(k => /^\d+$/.test(k));
39
-
40
- const addCondition = (column: string, op: string, value: any): string => {
41
- let clause: string;
42
- /*if (Buffer.isBuffer(value)) { // TODO - I want this as a parameterized option, for now default to faster
43
- params.push(value.toString('hex')); // Or use UNHEX(?) in SQL
44
- clause = `(${column} = UNHEX(?))`;
45
- } else {*/
46
- params.push(value);
47
- clause = `( ${column} ${op} ? )`;
48
- //}
49
- isVerbose() && console.log(`[WHERE] ➕ ${clause} ->`, value);
50
- return clause;
51
- };
52
-
53
- let sql: string;
54
- if (isNumericKeyed(set)) {
55
- isVerbose() && console.log(`[WHERE] Numeric keyed condition:`, set);
56
- switch (set.length) {
57
- case 2:
58
- sql = addCondition(set[0], '=', set[1]);
59
- break;
60
- case 3:
61
- if (!OPERATORS.includes(set[1])) throw new Error(`Invalid operator: ${set[1]}`);
62
- sql = addCondition(set[0], set[1], set[2]);
63
- break;
64
- default:
65
- throw new Error(`Invalid array condition: ${JSON.stringify(set)}`);
66
- }
67
- } else {
68
- const parts: string[] = [];
69
- for (const [key, value] of Object.entries(set)) {
70
- if (/^\d+$/.test(key)) {
71
- parts.push(this.buildBooleanJoinedConditions(value, !andMode, params));
72
- } else if (!Array.isArray(value) || isAggregateArray(value)) {
73
- parts.push(addCondition(key, '=', value));
74
- } else if (value.length === 2 && OPERATORS.includes(value[0])) {
75
- parts.push(addCondition(key, value[0], value[1]));
76
- } else {
77
- throw new Error(`Invalid condition for ${key}: ${JSON.stringify(value)}`);
78
- }
79
- }
80
- sql = parts.join(` ${booleanOperator} `);
81
- }
82
-
83
- isVerbose() && console.log(`[WHERE] Final: (${sql})`);
84
- return `(${sql})`;
85
- }
86
-
87
- /** Translate array or function calls into SQL expressions */
88
- protected buildAggregateField(field: string | any[]): string {
89
- if (typeof field === 'string') return field;
90
- if (!Array.isArray(field)) throw new Error('Invalid SELECT entry');
91
-
92
- let [fn, ...args] = field;
93
- let alias: string | undefined;
94
- if (args.length >= 2 && args[args.length - 2] === 'AS') {
95
- alias = String(args.pop());
96
- args.pop();
97
- }
98
- const F = String(fn).toUpperCase();
99
- const p = args.join(', ');
100
-
101
- isVerbose() && console.log(`[SELECT] ${F}(${p})${alias ? ` AS ${alias}` : ''}`);
102
- switch (F) {
103
- case 'DATE_ADD':
104
- return `DATE_ADD(${args[0]}, ${args[1]})${alias ? ` AS ${alias}` : ''}`;
105
- case 'DATE_SUB':
106
- return `DATE_SUB(${args[0]}, ${args[1]})${alias ? ` AS ${alias}` : ''}`;
107
- case 'YEAR':
108
- return `YEAR(${args[0]})${alias ? ` AS ${alias}` : ''}`;
109
- case 'MONTH':
110
- return `MONTH(${args[0]})${alias ? ` AS ${alias}` : ''}`;
111
- case 'DAY':
112
- return `DAY(${args[0]})${alias ? ` AS ${alias}` : ''}`;
113
- case 'ROUND':
114
- case 'CEIL':
115
- case 'FLOOR':
116
- case 'ABS':
117
- case 'SQRT':
118
- return `${F}(${p})${alias ? ` AS ${alias}` : ''}`;
119
- case 'ST_DISTANCE':
120
- return `ST_Distance(${p})${alias ? ` AS ${alias}` : ''}`;
121
- default:
122
- if (/^[A-Z_]+$/.test(F)) return `${F}(${p})${alias ? ` AS ${alias}` : ''}`;
123
- throw new Error(`Unsupported function: ${F}`);
124
- }
125
- }
126
-
127
- /** Compose a parameterized SELECT query with optional JOIN/WHERE/GROUP/HAVING/PAGINATION */
128
- protected buildSelectQuery<RestShortTableNames>(
129
- table: RestShortTableNames,
130
- primary: string | undefined,
131
- args: any,
132
- isSubSelect = false
133
- ): QueryResult {
134
- const model = this.config.C6.TABLES[table as string];
135
- const params: any[] = [];
136
-
137
- // SELECT
138
- const selectList = args?.[C6Constants.SELECT] ?? ['*'];
139
- const selectFields = Array.isArray(selectList)
140
- ? selectList.map(f => this.buildAggregateField(f)).join(', ')
141
- : '*';
142
- let sql = `SELECT ${selectFields} FROM ${table}`;
143
- isVerbose() && console.log(`[SELECT]`, selectFields);
144
-
145
- // JOIN
146
- if (args?.[C6Constants.JOIN]) {
147
- for (const jt in args[C6Constants.JOIN]) {
148
- const jk = jt.replace('_', ' ').toUpperCase();
149
- for (const jn in args[C6Constants.JOIN][jt]) {
150
- const on = this.buildBooleanJoinedConditions(args[C6Constants.JOIN][jt][jn], true, params);
151
- sql += ` ${jk} JOIN \`${jn}\` ON ${on}`;
152
- isVerbose() && console.log(`[JOIN]`, jk, jn, on);
153
- }
154
- }
155
- }
156
-
157
- // WHERE
158
- if (args?.[C6Constants.WHERE]) {
159
- let wc = this.buildBooleanJoinedConditions(args[C6Constants.WHERE], true, params);
160
- // Trim leading and trailing parentheses if they fully wrap the condition
161
- while (wc.startsWith('(') && wc.endsWith(')')) {
162
- wc = wc.slice(1, -1).trim();
163
- }
164
- sql += ` WHERE ${wc}`;
165
- }
166
-
167
- // GROUP BY
168
- if (args?.[C6Constants.GROUP_BY]) {
169
- const gb = Array.isArray(args[C6Constants.GROUP_BY])
170
- ? args[C6Constants.GROUP_BY].join(', ')
171
- : args[C6Constants.GROUP_BY];
172
- sql += ` GROUP BY ${gb}`;
173
- isVerbose() && console.log(`[GROUP BY]`, gb);
174
- }
175
-
176
- // HAVING
177
- if (args?.[C6Constants.HAVING]) {
178
- const hc = this.buildBooleanJoinedConditions(args[C6Constants.HAVING], true, params);
179
- sql += ` HAVING ${hc}`;
180
- }
181
-
182
- // PAGINATION
183
- if (args?.[C6Constants.PAGINATION]) {
184
- const p = args[C6Constants.PAGINATION];
185
- if (p[C6Constants.ORDER]) {
186
- const ord = Object.entries(p[C6Constants.ORDER]).map(([c, d]) => `${c} ${String(d).toUpperCase()}`);
187
- sql += ` ORDER BY ${ord.join(', ')}`;
188
- isVerbose() && console.log(`[ORDER BY]`, ord);
189
- }
190
- if (p[C6Constants.LIMIT] != null) {
191
- const lim = parseInt(p[C6Constants.LIMIT], 10);
192
- const pg = parseInt(p[C6Constants.PAGE] ?? 1, 10);
193
- const off = (pg - 1) * lim;
194
- sql += ` LIMIT ${off}, ${lim}`;
195
- isVerbose() && console.log(`[LIMIT]`, off, lim);
196
- }
197
- }
198
- // Fallback ORDER/LIMIT
199
- else if (!isSubSelect) {
200
- let ok: string | undefined;
201
- if (primary) ok = primary;
202
- else if (model?.PRIMARY_SHORT?.[0]) ok = model.PRIMARY_SHORT[0];
203
- else for (const ts of ['created_at', 'updated_at']) {
204
- if (model?.COLUMNS?.[`${table}.${ts}`]) {
205
- ok = ts;
206
- break;
207
- }
208
- }
209
- if (ok) {
210
- const dir = primary ? 'ASC' : 'DESC';
211
- const lim = primary ? 1 : 100;
212
- sql += ` ORDER BY ${ok} ${dir} LIMIT ${lim}`;
213
- isVerbose() && console.log(`[ORDER]`, ok, dir, lim);
214
- } else {
215
- sql += ` LIMIT 100`;
216
- isVerbose() && console.warn(`[ORDER] fallback LIMIT 100`);
217
- }
218
- }
219
-
220
- isVerbose() && console.log(`[SQL]`, sql, params);
221
- return {sql, params};
222
- }
223
- }