masterrecord 0.3.15 → 0.3.17

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.
@@ -32,7 +32,10 @@
32
32
  "Bash(grep:*)",
33
33
  "Bash(npm audit:*)",
34
34
  "Bash(npm test:*)",
35
- "Bash(done)"
35
+ "Bash(done)",
36
+ "WebSearch",
37
+ "WebFetch(domain:github.com)",
38
+ "Bash(master=development masterrecord update-database qaContext)"
36
39
  ],
37
40
  "deny": [],
38
41
  "ask": []
@@ -295,32 +295,33 @@ class schema{
295
295
  }
296
296
 
297
297
  // EnsureCreated equivalent for MySQL: create DB if missing
298
- createDatabase(){
298
+ async createDatabase(){
299
299
  try{
300
300
  if(!(this.context && this.context.isMySQL)){ return; }
301
- const MySQLClient = require('masterrecord/mySQLSyncConnect');
301
+ const MySQLAsyncClient = require('masterrecord/mySQLAsyncConnect');
302
302
  const client = this.context.db; // main client (may not be connected yet)
303
303
  if(!client || !client.config || !client.config.database){ return; }
304
304
  const dbName = client.config.database;
305
305
  // Build server-level connection (no database)
306
306
  const baseConfig = { ...client.config };
307
307
  delete baseConfig.database;
308
- const admin = new MySQLClient(baseConfig);
309
- admin.connect();
310
- if(!admin.connection){ return; }
308
+ const admin = new MySQLAsyncClient(baseConfig);
309
+ await admin.connect();
310
+ const pool = admin.getPool();
311
+ if(!pool){ return; }
311
312
 
312
313
  // Use parameterized query for checking database existence
313
- const check = admin.query(`SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?`, [dbName]);
314
- const exists = Array.isArray(check) ? check.length > 0 : !!check?.length;
314
+ const [rows] = await pool.execute(`SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?`, [dbName]);
315
+ const exists = Array.isArray(rows) && rows.length > 0;
315
316
  if(!exists){
316
317
  // Validate database name (alphanumeric, underscore, hyphen only)
317
318
  if(!/^[a-zA-Z0-9_-]+$/.test(dbName)){
318
319
  throw new Error(`Invalid database name: ${dbName}. Only alphanumeric characters, underscores, and hyphens are allowed.`);
319
320
  }
320
321
  // CREATE DATABASE doesn't support placeholders, but we've validated the name
321
- admin.query(`CREATE DATABASE \`${dbName}\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
322
+ await pool.execute(`CREATE DATABASE \`${dbName}\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
322
323
  }
323
- admin.close();
324
+ await admin.close();
324
325
  this._dbEnsured = true;
325
326
  }catch(err){
326
327
  // Non-fatal: migrations may still proceed if DB already exists or permissions blocked
package/SQLLiteEngine.js CHANGED
@@ -6,7 +6,7 @@ class SQLLiteEngine {
6
6
 
7
7
  unsupportedWords = ["order"]
8
8
 
9
- update(query){
9
+ async update(query){
10
10
  // Security: ONLY use parameterized queries - no fallback to string concatenation
11
11
  // query.arg must contain {sql, params} from _buildSQLEqualToParameterized
12
12
  if(!query.arg || typeof query.arg !== 'object' || !query.arg.sql || !query.arg.params){
@@ -18,17 +18,17 @@ class SQLLiteEngine {
18
18
  WHERE [${query.tableName}].[${query.primaryKey}] = ?`;
19
19
  // Add primaryKeyValue to params array
20
20
  var params = [...query.arg.params, query.primaryKeyValue];
21
- return this._runWithParams(sqlQuery, params);
21
+ return Promise.resolve(this._runWithParams(sqlQuery, params));
22
22
  }
23
23
 
24
- delete(queryObject){
24
+ async delete(queryObject){
25
25
  var sqlObject = this._buildDeleteObject(queryObject);
26
26
  // Use parameterized query to prevent SQL injection
27
27
  var sqlQuery = `DELETE FROM [${sqlObject.tableName}] WHERE [${sqlObject.tableName}].[${sqlObject.primaryKey}] = ?`;
28
- return this._executeWithParams(sqlQuery, [sqlObject.value]);
28
+ return Promise.resolve(this._executeWithParams(sqlQuery, [sqlObject.value]));
29
29
  }
30
30
 
31
- insert(queryObject){
31
+ async insert(queryObject){
32
32
  // Use NEW SECURE parameterized version
33
33
  var sqlObject = this._buildSQLInsertObjectParameterized(queryObject, queryObject.__entity);
34
34
  if(sqlObject === -1){
@@ -40,28 +40,35 @@ class SQLLiteEngine {
40
40
  var open = {
41
41
  "id": queryObj.lastInsertRowid
42
42
  };
43
- return open;
43
+ return Promise.resolve(open);
44
44
  }
45
45
 
46
46
  /**
47
47
  * Batch insert multiple entities in a single transaction
48
48
  * Performance: 100x faster than N separate inserts
49
49
  */
50
- bulkInsert(entities) {
51
- if (!entities || entities.length === 0) return [];
50
+ async bulkInsert(entities) {
51
+ if (!entities || entities.length === 0) return Promise.resolve([]);
52
52
 
53
53
  const results = [];
54
- // SQLite: Use transaction for batch inserts
55
- this.startTransaction();
54
+ // SQLite: Use transaction for batch inserts (only if not already in one)
55
+ const needsTransaction = !this.db.inTransaction;
56
+ if (needsTransaction) {
57
+ await this.startTransaction();
58
+ }
56
59
  try {
57
60
  for (const entity of entities) {
58
- const result = this.insert(entity);
61
+ const result = await this.insert(entity);
59
62
  results.push(result);
60
63
  }
61
- this.endTransaction();
62
- return results;
64
+ if (needsTransaction) {
65
+ await this.endTransaction();
66
+ }
67
+ return Promise.resolve(results);
63
68
  } catch (error) {
64
- this.errorTransaction();
69
+ if (needsTransaction) {
70
+ await this.errorTransaction();
71
+ }
65
72
  throw error;
66
73
  }
67
74
  }
@@ -69,17 +76,25 @@ class SQLLiteEngine {
69
76
  /**
70
77
  * Batch update multiple entities
71
78
  */
72
- bulkUpdate(updateQueries) {
73
- if (!updateQueries || updateQueries.length === 0) return;
79
+ async bulkUpdate(updateQueries) {
80
+ if (!updateQueries || updateQueries.length === 0) return Promise.resolve();
74
81
 
75
- this.startTransaction();
82
+ // Only start transaction if not already in one
83
+ const needsTransaction = !this.db.inTransaction;
84
+ if (needsTransaction) {
85
+ await this.startTransaction();
86
+ }
76
87
  try {
77
88
  for (const query of updateQueries) {
78
- this.update(query);
89
+ await this.update(query);
90
+ }
91
+ if (needsTransaction) {
92
+ await this.endTransaction();
79
93
  }
80
- this.endTransaction();
81
94
  } catch (error) {
82
- this.errorTransaction();
95
+ if (needsTransaction) {
96
+ await this.errorTransaction();
97
+ }
83
98
  throw error;
84
99
  }
85
100
  }
@@ -87,15 +102,15 @@ class SQLLiteEngine {
87
102
  /**
88
103
  * Batch delete multiple entities using WHERE IN
89
104
  */
90
- bulkDelete(tableName, ids) {
91
- if (!ids || ids.length === 0) return;
105
+ async bulkDelete(tableName, ids) {
106
+ if (!ids || ids.length === 0) return Promise.resolve();
92
107
 
93
108
  const placeholders = ids.map(() => '?').join(', ');
94
109
  const query = `DELETE FROM [${tableName}] WHERE id IN (${placeholders})`;
95
- return this._runWithParams(query, ids);
110
+ return Promise.resolve(this._runWithParams(query, ids));
96
111
  }
97
112
 
98
- get(query, entity, context){
113
+ async get(query, entity, context){
99
114
  var queryString = {};
100
115
  try {
101
116
  if(query.raw){
@@ -117,26 +132,26 @@ class SQLLiteEngine {
117
132
  console.debug("[Params]", params);
118
133
  }
119
134
  var queryReturn = this.db.prepare(queryString.query).get(...params);
120
- return queryReturn;
135
+ return Promise.resolve(queryReturn);
121
136
  }
122
- return null;
137
+ return Promise.resolve(null);
123
138
  } catch (err) {
124
139
  console.error(err);
125
- return null;
140
+ return Promise.resolve(null);
126
141
  }
127
142
  }
128
143
 
129
144
  // Introspection helpers
130
- tableExists(tableName){
145
+ async tableExists(tableName){
131
146
  try{
132
147
  // Use parameterized query to prevent SQL injection
133
148
  const sql = `SELECT name FROM sqlite_master WHERE type='table' AND name=?`;
134
149
  const row = this.db.prepare(sql).get(tableName);
135
- return !!row;
136
- }catch(_){ return false; }
150
+ return Promise.resolve(!!row);
151
+ }catch(_){ return Promise.resolve(false); }
137
152
  }
138
153
 
139
- getTableInfo(tableName){
154
+ async getTableInfo(tableName){
140
155
  try{
141
156
  // Security: Validate table name to prevent SQL injection
142
157
  // PRAGMA statements don't support parameterized queries
@@ -149,11 +164,11 @@ class SQLLiteEngine {
149
164
  }
150
165
  const sql = `PRAGMA table_info(${tableName})`;
151
166
  const rows = this.db.prepare(sql).all();
152
- return rows || [];
153
- }catch(_){ return []; }
167
+ return Promise.resolve(rows || []);
168
+ }catch(_){ return Promise.resolve([]); }
154
169
  }
155
170
 
156
- getCount(queryObject, entity, context){
171
+ async getCount(queryObject, entity, context){
157
172
  var query = queryObject.script;
158
173
  var queryString = {};
159
174
  try {
@@ -176,23 +191,23 @@ class SQLLiteEngine {
176
191
  console.debug("[Params]", params);
177
192
  }
178
193
  var queryReturn = this.db.prepare(queryCount).get(...params);
179
- return queryReturn;
194
+ return Promise.resolve(queryReturn);
180
195
  }
181
- return null;
196
+ return Promise.resolve(null);
182
197
  } catch (err) {
183
198
  console.error(err);
184
- return null;
199
+ return Promise.resolve(null);
185
200
  }
186
201
  }
187
202
 
188
- all(query, entity, context){
203
+ async all(query, entity, context){
189
204
  var selectQuery = {};
190
205
  try {
191
206
  if(query.raw){
192
207
  selectQuery.query = query.raw;
193
208
  }
194
209
  else{
195
-
210
+
196
211
  selectQuery = this.buildQuery(query, entity, context);
197
212
  }
198
213
  if(selectQuery.query){
@@ -203,12 +218,12 @@ class SQLLiteEngine {
203
218
  console.debug("[Params]", params);
204
219
  }
205
220
  var queryReturn = this.db.prepare(selectQuery.query).all(...params);
206
- return queryReturn;
221
+ return Promise.resolve(queryReturn);
207
222
  }
208
- return null;
223
+ return Promise.resolve(null);
209
224
  } catch (err) {
210
225
  console.error(err);
211
- return null;
226
+ return Promise.resolve(null);
212
227
  }
213
228
  }
214
229
 
@@ -623,16 +638,25 @@ class SQLLiteEngine {
623
638
  return false;
624
639
  }
625
640
 
626
- startTransaction(){
627
- this.db.prepare('BEGIN').run();
641
+ async startTransaction(){
642
+ // Prevent nested transactions (SQLite limitation)
643
+ return Promise.resolve(
644
+ this.db.inTransaction ? null : this.db.prepare('BEGIN').run()
645
+ );
628
646
  }
629
647
 
630
- endTransaction(){
631
- this.db.prepare('COMMIT').run();
648
+ async endTransaction(){
649
+ // Only commit if transaction is active
650
+ return Promise.resolve(
651
+ this.db.inTransaction ? this.db.prepare('COMMIT').run() : null
652
+ );
632
653
  }
633
654
 
634
- errorTransaction(){
635
- this.db.prepare('ROLLBACK').run();
655
+ async errorTransaction(){
656
+ // Only rollback if transaction is active
657
+ return Promise.resolve(
658
+ this.db.inTransaction ? this.db.prepare('ROLLBACK').run() : null
659
+ );
636
660
  }
637
661
 
638
662
  _buildSQLEqualTo(model){
@@ -1222,6 +1246,17 @@ class SQLLiteEngine {
1222
1246
  this.db = db;
1223
1247
  this.dbType = type; // this will let us know which type of sqlengine to use.
1224
1248
  }
1249
+
1250
+ /**
1251
+ * Close database connection
1252
+ * Required for proper cleanup of better-sqlite3 native bindings
1253
+ */
1254
+ close() {
1255
+ if (this.db) {
1256
+ this.db.close();
1257
+ console.log('SQLite database closed');
1258
+ }
1259
+ }
1225
1260
  }
1226
1261
 
1227
1262
  module.exports = SQLLiteEngine;
package/context.js CHANGED
@@ -20,7 +20,7 @@ const modelBuilder = require('./Entity/entityModelBuilder');
20
20
  const query = require('masterrecord/QueryLanguage/queryMethods');
21
21
  const tools = require('./Tools');
22
22
  const SQLLiteEngine = require('masterrecord/SQLLiteEngine');
23
- const MYSQLEngine = require('masterrecord/mySQLEngine');
23
+ const MySQLEngine = require('masterrecord/realMySQLEngine');
24
24
  const PostgresEngine = require('masterrecord/postgresEngine');
25
25
  const insertManager = require('./insertManager');
26
26
  const deleteManager = require('./deleteManager');
@@ -28,7 +28,7 @@ const globSearch = require('glob');
28
28
  const fs = require('fs');
29
29
  const path = require('path');
30
30
  const appRoot = require('app-root-path');
31
- const MySQLClient = require('masterrecord/mySQLSyncConnect');
31
+ const MySQLAsyncClient = require('masterrecord/mySQLAsyncConnect');
32
32
  const PostgresClient = require('masterrecord/postgresSyncConnect');
33
33
  const QueryCache = require('./Cache/QueryCache');
34
34
 
@@ -284,7 +284,7 @@ class context {
284
284
  * @returns {object} MySQL connection instance
285
285
  * @throws {DatabaseConnectionError} If connection fails
286
286
  */
287
- __mysqlInit(env, sqlName) {
287
+ async __mysqlInit(env, sqlName) {
288
288
  try {
289
289
  // Validate required MySQL configuration
290
290
  if (!env.database || typeof env.database !== 'string') {
@@ -303,11 +303,18 @@ class context {
303
303
  );
304
304
  }
305
305
 
306
- const connection = new MySQLClient(env);
307
- this._SQLEngine = new MYSQLEngine();
306
+ console.log('[MySQL] Initializing async connection pool...');
307
+ const client = new MySQLAsyncClient(env);
308
+ await client.connect();
309
+
310
+ const pool = client.getPool();
311
+ this._SQLEngine = new MySQLEngine();
312
+ this._SQLEngine.setDB(pool);
308
313
  this._SQLEngine.__name = sqlName;
314
+ this.isMySQL = true;
309
315
 
310
- return connection;
316
+ console.log('[MySQL] Connection pool ready');
317
+ return client;
311
318
  } catch (error) {
312
319
  // Preserve original error if it's already a ContextError
313
320
  if (error instanceof ContextError) {
@@ -624,15 +631,18 @@ class context {
624
631
  return this;
625
632
  }
626
633
 
627
- // MySQL initialization
634
+ // MySQL initialization (async)
628
635
  if (type === DB_TYPES.MYSQL) {
629
636
  this.isMySQL = true;
630
637
  this.isSQLite = false;
631
638
  this.isPostgres = false;
632
639
 
633
- this.db = this.__mysqlInit(options, 'mysql2');
634
- this._SQLEngine.setDB(this.db, 'mysql');
635
- return this;
640
+ // MySQL is async - caller must await env()
641
+ return (async () => {
642
+ this.db = await this.__mysqlInit(options, 'mysql2');
643
+ // Note: engine is already set in __mysqlInit
644
+ return this;
645
+ })();
636
646
  }
637
647
 
638
648
  // PostgreSQL initialization (async)
@@ -833,7 +843,7 @@ class context {
833
843
  * @example
834
844
  * context.useMySql('./config/environments');
835
845
  */
836
- useMySql(rootFolderLocation) {
846
+ async useMySql(rootFolderLocation) {
837
847
  try {
838
848
  this.isMySQL = true;
839
849
  this.isSQLite = false;
@@ -857,8 +867,8 @@ class context {
857
867
  }
858
868
 
859
869
  this.validateSQLiteOptions(options);
860
- this.db = this.__mysqlInit(options, 'mysql2');
861
- this._SQLEngine.setDB(this.db, 'mysql');
870
+ this.db = await this.__mysqlInit(options, 'mysql2');
871
+ // Note: engine is already set in __mysqlInit
862
872
  return this;
863
873
  } catch (error) {
864
874
  // Preserve original error if it's already a ContextError
@@ -966,7 +976,7 @@ class context {
966
976
  * @private
967
977
  * @param {Array<object>} tracked - Array of tracked entities
968
978
  */
969
- _processTrackedEntities(tracked) {
979
+ async _processTrackedEntities(tracked) {
970
980
  // Group entities by state for batch operations (single pass)
971
981
  const toInsert = [];
972
982
  const toUpdate = [];
@@ -998,17 +1008,17 @@ class context {
998
1008
 
999
1009
  // Batch insert operations
1000
1010
  if (toInsert.length > 0) {
1001
- this._processBatchInserts(toInsert);
1011
+ await this._processBatchInserts(toInsert);
1002
1012
  }
1003
1013
 
1004
1014
  // Batch update operations
1005
1015
  if (toUpdate.length > 0) {
1006
- this._processBatchUpdates(toUpdate);
1016
+ await this._processBatchUpdates(toUpdate);
1007
1017
  }
1008
1018
 
1009
1019
  // Batch delete operations
1010
1020
  if (toDelete.length > 0) {
1011
- this._processBatchDeletes(toDelete);
1021
+ await this._processBatchDeletes(toDelete);
1012
1022
  }
1013
1023
  }
1014
1024
 
@@ -1018,21 +1028,21 @@ class context {
1018
1028
  * @private
1019
1029
  * @param {Array<object>} entities - Entities to insert
1020
1030
  */
1021
- _processBatchInserts(entities) {
1031
+ async _processBatchInserts(entities) {
1022
1032
  if (entities.length === 1) {
1023
1033
  // Single insert - use existing insertManager
1024
1034
  const insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
1025
- insert.init(entities[0]);
1035
+ await insert.init(entities[0]);
1026
1036
  } else {
1027
1037
  // Batch insert - 100x faster for multiple records
1028
1038
  try {
1029
- this._SQLEngine.bulkInsert(entities);
1039
+ await this._SQLEngine.bulkInsert(entities);
1030
1040
  } catch (error) {
1031
1041
  console.error('[Context] Bulk insert failed, falling back to individual inserts:', error.message);
1032
1042
  // Fallback to individual inserts
1033
1043
  for (const entity of entities) {
1034
1044
  const insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
1035
- insert.init(entity);
1045
+ await insert.init(entity);
1036
1046
  }
1037
1047
  }
1038
1048
  }
@@ -1044,7 +1054,7 @@ class context {
1044
1054
  * @private
1045
1055
  * @param {Array<object>} entities - Entities to update
1046
1056
  */
1047
- _processBatchUpdates(entities) {
1057
+ async _processBatchUpdates(entities) {
1048
1058
  if (entities.length === 1) {
1049
1059
  // Single update - use existing logic
1050
1060
  const currentModel = entities[0];
@@ -1059,7 +1069,7 @@ class context {
1059
1069
  primaryKey: primaryKey,
1060
1070
  primaryKeyValue: cleanCurrentModel[primaryKey]
1061
1071
  };
1062
- this._SQLEngine.update(sqlUpdate);
1072
+ await this._SQLEngine.update(sqlUpdate);
1063
1073
  } else {
1064
1074
  console.warn('[Context] Entity marked for update but no changes detected');
1065
1075
  }
@@ -1084,12 +1094,12 @@ class context {
1084
1094
 
1085
1095
  if (updateQueries.length > 0) {
1086
1096
  try {
1087
- this._SQLEngine.bulkUpdate(updateQueries);
1097
+ await this._SQLEngine.bulkUpdate(updateQueries);
1088
1098
  } catch (error) {
1089
1099
  console.error('[Context] Bulk update failed, falling back to individual updates:', error.message);
1090
1100
  // Fallback to individual updates
1091
1101
  for (const query of updateQueries) {
1092
- this._SQLEngine.update(query);
1102
+ await this._SQLEngine.update(query);
1093
1103
  }
1094
1104
  }
1095
1105
  }
@@ -1102,11 +1112,11 @@ class context {
1102
1112
  * @private
1103
1113
  * @param {Array<object>} entities - Entities to delete
1104
1114
  */
1105
- _processBatchDeletes(entities) {
1115
+ async _processBatchDeletes(entities) {
1106
1116
  if (entities.length === 1) {
1107
1117
  // Single delete - use existing deleteManager
1108
1118
  const deleteObject = new deleteManager(this._SQLEngine, this.__entities);
1109
- deleteObject.init(entities[0]);
1119
+ await deleteObject.init(entities[0]);
1110
1120
  } else {
1111
1121
  // Batch delete - group by table for efficiency
1112
1122
  const deletesByTable = new Map(); // Use Map instead of object for better performance
@@ -1125,14 +1135,14 @@ class context {
1125
1135
  try {
1126
1136
  // Performance: Use for...of with Map entries
1127
1137
  for (const [tableName, ids] of deletesByTable.entries()) {
1128
- this._SQLEngine.bulkDelete(tableName, ids);
1138
+ await this._SQLEngine.bulkDelete(tableName, ids);
1129
1139
  }
1130
1140
  } catch (error) {
1131
1141
  console.error('[Context] Bulk delete failed, falling back to individual deletes:', error.message);
1132
1142
  // Fallback to individual deletes
1133
1143
  for (const entity of entities) {
1134
1144
  const deleteObject = new deleteManager(this._SQLEngine, this.__entities);
1135
- deleteObject.init(entity);
1145
+ await deleteObject.init(entity);
1136
1146
  }
1137
1147
  }
1138
1148
  }
@@ -1152,7 +1162,7 @@ class context {
1152
1162
  * user.name = 'Alice';
1153
1163
  * db.saveChanges();
1154
1164
  */
1155
- saveChanges() {
1165
+ async saveChanges() {
1156
1166
  try {
1157
1167
  const tracked = this.__trackedEntities;
1158
1168
 
@@ -1171,22 +1181,22 @@ class context {
1171
1181
 
1172
1182
  // Handle transactions based on database type
1173
1183
  if (this.isSQLite) {
1174
- this._SQLEngine.startTransaction();
1184
+ await this._SQLEngine.startTransaction();
1175
1185
  try {
1176
- this._processTrackedEntities(tracked);
1186
+ await this._processTrackedEntities(tracked);
1177
1187
  this.__clearErrorHandler();
1178
- this._SQLEngine.endTransaction();
1188
+ await this._SQLEngine.endTransaction();
1179
1189
  } catch (error) {
1180
- this._SQLEngine.errorTransaction();
1190
+ await this._SQLEngine.errorTransaction();
1181
1191
  throw error;
1182
1192
  }
1183
1193
  } else if (this.isMySQL) {
1184
- // MySQL: Synchronous operations (transaction handling managed elsewhere)
1185
- this._processTrackedEntities(tracked);
1194
+ // MySQL: Async operations
1195
+ await this._processTrackedEntities(tracked);
1186
1196
  this.__clearErrorHandler();
1187
1197
  } else if (this.isPostgres) {
1188
- // PostgreSQL: Async operations (transaction handling managed elsewhere)
1189
- this._processTrackedEntities(tracked);
1198
+ // PostgreSQL: Async operations
1199
+ await this._processTrackedEntities(tracked);
1190
1200
  this.__clearErrorHandler();
1191
1201
  }
1192
1202
 
package/deleteManager.js CHANGED
@@ -26,14 +26,14 @@ class DeleteManager {
26
26
  * @param {Object|Array} currentModel - Entity or entities to delete
27
27
  * @throws {Error} If deletion fails
28
28
  */
29
- init(currentModel) {
29
+ async init(currentModel) {
30
30
  // Input validation
31
31
  if (!currentModel) {
32
32
  throw new Error('DeleteManager.init() requires a valid model');
33
33
  }
34
34
 
35
35
  try {
36
- this.cascadeDelete(currentModel);
36
+ await this.cascadeDelete(currentModel);
37
37
  } catch (error) {
38
38
  // Add context to error
39
39
  const entityName = currentModel.__entity?.__name || 'unknown';
@@ -46,15 +46,15 @@ class DeleteManager {
46
46
  * @param {Object|Array} currentModel - Entity or entities to delete
47
47
  * @throws {Error} If cascade deletion fails
48
48
  */
49
- cascadeDelete(currentModel) {
49
+ async cascadeDelete(currentModel) {
50
50
  if (!currentModel) {
51
51
  return; // Nothing to delete
52
52
  }
53
53
 
54
54
  if (!Array.isArray(currentModel)) {
55
- this._deleteSingleEntity(currentModel);
55
+ await this._deleteSingleEntity(currentModel);
56
56
  } else {
57
- this._deleteMultipleEntities(currentModel);
57
+ await this._deleteMultipleEntities(currentModel);
58
58
  }
59
59
  }
60
60
 
@@ -63,7 +63,7 @@ class DeleteManager {
63
63
  * @private
64
64
  * @param {Object} entity - Entity to delete
65
65
  */
66
- _deleteSingleEntity(entity) {
66
+ async _deleteSingleEntity(entity) {
67
67
  // Validate entity structure
68
68
  if (!entity.__entity) {
69
69
  throw new Error('Entity missing __entity metadata');
@@ -90,13 +90,13 @@ class DeleteManager {
90
90
  }
91
91
  } else {
92
92
  // Recursively delete related entities
93
- this.cascadeDelete(relatedModel);
93
+ await this.cascadeDelete(relatedModel);
94
94
  }
95
95
  }
96
96
  }
97
97
 
98
98
  // Delete the entity itself after cascading
99
- this._SQLEngine.delete(entity);
99
+ await this._SQLEngine.delete(entity);
100
100
  }
101
101
 
102
102
  /**
@@ -104,7 +104,7 @@ class DeleteManager {
104
104
  * @private
105
105
  * @param {Array} entities - Array of entities to delete
106
106
  */
107
- _deleteMultipleEntities(entities) {
107
+ async _deleteMultipleEntities(entities) {
108
108
  if (entities.length === 0) {
109
109
  return; // Nothing to delete
110
110
  }
@@ -132,13 +132,13 @@ class DeleteManager {
132
132
  const relatedModel = entity[property];
133
133
 
134
134
  if (relatedModel !== null && relatedModel !== undefined) {
135
- this.cascadeDelete(relatedModel);
135
+ await this.cascadeDelete(relatedModel);
136
136
  }
137
137
  }
138
138
  }
139
139
 
140
140
  // Delete the entity
141
- this._SQLEngine.delete(entity);
141
+ await this._SQLEngine.delete(entity);
142
142
  }
143
143
  }
144
144