@carbonorm/carbonnode 4.0.0 → 4.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carbonorm/carbonnode",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "browser": "dist/index.umd.js",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1342,7 +1342,7 @@ export const TABLES = {
1342
1342
  };
1343
1343
  export const C6 = {
1344
1344
  ...C6Constants,
1345
- C6VERSION: '4.0.0',
1345
+ C6VERSION: '4.0.1',
1346
1346
  IMPORT: async (tableName) => {
1347
1347
  tableName = tableName.toLowerCase();
1348
1348
  // if tableName is not a key in the TABLES object then throw an error
@@ -2009,7 +2009,7 @@ export type RestTableInterfaces = iActor
2009
2009
 
2010
2010
  export const C6 : iC6Object<RestTableInterfaces> = {
2011
2011
  ...C6Constants,
2012
- C6VERSION: '4.0.0',
2012
+ C6VERSION: '4.0.1',
2013
2013
  IMPORT: async (tableName: string) : Promise<iDynamicApiImport> => {
2014
2014
 
2015
2015
  tableName = tableName.toLowerCase();
@@ -76,6 +76,23 @@ describe('SQL Builders', () => {
76
76
  expect(params).toEqual([JSON.stringify(payload)]);
77
77
  });
78
78
 
79
+ it('stringifies dotted-key JSON payloads for JSON columns on UPDATE', () => {
80
+ const config = buildTestConfig();
81
+ const payload = { 'section1.preparedBy': 'Prepared by Assessorly, Co.' };
82
+ const qb = new UpdateQueryBuilder(config as any, {
83
+ [C6C.UPDATE]: {
84
+ 'actor.json_data': payload,
85
+ },
86
+ WHERE: {
87
+ 'actor.actor_id': [C6C.EQUAL, 5],
88
+ }
89
+ } as any, false);
90
+
91
+ const { params } = qb.build('actor');
92
+
93
+ expect(params).toEqual([JSON.stringify(payload), 5]);
94
+ });
95
+
79
96
  it('throws on operator-shaped insert payloads', () => {
80
97
  const config = buildTestConfig();
81
98
  const qb = new PostQueryBuilder(config as any, {
@@ -378,19 +378,19 @@ export abstract class ConditionBuilder<
378
378
  throw new Error('Unsupported operand type in SQL expression.');
379
379
  }
380
380
 
381
- private isPlainArrayLiteral(value: any): boolean {
381
+ private isPlainArrayLiteral(value: any, allowColumnRefs = false): boolean {
382
382
  if (!Array.isArray(value)) return false;
383
383
  return value.every(item => {
384
384
  if (item === null) return true;
385
385
  const type = typeof item;
386
386
  if (type === 'string' || type === 'number' || type === 'boolean') return true;
387
- if (Array.isArray(item)) return this.isPlainArrayLiteral(item);
388
- if (item && typeof item === 'object') return this.isPlainObjectLiteral(item);
387
+ if (Array.isArray(item)) return this.isPlainArrayLiteral(item, allowColumnRefs);
388
+ if (item && typeof item === 'object') return this.isPlainObjectLiteral(item, allowColumnRefs);
389
389
  return false;
390
390
  });
391
391
  }
392
392
 
393
- private isPlainObjectLiteral(value: any): boolean {
393
+ private isPlainObjectLiteral(value: any, allowColumnRefs = false): boolean {
394
394
  if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
395
395
  if (value instanceof Date) return false;
396
396
  if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(value)) return false;
@@ -405,13 +405,30 @@ export abstract class ConditionBuilder<
405
405
  return false;
406
406
  }
407
407
 
408
- if (entries.some(([key]) => typeof key === 'string' && (this.isColumnRef(key) || key.includes('.')))) {
409
- return false;
408
+ if (!allowColumnRefs) {
409
+ if (entries.some(([key]) => typeof key === 'string' && (this.isColumnRef(key) || key.includes('.')))) {
410
+ return false;
411
+ }
410
412
  }
411
413
 
412
414
  return true;
413
415
  }
414
416
 
417
+ private resolveColumnDefinition(column?: string): any | undefined {
418
+ if (!column || typeof column !== 'string' || !column.includes('.')) return undefined;
419
+ const [prefix, colName] = column.split('.', 2);
420
+ const tableName = this.aliasMap[prefix] ?? prefix;
421
+ const table = this.config.C6?.TABLES?.[tableName];
422
+ if (!table?.TYPE_VALIDATION) return undefined;
423
+ return table.TYPE_VALIDATION[colName] ?? table.TYPE_VALIDATION[`${tableName}.${colName}`];
424
+ }
425
+
426
+ private isJsonColumn(column?: string): boolean {
427
+ const columnDef = this.resolveColumnDefinition(column);
428
+ const mysqlType = columnDef?.MYSQL_TYPE;
429
+ return typeof mysqlType === 'string' && mysqlType.toLowerCase().includes('json');
430
+ }
431
+
415
432
  protected serializeUpdateValue(
416
433
  value: any,
417
434
  params: any[] | Record<string, any>,
@@ -419,7 +436,10 @@ export abstract class ConditionBuilder<
419
436
  ): string {
420
437
  const normalized = value instanceof Map ? Object.fromEntries(value) : value;
421
438
 
422
- if (this.isPlainArrayLiteral(normalized) || this.isPlainObjectLiteral(normalized)) {
439
+ const allowColumnRefs = this.isJsonColumn(contextColumn);
440
+
441
+ if (this.isPlainArrayLiteral(normalized, allowColumnRefs)
442
+ || this.isPlainObjectLiteral(normalized, allowColumnRefs)) {
423
443
  return this.addParam(params, contextColumn ?? '', JSON.stringify(normalized));
424
444
  }
425
445