masterrecord 0.3.25 → 0.3.27

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.
@@ -37,7 +37,8 @@
37
37
  "WebFetch(domain:github.com)",
38
38
  "Bash(master=development masterrecord update-database qaContext)",
39
39
  "Bash(chmod:*)",
40
- "Bash(npm install:*)"
40
+ "Bash(npm install:*)",
41
+ "Bash(sqlite3:*)"
41
42
  ],
42
43
  "deny": [],
43
44
  "ask": []
package/SQLLiteEngine.js CHANGED
@@ -1251,11 +1251,10 @@ class SQLLiteEngine {
1251
1251
  * Close database connection
1252
1252
  * Required for proper cleanup of better-sqlite3 native bindings
1253
1253
  */
1254
- close() {
1255
- if (this.db) {
1256
- this.db.close();
1257
- console.log('SQLite database closed');
1258
- }
1254
+ async close() {
1255
+ return Promise.resolve(
1256
+ this.db ? (this.db.close(), console.log('SQLite database closed')) : null
1257
+ );
1259
1258
  }
1260
1259
  }
1261
1260
 
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/realMySQLEngine');
23
+ const MySQLEngine = require('masterrecord/mySQLEngine');
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 MySQLAsyncClient = require('masterrecord/mySQLAsyncConnect');
31
+ const MySQLAsyncClient = require('masterrecord/mySQLConnect');
32
32
  const PostgresClient = require('masterrecord/postgresSyncConnect');
33
33
  const QueryCache = require('./Cache/QueryCache');
34
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "masterrecord",
3
- "version": "0.3.25",
3
+ "version": "0.3.27",
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": {
package/readme.md CHANGED
@@ -416,7 +416,7 @@ const user = db.User.new();
416
416
  user.tags = ['admin', 'moderator']; // Assign array
417
417
  await db.saveChanges(); // Stored as '["admin","moderator"]'
418
418
 
419
- const loaded = db.User.findById(user.id);
419
+ const loaded = await db.User.findById(user.id);
420
420
  console.log(loaded.tags); // ['admin', 'moderator'] - JavaScript array!
421
421
  ```
422
422
 
@@ -425,19 +425,19 @@ console.log(loaded.tags); // ['admin', 'moderator'] - JavaScript array!
425
425
  ### Basic Queries
426
426
 
427
427
  ```javascript
428
- // Find all
429
- const users = db.User.toList();
428
+ // Find all (requires await)
429
+ const users = await db.User.toList();
430
430
 
431
- // Find by primary key
432
- const user = db.User.findById(123);
431
+ // Find by primary key (requires await)
432
+ const user = await db.User.findById(123);
433
433
 
434
- // Find single with where clause
435
- const alice = db.User
434
+ // Find single with where clause (requires await)
435
+ const alice = await db.User
436
436
  .where(u => u.email == $$, 'alice@example.com')
437
437
  .single();
438
438
 
439
- // Find multiple with conditions
440
- const adults = db.User
439
+ // Find multiple with conditions (requires await)
440
+ const adults = await db.User
441
441
  .where(u => u.age >= $$, 18)
442
442
  .toList();
443
443
  ```
@@ -447,16 +447,16 @@ const adults = db.User
447
447
  **Always use `$$` placeholders** for SQL injection protection:
448
448
 
449
449
  ```javascript
450
- // Single parameter
451
- const user = db.User.where(u => u.id == $$, 123).single();
450
+ // Single parameter (requires await)
451
+ const user = await db.User.where(u => u.id == $$, 123).single();
452
452
 
453
- // Multiple parameters
454
- const results = db.User
453
+ // Multiple parameters (requires await)
454
+ const results = await db.User
455
455
  .where(u => u.age > $$ && u.status == $$, 25, 'active')
456
456
  .toList();
457
457
 
458
- // Single $ for OR conditions
459
- const results = db.User
458
+ // Single $ for OR conditions (requires await)
459
+ const results = await db.User
460
460
  .where(u => u.status == $ || u.status == null, 'active')
461
461
  .toList();
462
462
  ```
@@ -464,22 +464,22 @@ const results = db.User
464
464
  ### IN Clauses
465
465
 
466
466
  ```javascript
467
- // Array parameter with .includes()
467
+ // Array parameter with .includes() (requires await)
468
468
  const ids = [1, 2, 3, 4, 5];
469
- const users = db.User
469
+ const users = await db.User
470
470
  .where(u => $$.includes(u.id), ids)
471
471
  .toList();
472
472
 
473
473
  // Generated SQL: WHERE id IN ($1, $2, $3, $4, $5)
474
474
  // PostgreSQL parameters: [1, 2, 3, 4, 5]
475
475
 
476
- // Alternative .any() syntax
477
- const users = db.User
476
+ // Alternative .any() syntax (requires await)
477
+ const users = await db.User
478
478
  .where(u => u.id.any($$), [1, 2, 3])
479
479
  .toList();
480
480
 
481
- // Comma-separated strings (auto-splits)
482
- const users = db.User
481
+ // Comma-separated strings (auto-splits) (requires await)
482
+ const users = await db.User
483
483
  .where(u => u.id.any($$), "1,2,3,4,5")
484
484
  .toList();
485
485
  ```
@@ -498,8 +498,8 @@ if (minAge) {
498
498
  query = query.where(u => u.age >= $$, minAge);
499
499
  }
500
500
 
501
- // Add sorting and pagination
502
- const users = query
501
+ // Add sorting and pagination (requires await)
502
+ const users = await query
503
503
  .orderBy(u => u.created_at)
504
504
  .skip(offset)
505
505
  .take(limit)
@@ -509,13 +509,13 @@ const users = query
509
509
  ### Ordering
510
510
 
511
511
  ```javascript
512
- // Ascending
513
- const users = db.User
512
+ // Ascending (requires await)
513
+ const users = await db.User
514
514
  .orderBy(u => u.name)
515
515
  .toList();
516
516
 
517
- // Descending
518
- const users = db.User
517
+ // Descending (requires await)
518
+ const users = await db.User
519
519
  .orderByDescending(u => u.created_at)
520
520
  .toList();
521
521
  ```
@@ -523,17 +523,17 @@ const users = db.User
523
523
  ### Pagination
524
524
 
525
525
  ```javascript
526
- // Skip 20, take 10
527
- const users = db.User
526
+ // Skip 20, take 10 (requires await)
527
+ const users = await db.User
528
528
  .orderBy(u => u.id)
529
529
  .skip(20)
530
530
  .take(10)
531
531
  .toList();
532
532
 
533
- // Page-based pagination
533
+ // Page-based pagination (requires await)
534
534
  const page = 2;
535
535
  const pageSize = 10;
536
- const users = db.User
536
+ const users = await db.User
537
537
  .skip(page * pageSize)
538
538
  .take(pageSize)
539
539
  .toList();
@@ -542,11 +542,11 @@ const users = db.User
542
542
  ### Counting
543
543
 
544
544
  ```javascript
545
- // Count all
546
- const total = db.User.count();
545
+ // Count all (requires await)
546
+ const total = await db.User.count();
547
547
 
548
- // Count with conditions
549
- const activeCount = db.User
548
+ // Count with conditions (requires await)
549
+ const activeCount = await db.User
550
550
  .where(u => u.status == $$, 'active')
551
551
  .count();
552
552
  ```
@@ -554,19 +554,19 @@ const activeCount = db.User
554
554
  ### Complex Queries
555
555
 
556
556
  ```javascript
557
- // Multiple conditions with OR
558
- const results = db.User
557
+ // Multiple conditions with OR (requires await)
558
+ const results = await db.User
559
559
  .where(u => (u.status == 'active' || u.status == 'pending') && u.age >= $$, 18)
560
560
  .orderBy(u => u.name)
561
561
  .toList();
562
562
 
563
- // Nullable checks
564
- const usersWithoutEmail = db.User
563
+ // Nullable checks (requires await)
564
+ const usersWithoutEmail = await db.User
565
565
  .where(u => u.email == null)
566
566
  .toList();
567
567
 
568
- // LIKE queries
569
- const matching = db.User
568
+ // LIKE queries (requires await)
569
+ const matching = await db.User
570
570
  .where(u => u.name.like($$), '%john%')
571
571
  .toList();
572
572
  ```
@@ -838,13 +838,13 @@ const user = db.User.findById(1); // DB query
838
838
  const user2 = db.User.findById(1); // DB query again (no cache)
839
839
 
840
840
  // OPT-IN: Enable caching with .cache()
841
- const categories = db.Categories.cache().toList(); // DB query, cached
842
- const categories2 = db.Categories.cache().toList(); // Cache hit! (instant)
841
+ const categories = await db.Categories.cache().toList(); // DB query, cached
842
+ const categories2 = await db.Categories.cache().toList(); // Cache hit! (instant)
843
843
 
844
844
  // Update invalidates cache automatically
845
- const cat = db.Categories.findById(1);
845
+ const cat = await db.Categories.findById(1);
846
846
  cat.name = "Updated";
847
- db.saveChanges(); // Cache for Categories table cleared
847
+ await db.saveChanges(); // Cache for Categories table cleared
848
848
 
849
849
  // End request (clears cache - like Active Record)
850
850
  db.endRequest(); // Cache cleared for next request
@@ -865,9 +865,9 @@ app.use((req, res, next) => {
865
865
  });
866
866
 
867
867
  // In your routes
868
- app.get('/categories', (req, res) => {
868
+ app.get('/categories', async (req, res) => {
869
869
  // Cache is fresh for this request
870
- const categories = req.db.Categories.cache().toList();
870
+ const categories = await req.db.Categories.cache().toList();
871
871
  res.json(categories);
872
872
  // Cache auto-cleared after response
873
873
  });
@@ -900,14 +900,14 @@ Use `.cache()` for frequently accessed, rarely changed data:
900
900
 
901
901
  ```javascript
902
902
  // DEFAULT: Always hits database (safe)
903
- const liveData = db.Analytics
903
+ const liveData = await db.Analytics
904
904
  .where(a => a.date == $$, today)
905
905
  .toList(); // No caching (default)
906
906
 
907
907
  // OPT-IN: Cache reference data
908
- const categories = db.Categories.cache().toList(); // Cached for 5 seconds (default TTL)
909
- const settings = db.Settings.cache().toList(); // Cached
910
- const countries = db.Countries.cache().toList(); // Cached
908
+ const categories = await db.Categories.cache().toList(); // Cached for 5 seconds (default TTL)
909
+ const settings = await db.Settings.cache().toList(); // Cached
910
+ const countries = await db.Countries.cache().toList(); // Cached
911
911
 
912
912
  // When to use .cache():
913
913
  // ✅ Reference data (categories, settings, countries)
@@ -940,7 +940,7 @@ db.clearQueryCache();
940
940
 
941
941
  // Disable caching temporarily
942
942
  db.setQueryCacheEnabled(false);
943
- const freshData = db.User.toList();
943
+ const freshData = await db.User.toList();
944
944
  db.setQueryCacheEnabled(true);
945
945
  ```
946
946
 
@@ -983,21 +983,21 @@ MasterRecord automatically invalidates cache entries when data changes:
983
983
 
984
984
  ```javascript
985
985
  // Query with caching enabled
986
- const categories = db.Categories.cache().toList(); // DB query, cached
986
+ const categories = await db.Categories.cache().toList(); // DB query, cached
987
987
 
988
988
  // Any modification to Categories table invalidates ALL cached Category queries
989
- const cat = db.Categories.findById(1);
989
+ const cat = await db.Categories.findById(1);
990
990
  cat.name = "Updated";
991
- db.saveChanges(); // Invalidates all cached Categories queries
991
+ await db.saveChanges(); // Invalidates all cached Categories queries
992
992
 
993
993
  // Next cached query hits database (fresh data)
994
- const categoriesAgain = db.Categories.cache().toList(); // DB query (cache cleared)
994
+ const categoriesAgain = await db.Categories.cache().toList(); // DB query (cache cleared)
995
995
 
996
996
  // Non-cached queries are unaffected (always fresh)
997
- const users = db.User.toList(); // No .cache() = always DB query
997
+ const users = await db.User.toList(); // No .cache() = always DB query
998
998
 
999
999
  // Queries for OTHER tables' caches are unaffected
1000
- const settings = db.Settings.cache().toList(); // Still cached (different table)
1000
+ const settings = await db.Settings.cache().toList(); // Still cached (different table)
1001
1001
  ```
1002
1002
 
1003
1003
  **Invalidation rules:**
@@ -1024,12 +1024,12 @@ Expected performance improvements:
1024
1024
  **DO use .cache():**
1025
1025
  ```javascript
1026
1026
  // Reference data (rarely changes)
1027
- const categories = db.Categories.cache().toList();
1028
- const settings = db.Settings.cache().toList();
1029
- const countries = db.Countries.cache().toList();
1027
+ const categories = await db.Categories.cache().toList();
1028
+ const settings = await db.Settings.cache().toList();
1029
+ const countries = await db.Countries.cache().toList();
1030
1030
 
1031
1031
  // Expensive aggregations (stable results)
1032
- const totalRevenue = db.Orders
1032
+ const totalRevenue = await db.Orders
1033
1033
  .where(o => o.year == $$, 2024)
1034
1034
  .cache()
1035
1035
  .count();
@@ -1038,20 +1038,20 @@ const totalRevenue = db.Orders
1038
1038
  **DON'T use .cache():**
1039
1039
  ```javascript
1040
1040
  // User-specific data (default is safe - no caching)
1041
- const user = db.User.findById(userId); // Always fresh
1041
+ const user = await db.User.findById(userId); // Always fresh
1042
1042
 
1043
1043
  // Real-time data (default is safe)
1044
- const liveOrders = db.Orders
1044
+ const liveOrders = await db.Orders
1045
1045
  .where(o => o.status == $$, 'pending')
1046
1046
  .toList(); // Always fresh
1047
1047
 
1048
1048
  // Financial transactions (default is safe)
1049
- const balance = db.Transactions
1049
+ const balance = await db.Transactions
1050
1050
  .where(t => t.user_id == $$, userId)
1051
1051
  .toList(); // Always fresh
1052
1052
 
1053
1053
  // User-specific sensitive data (default is safe)
1054
- const permissions = db.UserPermissions
1054
+ const permissions = await db.UserPermissions
1055
1055
  .where(p => p.user_id == $$, userId)
1056
1056
  .toList(); // Always fresh
1057
1057
  ```
@@ -1089,12 +1089,12 @@ app.use((req, res, next) => {
1089
1089
  });
1090
1090
 
1091
1091
  // In routes - cache is fresh per request
1092
- app.get('/api/categories', (req, res) => {
1092
+ app.get('/api/categories', async (req, res) => {
1093
1093
  // First call in this request - DB query
1094
- const categories = req.db.Categories.cache().toList();
1094
+ const categories = await req.db.Categories.cache().toList();
1095
1095
 
1096
1096
  // Second call in same request - cache hit
1097
- const categoriesAgain = req.db.Categories.cache().toList();
1097
+ const categoriesAgain = await req.db.Categories.cache().toList();
1098
1098
 
1099
1099
  res.json(categories);
1100
1100
  // After response, cache is automatically cleared
@@ -1118,18 +1118,18 @@ const db1 = new AppContext();
1118
1118
  const db2 = new AppContext();
1119
1119
 
1120
1120
  // Context 1: Cache data with .cache()
1121
- const categories1 = db1.Categories.cache().toList(); // DB query, cached
1121
+ const categories1 = await db1.Categories.cache().toList(); // DB query, cached
1122
1122
 
1123
1123
  // Context 2: Sees cached data
1124
- const categories2 = db2.Categories.cache().toList(); // Cache hit!
1124
+ const categories2 = await db2.Categories.cache().toList(); // Cache hit!
1125
1125
 
1126
1126
  // Context 2: Updates invalidate cache for BOTH contexts
1127
- const cat = db2.Categories.findById(1);
1127
+ const cat = await db2.Categories.findById(1);
1128
1128
  cat.name = "Updated";
1129
- db2.saveChanges(); // Invalidates shared cache
1129
+ await db2.saveChanges(); // Invalidates shared cache
1130
1130
 
1131
1131
  // Context 1: Sees fresh data
1132
- const categories3 = db1.Categories.cache().toList(); // Cache miss, fresh data
1132
+ const categories3 = await db1.Categories.cache().toList(); // Cache miss, fresh data
1133
1133
  console.log(categories3[0].name); // "Updated"
1134
1134
  ```
1135
1135
 
@@ -1168,8 +1168,9 @@ class AnalyticsContext extends context {
1168
1168
  const userDb = new UserContext();
1169
1169
  const analyticsDb = new AnalyticsContext();
1170
1170
 
1171
- const user = userDb.User.findById(123);
1172
- analyticsDb.Event.new().log('user_login', user.id);
1171
+ const user = await userDb.User.findById(123);
1172
+ const event = analyticsDb.Event.new();
1173
+ event.log('user_login', user.id);
1173
1174
  await analyticsDb.saveChanges();
1174
1175
  ```
1175
1176
 
@@ -1232,7 +1233,7 @@ context.setQueryCacheEnabled(bool) // Enable/disable caching
1232
1233
  ### Query Methods
1233
1234
 
1234
1235
  ```javascript
1235
- // Chainable query builders
1236
+ // Chainable query builders (do not execute query)
1236
1237
  .where(query, ...params) // Add WHERE condition
1237
1238
  .and(query, ...params) // Add AND condition
1238
1239
  .orderBy(field) // Sort ascending
@@ -1242,18 +1243,18 @@ context.setQueryCacheEnabled(bool) // Enable/disable caching
1242
1243
  .include(relationship) // Eager load
1243
1244
  .cache() // Enable caching for this query (opt-in)
1244
1245
 
1245
- // Terminal methods (execute query)
1246
- .toList() // Return array of all records
1247
- .single() // Return one or null
1248
- .first() // Return first or null
1249
- .count() // Return count
1250
- .any() // Return boolean
1246
+ // Terminal methods (execute query - ALL REQUIRE AWAIT)
1247
+ await .toList() // Return array of all records
1248
+ await .single() // Return one or null
1249
+ await .first() // Return first or null
1250
+ await .count() // Return count
1251
+ await .any() // Return boolean
1251
1252
 
1252
- // Convenience methods
1253
- .findById(id) // Find by primary key
1254
- .new() // Create new entity instance
1253
+ // Convenience methods (REQUIRE AWAIT)
1254
+ await .findById(id) // Find by primary key
1255
+ .new() // Create new entity instance (synchronous)
1255
1256
 
1256
- // Entity methods (Active Record style)
1257
+ // Entity methods (Active Record style - REQUIRE AWAIT)
1257
1258
  await entity.save() // Save this entity (and all tracked changes)
1258
1259
  ```
1259
1260
 
@@ -1315,14 +1316,14 @@ demo();
1315
1316
  async function getUsers(page = 0, pageSize = 10) {
1316
1317
  const db = new AppContext();
1317
1318
 
1318
- const users = db.User
1319
+ const users = await db.User
1319
1320
  .where(u => u.status == $$, 'active')
1320
1321
  .orderBy(u => u.created_at)
1321
1322
  .skip(page * pageSize)
1322
1323
  .take(pageSize)
1323
1324
  .toList();
1324
1325
 
1325
- const total = db.User
1326
+ const total = await db.User
1326
1327
  .where(u => u.status == $$, 'active')
1327
1328
  .count();
1328
1329
 
@@ -1373,7 +1374,7 @@ async function searchUsers(filters) {
1373
1374
  .take(filters.pageSize);
1374
1375
  }
1375
1376
 
1376
- return query.toList();
1377
+ return await query.toList();
1377
1378
  }
1378
1379
  ```
1379
1380
 
@@ -1403,7 +1404,7 @@ post.author_id = author.id;
1403
1404
  await db.saveChanges();
1404
1405
 
1405
1406
  // Query with relationships
1406
- const posts = db.Post
1407
+ const posts = await db.Post
1407
1408
  .where(p => p.author_id == $$, author.id)
1408
1409
  .toList();
1409
1410
 
@@ -1416,15 +1417,15 @@ console.log(`${author.name} has ${posts.length} posts`);
1416
1417
 
1417
1418
  ```javascript
1418
1419
  // ✅ GOOD: Cache reference data that rarely changes
1419
- const categories = db.Categories.cache().toList(); // Opt-in caching
1420
- const settings = db.Settings.cache().toList();
1420
+ const categories = await db.Categories.cache().toList(); // Opt-in caching
1421
+ const settings = await db.Settings.cache().toList();
1421
1422
 
1422
1423
  // ✅ GOOD: Queries without .cache() are always fresh (safe default)
1423
- const user1 = db.User.findById(123); // Always DB query (no cache)
1424
- const user2 = db.User.findById(123); // Always DB query (no cache)
1424
+ const user1 = await db.User.findById(123); // Always DB query (no cache)
1425
+ const user2 = await db.User.findById(123); // Always DB query (no cache)
1425
1426
 
1426
1427
  // ✅ GOOD: Cache expensive queries with stable results
1427
- const revenue2024 = db.Orders
1428
+ const revenue2024 = await db.Orders
1428
1429
  .where(o => o.year == $$, 2024)
1429
1430
  .cache() // Historical data doesn't change
1430
1431
  .count();
@@ -1472,13 +1473,13 @@ class User {
1472
1473
 
1473
1474
  ```javascript
1474
1475
  // ✅ GOOD: Limit results
1475
- const recentUsers = db.User
1476
+ const recentUsers = await db.User
1476
1477
  .orderByDescending(u => u.created_at)
1477
1478
  .take(100)
1478
1479
  .toList();
1479
1480
 
1480
1481
  // ❌ BAD: Load everything
1481
- const allUsers = db.User.toList();
1482
+ const allUsers = await db.User.toList();
1482
1483
  ```
1483
1484
 
1484
1485
  ### 5. Use Connection Pooling (PostgreSQL)
@@ -1500,7 +1501,7 @@ MasterRecord uses **parameterized queries throughout** to prevent SQL injection:
1500
1501
 
1501
1502
  ```javascript
1502
1503
  // ✅ SAFE: Parameterized
1503
- const user = db.User.where(u => u.name == $$, userInput).single();
1504
+ const user = await db.User.where(u => u.name == $$, userInput).single();
1504
1505
 
1505
1506
  // ❌ UNSAFE: Never do this
1506
1507
  // const query = `SELECT * FROM User WHERE name = '${userInput}'`;
@@ -1520,12 +1521,12 @@ While SQL injection is prevented, always validate business logic:
1520
1521
 
1521
1522
  ```javascript
1522
1523
  // Validate input before querying
1523
- function getUser(userId) {
1524
+ async function getUser(userId) {
1524
1525
  if (!Number.isInteger(userId) || userId <= 0) {
1525
1526
  throw new Error('Invalid user ID');
1526
1527
  }
1527
1528
 
1528
- return db.User.findById(userId);
1529
+ return await db.User.findById(userId);
1529
1530
  }
1530
1531
  ```
1531
1532
 
File without changes
File without changes