masterrecord 0.3.17 → 0.3.19

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/Migrations/cli.js CHANGED
@@ -153,7 +153,7 @@ program.option('-V', 'output the version');
153
153
 
154
154
  if(typeof mig.createdatabase === 'function'){
155
155
  try{
156
- mig.createdatabase();
156
+ await mig.createdatabase();
157
157
  console.log('✓ Database ensured');
158
158
  await __cleanupAndExit(contextInstance, 0);
159
159
  }catch(err){
@@ -163,7 +163,7 @@ program.option('-V', 'output the version');
163
163
  }
164
164
  } else if(typeof mig.createDatabase === 'function'){
165
165
  try{
166
- mig.createDatabase();
166
+ await mig.createDatabase();
167
167
  console.log('✓ Database ensured');
168
168
  await __cleanupAndExit(contextInstance, 0);
169
169
  }catch(err){
@@ -447,7 +447,7 @@ program.option('-V', 'output the version');
447
447
  try{
448
448
  var newMigrationProjectInstance = new migrationProjectFile(ContextCtor);
449
449
  var tableObj = migration.buildUpObject(contextSnapshot.schema, cleanEntities);
450
- newMigrationProjectInstance.up(tableObj);
450
+ await newMigrationProjectInstance.up(tableObj);
451
451
  }catch(err){
452
452
  console.error(`\n❌ Error - Migration failed during execution`);
453
453
  console.error(`\nMigration file: ${mFile}`);
@@ -571,7 +571,7 @@ program.option('-V', 'output the version');
571
571
  var MigCtor = require(latestFile);
572
572
  var migInstance = new MigCtor(ContextCtor);
573
573
  if(typeof migInstance.down === 'function'){
574
- migInstance.down(tableObj);
574
+ await migInstance.down(tableObj);
575
575
  }else{
576
576
  console.log(`Warning - Migration '${path.basename(latestFile)}' has no down method; skipping.`);
577
577
  }
@@ -668,7 +668,7 @@ program.option('-V', 'output the version');
668
668
  var migrationProjectFile = require(migFile);
669
669
  var newMigrationProjectInstance = new migrationProjectFile(ContextCtor);
670
670
  var tableObj = migration.buildUpObject(contextSnapshot.schema, cleanEntities);
671
- newMigrationProjectInstance.up(tableObj);
671
+ await newMigrationProjectInstance.up(tableObj);
672
672
  }
673
673
  const snap = {
674
674
  file : contextAbs,
@@ -820,7 +820,7 @@ program.option('-V', 'output the version');
820
820
  var MigCtor = require(migFile);
821
821
  var migInstance = new MigCtor(ContextCtor);
822
822
  if(typeof migInstance.down === 'function'){
823
- migInstance.down(tableObj);
823
+ await migInstance.down(tableObj);
824
824
  } else {
825
825
  console.log(`Warning - Migration '${path.basename(migFile)}' has no down method; skipping.`);
826
826
  }
@@ -1004,7 +1004,7 @@ program.option('-V', 'output the version');
1004
1004
  var newMigrationProjectInstance = new migrationProjectFile(ContextCtor);
1005
1005
  var cleanEntities = migration.cleanEntities(contextInstance.__entities);
1006
1006
  var tableObj = migration.buildUpObject(entry.cs.schema, cleanEntities);
1007
- newMigrationProjectInstance.up(tableObj);
1007
+ await newMigrationProjectInstance.up(tableObj);
1008
1008
  var snap = {
1009
1009
  file : entry.contextAbs,
1010
1010
  executedLocation : executedLocation,
@@ -82,13 +82,13 @@ class schema{
82
82
  }
83
83
  }
84
84
 
85
- createTable(table){
85
+ async createTable(table){
86
86
 
87
87
  if(table){
88
88
  // If table exists, run sync instead of blind create
89
89
  const tableName = table.__name;
90
- if(this.context._SQLEngine.tableExists && this.context._SQLEngine.tableExists(tableName)){
91
- this.syncTable(table);
90
+ if(this.context._SQLEngine.tableExists && await this.context._SQLEngine.tableExists(tableName)){
91
+ await this.syncTable(table);
92
92
  } else {
93
93
  if(this.context.isSQLite){
94
94
  var sqliteQuery = require("./migrationSQLiteQuery");
@@ -117,10 +117,10 @@ class schema{
117
117
  }
118
118
 
119
119
  // Compute diffs and apply minimal changes
120
- syncTable(table){
120
+ async syncTable(table){
121
121
  const engine = this.context._SQLEngine;
122
122
  const tableName = table.__name;
123
- const existing = engine.getTableInfo ? engine.getTableInfo(tableName) : [];
123
+ const existing = engine.getTableInfo ? await engine.getTableInfo(tableName) : [];
124
124
  // Build a set of existing columns (sqlite: name, mysql: name)
125
125
  const existingNames = new Set((existing || []).map(c => (c.name || c.COLUMN_NAME))); // both engines map to name
126
126
  // Add missing columns only (safe path)
@@ -254,7 +254,7 @@ class schema{
254
254
  const create = queryBuilder.createTable(table);
255
255
  this.context._execute(create);
256
256
  // compute common columns
257
- const oldInfo = engine.getTableInfo(tableName.replace(/.*/, '_temp_alter_column_update')) || engine.getTableInfo("_temp_alter_column_update");
257
+ const oldInfo = await engine.getTableInfo(tableName.replace(/.*/, '_temp_alter_column_update')) || await engine.getTableInfo("_temp_alter_column_update");
258
258
  const oldNames = new Set((oldInfo || existing).map(r => r.name));
259
259
  const newNames = desiredCols.map(d => d.name);
260
260
  const common = newNames.filter(n => oldNames.has(n));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "masterrecord",
3
- "version": "0.3.17",
3
+ "version": "0.3.19",
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
@@ -23,8 +23,8 @@
23
23
  | Database | Version | Features |
24
24
  |------------|--------------|---------------------------------------------------|
25
25
  | PostgreSQL | 9.6+ (12+) | JSONB, UUID, async/await, connection pooling |
26
- | MySQL | 5.7+ (8.0+) | JSON, transactions, AUTO_INCREMENT |
27
- | SQLite | 3.x | Embedded, zero-config, file-based |
26
+ | MySQL | 5.7+ (8.0+) | JSON, async/await, connection pooling, AUTO_INCREMENT |
27
+ | SQLite | 3.x | Embedded, zero-config, file-based, async API wrapper |
28
28
 
29
29
  ## Table of Contents
30
30
 
@@ -64,9 +64,9 @@ npm install masterrecord better-sqlite3 # SQLite
64
64
  ### Dependencies
65
65
 
66
66
  MasterRecord includes the following database drivers by default:
67
- - `pg@^8.17.2` - PostgreSQL
68
- - `sync-mysql2@^1.0.8` - MySQL
69
- - `better-sqlite3@^12.6.2` - SQLite
67
+ - `pg@^8.17.2` - PostgreSQL (async)
68
+ - `mysql2@^3.11.5` - MySQL (async with connection pooling)
69
+ - `better-sqlite3@^12.6.2` - SQLite (async API wrapper for consistency)
70
70
 
71
71
  ## Two Patterns: Entity Framework & Active Record
72
72
 
@@ -212,7 +212,7 @@ const db = new AppContext();
212
212
  await db.saveChanges(); // PostgreSQL is async
213
213
  ```
214
214
 
215
- ### MySQL (Synchronous)
215
+ ### MySQL (Async with Connection Pooling)
216
216
 
217
217
  ```javascript
218
218
  class AppContext extends context {
@@ -225,19 +225,20 @@ class AppContext extends context {
225
225
  port: 3306,
226
226
  database: 'myapp',
227
227
  user: 'root',
228
- password: 'password'
228
+ password: 'password',
229
+ connectionLimit: 10 // Connection pool size (optional)
229
230
  });
230
231
 
231
232
  this.dbset(User);
232
233
  }
233
234
  }
234
235
 
235
- // Usage is synchronous
236
+ // Usage requires await (async like PostgreSQL)
236
237
  const db = new AppContext();
237
- db.saveChanges(); // No await needed
238
+ await db.saveChanges(); // MySQL now uses async/await
238
239
  ```
239
240
 
240
- ### SQLite (Synchronous)
241
+ ### SQLite (Async API)
241
242
 
242
243
  ```javascript
243
244
  class AppContext extends context {
@@ -252,6 +253,10 @@ class AppContext extends context {
252
253
  this.dbset(User);
253
254
  }
254
255
  }
256
+
257
+ // Usage requires await for consistency across databases
258
+ const db = new AppContext();
259
+ await db.saveChanges(); // SQLite now has async API wrapper
255
260
  ```
256
261
 
257
262
  ### Environment Files
@@ -593,33 +598,48 @@ masterrecord update-database-all # Apply all pending migrations
593
598
 
594
599
  ```javascript
595
600
  // db/migrations/20250111_143052_CreateUser.js
596
- module.exports = {
597
- up: function(table, schema) {
598
- // Create table
599
- schema.createTable(table.User);
601
+ const masterrecord = require('masterrecord');
602
+
603
+ class CreateUser extends masterrecord.schema {
604
+ constructor(context) {
605
+ super(context);
606
+ }
607
+
608
+ // IMPORTANT: Migrations must be async
609
+ async up(table) {
610
+ this.init(table);
611
+
612
+ // Create table (requires await)
613
+ await this.createTable(table.User);
600
614
 
601
615
  // Seed initial data
602
- schema.seed('User', {
616
+ this.seed('User', {
603
617
  name: 'Admin',
604
618
  email: 'admin@example.com',
605
619
  role: 'admin'
606
620
  });
607
- },
621
+ }
622
+
623
+ async down(table) {
624
+ this.init(table);
608
625
 
609
- down: function(table, schema) {
610
626
  // Rollback
611
- schema.dropTable(table.User);
627
+ this.dropTable(table.User);
612
628
  }
613
- };
629
+ }
630
+
631
+ module.exports = CreateUser;
614
632
  ```
615
633
 
616
634
  ### Migration Operations
617
635
 
618
636
  ```javascript
619
- module.exports = {
620
- up: function(table, schema) {
621
- // Create table
622
- schema.createTable(table.User);
637
+ class MyMigration extends masterrecord.schema {
638
+ async up(table) {
639
+ this.init(table);
640
+
641
+ // Create table (requires await)
642
+ await this.createTable(table.User);
623
643
 
624
644
  // Add column
625
645
  schema.addColumn({
@@ -1189,9 +1209,8 @@ const users = db._SQLEngine.exec(
1189
1209
  context.dbset(EntityClass)
1190
1210
  context.dbset(EntityClass, 'custom_table_name')
1191
1211
 
1192
- // Save changes
1193
- await context.saveChanges() // PostgreSQL (async)
1194
- context.saveChanges() // MySQL/SQLite (sync)
1212
+ // Save changes (all databases now async)
1213
+ await context.saveChanges() // PostgreSQL, MySQL, SQLite (all async)
1195
1214
 
1196
1215
  // Add/Remove entities
1197
1216
  context.EntityName.add(entity)
@@ -1585,9 +1604,9 @@ user.name = null; // Error if name is { nullable: false }
1585
1604
  | PostgreSQL | 9.6+ (12+) | Tested with 12, 13, 14, 15, 16 |
1586
1605
  | MySQL | 5.7+ (8.0+) | Tested with 8.0+ |
1587
1606
  | SQLite | 3.x | Any recent version |
1588
- | pg | 8.17.2+ | PostgreSQL driver |
1589
- | sync-mysql2 | 1.0.8+ | MySQL driver |
1590
- | better-sqlite3| 12.6.2+ | SQLite driver |
1607
+ | pg | 8.17.2+ | PostgreSQL driver (async) |
1608
+ | mysql2 | 3.11.5+ | MySQL driver (async with connection pooling) |
1609
+ | better-sqlite3| 12.6.2+ | SQLite driver (wrapped with async API) |
1591
1610
 
1592
1611
  ## Documentation
1593
1612
 
@@ -1814,19 +1833,75 @@ $ grep -A1 "catch.*{$" insertManager.js | grep "^\s*}$"
1814
1833
  # ✅ No empty catch blocks - all log errors appropriately
1815
1834
  ```
1816
1835
 
1817
- ### Breaking Changes
1836
+ ### Breaking Changes (v0.3.17+)
1837
+
1838
+ **🔴 CRITICAL: All databases now require async/await for consistency**
1839
+
1840
+ MasterRecord now provides a **unified async API** across all database engines (SQLite, MySQL, PostgreSQL). This follows industry best practices from Sequelize, TypeORM, and Prisma.
1818
1841
 
1819
- **PostgreSQL users must now await `env()`:**
1842
+ **1. Database Operations (All Engines)**
1820
1843
  ```javascript
1821
- // PostgreSQL (async/await REQUIRED):
1844
+ // NEW (v0.3.17+): All databases use async/await
1822
1845
  const db = new AppContext();
1823
- await db.env('./config/environments'); // Must await for PostgreSQL
1846
+ await db.saveChanges(); // Required for SQLite, MySQL, PostgreSQL
1824
1847
 
1825
- // MySQL/SQLite (synchronous - no await):
1826
- const db = new AppContext();
1827
- db.env('./config/environments'); // No await needed for MySQL/SQLite
1848
+ // OLD (v0.3.16 and earlier): Mixed sync/async
1849
+ db.saveChanges(); // SQLite/MySQL were sync (no longer works)
1850
+ await db.saveChanges(); // Only PostgreSQL was async
1828
1851
  ```
1829
1852
 
1853
+ **2. Migration Files (Critical)**
1854
+ ```javascript
1855
+ // ✅ NEW (v0.3.17+): Migrations must be async
1856
+ class CreateUser extends masterrecord.schema {
1857
+ async up(table) { // Must be async
1858
+ this.init(table);
1859
+ await this.createTable(table.User); // Must await
1860
+ }
1861
+
1862
+ async down(table) { // Must be async
1863
+ this.init(table);
1864
+ this.dropTable(table.User);
1865
+ }
1866
+ }
1867
+
1868
+ // ❌ OLD (v0.3.16 and earlier): Migrations were sync
1869
+ up(table) {
1870
+ this.createTable(table.User); // No await (no longer works)
1871
+ }
1872
+ ```
1873
+
1874
+ **3. MySQL Connection**
1875
+ ```javascript
1876
+ // ✅ NEW (v0.3.17+): MySQL uses mysql2/promise with async connection pooling
1877
+ this.env({
1878
+ type: 'mysql',
1879
+ host: 'localhost',
1880
+ port: 3306,
1881
+ database: 'myapp',
1882
+ user: 'root',
1883
+ password: 'password',
1884
+ connectionLimit: 10 // Connection pool size
1885
+ });
1886
+
1887
+ // ❌ OLD (v0.3.16 and earlier): MySQL used sync-mysql2 (synchronous driver)
1888
+ ```
1889
+
1890
+ **Why This Change?**
1891
+ - ✅ **Consistent API**: Same code works for SQLite, MySQL, and PostgreSQL
1892
+ - ✅ **Industry Standard**: Matches Sequelize, TypeORM, Prisma patterns
1893
+ - ✅ **Better Performance**: MySQL now uses connection pooling
1894
+ - ✅ **Real MySQL**: No longer using SQLite disguised as MySQL
1895
+ - ✅ **Portable Code**: Switch databases without code changes
1896
+
1897
+ **Migration Path:**
1898
+ 1. Update all `db.saveChanges()` calls to use `await`
1899
+ 2. Make all migration `up()` and `down()` methods async
1900
+ 3. Add `await` before `createTable()` calls in migrations
1901
+ 4. Update `package.json`: Remove `sync-mysql2`, ensure `mysql2@^3.11.5`
1902
+
1903
+ **For more details, see:** `CHANGES.md`
1904
+
1830
1905
  **For more details, see:** `CHANGES.md`
1831
1906
 
1832
1907
  ---