masterrecord 0.3.8 → 0.3.10

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.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Path utilities for MasterRecord migrations
3
+ * Handles path resolution to prevent duplicate db/migrations in paths
4
+ */
5
+
6
+ const path = require('path');
7
+
8
+ /**
9
+ * Resolve migrations directory, avoiding duplicate db/migrations paths
10
+ *
11
+ * If the context file is already inside a db/migrations folder structure,
12
+ * returns that directory. Otherwise, appends db/migrations to the context directory.
13
+ *
14
+ * This prevents the bug where:
15
+ * /components/qa/app/models/db/migrations/qaContext.js
16
+ * becomes:
17
+ * /components/qa/app/models/db/migrations/db/migrations/qacontext_contextSnapShot.json
18
+ *
19
+ * @param {string} contextFilePath - Absolute path to the context file
20
+ * @returns {string} Absolute path to the migrations directory
21
+ *
22
+ * @example
23
+ * // Context already in migrations folder
24
+ * resolveMigrationsDirectory('/app/models/db/migrations/Context.js')
25
+ * // Returns: /app/models/db/migrations
26
+ *
27
+ * @example
28
+ * // Context NOT in migrations folder
29
+ * resolveMigrationsDirectory('/app/models/Context.js')
30
+ * // Returns: /app/models/db/migrations
31
+ */
32
+ function resolveMigrationsDirectory(contextFilePath) {
33
+ const contextDir = path.dirname(contextFilePath);
34
+ const contextDirNormalized = contextDir.split(path.sep).join('/');
35
+
36
+ // Check if context is already inside a db/migrations folder
37
+ const alreadyInMigrations = contextDirNormalized.includes('/db/migrations') ||
38
+ contextDirNormalized.includes('\\db\\migrations');
39
+
40
+ if (alreadyInMigrations) {
41
+ // Context is already in db/migrations - find and use that directory
42
+ let currentDir = contextDir;
43
+ while (currentDir && currentDir !== path.dirname(currentDir)) {
44
+ const dirName = path.basename(currentDir);
45
+ const parentName = path.basename(path.dirname(currentDir));
46
+
47
+ if (dirName === 'migrations' && parentName === 'db') {
48
+ return currentDir;
49
+ }
50
+ currentDir = path.dirname(currentDir);
51
+ }
52
+
53
+ // Fallback if we couldn't find it (shouldn't happen)
54
+ console.warn(`[pathUtils] Warning: Context is in a path containing 'db/migrations' but couldn't locate exact directory. Using context directory: ${contextDir}`);
55
+ return contextDir;
56
+ } else {
57
+ // Context is NOT in db/migrations - return standard path
58
+ return path.join(contextDir, 'db', 'migrations');
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Check if a path is already inside a db/migrations directory structure
64
+ *
65
+ * @param {string} filePath - Path to check
66
+ * @returns {boolean} True if path contains db/migrations
67
+ */
68
+ function isInMigrationsDirectory(filePath) {
69
+ const normalized = filePath.split(path.sep).join('/');
70
+ return normalized.includes('/db/migrations') || normalized.includes('\\db\\migrations');
71
+ }
72
+
73
+ module.exports = {
74
+ resolveMigrationsDirectory,
75
+ isInMigrationsDirectory
76
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Tests for pathUtils.js - Path resolution for migrations
3
+ *
4
+ * Run with: node pathUtils.test.js
5
+ */
6
+
7
+ const path = require('path');
8
+ const { resolveMigrationsDirectory, isInMigrationsDirectory } = require('./pathUtils');
9
+
10
+ function assert(condition, message) {
11
+ if (!condition) {
12
+ throw new Error(`❌ Test failed: ${message}`);
13
+ }
14
+ console.log(`✓ ${message}`);
15
+ }
16
+
17
+ console.log('\n🧪 Running pathUtils tests...\n');
18
+
19
+ // Test 1: Context NOT in migrations folder
20
+ const test1 = resolveMigrationsDirectory('/app/models/Context.js');
21
+ const expected1 = path.join('/app/models', 'db', 'migrations');
22
+ assert(test1 === expected1,
23
+ `Context NOT in migrations: Expected '${expected1}', got '${test1}'`);
24
+
25
+ // Test 2: Context already IN migrations folder
26
+ const test2 = resolveMigrationsDirectory('/app/models/db/migrations/Context.js');
27
+ const expected2 = '/app/models/db/migrations';
28
+ assert(test2 === expected2,
29
+ `Context IN migrations: Expected '${expected2}', got '${test2}'`);
30
+
31
+ // Test 3: Context in nested migrations folder
32
+ const test3 = resolveMigrationsDirectory('/components/qa/app/models/db/migrations/qaContext.js');
33
+ const expected3 = '/components/qa/app/models/db/migrations';
34
+ assert(test3 === expected3,
35
+ `Context in nested migrations: Expected '${expected3}', got '${test3}'`);
36
+
37
+ // Test 4: isInMigrationsDirectory - true case
38
+ const test4 = isInMigrationsDirectory('/app/models/db/migrations/Context.js');
39
+ assert(test4 === true,
40
+ 'isInMigrationsDirectory should return true for path with db/migrations');
41
+
42
+ // Test 5: isInMigrationsDirectory - false case
43
+ const test5 = isInMigrationsDirectory('/app/models/Context.js');
44
+ assert(test5 === false,
45
+ 'isInMigrationsDirectory should return false for path without db/migrations');
46
+
47
+ // Test 6: Windows path with backslashes
48
+ const test6Windows = '/app\\models\\db\\migrations\\Context.js'.replace(/\\/g, path.sep);
49
+ const result6 = isInMigrationsDirectory(test6Windows);
50
+ assert(result6 === true,
51
+ 'isInMigrationsDirectory should handle Windows paths with backslashes');
52
+
53
+ console.log('\n✅ All tests passed!\n');
@@ -465,6 +465,21 @@ class queryMethods{
465
465
  }
466
466
  }
467
467
 
468
+ // Add Active Record-style .save() method
469
+ newEntity.save = async function() {
470
+ if (!this.__context) {
471
+ throw new Error('Cannot save: entity is not attached to a context');
472
+ }
473
+
474
+ // Ensure entity is tracked
475
+ if (!this.__context.__trackedEntitiesMap.has(this.__ID)) {
476
+ this.__context.__track(this);
477
+ }
478
+
479
+ // Save all tracked changes in the context
480
+ return await this.__context.saveChanges();
481
+ };
482
+
468
483
  // Track the entity
469
484
  this.__context.__track(newEntity);
470
485
  return newEntity;