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,202 @@
1
+ # .includes() Syntax Clarification
2
+
3
+ ## Common Confusion: Two Different `.includes()` Methods
4
+
5
+ There are **two different** `.includes()` methods that developers confuse:
6
+
7
+ ### 1. JavaScript's Native `.includes()` ❌ NOT SUPPORTED
8
+ ```javascript
9
+ // ❌ WRONG - This is JavaScript's array.includes()
10
+ const ids = [1, 2, 3];
11
+ context.User.where(u => ids.includes(u.id)).toList();
12
+ // ^^^ JavaScript variable reference
13
+ ```
14
+
15
+ **Why it doesn't work:**
16
+ - The lambda `u => ...` is parsed as a **string**, not executed as JavaScript
17
+ - Cannot access JavaScript variables (`ids`) from inside the lambda string
18
+ - Cannot call JavaScript methods on those variables
19
+
20
+ **Error:**
21
+ ```
22
+ ReferenceError: ids is not defined
23
+ ```
24
+
25
+ ---
26
+
27
+ ### 2. MasterRecord's `.includes()` ✅ FULLY SUPPORTED
28
+ ```javascript
29
+ // ✅ CORRECT - This is MasterRecord's special syntax
30
+ const ids = [1, 2, 3];
31
+ context.User.where(u => $$.includes(u.id), ids).toList();
32
+ // ^^ MasterRecord placeholder
33
+ // ^^^ Pass array as argument
34
+ ```
35
+
36
+ **Why it works:**
37
+ - `$$` is a **placeholder** that MasterRecord recognizes
38
+ - The array `ids` is passed as a **separate argument**
39
+ - MasterRecord transforms `$$.includes(u.id)` → `u.id.any($$)` internally
40
+ - Generates proper SQL: `WHERE id IN (?, ?, ?)`
41
+
42
+ **Generated SQL:**
43
+ ```sql
44
+ SELECT * FROM User WHERE id IN (?, ?, ?)
45
+ -- Parameters: [1, 2, 3]
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Side-by-Side Comparison
51
+
52
+ | Feature | JavaScript `.includes()` | MasterRecord `.includes()` |
53
+ |---------|-------------------------|---------------------------|
54
+ | Syntax | `array.includes(field)` | `$$.includes(field)` |
55
+ | Where used | JavaScript code | MasterRecord lambda strings |
56
+ | Array location | Inside lambda | Separate argument |
57
+ | Supported | ❌ No | ✅ Yes |
58
+
59
+ ---
60
+
61
+ ## Examples: Wrong vs Right
62
+
63
+ ### ❌ Wrong: JavaScript Syntax
64
+ ```javascript
65
+ // Trying to use JavaScript's includes() - WON'T WORK
66
+ const userIds = [1, 2, 3];
67
+ const roleIds = [10, 20];
68
+
69
+ // ❌ Wrong
70
+ context.User
71
+ .where(u => userIds.includes(u.id) && roleIds.includes(u.role_id))
72
+ .toList();
73
+
74
+ // Error: userIds is not defined
75
+ ```
76
+
77
+ ### ✅ Right: MasterRecord Syntax
78
+ ```javascript
79
+ // Using MasterRecord's includes() - WORKS
80
+ const userIds = [1, 2, 3];
81
+ const roleIds = [10, 20];
82
+
83
+ // ✅ Correct
84
+ context.User
85
+ .where(u => $$.includes(u.id), userIds)
86
+ .and(u => $$.includes(u.role_id), roleIds)
87
+ .toList();
88
+
89
+ // Generates: WHERE id IN (?, ?, ?) AND role_id IN (?, ?)
90
+ // Params: [1, 2, 3, 10, 20]
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Alternative Syntax: `.any()`
96
+
97
+ You can also use `.any()` directly (it's what `.includes()` transforms to):
98
+
99
+ ```javascript
100
+ // Option 1: .includes() (modern, readable)
101
+ context.User.where(u => $$.includes(u.id), [1, 2, 3]).toList();
102
+
103
+ // Option 2: .any() (classic syntax)
104
+ context.User.where(u => u.id.any($$), [1, 2, 3]).toList();
105
+
106
+ // Option 3: .any() with comma string (also works)
107
+ context.User.where(u => u.id.any($$), "1,2,3").toList();
108
+
109
+ // All three generate the same SQL:
110
+ // WHERE id IN (?, ?, ?)
111
+ // Params: [1, 2, 3]
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Why The Lambda is a String
117
+
118
+ MasterRecord's lambda expressions are **not executed as JavaScript**. They are:
119
+
120
+ 1. **Converted to strings**: `r => r.id == $` becomes the string `"r => r.id == $"`
121
+ 2. **Parsed**: MasterRecord extracts entity name (`r`), field name (`id`), operator (`==`), placeholder (`$`)
122
+ 3. **Converted to SQL**: Generates `WHERE id = ?`
123
+
124
+ **This means:**
125
+ - ✅ Can use: MasterRecord syntax (`$$`, `$`, `.any()`, `.includes()`, `.like()`)
126
+ - ❌ Cannot use: JavaScript variables, JavaScript methods, JavaScript operators beyond basic comparison
127
+
128
+ ---
129
+
130
+ ## Common Mistakes and Solutions
131
+
132
+ ### Mistake 1: Referencing JavaScript Variables
133
+ ```javascript
134
+ // ❌ Wrong
135
+ const minAge = 18;
136
+ context.User.where(u => u.age > minAge).toList();
137
+ // Error: minAge is not defined
138
+
139
+ // ✅ Right
140
+ const minAge = 18;
141
+ context.User.where(u => u.age > $, minAge).toList();
142
+ ```
143
+
144
+ ### Mistake 2: Using JavaScript Array Methods
145
+ ```javascript
146
+ // ❌ Wrong
147
+ const ids = [1, 2, 3];
148
+ context.User.where(u => ids.includes(u.id)).toList();
149
+ // Error: ids is not defined
150
+
151
+ // ✅ Right
152
+ const ids = [1, 2, 3];
153
+ context.User.where(u => $$.includes(u.id), ids).toList();
154
+ ```
155
+
156
+ ### Mistake 3: Calling JavaScript String Methods
157
+ ```javascript
158
+ // ❌ Wrong
159
+ context.User.where(u => u.name.startsWith("John")).toList();
160
+ // Error: startsWith is not defined
161
+
162
+ // ✅ Right - Use SQL LIKE
163
+ context.User.where(u => u.name.like($$), "John%").toList();
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Quick Reference
169
+
170
+ **When writing MasterRecord queries:**
171
+
172
+ ✅ **DO use:**
173
+ - `$$` - Placeholder for parameters
174
+ - `$` - Single placeholder (backwards compatibility)
175
+ - `$$.includes(field)` - IN clause with arrays
176
+ - `field.any($$)` - Alternative IN clause syntax
177
+ - `field.like($$)` - LIKE clause
178
+ - Basic operators: `==`, `!=`, `>`, `<`, `>=`, `<=`, `&&`, `||`
179
+
180
+ ❌ **DON'T use:**
181
+ - JavaScript variables directly (use `$$` placeholders instead)
182
+ - JavaScript methods (`.includes()`, `.startsWith()`, etc.)
183
+ - Complex JavaScript expressions
184
+
185
+ ---
186
+
187
+ ## Summary
188
+
189
+ **MasterRecord's `.includes()` is fully supported and works great!**
190
+
191
+ Just remember:
192
+ 1. Use `$$.includes(field)` not `array.includes(field)`
193
+ 2. Pass the array as a separate argument
194
+ 3. The lambda is a string, not JavaScript code
195
+
196
+ **Correct Pattern:**
197
+ ```javascript
198
+ const values = [1, 2, 3];
199
+ context.Model.where(m => $$.includes(m.field), values).toList();
200
+ ```
201
+
202
+ This is **not a bug** - it's working as designed. The confusion comes from developers trying to use JavaScript's native `.includes()` method inside the lambda string, which isn't possible because lambdas are parsed, not executed.
@@ -0,0 +1,184 @@
1
+ # MasterRecord Methods Reference
2
+
3
+ ## Common Confusion: Dbset Methods vs Lambda Functions
4
+
5
+ MasterRecord has two types of methods that are often confused:
6
+
7
+ ### 1. Dbset Methods (Called on context.EntityName)
8
+
9
+ These are methods you call **directly on the dbset**:
10
+
11
+ ```javascript
12
+ const user = context.User.new(); // ✅ Dbset method
13
+ context.User.add(user); // ✅ Dbset method
14
+ context.User.where(u => u.id == $, 1); // ✅ Dbset method
15
+ context.User.toList(); // ✅ Dbset method
16
+ context.User.single(); // ✅ Dbset method
17
+ context.User.remove(user); // ✅ Dbset method
18
+ ```
19
+
20
+ **Location**: `QueryLanguage/queryMethods.js`
21
+
22
+ ### 2. Lambda Expression Functions (Used inside WHERE/AND clauses)
23
+
24
+ These are **functions used INSIDE the lambda expression string**:
25
+
26
+ ```javascript
27
+ // ✅ .any() - Used inside lambda
28
+ context.User.where(u => u.id.any($$), "1,2,3").toList();
29
+
30
+ // ✅ .like() - Used inside lambda
31
+ context.User.where(u => u.name.like($$), "John%").toList();
32
+
33
+ // ✅ .includes() - Used inside lambda (transforms to .any())
34
+ context.User.where(u => $$.includes(u.id), [1, 2, 3]).toList();
35
+ ```
36
+
37
+ **Location**: `QueryLanguage/queryScript.js` (parsed from lambda string)
38
+
39
+ ---
40
+
41
+ ## Complete Method List
42
+
43
+ ### Dbset Methods
44
+
45
+ | Method | Description | Example |
46
+ |--------|-------------|---------|
47
+ | `.new()` | Create new entity instance | `const user = context.User.new();` |
48
+ | `.add(entity)` | Track entity for INSERT | `context.User.add(user);` |
49
+ | `.remove(entity)` | Track entity for DELETE | `context.User.remove(user);` |
50
+ | `.where(lambda, ...args)` | Filter query | `context.User.where(u => u.id == $, 1)` |
51
+ | `.and(lambda, ...args)` | Add AND condition | `.where(...).and(u => u.active == true)` |
52
+ | `.orderBy(lambda)` | Sort ascending | `.orderBy(u => u.name)` |
53
+ | `.orderByDescending(lambda)` | Sort descending | `.orderByDescending(u => u.created_at)` |
54
+ | `.take(n)` | Limit results | `.take(10)` |
55
+ | `.skip(n)` | Offset results | `.skip(20)` |
56
+ | `.toList()` | Execute and return array | `.where(...).toList()` |
57
+ | `.single()` | Execute and return one | `.where(...).single()` |
58
+ | `.first()` | Execute and return first | `.where(...).first()` |
59
+ | `.include(lambda)` | Eager load relationships | `.include(u => u.Posts)` |
60
+ | `.raw(sql)` | Execute raw SQL | `.raw("SELECT * FROM User")` |
61
+
62
+ ### Lambda Expression Functions
63
+
64
+ | Function | Used Inside | Description | Example |
65
+ |----------|-------------|-------------|---------|
66
+ | `.any($$)` | WHERE/AND | IN clause (comma-separated) | `u => u.id.any($$), "1,2,3"` |
67
+ | `.like($$)` | WHERE/AND | LIKE clause | `u => u.name.like($$), "John%"` |
68
+ | `.includes()` | WHERE/AND | IN clause (array) | `$$.includes(u.id), [1,2,3]` |
69
+
70
+ ---
71
+
72
+ ## Common Patterns
73
+
74
+ ### Creating and Inserting Entities
75
+
76
+ ```javascript
77
+ // Create new entity
78
+ const user = context.User.new();
79
+ user.name = "John Doe";
80
+ user.email = "john@example.com";
81
+ user.age = 30;
82
+
83
+ // Track for insert
84
+ context.User.add(user); // Optional - .new() auto-tracks
85
+
86
+ // Save to database
87
+ context.saveChanges();
88
+ ```
89
+
90
+ ### IN Clause Queries
91
+
92
+ ```javascript
93
+ // Option 1: .any() with comma-separated string
94
+ context.User.where(u => u.id.any($$), "1,2,3").toList();
95
+
96
+ // Option 2: .includes() with array (Recommended)
97
+ const ids = [1, 2, 3];
98
+ context.User.where(u => $$.includes(u.id), ids).toList();
99
+
100
+ // Both produce: SELECT * FROM User WHERE id IN (?, ?, ?)
101
+ ```
102
+
103
+ ### LIKE Queries
104
+
105
+ ```javascript
106
+ // Starts with
107
+ context.User.where(u => u.name.like($$), "John%").toList();
108
+
109
+ // Contains
110
+ context.User.where(u => u.email.like($$), "%@example.com%").toList();
111
+
112
+ // Produces: SELECT * FROM User WHERE name LIKE ?
113
+ ```
114
+
115
+ ### Complex Queries
116
+
117
+ ```javascript
118
+ const activeUsers = context.User
119
+ .where(u => $$.includes(u.role_id), [1, 2, 3])
120
+ .and(u => u.active == true)
121
+ .and(u => u.created_at > $, lastWeek)
122
+ .orderByDescending(u => u.created_at)
123
+ .take(50)
124
+ .toList();
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Why Your LLM Might Be Confused
130
+
131
+ ### ❌ Common Misunderstanding
132
+
133
+ ```javascript
134
+ // ❌ WRONG - Trying to call .any() on the dbset
135
+ context.User.any(...) // Error: .any is not a function
136
+
137
+ // ✅ CORRECT - Use .any() inside the lambda expression
138
+ context.User.where(u => u.id.any($$), "1,2,3")
139
+ ```
140
+
141
+ ### Key Difference
142
+
143
+ - **Dbset methods**: Called on `context.EntityName` (e.g., `.new()`, `.add()`)
144
+ - **Lambda functions**: Used inside the `where()` lambda string (e.g., `.any()`, `.like()`)
145
+
146
+ ---
147
+
148
+ ## Implementation Details
149
+
150
+ ### Dbset Methods
151
+ - **File**: `QueryLanguage/queryMethods.js`
152
+ - **Class**: `queryMethods`
153
+ - **Instance**: Created via `context.dbset(Model)`
154
+ - **Methods**: Prototype methods on `queryMethods` class
155
+
156
+ ### Lambda Functions
157
+ - **File**: `QueryLanguage/queryScript.js`
158
+ - **Parsing**: `describeExpressionPartsFunctions()` (lines 304-388)
159
+ - **Whitelist**: `isFunction()` method (lines 505-513)
160
+ - **Recognized**: `any`, `like`, `include`
161
+
162
+ ---
163
+
164
+ ## Quick Reference
165
+
166
+ | What You Want | Correct Syntax |
167
+ |---------------|----------------|
168
+ | Create entity | `context.User.new()` |
169
+ | Add to context | `context.User.add(entity)` |
170
+ | Find by ID | `context.User.where(u => u.id == $, 1).single()` |
171
+ | Find multiple IDs | `context.User.where(u => $$.includes(u.id), [1,2,3]).toList()` |
172
+ | Search text | `context.User.where(u => u.name.like($$), "John%").toList()` |
173
+ | Complex filter | `context.User.where(u => u.active == true).and(u => u.age > $, 18).toList()` |
174
+
175
+ ---
176
+
177
+ ## Summary
178
+
179
+ ✅ **`.new()` is a dbset method** - Added in latest version
180
+ ✅ **`.any()` is a lambda function** - Already existed
181
+ ✅ **`.includes()` is a lambda function** - Transforms to `.any()`
182
+ ✅ **Both are different types of methods used in different places**
183
+
184
+ If your LLM says `.any()` doesn't exist, clarify that it's looking for a **dbset method** when it should be looking for a **lambda expression function**.