@carbonorm/carbonnode 6.0.3 → 6.0.5

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 (71) hide show
  1. package/dist/executors/SqlExecutor.d.ts +2 -15
  2. package/dist/handlers/ExpressHandler.d.ts +3 -2
  3. package/dist/index.cjs.js +111 -18
  4. package/dist/index.cjs.js.map +1 -1
  5. package/dist/index.esm.js +111 -18
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/types/ormInterfaces.d.ts +14 -3
  8. package/package.json +2 -2
  9. package/src/__tests__/sakila-db/C6.js +1 -1
  10. package/src/__tests__/sakila-db/C6.mysqldump.json +1 -1
  11. package/src/__tests__/sakila-db/C6.mysqldump.sql +1 -1
  12. package/src/__tests__/sakila-db/C6.ts +1 -1
  13. package/src/__tests__/sakila-db/sqlResponses/C6.actor.delete.json +1 -0
  14. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +4 -3
  15. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +3 -3
  16. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +2 -1
  17. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +3 -3
  18. package/src/__tests__/sakila-db/sqlResponses/C6.address.delete.json +1 -0
  19. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +6 -5
  20. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +5 -5
  21. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +2 -1
  22. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +5 -5
  23. package/src/__tests__/sakila-db/sqlResponses/C6.category.delete.json +1 -0
  24. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +3 -2
  25. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +2 -2
  26. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +2 -1
  27. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +2 -2
  28. package/src/__tests__/sakila-db/sqlResponses/C6.city.delete.json +1 -0
  29. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +3 -2
  30. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +2 -2
  31. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +2 -1
  32. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +2 -2
  33. package/src/__tests__/sakila-db/sqlResponses/C6.country.delete.json +1 -0
  34. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +3 -2
  35. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +2 -2
  36. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +2 -1
  37. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +2 -2
  38. package/src/__tests__/sakila-db/sqlResponses/C6.customer.delete.json +1 -0
  39. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +6 -5
  40. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +5 -5
  41. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +2 -1
  42. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +5 -5
  43. package/src/__tests__/sakila-db/sqlResponses/C6.film.delete.json +1 -0
  44. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +3 -2
  45. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +2 -2
  46. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +2 -1
  47. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +2 -2
  48. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.delete.json +1 -0
  49. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +2 -1
  50. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +1 -1
  51. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +2 -1
  52. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +1 -1
  53. package/src/__tests__/sakila-db/sqlResponses/C6.language.delete.json +1 -0
  54. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +3 -2
  55. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +2 -2
  56. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +2 -1
  57. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +2 -2
  58. package/src/__tests__/sakila-db/sqlResponses/C6.payment.delete.json +1 -0
  59. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +3 -2
  60. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +2 -2
  61. package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.json +1 -0
  62. package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +2 -2
  63. package/src/__tests__/sakila-db/sqlResponses/C6.rental.delete.json +1 -0
  64. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +4 -3
  65. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +3 -3
  66. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +2 -1
  67. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +3 -3
  68. package/src/executors/HttpExecutor.ts +2 -1
  69. package/src/executors/SqlExecutor.ts +132 -22
  70. package/src/handlers/ExpressHandler.ts +4 -1
  71. package/src/types/ormInterfaces.ts +13 -3
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "affected": 1,
3
+ "insertId": 0,
3
4
  "rest": [],
4
5
  "sql": {
5
6
  "sql": "UPDATE `language` SET `name` = ? WHERE (language.language_id) = ?",
6
7
  "values": [
7
- "name_updated_1769751",
8
+ "name_updated_1769973",
8
9
  7
9
10
  ]
10
11
  }
@@ -2,8 +2,8 @@
2
2
  "rest": [
3
3
  {
4
4
  "language_id": 7,
5
- "name": "name_updated_1769751",
6
- "last_update": "2026-01-30T05:41:08.000Z"
5
+ "name": "name_updated_1769973",
6
+ "last_update": "2026-02-01T19:13:51.000Z"
7
7
  }
8
8
  ],
