masterrecord 0.2.34 → 0.3.0

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.
Files changed (40) hide show
  1. package/.claude/settings.local.json +25 -1
  2. package/Entity/entityModel.js +6 -0
  3. package/Entity/entityTrackerModel.js +20 -3
  4. package/Entity/fieldTransformer.js +266 -0
  5. package/Migrations/migrationMySQLQuery.js +145 -1
  6. package/Migrations/migrationPostgresQuery.js +402 -0
  7. package/Migrations/migrationSQLiteQuery.js +145 -1
  8. package/Migrations/schema.js +131 -28
  9. package/QueryLanguage/queryMethods.js +193 -15
  10. package/QueryLanguage/queryParameters.js +136 -0
  11. package/QueryLanguage/queryScript.js +14 -5
  12. package/SQLLiteEngine.js +309 -19
  13. package/context.js +57 -12
  14. package/docs/INCLUDES_CLARIFICATION.md +202 -0
  15. package/docs/METHODS_REFERENCE.md +184 -0
  16. package/docs/MIGRATIONS_GUIDE.md +699 -0
  17. package/docs/POSTGRESQL_SETUP.md +415 -0
  18. package/examples/jsonArrayTransformer.js +215 -0
  19. package/mySQLEngine.js +249 -17
  20. package/package.json +6 -6
  21. package/postgresEngine.js +434 -491
  22. package/postgresSyncConnect.js +209 -0
  23. package/readme.md +1121 -265
  24. package/test/anyCommaStringTest.js +237 -0
  25. package/test/anyMethodTest.js +176 -0
  26. package/test/findByIdTest.js +227 -0
  27. package/test/includesFeatureTest.js +183 -0
  28. package/test/includesTransformTest.js +110 -0
  29. package/test/newMethodTest.js +330 -0
  30. package/test/newMethodUnitTest.js +320 -0
  31. package/test/parameterizedPlaceholderTest.js +159 -0
  32. package/test/postgresEngineTest.js +463 -0
  33. package/test/postgresIntegrationTest.js +381 -0
  34. package/test/securityTest.js +268 -0
  35. package/test/singleDollarPlaceholderTest.js +238 -0
  36. package/test/tablePrefixTest.js +100 -0
  37. package/test/transformerTest.js +287 -0
  38. package/test/verifyFindById.js +169 -0
  39. package/test/verifyNewMethod.js +191 -0
  40. package/test/whereChainingTest.js +88 -0
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Verify .findById() Method Implementation
3
+ *
4
+ * Simple verification that the .findById() method exists
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ console.log("╔════════════════════════════════════════════════════════════════╗");
11
+ console.log("║ Verify .findById() Method Implementation ║");
12
+ console.log("╚════════════════════════════════════════════════════════════════╝\n");
13
+
14
+ let passed = 0;
15
+ let failed = 0;
16
+
17
+ // Test 1: Check file exists
18
+ console.log("📝 Test 1: Check queryMethods.js exists");
19
+ console.log("──────────────────────────────────────────────────");
20
+
21
+ const queryMethodsPath = path.join(__dirname, '../QueryLanguage/queryMethods.js');
22
+
23
+ try {
24
+ if(fs.existsSync(queryMethodsPath)) {
25
+ console.log(" ✓ queryMethods.js found");
26
+ passed++;
27
+ } else {
28
+ console.log(" ✗ queryMethods.js not found");
29
+ failed++;
30
+ }
31
+ } catch(err) {
32
+ console.log(` ✗ Error: ${err.message}`);
33
+ failed++;
34
+ }
35
+
36
+ // Test 2: Check .findById() method exists
37
+ console.log("\n📝 Test 2: Check .findById() method is implemented");
38
+ console.log("──────────────────────────────────────────────────");
39
+
40
+ try {
41
+ const content = fs.readFileSync(queryMethodsPath, 'utf8');
42
+
43
+ const hasFindByIdMethod = /findById\s*\(\s*id\s*\)\s*\{/.test(content);
44
+ const hasComment = /Convenience method.*Find record by primary key/i.test(content);
45
+ const hasPrimaryKeyDetection = /Find the primary key field/i.test(content);
46
+
47
+ if(hasFindByIdMethod) {
48
+ console.log(" ✓ findById() method definition found");
49
+
50
+ if(hasComment) {
51
+ console.log(" ✓ Method documentation present");
52
+ }
53
+
54
+ if(hasPrimaryKeyDetection) {
55
+ console.log(" ✓ Primary key detection code present");
56
+ }
57
+
58
+ passed++;
59
+ } else {
60
+ console.log(" ✗ findById() method not found");
61
+ failed++;
62
+ }
63
+ } catch(err) {
64
+ console.log(` ✗ Error: ${err.message}`);
65
+ failed++;
66
+ }
67
+
68
+ // Test 3: Verify method implementation
69
+ console.log("\n📝 Test 3: Verify method implementation details");
70
+ console.log("──────────────────────────────────────────────────");
71
+
72
+ try {
73
+ const content = fs.readFileSync(queryMethodsPath, 'utf8');
74
+
75
+ const checks = [
76
+ { name: 'Primary key loop', pattern: /for\s*\(\s*const\s+fieldName\s+in\s+this\.__entity\s*\)/ },
77
+ { name: 'Primary key check', pattern: /field\.primary\s*===\s*true/ },
78
+ { name: 'Error for missing PK', pattern: /No primary key defined/ },
79
+ { name: 'WHERE clause builder', pattern: /whereClause\s*=/ },
80
+ { name: 'Calls .where()', pattern: /this\.where\(whereClause/ },
81
+ { name: 'Calls .single()', pattern: /\.single\(\)/ }
82
+ ];
83
+
84
+ let allChecksPass = true;
85
+
86
+ checks.forEach(check => {
87
+ if(check.pattern.test(content)) {
88
+ console.log(` ✓ ${check.name}`);
89
+ } else {
90
+ console.log(` ✗ Missing: ${check.name}`);
91
+ allChecksPass = false;
92
+ }
93
+ });
94
+
95
+ if(allChecksPass) {
96
+ passed++;
97
+ } else {
98
+ failed++;
99
+ }
100
+ } catch(err) {
101
+ console.log(` ✗ Error: ${err.message}`);
102
+ failed++;
103
+ }
104
+
105
+ // Test 4: Check method placement
106
+ console.log("\n📝 Test 4: Verify method placement in file");
107
+ console.log("──────────────────────────────────────────────────");
108
+
109
+ try {
110
+ const content = fs.readFileSync(queryMethodsPath, 'utf8');
111
+
112
+ const findByIdIndex = content.indexOf('findById(id)');
113
+ const singleIndex = content.indexOf('single(){');
114
+
115
+ if(findByIdIndex > 0 && singleIndex > 0 && findByIdIndex < singleIndex) {
116
+ console.log(" ✓ findById() placed before single()");
117
+ console.log(" ✓ Correct location in class structure");
118
+ passed++;
119
+ } else if(findByIdIndex > 0) {
120
+ console.log(" ⚠ findById() exists but not in expected location");
121
+ console.log(` ℹ findById at position ${findByIdIndex}`);
122
+ passed++;
123
+ } else {
124
+ console.log(" ✗ Could not verify method placement");
125
+ failed++;
126
+ }
127
+ } catch(err) {
128
+ console.log(` ✗ Error: ${err.message}`);
129
+ failed++;
130
+ }
131
+
132
+ // Summary
133
+ console.log("\n\n╔════════════════════════════════════════════════════════════════╗");
134
+ console.log("║ Verification Summary ║");
135
+ console.log("╚════════════════════════════════════════════════════════════════╝");
136
+
137
+ const total = passed + failed;
138
+ const successRate = total > 0 ? Math.round((passed/total)*100) : 0;
139
+
140
+ console.log(`\n Total Checks: ${total}`);
141
+ console.log(` ✅ Passed: ${passed}`);
142
+ console.log(` ❌ Failed: ${failed}`);
143
+ console.log(` Success Rate: ${successRate}%\n`);
144
+
145
+ if(failed === 0){
146
+ console.log("🎉 All verification checks passed!");
147
+ console.log("\n✨ .findById() Method Successfully Implemented!");
148
+ console.log("\n📖 Implementation Details:");
149
+ console.log(" File: QueryLanguage/queryMethods.js");
150
+ console.log(" Method: findById(id)");
151
+ console.log(" Purpose: Convenience method for finding by primary key");
152
+ console.log("\n📖 Features:");
153
+ console.log(" ✓ Automatically detects primary key field");
154
+ console.log(" ✓ Works with any primary key name (id, user_id, etc.)");
155
+ console.log(" ✓ Validates entity has a primary key");
156
+ console.log(" ✓ Returns single record or null");
157
+ console.log(" ✓ Generates proper parameterized query");
158
+ console.log("\n📖 Usage Examples:");
159
+ console.log(" const user = context.User.findById(123);");
160
+ console.log(" const lead = qaContext.QaLead.findById(leadId);");
161
+ console.log(" const post = context.Post.findById(postId);");
162
+ console.log("\n📖 Equivalent To:");
163
+ console.log(" const user = context.User.where(u => u.id == $$, 123).single();");
164
+ console.log("\n✅ Now compatible with Mongoose/Sequelize-style syntax!\n");
165
+ process.exit(0);
166
+ } else {
167
+ console.log("⚠️ Some verification checks failed.");
168
+ process.exit(1);
169
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Verify .new() Method Implementation
3
+ *
4
+ * Simple verification that the .new() method exists in queryMethods.js
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ console.log("╔════════════════════════════════════════════════════════════════╗");
11
+ console.log("║ Verify .new() Method Implementation ║");
12
+ console.log("╚════════════════════════════════════════════════════════════════╝\n");
13
+
14
+ let passed = 0;
15
+ let failed = 0;
16
+
17
+ // Test 1: Check file exists
18
+ console.log("📝 Test 1: Check queryMethods.js exists");
19
+ console.log("──────────────────────────────────────────────────");
20
+
21
+ const queryMethodsPath = path.join(__dirname, '../QueryLanguage/queryMethods.js');
22
+
23
+ try {
24
+ if(fs.existsSync(queryMethodsPath)) {
25
+ console.log(" ✓ queryMethods.js found");
26
+ passed++;
27
+ } else {
28
+ console.log(" ✗ queryMethods.js not found");
29
+ failed++;
30
+ }
31
+ } catch(err) {
32
+ console.log(` ✗ Error: ${err.message}`);
33
+ failed++;
34
+ }
35
+
36
+ // Test 2: Check .new() method exists in file
37
+ console.log("\n📝 Test 2: Check .new() method is implemented");
38
+ console.log("──────────────────────────────────────────────────");
39
+
40
+ try {
41
+ const content = fs.readFileSync(queryMethodsPath, 'utf8');
42
+
43
+ // Check for method definition
44
+ const hasNewMethod = /new\s*\(\s*\)\s*\{/.test(content);
45
+ const hasComment = /Creates a new empty entity instance/i.test(content);
46
+ const hasTracking = /this\.__context\.__track\(newEntity\)/.test(content);
47
+ const hasPropertySetup = /Object\.defineProperty\(newEntity/.test(content);
48
+
49
+ if(hasNewMethod) {
50
+ console.log(" ✓ new() method definition found");
51
+
52
+ if(hasComment) {
53
+ console.log(" ✓ Method documentation present");
54
+ }
55
+
56
+ if(hasTracking) {
57
+ console.log(" ✓ Entity tracking code present");
58
+ }
59
+
60
+ if(hasPropertySetup) {
61
+ console.log(" ✓ Property definition code present");
62
+ }
63
+
64
+ passed++;
65
+ } else {
66
+ console.log(" ✗ new() method not found in queryMethods.js");
67
+ failed++;
68
+ }
69
+ } catch(err) {
70
+ console.log(` ✗ Error: ${err.message}`);
71
+ failed++;
72
+ }
73
+
74
+ // Test 3: Verify method structure
75
+ console.log("\n📝 Test 3: Verify method implementation structure");
76
+ console.log("──────────────────────────────────────────────────");
77
+
78
+ try {
79
+ const content = fs.readFileSync(queryMethodsPath, 'utf8');
80
+
81
+ // Extract the new() method
82
+ const methodMatch = content.match(/new\s*\(\s*\)\s*\{[\s\S]*?^\s{4}\}/m);
83
+
84
+ if(methodMatch) {
85
+ const methodCode = methodMatch[0];
86
+
87
+ const checks = [
88
+ { name: '__state = "insert"', pattern: /__state\s*:\s*"insert"/ },
89
+ { name: '__entity reference', pattern: /__entity\s*:\s*this\.__entity/ },
90
+ { name: '__context reference', pattern: /__context\s*:\s*this\.__context/ },
91
+ { name: '__dirtyFields array', pattern: /__dirtyFields\s*:\s*\[/ },
92
+ { name: 'Property loop', pattern: /for\s*\(\s*var\s+fieldName\s+in\s+this\.__entity\s*\)/ },
93
+ { name: 'Skip navigational', pattern: /isNavigational/ },
94
+ { name: 'defineProperty', pattern: /Object\.defineProperty/ },
95
+ { name: 'Track entity', pattern: /this\.__context\.__track/ },
96
+ { name: 'Return entity', pattern: /return\s+newEntity/ }
97
+ ];
98
+
99
+ let allChecksPass = true;
100
+
101
+ checks.forEach(check => {
102
+ if(check.pattern.test(methodCode)) {
103
+ console.log(` ✓ ${check.name}`);
104
+ } else {
105
+ console.log(` ✗ Missing: ${check.name}`);
106
+ allChecksPass = false;
107
+ }
108
+ });
109
+
110
+ if(allChecksPass) {
111
+ passed++;
112
+ } else {
113
+ failed++;
114
+ }
115
+ } else {
116
+ console.log(" ✗ Could not extract new() method code");
117
+ failed++;
118
+ }
119
+ } catch(err) {
120
+ console.log(` ✗ Error: ${err.message}`);
121
+ failed++;
122
+ }
123
+
124
+ // Test 4: Check method placement
125
+ console.log("\n📝 Test 4: Verify method placement in file");
126
+ console.log("──────────────────────────────────────────────────");
127
+
128
+ try {
129
+ const content = fs.readFileSync(queryMethodsPath, 'utf8');
130
+
131
+ // Check that new() comes before add()
132
+ const newIndex = content.indexOf('new()');
133
+ const addIndex = content.indexOf('add(entityValue)');
134
+
135
+ if(newIndex > 0 && addIndex > 0 && newIndex < addIndex) {
136
+ console.log(" ✓ new() method placed before add()");
137
+ console.log(" ✓ Correct location in class structure");
138
+ passed++;
139
+ } else if(newIndex > 0 && addIndex > 0) {
140
+ console.log(" ⚠ new() method exists but not in expected location");
141
+ console.log(` ℹ new() at position ${newIndex}, add() at ${addIndex}`);
142
+ passed++;
143
+ } else {
144
+ console.log(" ✗ Could not verify method placement");
145
+ failed++;
146
+ }
147
+ } catch(err) {
148
+ console.log(` ✗ Error: ${err.message}`);
149
+ failed++;
150
+ }
151
+
152
+ // Summary
153
+ console.log("\n\n╔════════════════════════════════════════════════════════════════╗");
154
+ console.log("║ Verification Summary ║");
155
+ console.log("╚════════════════════════════════════════════════════════════════╝");
156
+
157
+ const total = passed + failed;
158
+ const successRate = total > 0 ? Math.round((passed/total)*100) : 0;
159
+
160
+ console.log(`\n Total Checks: ${total}`);
161
+ console.log(` ✅ Passed: ${passed}`);
162
+ console.log(` ❌ Failed: ${failed}`);
163
+ console.log(` Success Rate: ${successRate}%\n`);
164
+
165
+ if(failed === 0){
166
+ console.log("🎉 All verification checks passed!");
167
+ console.log("\n✨ .new() Method Successfully Implemented!");
168
+ console.log("\n📖 Implementation Details:");
169
+ console.log(" File: QueryLanguage/queryMethods.js");
170
+ console.log(" Method: new()");
171
+ console.log(" Purpose: Create empty entity instances for INSERT operations");
172
+ console.log("\n📖 Features:");
173
+ console.log(" ✓ Creates entity with __state = 'insert'");
174
+ console.log(" ✓ Sets up property getters/setters for all fields");
175
+ console.log(" ✓ Tracks dirty fields automatically");
176
+ console.log(" ✓ Skips navigational properties (relationships)");
177
+ console.log(" ✓ Automatically tracks entity in context");
178
+ console.log("\n📖 Usage Example:");
179
+ console.log(" const job = context.QaIntelligenceJob.new();");
180
+ console.log(" job.annotation_id = 123;");
181
+ console.log(" job.job_type = 'auto_rewrite';");
182
+ console.log(" job.status = 'queued';");
183
+ console.log(" job.created_at = Date.now().toString();");
184
+ console.log(" context.saveChanges(); // INSERT INTO QaIntelligenceJob...");
185
+ console.log("\n✅ Bug Fixed: context.QaIntelligenceJob.new() now works!\n");
186
+ process.exit(0);
187
+ } else {
188
+ console.log("⚠️ Some verification checks failed.");
189
+ console.log(" Review the implementation in queryMethods.js");
190
+ process.exit(1);
191
+ }
@@ -0,0 +1,88 @@
1
+ // Test for where() chaining functionality
2
+ // This test verifies that multiple .where() calls combine properly with AND
3
+
4
+ var queryScript = require('../QueryLanguage/queryScript');
5
+
6
+ console.log('=== MasterRecord where() Chaining Test ===\n');
7
+
8
+ // Simulate what happens in the query builder
9
+ const qs = new queryScript();
10
+
11
+ // Test Case: Two chained where() calls (like the user's example)
12
+ // let query = this._qaContext.QaTask;
13
+ // query = query.where(t => t.assigned_worker_id == $$, this._currentUser.id);
14
+ // query = query.where(t => t.status == $$, status);
15
+
16
+ console.log('Test: Chaining two where() calls');
17
+ console.log(' First: where(t => t.assigned_worker_id == 123)');
18
+ console.log(' Second: where(t => t.status == "pending")');
19
+ console.log();
20
+
21
+ // First where call
22
+ qs.where('t => t.assigned_worker_id == 123', 'QaTask');
23
+
24
+ console.log('After first where():');
25
+ console.log(' script.where exists:', qs.script.where !== false);
26
+ console.log(' script.where.QaTask exists:', qs.script.where && qs.script.where.QaTask !== undefined);
27
+ if (qs.script.where && qs.script.where.QaTask && qs.script.where.QaTask.query) {
28
+ const exprs1 = qs.script.where.QaTask.query.expressions || [];
29
+ console.log(' Expressions count:', exprs1.length);
30
+ console.log(' Expression 1:', JSON.stringify(exprs1[0]));
31
+ }
32
+ console.log();
33
+
34
+ // Second where call (this should MERGE, not overwrite)
35
+ qs.where('t => t.status == "pending"', 'QaTask');
36
+
37
+ console.log('After second where():');
38
+ console.log(' script.where exists:', qs.script.where !== false);
39
+ console.log(' script.where.QaTask exists:', qs.script.where && qs.script.where.QaTask !== undefined);
40
+
41
+ if (qs.script.where && qs.script.where.QaTask && qs.script.where.QaTask.query) {
42
+ const exprs2 = qs.script.where.QaTask.query.expressions || [];
43
+ console.log(' Expressions count:', exprs2.length);
44
+ console.log(' Expression 1:', JSON.stringify(exprs2[0]));
45
+ console.log(' Expression 2:', JSON.stringify(exprs2[1]));
46
+ console.log();
47
+
48
+ // Verify results
49
+ console.log('=== Test Results ===');
50
+ if (exprs2.length === 2) {
51
+ console.log('✓ PASS: Both where conditions are present');
52
+ console.log(' - First condition: assigned_worker_id == 123');
53
+ console.log(' - Second condition: status == "pending"');
54
+ console.log(' - These should be combined with AND in the SQL');
55
+ } else {
56
+ console.log('✗ FAIL: Expected 2 expressions, got', exprs2.length);
57
+ if (exprs2.length === 1) {
58
+ console.log(' - Only the last where() was applied (bug not fixed)');
59
+ }
60
+ }
61
+ } else {
62
+ console.log('✗ FAIL: script.where structure is invalid');
63
+ }
64
+
65
+ console.log();
66
+ console.log('=== Additional Test: Three where() calls ===');
67
+
68
+ // Test with three chained where calls
69
+ const qs2 = new queryScript();
70
+ qs2.where('t => t.user_id == 1', 'Task');
71
+ qs2.where('t => t.status == "active"', 'Task');
72
+ qs2.where('t => t.priority == "high"', 'Task');
73
+
74
+ if (qs2.script.where && qs2.script.where.Task && qs2.script.where.Task.query) {
75
+ const exprs3 = qs2.script.where.Task.query.expressions || [];
76
+ console.log('Expressions count:', exprs3.length);
77
+ if (exprs3.length === 3) {
78
+ console.log('✓ PASS: All three where conditions are present');
79
+ exprs3.forEach((expr, idx) => {
80
+ console.log(` ${idx + 1}. ${expr.field} ${expr.func} ${expr.arg}`);
81
+ });
82
+ } else {
83
+ console.log('✗ FAIL: Expected 3 expressions, got', exprs3.length);
84
+ }
85
+ }
86
+
87
+ console.log();
88
+ console.log('=== Test Complete ===');