@truto/sqlite-builder 2.0.2-canary.0 → 2.0.2-canary.11

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 (2) hide show
  1. package/README.md +225 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+
2
+ // Interpolate it directly into a sql template — no sql.raw() needed. The
3
+ // filter's placeholders and values are collected automatically and stay aligned.
4
+ const query = sql`
5
+ SELECT * FROM users
6
+ WHERE ${result}
7
+ `
8
+ ```
9
+
10
+ ### Supported Operators
11
+
12
+ | Operator Family | JSON Form | SQL Fragment | Description |
13
+ | ------------------------- | ------------------------------------- | ---------------------------------------- | ----------------------------------- |
14
+ | **Equality** | `"field": value` | `"field" = ?` | Direct value comparison |
15
+ | **Inequality** | `"field": { "ne": value }` | `"field" <> ?` | Not equal comparison |
16
+ | **Comparison** | `"field": { "gt": value }` | `"field" > ?` | Greater than, gte, lt, lte |
17
+ | **Set Membership** | `"field": { "in": [1, 2, 3] }` | `"field" IN (?,?,?)` | Value in array |
18
+ | **Negative Set** | `"field": { "nin": [1, 2] }` | `"field" NOT IN (?,?)` | Value not in array |
19
+ | **NULL Checks** | `"field": { "exists": false }` | `"field" IS NULL` | Check for NULL/NOT NULL |
20
+ | **LIKE Patterns** | `"field": { "like": "john%" }` | `"field" LIKE ?` | Pattern matching |
21
+ | **Case-insensitive LIKE** | `"field": { "ilike": "%DOE%" }` | `"field" LIKE ? COLLATE NOCASE` | Case-insensitive patterns |
22
+ | **Regular Expressions** | `"field": { "regex": "^[A-Z]+" }` | `"field" REGEXP ?` | Regex patterns (requires extension) |
23
+ | **Logical AND** | `"and": [filter1, filter2]` | `(filter1 AND filter2)` | All conditions must match |
24
+ | **Logical OR** | `"or": [filter1, filter2]` | `(filter1 OR filter2)` | Any condition must match |
25
+ | **JSON Path** | `"profile.email": "test@example.com"` | `json_extract("profile", '$.email') = ?` | Query JSON column fields |
26
+ | **Alias Blocks** | `"$alias": { "field": value }` | `alias."field" = ?` | Table/alias qualified fields |
27
+
28
+ ### Filter Examples
29
+
30
+ #### Basic Operations
31
+
32
+ ```typescript
33
+ // Equality and comparison
34
+ const filter1 = {
35
+ status: 'ACTIVE',
36
+ age: { gte: 18, lt: 65 },
37
+ score: { gt: 80, lte: 100 },
38
+ }
39
+ // SQL: (("status" = ? AND "age" >= ? AND "age" < ? AND "score" > ? AND "score" <= ?))
40
+
41
+ // Set membership
42
+ const filter2 = {
43
+ role: { in: ['ADMIN', 'EDITOR'] },
44
+ department: { nin: ['ARCHIVED', 'DELETED'] },
45
+ }
46
+ // SQL: (("role" IN (?,?) AND "department" NOT IN (?,?)))
47
+
48
+ // NULL checks
49
+ const filter3 = {
50
+ email: { exists: true }, // IS NOT NULL
51
+ deleted_at: { exists: false }, // IS NULL
52
+ }
53
+ // SQL: (("email" IS NOT NULL AND "deleted_at" IS NULL))
54
+ ```
55
+
56
+ #### Pattern Matching
57
+
58
+ ```typescript
59
+ // LIKE patterns
60
+ const filter4 = {
61
+ username: { like: 'john%' }, // Starts with 'john'
62
+ email: { ilike: '%@EXAMPLE.COM%' }, // Case-insensitive contains
63
+ }
64
+ // SQL: (("username" LIKE ? AND "email" LIKE ? COLLATE NOCASE))
65
+
66
+ // Regular expressions (requires REGEXP extension)
67
+ const filter5 = {
68
+ email: { regex: '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$' },
69
+ phone: { regex: '^\\+1[0-9]{10}$' },
70
+ }
71
+ // SQL: (("email" REGEXP ? AND "phone" REGEXP ?))
72
+ ```
73
+
74
+ #### Logical Operators
75
+
76
+ ```typescript
77
+ // AND operator (explicit)
78
+ const filter6 = {
79
+ and: [
80
+ { status: 'ACTIVE' },
81
+ { age: { gte: 18 } },
82
+ { country: { in: ['US', 'CA', 'GB'] } },
83
+ ],
84
+ }
85
+ // SQL: ((("status" = ?) AND ("age" >= ?) AND ("country" IN (?,?,?))))
86
+
87
+ // OR operator
88
+ const filter7 = {
89
+ or: [
90
+ { age: { lt: 18 } }, // Minors
91
+ { age: { gte: 65 } }, // Seniors
92
+ ],
93
+ }
94
+ // SQL: ((("age" < ?) OR ("age" >= ?)))
95
+
96
+ // Mixed AND/OR logic
97
+ const filter8 = {
98
+ status: 'ACTIVE', // Implicit AND
99
+ or: [{ tags: { ilike: '%urgent%' } }, { priority: { gte: 8 } }],
100
+ }
101
+ // SQL: (((("tags" LIKE ? COLLATE NOCASE) OR ("priority" >= ?)) AND ("status" = ?)))
102
+ ```
103
+
104
+ #### JSON Path Querying
105
+
106
+ For SQLite JSON columns, use dot notation to query nested fields:
107
+
108
+ ```typescript
109
+ // Query JSON fields
110
+ const filter9 = {
111
+ 'profile.email': { regex: '.*@example\\.org$' },
112
+ 'profile.age': { gte: 21 },
113
+ 'settings.theme': { in: ['dark', 'light'] },
114
+ 'metadata.tags': { exists: true },
115
+ }
116
+ // SQL: ((json_extract("profile", '$.email') REGEXP ? AND
117
+ // json_extract("profile", '$.age') >= ? AND
118
+ // json_extract("settings", '$.theme') IN (?,?) AND
119
+ // json_extract("metadata", '$.tags') IS NOT NULL))
120
+
121
+ // Complex nested JSON query
122
+ const filter10 = {
123
+ and: [
124
+ { 'user.profile.email': { regex: '.*@company\\.com$' } },
125
+ { or: [{ 'user.role': 'ADMIN' }, { 'user.permissions.canEdit': true }] },
126
+ ],
127
+ }
128
+ ```
129
+
130
+ #### Qualified Filters for JOINs
131
+
132
+ For complex queries involving multiple tables or aliases, use **alias blocks** with keys starting with `$`:
133
+
134
+ ```typescript
135
+ // Basic alias usage
136
+ const joinFilter = {
137
+ // Main table fields (no prefix)
138
+ status: 'ACTIVE',
139
+ age: { gte: 18, lt: 65 },
140
+
141
+ // Table alias 't2'
142
+ $t2: {
143
+ column_in_table_2: { gte: 100 },
144
+ 'stats.avg': { lt: 10 }, // JSON path in aliased table
145
+ },
146
+
147
+ // Table alias 'orders'
148
+ $orders: {
149
+ amount: { gt: 500 },
150
+ status: { in: ['completed', 'shipped'] },
151
+ },
152
+ }
153
+
154
+ const result = compileFilter(joinFilter)
155
+ // SQL: ((("status" = ?) AND ("age" >= ? AND "age" < ?) AND
156
+ // ((t2."column_in_table_2" >= ?) AND (json_extract(t2."stats", '$.avg') < ?)) AND
157
+ // ((orders."amount" > ?) AND (orders."status" IN (?,?)))))
158
+ ```
159
+
160
+ **Alias Block Rules:**
161
+
162
+ - **Alias names**: Must be valid SQL identifiers (`[A-Za-z_][A-Za-z0-9_]*`)
163
+ - **Prefixing**: All fields in alias blocks get prefixed with `alias.`
164
+ - **JSON paths**: Work seamlessly with aliases: `json_extract(alias."column", '$.path')`
165
+ - **Combination**: Alias blocks are AND-combined with root fields and each other
166
+ - **Order**: Regular fields processed first, then alias blocks
167
+
168
+ ```typescript
169
+ // Complex alias example with logical operators
170
+ const complexJoinFilter = {
171
+ // Primary table conditions
172
+ user_status: 'ACTIVE',
173
+
174
+ // User profile table
175
+ $profile: {
176
+ verified: true,
177
+ 'preferences.notifications': { ne: false },
178
+ or: [{ subscription_type: 'premium' }, { credits: { gte: 100 } }],
179
+ },
180
+
181
+ // Orders table with complex conditions
182
+ $orders: {
183
+ and: [
184
+ { created_at: { gte: '2024-01-01' } },
185
+ { or: [{ total_amount: { gt: 1000 } }, { item_count: { gte: 5 } }] },
186
+ ],
187
+ },
188
+ }
189
+
190
+ // Use in JOIN queries
191
+ const query = sql`
192
+ SELECT u.id, u.name, p.subscription_type, o.total_amount
193
+ FROM users u
194
+ JOIN profiles p ON u.id = p.user_id
195
+ JOIN orders o ON u.id = o.user_id
196
+ WHERE ${compileFilter(complexJoinFilter)}
197
+ `
198
+ ```
199
+
200
+ **Security & Validation:**
201
+
202
+ ```typescript
203
+ // ✅ Valid alias identifiers
204
+ $users: { name: 'John' } // Simple identifier
205
+ $user_profiles: { age: 25 } // Underscore allowed
206
+ $_temp: { status: 'active' } // Starting underscore allowed
207
+
208
+ // ❌ Invalid alias identifiers (will throw SyntaxError)
209
+ $123invalid: { ... } // Cannot start with number
210
+ $'invalid-alias': { ... } // Hyphens not allowed
211
+ $'table.alias': { ... } // Dots not allowed in alias name
212
+ ```
213
+
214
+ **Integration with Complex Queries:**
215
+
216
+ ```typescript
217
+ // Real-world JOIN example
218
+ const userOrderFilter = {
219
+ // Users table
220
+ active: true,
221
+ email: { exists: true },
222
+
223
+ // User profiles
224
+ $profiles: {
225
+ 'settings.email_notifications': true,
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@truto/sqlite-builder","version":"2.0.2-canary.0","description":"debug canary","license":"MIT","main":"index.js"}
1
+ {"name":"@truto/sqlite-builder","version":"2.0.2-canary.11","description":"debug canary","license":"MIT","main":"index.js"}