9
9
  "sql": {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "affected": 1,
3
+ "insertId": 0,
3
4
  "rest": [],
4
5
  "sql": {
5
6
  "sql": "DELETE `payment` FROM `payment` WHERE (payment.payment_id) = ?",
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "affected": 1,
3
+ "insertId": 16050,
3
4
  "rest": [],
4
5
  "sql": {
5
6
  "sql": "INSERT INTO `payment` (\n `customer_id`, `staff_id`, `rental_id`, `amount`, `payment_date`, `last_update`\n ) VALUES (\n ?, ?, ?, ?, ?, ?\n )",
@@ -8,8 +9,8 @@
8
9
  1,
9
10
  1,
10
11
  1,
11
- "2026-01-30 05:41:08",
12
- "2026-01-30 05:41:08"
12
+ "2026-02-01 19:13:51",
13
+ "2026-02-01 19:13:51"
13
14
  ]
14
15
  }
15
16
  }
@@ -6,8 +6,8 @@
6
6
  "staff_id": 1,
7
7
  "rental_id": 1,
8
8
  "amount": "1.00",
9
- "payment_date": "2026-01-30T05:41:08.000Z",
10
- "last_update": "2026-01-30T05:41:08.000Z"
9
+ "payment_date": "2026-02-01T19:13:51.000Z",
10
+ "last_update": "2026-02-01T19:13:51.000Z"
11
11
  }
12
12
  ],
13
13
  "sql": {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "affected": 1,
3
+ "insertId": 0,
3
4
  "rest": [],
4
5
  "sql": {
5
6
  "sql": "UPDATE `payment` SET `amount` = ? WHERE (payment.payment_id) = ?",
@@ -6,8 +6,8 @@
6
6
  "staff_id": 1,
7
7
  "rental_id": 1,
8
8
  "amount": "1.00",
9
- "payment_date": "2026-01-30T05:41:08.000Z",
10
- "last_update": "2026-01-30T05:41:08.000Z"
9
+ "payment_date": "2026-02-01T19:13:51.000Z",
10
+ "last_update": "2026-02-01T19:13:51.000Z"
11
11
  }
12
12
  ],
