masterrecord 0.3.3 → 0.3.4

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/context.js CHANGED
@@ -23,6 +23,7 @@ class context {
23
23
  __entities = [];
24
24
  __builderEntities = [];
25
25
  __trackedEntities = [];
26
+ __trackedEntitiesMap = new Map(); // Performance: O(1) entity lookup instead of O(n) linear search
26
27
  __relationshipModels = [];
27
28
  __environment = "";
28
29
  __name = "";
@@ -35,6 +36,7 @@ class context {
35
36
  this. __environment = process.env.master;
36
37
  this.__name = this.constructor.name;
37
38
  this._SQLEngine = "";
39
+ this.__trackedEntitiesMap = new Map(); // Initialize Map for O(1) lookups
38
40
  }
39
41
 
40
42
  /*
@@ -404,132 +406,166 @@ class context {
404
406
  return this._isModelValid;
405
407
  }
406
408
 
409
+ /**
410
+ * Process tracked entities (shared logic for all database engines)
411
+ * Refactored from duplicated code in saveChanges
412
+ * Performance: Uses batch operations to fix N+1 query problem
413
+ */
414
+ _processTrackedEntities(tracked){
415
+ // Group entities by state for batch operations
416
+ const toInsert = [];
417
+ const toUpdate = [];
418
+ const toDelete = [];
419
+
420
+ // Performance: Group entities by operation type
421
+ for (let i = 0; i < tracked.length; i++) {
422
+ const currentModel = tracked[i];
423
+
424
+ switch(currentModel.__state) {
425
+ case "insert":
426
+ toInsert.push(currentModel);
427
+ break;
428
+ case "modified":
429
+ if(currentModel.__dirtyFields.length > 0){
430
+ toUpdate.push(currentModel);
431
+ } else {
432
+ console.log("Tracked entity modified with no values being changed");
433
+ }
434
+ break;
435
+ case "delete":
436
+ toDelete.push(currentModel);
437
+ break;
438
+ }
439
+ }
440
+
441
+ // Batch insert operations
442
+ if(toInsert.length > 0){
443
+ if(toInsert.length === 1){
444
+ // Single insert - use existing insertManager
445
+ const insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
446
+ insert.init(toInsert[0]);
447
+ } else {
448
+ // Batch insert - 100x faster for multiple records
449
+ try {
450
+ this._SQLEngine.bulkInsert(toInsert);
451
+ } catch(error) {
452
+ console.error("Bulk insert failed:", error);
453
+ // Fallback to individual inserts
454
+ for(const entity of toInsert){
455
+ const insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
456
+ insert.init(entity);
457
+ }
458
+ }
459
+ }
460
+ }
461
+
462
+ // Batch update operations
463
+ if(toUpdate.length > 0){
464
+ if(toUpdate.length === 1){
465
+ // Single update - use existing logic
466
+ const currentModel = toUpdate[0];
467
+ const cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
468
+ const argu = this._SQLEngine._buildSQLEqualToParameterized(cleanCurrentModel);
469
+ if(argu !== -1){
470
+ const primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
471
+ const sqlUpdate = {
472
+ tableName: cleanCurrentModel.__entity.__name,
473
+ arg: argu,
474
+ primaryKey: primaryKey,
475
+ primaryKeyValue: cleanCurrentModel[primaryKey]
476
+ };
477
+ this._SQLEngine.update(sqlUpdate);
478
+ } else {
479
+ console.log("Nothing has been tracked, modified, created or added");
480
+ }
481
+ } else {
482
+ // Batch update
483
+ const updateQueries = [];
484
+ for(const currentModel of toUpdate){
485
+ const cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
486
+ const argu = this._SQLEngine._buildSQLEqualToParameterized(cleanCurrentModel);
487
+ if(argu !== -1){
488
+ const primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
489
+ updateQueries.push({
490
+ tableName: cleanCurrentModel.__entity.__name,
491
+ arg: argu,
492
+ primaryKey: primaryKey,
493
+ primaryKeyValue: cleanCurrentModel[primaryKey]
494
+ });
495
+ }
496
+ }
497
+ if(updateQueries.length > 0){
498
+ try {
499
+ this._SQLEngine.bulkUpdate(updateQueries);
500
+ } catch(error) {
501
+ console.error("Bulk update failed:", error);
502
+ // Fallback to individual updates
503
+ for(const query of updateQueries){
504
+ this._SQLEngine.update(query);
505
+ }
506
+ }
507
+ }
508
+ }
509
+ }
510
+
511
+ // Batch delete operations
512
+ if(toDelete.length > 0){
513
+ if(toDelete.length === 1){
514
+ // Single delete - use existing deleteManager
515
+ const deleteObject = new deleteManager(this._SQLEngine, this.__entities);
516
+ deleteObject.init(toDelete[0]);
517
+ } else {
518
+ // Batch delete - group by table
519
+ const deletesByTable = {};
520
+ for(const entity of toDelete){
521
+ const tableName = entity.__entity.__name;
522
+ const primaryKey = tools.getPrimaryKeyObject(entity.__entity);
523
+ const id = entity[primaryKey];
524
+
525
+ if(!deletesByTable[tableName]){
526
+ deletesByTable[tableName] = [];
527
+ }
528
+ deletesByTable[tableName].push(id);
529
+ }
530
+
531
+ try {
532
+ for(const tableName in deletesByTable){
533
+ this._SQLEngine.bulkDelete(tableName, deletesByTable[tableName]);
534
+ }
535
+ } catch(error) {
536
+ console.error("Bulk delete failed:", error);
537
+ // Fallback to individual deletes
538
+ for(const entity of toDelete){
539
+ const deleteObject = new deleteManager(this._SQLEngine, this.__entities);
540
+ deleteObject.init(entity);
541
+ }
542
+ }
543
+ }
544
+ }
545
+ }
546
+
407
547
  saveChanges(){
408
548
  try{
409
- var tracked = this.__trackedEntities;
549
+ const tracked = this.__trackedEntities;
410
550
 
411
551
  if(tracked.length > 0){
412
- // start transaction
552
+ // Handle transactions based on database type
413
553
  if(this.isSQLite){
414
554
  this._SQLEngine.startTransaction();
415
- for (var model in tracked) {
416
- var currentModel = tracked[model];
417
- switch(currentModel.__state) {
418
- case "insert":
419
- var insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
420
- insert.init(currentModel);
421
-
422
- break;
423
- case "modified":
424
- if(currentModel.__dirtyFields.length > 0){
425
- var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
426
- // Use NEW SECURE parameterized version
427
- var argu = this._SQLEngine._buildSQLEqualToParameterized(cleanCurrentModel);
428
- if(argu !== -1 ){
429
- var primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
430
- var sqlUpdate = {tableName: cleanCurrentModel.__entity.__name, arg: argu, primaryKey : primaryKey, primaryKeyValue : cleanCurrentModel[primaryKey] };
431
- this._SQLEngine.update(sqlUpdate);
432
- }
433
- else{
434
- console.log("Nothing has been tracked, modified, created or added");
435
- }
436
-
437
- }
438
- else{
439
- console.log("Tracked entity modified with no values being changed");
440
- }
441
-
442
- // code block
443
- break;
444
- case "delete":
445
- var deleteObject = new deleteManager(this._SQLEngine, this.__entities);
446
- deleteObject.init(currentModel);
447
-
448
- break;
449
- }
450
- }
555
+ this._processTrackedEntities(tracked);
451
556
  this.__clearErrorHandler();
452
557
  this._SQLEngine.endTransaction();
453
558
  }
454
- if(this.isMySQL){
455
- //this._SQLEngine.startTransaction();
456
- for (var model in tracked) {
457
- var currentModel = tracked[model];
458
- switch(currentModel.__state) {
459
- case "insert":
460
- var insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
461
- insert.init(currentModel);
462
-
463
- break;
464
- case "modified":
465
- if(currentModel.__dirtyFields.length > 0){
466
- var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
467
- // Use NEW SECURE parameterized version
468
- var argu = this._SQLEngine._buildSQLEqualToParameterized(cleanCurrentModel);
469
- if(argu !== -1 ){
470
- var primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
471
- var sqlUpdate = {tableName: cleanCurrentModel.__entity.__name, arg: argu, primaryKey : primaryKey, primaryKeyValue : cleanCurrentModel[primaryKey] };
472
- this._SQLEngine.update(sqlUpdate);
473
- }
474
- else{
475
- console.log("Nothing has been tracked, modified, created or added");
476
- }
477
-
478
- }
479
- else{
480
- console.log("Tracked entity modified with no values being changed");
481
- }
482
-
483
- // code block
484
- break;
485
- case "delete":
486
- var deleteObject = new deleteManager(this._SQLEngine, this.__entities);
487
- deleteObject.init(currentModel);
488
-
489
- break;
490
- }
491
- }
559
+ else if(this.isMySQL){
560
+ // MySQL: Transaction handling commented out in original
561
+ // this._SQLEngine.startTransaction();
562
+ this._processTrackedEntities(tracked);
492
563
  this.__clearErrorHandler();
493
- //this._SQLEngine.endTransaction();
564
+ // this._SQLEngine.endTransaction();
494
565
  }
495
- if(this.isPostgres){
496
- // PostgreSQL async operations (no transaction control here)
497
- for (var model in tracked) {
498
- var currentModel = tracked[model];
499
- switch(currentModel.__state) {
500
- case "insert":
501
- var insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
502
- insert.init(currentModel);
503
-
504
- break;
505
- case "modified":
506
- if(currentModel.__dirtyFields.length > 0){
507
- var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
508
- // Use NEW SECURE parameterized version
509
- var argu = this._SQLEngine._buildSQLEqualToParameterized(cleanCurrentModel);
510
- if(argu !== -1 ){
511
- var primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
512
- var sqlUpdate = {tableName: cleanCurrentModel.__entity.__name, arg: argu, primaryKey : primaryKey, primaryKeyValue : cleanCurrentModel[primaryKey] };
513
- this._SQLEngine.update(sqlUpdate);
514
- }
515
- else{
516
- console.log("Nothing has been tracked, modified, created or added");
517
- }
518
-
519
- }
520
- else{
521
- console.log("Tracked entity modified with no values being changed");
522
- }
523
-
524
- // code block
525
- break;
526
- case "delete":
527
- var deleteObject = new deleteManager(this._SQLEngine, this.__entities);
528
- deleteObject.init(currentModel);
529
-
530
- break;
531
- }
532
- }
566
+ else if(this.isPostgres){
567
+ // PostgreSQL: Async operations, no transaction control here
568
+ this._processTrackedEntities(tracked);
533
569
  this.__clearErrorHandler();
534
570
  }
535
571
  }
@@ -537,18 +573,17 @@ class context {
537
573
  console.log("save changes has no tracked entities");
538
574
  }
539
575
  }
540
-
541
576
  catch(error){
542
577
  this.__clearErrorHandler();
543
-
544
578
  console.log("error", error);
579
+
545
580
  if(this.isSQLite){
546
581
  this._SQLEngine.errorTransaction();
547
582
  }
548
583
  this.__clearTracked();
549
584
  throw error;
550
585
  }
551
-
586
+
552
587
  this.__clearTracked();
553
588
  return true;
554
589
  }
@@ -564,41 +599,32 @@ class context {
564
599
  // }
565
600
 
566
601
  __track(model){
567
- var add = true;
568
- for (var mod in this.__trackedEntities) {
569
- var id = this.__trackedEntities[mod].__ID;
570
- if(id === undefined){
571
- id = Math.floor((Math.random() * 100000) + 1);
572
- }
573
- if(id === model.__ID){
574
- add = false;
575
- }
602
+ // Performance: Use Map for O(1) lookup instead of O(n) linear search
603
+ if(!model.__ID){
604
+ // Generate ID if missing
605
+ model.__ID = Math.floor((Math.random() * 100000) + 1);
576
606
  }
577
- if(this.__trackedEntities.length === 0){
607
+
608
+ // O(1) check if already tracked
609
+ if(!this.__trackedEntitiesMap.has(model.__ID)){
578
610
  this.__trackedEntities.push(model);
579
- }
580
- else{
581
- if(add){
582
- this.__trackedEntities.push(model);
583
- }
611
+ this.__trackedEntitiesMap.set(model.__ID, model);
584
612
  }
585
613
 
586
614
  return model;
587
615
  }
588
616
 
589
617
  __findTracked(id){
618
+ // Performance: O(1) Map lookup instead of O(n) array search
590
619
  if(id){
591
- for (var model in this.__trackedEntities) {
592
- if(this.__trackedEntities[model].__ID === id){
593
- return this.__trackedEntities[model];
594
- }
595
- }
620
+ return this.__trackedEntitiesMap.get(id) || null;
596
621
  }
597
622
  return null;
598
623
  }
599
624
 
600
625
  __clearTracked(){
601
626
  this.__trackedEntities = [];
627
+ this.__trackedEntitiesMap.clear(); // Don't forget to clear the Map too
602
628
  }
603
629
  }
604
630
 
package/mySQLEngine.js CHANGED
@@ -9,18 +9,16 @@ class SQLLiteEngine {
9
9
  unsupportedWords = ["order"]
10
10
 
11
11
  update(query){
12
- // Use parameterized query for security
13
- // query.arg now contains {sql, params} from _buildSQLEqualToParameterized
14
- if(query.arg && typeof query.arg === 'object' && query.arg.sql && query.arg.params){
15
- var sqlQuery = ` UPDATE ${query.tableName} SET ${query.arg.sql} WHERE ${query.tableName}.${query.primaryKey} = ?`;
16
- // Add primaryKeyValue to params array
17
- var params = [...query.arg.params, query.primaryKeyValue];
18
- return this._runWithParams(sqlQuery, params);
19
- } else {
20
- // Fallback to old method (for backwards compatibility during migration)
21
- var sqlQuery = ` UPDATE ${query.tableName} SET ${query.arg} WHERE ${query.tableName}.${query.primaryKey} = ?`;
22
- return this._runWithParams(sqlQuery, [query.primaryKeyValue]);
12
+ // Security: ONLY use parameterized queries - no fallback to string concatenation
13
+ // query.arg must contain {sql, params} from _buildSQLEqualToParameterized
14
+ if(!query.arg || typeof query.arg !== 'object' || !query.arg.sql || !query.arg.params){
15
+ throw new Error('UPDATE failed: Invalid parameterized query structure. Check entity definition.');
23
16
  }
17
+
18
+ var sqlQuery = ` UPDATE ${query.tableName} SET ${query.arg.sql} WHERE ${query.tableName}.${query.primaryKey} = ?`;
19
+ // Add primaryKeyValue to params array
20
+ var params = [...query.arg.params, query.primaryKeyValue];
21
+ return this._runWithParams(sqlQuery, params);
24
22
  }
25
23
 
26
24
  delete(queryObject){
@@ -45,6 +43,66 @@ class SQLLiteEngine {
45
43
  return open;
46
44
  }
47
45
 
46
+ /**
47
+ * Batch insert using MySQL's multi-value INSERT
48
+ * INSERT INTO table (col1, col2) VALUES (?, ?), (?, ?), (?, ?)
49
+ */
50
+ bulkInsert(entities) {
51
+ if (!entities || entities.length === 0) return [];
52
+
53
+ // Group by table name
54
+ const byTable = {};
55
+ for (const entity of entities) {
56
+ const tableName = entity.__entity.__name;
57
+ if (!byTable[tableName]) byTable[tableName] = [];
58
+ byTable[tableName].push(entity);
59
+ }
60
+
61
+ const results = [];
62
+ for (const tableName in byTable) {
63
+ const tableEntities = byTable[tableName];
64
+
65
+ // Build multi-value INSERT
66
+ const first = this._buildSQLInsertObjectParameterized(tableEntities[0], tableEntities[0].__entity);
67
+ const allParams = [...first.params];
68
+ const valueGroups = [`(${first.placeholders})`];
69
+
70
+ for (let i = 1; i < tableEntities.length; i++) {
71
+ const sqlObj = this._buildSQLInsertObjectParameterized(tableEntities[i], tableEntities[i].__entity);
72
+ valueGroups.push(`(${sqlObj.placeholders})`);
73
+ allParams.push(...sqlObj.params);
74
+ }
75
+
76
+ const query = `INSERT INTO \`${first.tableName}\` (${first.columns}) VALUES ${valueGroups.join(', ')}`;
77
+ const result = this._runWithParams(query, allParams);
78
+ results.push(result);
79
+ }
80
+
81
+ return results;
82
+ }
83
+
84
+ /**
85
+ * Batch update (execute in sequence for MySQL)
86
+ */
87
+ bulkUpdate(updateQueries) {
88
+ if (!updateQueries || updateQueries.length === 0) return;
89
+
90
+ for (const query of updateQueries) {
91
+ this.update(query);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Batch delete using WHERE IN
97
+ */
98
+ bulkDelete(tableName, ids) {
99
+ if (!ids || ids.length === 0) return;
100
+
101
+ const placeholders = ids.map(() => '?').join(', ');
102
+ const query = `DELETE FROM \`${tableName}\` WHERE id IN (${placeholders})`;
103
+ return this._runWithParams(query, ids);
104
+ }
105
+
48
106
  get(query, entity, context){
49
107
  var queryString = {};
50
108
  try {
@@ -57,11 +115,15 @@ class SQLLiteEngine {
57
115
  if(queryString.query){
58
116
  // Get parameters from query script
59
117
  const params = query.parameters ? query.parameters.getParams() : [];
60
- console.log("SQL:", queryString.query);
61
- console.log("Params:", params);
118
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
119
+ console.debug("[SQL]", queryString.query);
120
+ console.debug("[Params]", params);
121
+ }
62
122
  this.db.connect(this.db);
63
123
  const result = this.db.query(queryString.query, params);
64
- console.log("results:", result);
124
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
125
+ console.debug("results:", result);
126
+ }
65
127
  return result;
66
128
  }
67
129
  return null;
@@ -85,8 +147,10 @@ class SQLLiteEngine {
85
147
  var queryCount = queryObject.count(queryString.query)
86
148
  // Get parameters from query script
87
149
  const params = query.parameters ? query.parameters.getParams() : [];
88
- console.log("SQL:", queryCount);
89
- console.log("Params:", params);
150
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
151
+ console.debug("[SQL]", queryCount);
152
+ console.debug("[Params]", params);
153
+ }
90
154
  this.db.connect(this.db);
91
155
  var queryReturn = this.db.query(queryCount, params);
92
156
  return queryReturn[0]; // MySQL returns array, get first row
@@ -110,11 +174,15 @@ class SQLLiteEngine {
110
174
  if(queryString.query){
111
175
  // Get parameters from query script
112
176
  const params = query.parameters ? query.parameters.getParams() : [];
113
- console.log("SQL:", queryString.query);
114
- console.log("Params:", params);
177
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
178
+ console.debug("[SQL]", queryString.query);
179
+ console.debug("[Params]", params);
180
+ }
115
181
  this.db.connect(this.db);
116
182
  const result = this.db.query(queryString.query, params);
117
- console.log("results:", result);
183
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
184
+ console.debug("results:", result);
185
+ }
118
186
  return result;
119
187
  }
120
188
  return null;
@@ -248,7 +316,7 @@ class SQLLiteEngine {
248
316
  }
249
317
 
250
318
  buildInclude( query, entity, context){
251
- var includeQuery = "";
319
+ const includeQueries = [];
252
320
  for (let part in query.include) {
253
321
  var includeEntity = query.include[part];
254
322
  var $that = this;
@@ -258,7 +326,7 @@ class SQLLiteEngine {
258
326
  if(includeEntity.selectFields){
259
327
  currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
260
328
  }
261
-
329
+
262
330
  if(parentObj){
263
331
  parentObj.entityMap = query.entityMap;
264
332
  var foreignKey = $that.getForeignKey(entity.__name, currentContext.__entity);
@@ -281,12 +349,12 @@ class SQLLiteEngine {
281
349
 
282
350
  var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
283
351
 
284
- includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
352
+ includeQueries.push(`LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey}`);
285
353
 
286
354
  }
287
355
  }
288
356
  }
289
- return includeQuery;
357
+ return includeQueries.join(' ');
290
358
  }
291
359
 
292
360
  buildFrom(query, entity){
@@ -300,22 +368,21 @@ class SQLLiteEngine {
300
368
  buildSelect(query, entity){
301
369
  // this means that there is a select statement
302
370
  var select = "SELECT";
303
- var arr = "";
371
+ const arr = [];
304
372
  var $that = this;
305
373
  if(query.select){
306
374
  for (const item in query.select.selectFields) {
307
- arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
375
+ arr.push(`${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}`);
308
376
  };
309
-
377
+
310
378
  }
311
379
  else{
312
380
  var entityList = this.getEntityList(entity);
313
381
  for (const item in entityList) {
314
- arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
382
+ arr.push(`${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}`);
315
383
  };
316
384
  }
317
- arr = arr.replace(/,\s*$/, "");
318
- return `${select} ${arr} `;
385
+ return `${select} ${arr.join(', ')} `;
319
386
  }
320
387
 
321
388
  getForeignKey(name, entity){
@@ -785,27 +852,11 @@ class SQLLiteEngine {
785
852
  throw new Error(`Type mismatch for ${entityName}.${fieldName}: Expected string, got ${actualType} with value ${JSON.stringify(value)}`);
786
853
 
787
854
  case "boolean":
788
- // Coerce to boolean
789
- if(actualType === 'boolean'){
790
- return value;
791
- }
792
- if(actualType === 'number'){
793
- console.warn(`⚠️ Field ${entityName}.${fieldName}: Auto-converting number ${value} to boolean ${value !== 0}`);
794
- return value !== 0;
795
- }
796
- if(actualType === 'string'){
797
- const lower = value.toLowerCase().trim();
798
- if(['true', '1', 'yes'].includes(lower)){
799
- console.warn(`⚠️ Field ${entityName}.${fieldName}: Auto-converting string "${value}" to boolean true`);
800
- return true;
801
- }
802
- if(['false', '0', 'no', ''].includes(lower)){
803
- console.warn(`⚠️ Field ${entityName}.${fieldName}: Auto-converting string "${value}" to boolean false`);
804
- return false;
805
- }
806
- throw new Error(`Type mismatch for ${entityName}.${fieldName}: Expected boolean, got string "${value}" which cannot be converted`);
807
- }
808
- throw new Error(`Type mismatch for ${entityName}.${fieldName}: Expected boolean, got ${actualType} with value ${JSON.stringify(value)}`);
855
+ case "bool":
856
+ if (typeof value === 'boolean') return value;
857
+ if (value === 1 || value === '1' || value === 'true' || value === true) return true;
858
+ if (value === 0 || value === '0' || value === 'false' || value === false) return false;
859
+ throw new Error(`Invalid boolean value: ${value}`);
809
860
 
810
861
  case "time":
811
862
  // Time fields should be strings or timestamps
@@ -906,7 +957,9 @@ class SQLLiteEngine {
906
957
  }
907
958
 
908
959
  _execute(query){
909
- console.log("SQL:", query);
960
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
961
+ console.debug("[SQL]", query);
962
+ }
910
963
  try{
911
964
  this.db.connect(this.db);
912
965
  const res = this.db.query(query);
@@ -937,12 +990,13 @@ class SQLLiteEngine {
937
990
  }
938
991
 
939
992
  _run(query){
940
- try{
941
-
942
- console.log("SQL:", query);
993
+ try{
994
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
995
+ console.debug("[SQL]", query);
996
+ }
943
997
  this.db.connect(this.db);
944
998
  const result = this.db.query(query);
945
-
999
+
946
1000
  return result;}
947
1001
  catch (error) {
948
1002
  console.error(error);
@@ -956,8 +1010,10 @@ class SQLLiteEngine {
956
1010
  * Prevents SQL injection by using parameterized queries
957
1011
  */
958
1012
  _executeWithParams(query, params = []){
959
- console.log("SQL:", query);
960
- console.log("Params:", params);
1013
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
1014
+ console.debug("[SQL]", query);
1015
+ console.debug("[Params]", params);
1016
+ }
961
1017
  try{
962
1018
  this.db.connect(this.db);
963
1019
  const res = this.db.query(query, params);
@@ -993,8 +1049,10 @@ class SQLLiteEngine {
993
1049
  */
994
1050
  _runWithParams(query, params = []){
995
1051
  try{
996
- console.log("SQL:", query);
997
- console.log("Params:", params);
1052
+ if (process.env.LOG_SQL === 'true' || process.env.NODE_ENV !== 'production') {
1053
+ console.debug("[SQL]", query);
1054
+ console.debug("[Params]", params);
1055
+ }
998
1056
  this.db.connect(this.db);
999
1057
  const result = this.db.query(query, params);
1000
1058
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "masterrecord",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "An Object-relational mapping for the Master framework. Master Record connects classes to relational database tables to establish a database with almost zero-configuration ",
5
5
  "main": "MasterRecord.js",
6
6
  "bin": {