13
13
  "sql": {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "affected": 1,
3
+ "insertId": 0,
3
4
  "rest": [],
4
5
  "sql": {
5
6
  "sql": "DELETE `rental` FROM `rental` WHERE (rental.rental_id) = ?",
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "affected": 1,
3
+ "insertId": 16050,
3
4
  "rest": [],
4
5
  "sql": {
5
6
  "sql": "INSERT INTO `rental` (\n `rental_date`, `inventory_id`, `customer_id`, `return_date`, `staff_id`, `last_update`\n ) VALUES (\n ?, ?, ?, ?, ?, ?\n )",
6
7
  "values": [
7
- "2026-01-30 05:41:08",
8
+ "2026-02-01 19:13:51",
8
9
  1,
9
10
  1,
10
- "2026-01-30 05:41:08",
11
+ "2026-02-01 19:13:51",
11
12
  1,
12
- "2026-01-30 05:41:08"
13
+ "2026-02-01 19:13:51"
13
14
  ]
14
15
  }
15
16
  }
@@ -2,12 +2,12 @@
2
2
  "rest": [
3
3
  {
4
4
  "rental_id": 16050,
5
- "rental_date": "2026-01-30T05:41:08.000Z",
5
+ "rental_date": "2026-02-01T19:13:51.000Z",
6
6
  "inventory_id": 1,
7
7
  "customer_id": 1,
8
- "return_date": "2026-01-30T05:41:08.000Z",
8
+ "return_date": "2026-02-01T19:13:51.000Z",
9
9
  "staff_id": 1,
10
- "last_update": "2026-01-30T05:41:08.000Z"
10
+ "last_update": "2026-02-01T19:13:51.000Z"
11
11
  }
12
12
  ],
13
13
  "sql": {
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "affected": 1,
3
+ "insertId": 0,
3
4
  "rest": [],
4
5
  "sql": {
5
6
  "sql": "UPDATE `rental` SET `rental_date` = ? WHERE (rental.rental_id) = ?",
6
7
  "values": [
7
- "2026-01-30 05:41:08",
8
+ "2026-02-01 19:13:51",
8
9
  16050
9
10
  ]
10
11
  }
@@ -2,12 +2,12 @@
2
2
  "rest": [
3
3
  {
4
4
  "rental_id": 16050,
5
- "rental_date": "2026-01-30T05:41:08.000Z",
5
+ "rental_date": "2026-02-01T19:13:51.000Z",
6
6
  "inventory_id": 1,
7
7
  "customer_id": 1,
8
- "return_date": "2026-01-30T05:41:08.000Z",
8
+ "return_date": "2026-02-01T19:13:51.000Z",
9
9
  "staff_id": 1,
10
- "last_update": "2026-01-30T05:41:08.000Z"
10
+ "last_update": "2026-02-01T19:13:51.000Z"
11
11
  }
12
12
  ],
13
13
  "sql": {
@@ -529,7 +529,8 @@ export class HttpExecutor<
529
529
 
530
530
  if (C6.GET === requestMethod && this.isRestResponse(response)) {
531
531
 
532
- const responseData = response.data;
532
+ const responseData =
533
+ response.data as DetermineResponseDataType<'GET', G['RestTableInterface']>;
533
534
 
534
535
  const pageLimit = query?.[C6.PAGINATION]?.[C6.LIMIT];
535
536
 
@@ -47,19 +47,19 @@ export class SqlExecutor<
47
47
 
48
48
  case 'POST': {
49
49
  const result = await this.runQuery();
50
- await this.broadcastWebsocketIfConfigured();
50
+ await this.broadcastWebsocketIfConfigured(result);
51
51
  return result as DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>;
52
52
  }
53
53
 
54
54
  case 'PUT': {
55
55
  const result = await this.runQuery();
56
- await this.broadcastWebsocketIfConfigured();
56
+ await this.broadcastWebsocketIfConfigured(result);
57
57
  return result as DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>;
58
58
  }
59
59
 
60
60
  case 'DELETE': {
61
61
  const result = await this.runQuery();
62
- await this.broadcastWebsocketIfConfigured();
62
+ await this.broadcastWebsocketIfConfigured(result);
63
63
  return result as DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>;
64
64
  }
65
65
 
@@ -110,7 +110,7 @@ export class SqlExecutor<
110
110
  return `'${JSON.stringify(val)}'`;
111
111
  }
112
112
 
113
- private stripRequestMetadata(source: Record<string, any>): Record<string, any> {
113
+ private stripRequestMetadata(source: Record<string, unknown>): Record<string, unknown> {
114
114
  const ignoredKeys = new Set<string>([
115
115
  C6C.SELECT,
116
116
  C6C.UPDATE,
@@ -128,7 +128,7 @@ export class SqlExecutor<
128
128
  "error",
129
129
  ]);
130
130
 
131
- const filtered: Record<string, any> = {};
131
+ const filtered: Record<string, unknown> = {};
132
132
  for (const [key, value] of Object.entries(source)) {
133
133
  if (!ignoredKeys.has(key)) {
134
134
  filtered[key] = value;
@@ -137,10 +137,10 @@ export class SqlExecutor<
137
137
  return filtered;
138
138
  }
139
139
 
140
- private normalizeRequestPayload(source: Record<string, any>): Record<string, any> {
140
+ private normalizeRequestPayload(source: Record<string, unknown>): Record<string, unknown> {
141
141
  const columns = this.config.restModel.COLUMNS as Record<string, string>;
142
142
  const validColumns = new Set(Object.values(columns));
143
- const normalized: Record<string, any> = {};
143
+ const normalized: Record<string, unknown> = {};
144
144
 
145
145
  for (const [key, value] of Object.entries(source)) {
146
146
  const shortKey = columns[key] ?? (key.includes(".") ? key.split(".").pop()! : key);
@@ -152,25 +152,27 @@ export class SqlExecutor<
152
152
  return normalized;
153
153
  }
154
154
 
155
- private extractRequestBody(): Record<string, any> {
156
- const request = this.request as Record<string, any>;
155
+
156
+ private extractRequestBody() {
157
+ const request = this.request;
157
158
 
158
159
  if (this.config.requestMethod === C6C.POST) {
159
- if (Array.isArray(request.dataInsertMultipleRows) && request.dataInsertMultipleRows.length > 0) {
160
- return request.dataInsertMultipleRows[0] as Record<string, any>;
160
+ const insertRows = request.dataInsertMultipleRows;
161
+ if (Array.isArray(insertRows) && insertRows.length > 0) {
162
+ return insertRows[0] as Record<string, unknown>;
161
163
  }
162
164
  if (C6C.INSERT in request) {
163
- return (request as any)[C6C.INSERT] ?? {};
165
+ return request[C6C.INSERT] ?? {};
164
166
  }
165
167
  if (C6C.REPLACE in request) {
166
- return (request as any)[C6C.REPLACE] ?? {};
168
+ return request[C6C.REPLACE] ?? {};
167
169
  }
168
170
  return this.stripRequestMetadata(request);
169
171
  }
170
172
 
171
173
  if (this.config.requestMethod === C6C.PUT) {
172
174
  if (request[C6C.UPDATE] && typeof request[C6C.UPDATE] === "object") {
173
- return request[C6C.UPDATE] as Record<string, any>;
175
+ return request[C6C.UPDATE] as Record<string, unknown>;
174
176
  }
175
177
  return this.stripRequestMetadata(request);
176
178
  }
@@ -223,29 +225,136 @@ export class SqlExecutor<
223
225
  return Object.keys(pkValues).length > 0 ? pkValues : null;
224
226
  }
225
227
 
226
- private async broadcastWebsocketIfConfigured(): Promise<void> {
228
+ private extractPrimaryKeyValuesFromData(data: any): Record<string, any> | null {
229
+ if (!data) return null;
230
+ const row = Array.isArray(data) ? data[0] : data;
231
+ if (!row || typeof row !== "object") return null;
232
+
233
+ const pkShorts = this.config.restModel.PRIMARY_SHORT;
234
+ const columns = this.config.restModel.COLUMNS as Record<string, string>;
235
+ const pkValues: Record<string, any> = {};
236
+
237
+ for (const pk of pkShorts) {
238
+ if (pk in row) {
239
+ pkValues[pk] = (row as any)[pk];
240
+ continue;
241
+ }
242
+
243
+ const fullKey = Object.keys(columns).find(
244
+ (key) => columns[key] === pk,
245
+ );
246
+
247
+ if (fullKey && fullKey in row) {
248
+ pkValues[pk] = (row as any)[fullKey];
249
+ }
250
+ }
251
+
252
+ if (pkShorts.length > 0 && Object.keys(pkValues).length < pkShorts.length) {
253
+ return null;
254
+ }
255
+
256
+ return Object.keys(pkValues).length > 0 ? pkValues : null;
257
+ }
258
+
259
+ private async broadcastWebsocketIfConfigured(
260
+ response?: DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>
261
+ ): Promise<void> {
227
262
  const broadcast = this.config.websocketBroadcast;
228
- if (!broadcast || this.config.requestMethod === C6C.GET) return;
263
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 broadcastWebsocketIfConfigured start", {
264
+ method: this.config.requestMethod,
265
+ hasBroadcast: Boolean(broadcast),
266
+ });
267
+ if (!broadcast || this.config.requestMethod === C6C.GET) {
268
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 websocket broadcast skipped", {
269
+ reason: !broadcast ? "no broadcast configured" : "GET request",
270
+ });
271
+ return;
272
+ }
273
+
274
+ const normalizedRequest = this.normalizeRequestPayload(this.extractRequestBody());
275
+ const pkShorts = this.config.restModel.PRIMARY_SHORT ?? [];
276
+ const columns = this.config.restModel.COLUMNS as Record<string, string>;
277
+ const validColumns = new Set(Object.values(columns));
278
+ let responseRest = response?.rest;
279
+ let responsePrimaryKey = this.extractPrimaryKeyValuesFromData(responseRest);
280
+
281
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 websocket request payload", {
282
+ normalizedRequest,
283
+ requestPrimaryKey: this.extractPrimaryKeyValues(),
284
+ pkShorts,
285
+ });
286
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 websocket response payload", {
287
+ responseRest,
288
+ responsePrimaryKey,
289
+ });
290
+
291
+ if (
292
+ (responseRest === null || (Array.isArray(responseRest) && responseRest.length === 0))
293
+ && this.config.requestMethod === C6C.POST
294
+ ) {
295
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 response rest empty, attempting synthesize", {
296
+ responseRest,
297
+ });
298
+ const insertId = (response as DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']> & { insertId?: number | string | null })?.insertId;
299
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 POST insertId lookup", {
300
+ insertId,
301
+ });
302
+ if (insertId !== undefined && pkShorts.length === 1) {
303
+ const synthesizedRequest = {
304
+ ...normalizedRequest,
305
+ };
306
+ const now = new Date().toISOString();
307
+ if (validColumns.has("changed_at") && (synthesizedRequest as Record<string, unknown>).changed_at === undefined) {
308
+ synthesizedRequest.changed_at = now;
309
+ }
310
+ if (validColumns.has("created_at") && (synthesizedRequest as Record<string, unknown>).created_at === undefined) {
311
+ synthesizedRequest.created_at = now;
312
+ }
313
+ if (validColumns.has("updated_at") && (synthesizedRequest as Record<string, unknown>).updated_at === undefined) {
314
+ synthesizedRequest.updated_at = now;
315
+ }
316
+
317
+ const synthesized = {
318
+ ...synthesizedRequest,
319
+ [pkShorts[0]]: insertId,
320
+ };
321
+ // @ts-ignore - todo
322
+ responseRest = [synthesized];
323
+ responsePrimaryKey = {
324
+ [pkShorts[0]]: insertId,
325
+ };
326
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 synthesized response payload", {
327
+ synthesized,
328
+ responsePrimaryKey,
329
+ });
330
+ }
331
+ }
229
332
 
230
333
  const payload: iRestWebsocketPayload = {
231
334
  REST: {
232
335
  TABLE_NAME: this.config.restModel.TABLE_NAME as string,
233
336
  TABLE_PREFIX: this.config.C6?.PREFIX ?? "",
234
337
  METHOD: this.config.requestMethod,
235
- REQUEST: this.normalizeRequestPayload(this.extractRequestBody()),
338
+ REQUEST: normalizedRequest,
236
339
  REQUEST_PRIMARY_KEY: this.extractPrimaryKeyValues(),
340
+ RESPONSE: responseRest,
341
+ RESPONSE_PRIMARY_KEY: responsePrimaryKey,
237
342
  },
238
343
  };
239
344
 
345
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 websocket payload ready", payload);
346
+
240
347
  try {
348
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 websocket broadcast dispatch start");
241
349
  await broadcast(payload);
350
+ this.config.verbose && console.log("[SQL EXECUTOR] 📣 websocket broadcast dispatch complete");
242
351
  } catch (error) {
243
352
  if (this.config.verbose) {
244
353
  console.error("[SQL EXECUTOR] websocketBroadcast failed", error);
245
354
  }
246
355
  }
247
356
  }
248
- async runQuery() {
357
+ async runQuery(): Promise<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>> {
249
358
  const {TABLE_NAME} = this.config.restModel;
250
359
  const method = this.config.requestMethod;
251
360
  let builder: SelectQueryBuilder<G> | UpdateQueryBuilder<G> | DeleteQueryBuilder<G> | PostQueryBuilder<G>;
@@ -286,14 +395,15 @@ export class SqlExecutor<
286
395
  return {
287
396
  rest: result.map(this.serialize),
288
397
  sql: {sql, values}
289
- };
398
+ } as DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>;
290
399
  } else {
291
400
  this.config.verbose && console.log(`[SQL EXECUTOR] ✏️ Rows affected:`, result.affectedRows);
292
401
  return {
293
- affected: result.affectedRows,
294
- rest: [],
402
+ affected: result.affectedRows as number,
403
+ insertId: result.insertId as number,
404
+ rest: [], // TODO - remove rest empty array from non-GET responses?
295
405
  sql: {sql, values}
296
- };
406
+ } as DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>;
297
407
  }
298
408
  });
299
409
  }
@@ -2,7 +2,7 @@ import type {Request, Response, NextFunction} from "express";
2
2
  import type {Pool} from "mysql2/promise";
3
3
  import {C6C} from "../constants/C6Constants";
4
4
  import restRequest from "../api/restRequest";
5
- import type {iC6Object, iRestMethods} from "../types/ormInterfaces";
5
+ import type {iC6Object, iRestMethods, tWebsocketBroadcast} from "../types/ormInterfaces";
6
6
 
7
7
 
8
8
  // TODO - WE MUST make this a generic - optional, but helpful
@@ -11,10 +11,12 @@ export function ExpressHandler({
11
11
  C6,
12
12
  mysqlPool,
13
13
  sqlAllowListPath,
14
+ websocketBroadcast,
14
15
  }: {
15
16
  C6: iC6Object;
16
17
  mysqlPool: Pool;
17
18
  sqlAllowListPath?: string;
19
+ websocketBroadcast?: tWebsocketBroadcast;
18
20
  }) {
19
21
 
20
22
  return async (req: Request, res: Response, next: NextFunction) => {
@@ -101,6 +103,7 @@ export function ExpressHandler({
101
103
  C6,
102
104
  mysqlPool,
103
105
  sqlAllowListPath,
106
+ websocketBroadcast,
104
107
  requestMethod: method,
105
108
  restModel: C6.TABLES[table]
106
109
  })(payload);
@@ -77,6 +77,11 @@ export type RequestGetPutDeleteBody<T extends { [key: string]: any } = any> = T
77
77
  PAGINATION?: Pagination<T>;
78
78
  };
79
79
 
80
+ export type RequestPostBody<T extends { [key: string]: any } = any> = T | {
81
+ INSERT?: Partial<T>;
82
+ REPLACE?: Partial<T>;
83
+ };
84
+
80
85
  export type iAPI<T extends { [key: string]: any }> = T & {
81
86
  dataInsertMultipleRows?: T[];
82
87
  cacheResults?: boolean;
@@ -94,7 +99,7 @@ export type RequestQueryBody<
94
99
  Overrides extends { [key: string]: any } = {}
95
100
  > = Method extends 'GET' | 'PUT' | 'DELETE'
96
101
  ? iAPI<RequestGetPutDeleteBody<Modify<T, Overrides> & Custom>>
97
- : iAPI<Modify<T, Overrides> & Custom>;
102
+ : iAPI<RequestPostBody<Modify<T, Overrides> & Custom>>;
98
103
 
99
104
  export interface iCacheAPI<ResponseDataType = any> {
100
105
  requestArgumentsSerialized: string;
@@ -119,12 +124,15 @@ export type C6RestResponse<
119
124
  RestData extends { [key: string]: any },
120
125
  Overrides = {}
121
126
  > = {
122
- rest: Method extends 'GET' ? Modify<RestData, Overrides>[] : Modify<RestData, Overrides>;
127
+ rest: Method extends 'GET' ? Modify<RestData, Overrides>[] : never;
123
128
  session?: any;
124
129
  sql?: any;
125
130
  } & (Method extends 'GET'
126
131
  ? { next?: () => Promise<DetermineResponseDataType<'GET', RestData, Overrides>> }
127
- : {});
132
+ : {
133
+ affected: number,
134
+ insertId?: number | string,
135
+ });
128
136
 
129
137
  export interface iC6RestResponse<RestData> {
130
138
  // Backwards compatibility: base interface for rest/sql/session (singular)
@@ -172,6 +180,8 @@ export type iRestWebsocketPayload = {
172
180
  METHOD: iRestMethods;
173
181
  REQUEST: Record<string, any>;
174
182
  REQUEST_PRIMARY_KEY: Record<string, any> | null;
183
+ RESPONSE?: Record<string, any> | Record<string, any>[];
184
+ RESPONSE_PRIMARY_KEY?: Record<string, any> | null;
175
185
  };
176
186
  };
177
187