@truto/sqlite-builder 1.0.3 → 2.0.1

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/README.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - 🎯 **TypeScript-first**: Full type safety with excellent IDE support
19
19
  - 🔧 **Helper functions**: Built-in utilities for identifiers, IN clauses, and more
20
20
  - 🔍 **JSON Filter Language**: MongoDB-style JSON filters for WHERE clauses
21
+ - 🔗 **Qualified Filters**: Table/alias scoping for complex JOINs using `$alias` blocks
21
22
  - ⚡ **Lightweight**: Minimal bundle size with tree-shaking support
22
23
 
23
24
  ## 📦 Installation
@@ -58,13 +59,36 @@ const filter = {
58
59
  or: [{ email: { regex: '.*@example.com$' } }, { phone: { exists: false } }],
59
60
  }
60
61
 
61
- const { text: whereText, values: whereValues } = compileFilter(filter)
62
+ // Interpolate the compiled filter directly: its placeholders and values are
63
+ // collected automatically, so query.values is always correctly aligned.
62
64
  const query = sql`
63
65
  SELECT * FROM users
64
- WHERE ${sql.raw(whereText)}
66
+ WHERE ${compileFilter(filter)}
65
67
  `
66
68
 
67
69
  const results = db.prepare(query.text).all(...query.values)
70
+
71
+ // Qualified filters for JOINs with alias blocks
72
+ const joinFilter = {
73
+ status: 'ACTIVE', // Main table
74
+ $profiles: {
75
+ // Profile table alias
76
+ verified: true,
77
+ 'settings.theme': 'dark', // JSON path in profile
78
+ },
79
+ $orders: {
80
+ // Orders table alias
81
+ total: { gt: 100 },
82
+ },
83
+ }
84
+
85
+ const joinQuery = sql`
86
+ SELECT u.name, p.verified, o.total
87
+ FROM users u
88
+ JOIN profiles p ON u.id = p.user_id
89
+ JOIN orders o ON u.id = o.user_id
90
+ WHERE ${compileFilter(joinFilter)}
91
+ `
68
92
  ```
69
93
 
70
94
  ## 📖 API Reference
@@ -131,7 +155,7 @@ const insertQuery = sql`
131
155
  // Returns: { text: 'INSERT INTO users ("name", "email", "age") VALUES (?, ?, ?)', values: [name, email, age] }
132
156
  ```
133
157
 
134
- **Security:** Only accepts valid ANSI identifiers (simple: `name`, qualified: `table.column`) for string elements. SQL fragments are passed through as-is.
158
+ **Security:** Only accepts valid ANSI identifiers (simple: `name`, qualified: `table.column`) for string elements, each capped at 255 characters. SQL fragments are passed through as-is, but **only fragments minted by this library** (e.g. `sql.raw`, nested `sql\`\``) are accepted — a plain `{ text, values }`object (for example from`JSON.parse`) is rejected, so untrusted data can never masquerade as raw SQL.
135
159
 
136
160
  ### `sql.in(array: readonly unknown[])`
137
161
 
@@ -157,9 +181,9 @@ const query = sql`SELECT * FROM users WHERE created_at > ${sql.raw('datetime("no
157
181
  // Returns: { text: 'SELECT * FROM users WHERE created_at > datetime("now", "-1 day")', values: [] }
158
182
  ```
159
183
 
160
- **⚠️ Warning:** Never use `sql.raw()` with user input. Only use with trusted, static SQL fragments.
184
+ **⚠️ Warning:** `sql.raw()` is the library's single, explicit trust boundary — its argument becomes SQL verbatim. Never pass user input to it. For dynamic WHERE clauses use `compileFilter()`; for identifiers use `sql.ident()`. As a safety net, the `sql` tag verifies that the number of `?` placeholders in the final query exactly matches the number of bound values, so a raw fragment that smuggles a stray `?` (or forgets to carry its value) throws instead of producing a misaligned query.
161
185
 
162
- ### `sql.join(fragments: SqlFragment[], separator?: string)`
186
+ ### `sql.join(fragments: SqlFragment[], separator?: string | SqlFragment)`
163
187
 
164
188
  Joins multiple SQL fragments with a separator.
165
189
 
@@ -174,6 +198,13 @@ const query = sql`SELECT * FROM users WHERE ${sql.join(conditions, ' AND ')}`
174
198
  // Returns: { text: "SELECT * FROM users WHERE name = ? AND age = ? AND active = ?", values: ['John', 30, true] }
175
199
  ```
176
200
 
201
+ **Fragments** must be library-minted fragments (forged `{ text, values }` objects are rejected).
202
+
203
+ **Separators** support any structural connector safely:
204
+
205
+ - A **string** separator (e.g. `', '`, `' AND '`, `' UNION ALL '`) is validated to be a pure connector. Separators containing string/identifier quotes (`'`, `"`, `` ` ``, `[`, `]`), statement terminators (`;`), comment markers (`--`, `/*`, `*/`), `NUL`, backslashes, or unbalanced parentheses are rejected — so even an untrusted separator cannot break out of the expression.
206
+ - A **`SqlFragment`** separator (e.g. `sql\` OR weight = ${w} OR \``) lets you parameterize the connector itself; its values are interleaved between fragments automatically.
207
+
177
208
  ## 🔍 JSON Filter Language
178
209
 
179
210
  Build complex WHERE clauses using MongoDB-style JSON filters. Perfect for APIs and dynamic queries.
@@ -191,12 +222,13 @@ const filter = {
191
222
  }
192
223
 
193
224
  const result = compileFilter(filter)
194
- // Returns: { text: '(("status" = ? AND "age" >= ? AND "age" < ?))', values: ['ACTIVE', 18, 65] }
225
+ // Returns a branded SQL fragment: { text: '(("status" = ?) AND ("age" >= ?) AND ("age" < ?))', values: ['ACTIVE', 18, 65] }
195
226
 
196
- // Use with the main sql template
227
+ // Interpolate it directly into a sql template — no sql.raw() needed. The
228
+ // filter's placeholders and values are collected automatically and stay aligned.
197
229
  const query = sql`
198
230
  SELECT * FROM users
199
- WHERE ${sql.raw(result.text)}
231
+ WHERE ${result}
200
232
  `
201
233
  ```
202
234
 
@@ -216,6 +248,7 @@ const query = sql`
216
248
  | **Logical AND** | `"and": [filter1, filter2]` | `(filter1 AND filter2)` | All conditions must match |
217
249
  | **Logical OR** | `"or": [filter1, filter2]` | `(filter1 OR filter2)` | Any condition must match |
218
250
  | **JSON Path** | `"profile.email": "test@example.com"` | `json_extract("profile", '$.email') = ?` | Query JSON column fields |
251
+ | **Alias Blocks** | `"$alias": { "field": value }` | `alias."field" = ?` | Table/alias qualified fields |
219
252
 
220
253
  ### Filter Examples
221
254
 
@@ -319,6 +352,133 @@ const filter10 = {
319
352
  }
320
353
  ```
321
354
 
355
+ #### Qualified Filters for JOINs
356
+
357
+ For complex queries involving multiple tables or aliases, use **alias blocks** with keys starting with `$`:
358
+
359
+ ```typescript
360
+ // Basic alias usage
361
+ const joinFilter = {
362
+ // Main table fields (no prefix)
363
+ status: 'ACTIVE',
364
+ age: { gte: 18, lt: 65 },
365
+
366
+ // Table alias 't2'
367
+ $t2: {
368
+ column_in_table_2: { gte: 100 },
369
+ 'stats.avg': { lt: 10 }, // JSON path in aliased table
370
+ },
371
+
372
+ // Table alias 'orders'
373
+ $orders: {
374
+ amount: { gt: 500 },
375
+ status: { in: ['completed', 'shipped'] },
376
+ },
377
+ }
378
+
379
+ const result = compileFilter(joinFilter)
380
+ // SQL: ((("status" = ?) AND ("age" >= ? AND "age" < ?) AND
381
+ // ((t2."column_in_table_2" >= ?) AND (json_extract(t2."stats", '$.avg') < ?)) AND
382
+ // ((orders."amount" > ?) AND (orders."status" IN (?,?)))))
383
+ ```
384
+
385
+ **Alias Block Rules:**
386
+
387
+ - **Alias names**: Must be valid SQL identifiers (`[A-Za-z_][A-Za-z0-9_]*`)
388
+ - **Prefixing**: All fields in alias blocks get prefixed with `alias.`
389
+ - **JSON paths**: Work seamlessly with aliases: `json_extract(alias."column", '$.path')`
390
+ - **Combination**: Alias blocks are AND-combined with root fields and each other
391
+ - **Order**: Regular fields processed first, then alias blocks
392
+
393
+ ```typescript
394
+ // Complex alias example with logical operators
395
+ const complexJoinFilter = {
396
+ // Primary table conditions
397
+ user_status: 'ACTIVE',
398
+
399
+ // User profile table
400
+ $profile: {
401
+ verified: true,
402
+ 'preferences.notifications': { ne: false },
403
+ or: [{ subscription_type: 'premium' }, { credits: { gte: 100 } }],
404
+ },
405
+
406
+ // Orders table with complex conditions
407
+ $orders: {
408
+ and: [
409
+ { created_at: { gte: '2024-01-01' } },
410
+ { or: [{ total_amount: { gt: 1000 } }, { item_count: { gte: 5 } }] },
411
+ ],
412
+ },
413
+ }
414
+
415
+ // Use in JOIN queries
416
+ const query = sql`
417
+ SELECT u.id, u.name, p.subscription_type, o.total_amount
418
+ FROM users u
419
+ JOIN profiles p ON u.id = p.user_id
420
+ JOIN orders o ON u.id = o.user_id
421
+ WHERE ${compileFilter(complexJoinFilter)}
422
+ `
423
+ ```
424
+
425
+ **Security & Validation:**
426
+
427
+ ```typescript
428
+ // ✅ Valid alias identifiers
429
+ $users: { name: 'John' } // Simple identifier
430
+ $user_profiles: { age: 25 } // Underscore allowed
431
+ $_temp: { status: 'active' } // Starting underscore allowed
432
+
433
+ // ❌ Invalid alias identifiers (will throw SyntaxError)
434
+ $123invalid: { ... } // Cannot start with number
435
+ $'invalid-alias': { ... } // Hyphens not allowed
436
+ $'table.alias': { ... } // Dots not allowed in alias name
437
+ ```
438
+
439
+ **Integration with Complex Queries:**
440
+
441
+ ```typescript
442
+ // Real-world JOIN example
443
+ const userOrderFilter = {
444
+ // Users table
445
+ active: true,
446
+ email: { exists: true },
447
+
448
+ // User profiles
449
+ $profiles: {
450
+ 'settings.email_notifications': true,
451
+ verified_at: { exists: true },
452
+ },
453
+
454
+ // Recent orders
455
+ $recent_orders: {
456
+ created_at: { gte: '2024-01-01' },
457
+ status: { in: ['completed', 'shipped'] },
458
+ total: { gt: 50 },
459
+ },
460
+ }
461
+
462
+ const complexQuery = sql`
463
+ SELECT
464
+ u.id,
465
+ u.name,
466
+ u.email,
467
+ p.verified_at,
468
+ COUNT(ro.id) as recent_order_count,
469
+ SUM(ro.total) as recent_order_total
470
+ FROM users u
471
+ JOIN profiles p ON u.id = p.user_id
472
+ JOIN orders ro ON u.id = ro.user_id
473
+ WHERE ${compileFilter(userOrderFilter)}
474
+ GROUP BY u.id, u.name, u.email, p.verified_at
475
+ HAVING recent_order_count > 0
476
+ ORDER BY recent_order_total DESC
477
+ `
478
+
479
+ const results = db.prepare(complexQuery.text).all(...complexQuery.values)
480
+ ```
481
+
322
482
  #### Kitchen Sink Examples
323
483
 
324
484
  Real-world complex filters:
@@ -360,13 +520,12 @@ const filter = {
360
520
  role: { in: ['USER', 'ADMIN'] },
361
521
  }
362
522
 
363
- const whereClause = compileFilter(filter)
364
-
365
- // Use in complete query
523
+ // Use in complete query — the filter fragment and the LIMIT value are
524
+ // collected in order, so query.values lines up with the placeholders.
366
525
  const query = sql`
367
526
  SELECT id, name, email, created_at
368
527
  FROM users
369
- WHERE ${sql.raw(whereClause.text)}
528
+ WHERE ${compileFilter(filter)}
370
529
  ORDER BY created_at DESC
371
530
  LIMIT ${limit}
372
531
  `
@@ -389,7 +548,7 @@ The JSON filter compiler includes comprehensive security measures:
389
548
  ```typescript
390
549
  // ❌ These will throw errors
391
550
  compileFilter({ age: { unknown: 18 } }) // Unknown operator
392
- compileFilter({ 'user; DROP TABLE': 'value' }) // Invalid identifier
551
+ compileFilter({ 'user;--': 'value' }) // Invalid identifier (contains ';')
393
552
  compileFilter({ role: { in: [] } }) // Empty array
394
553
  compileFilter({ role: { in: new Array(1000).fill('x') } }) // Too large array
395
554
 
@@ -424,9 +583,13 @@ const filter = {
424
583
  ### What's Protected
425
584
 
426
585
  - **SQL Injection**: All interpolated values are parameterized
427
- - **Stacked Queries**: Queries containing `;` followed by additional SQL are rejected
428
- - **Identifier Safety**: `sql.ident()` validates against ANSI identifier rules
429
- - **Length Limits**: Queries exceeding 100KB are rejected
586
+ - **Unforgeable fragments**: Only fragments created by this library can contribute raw SQL text. A plain `{ text, values }` object (e.g. from `JSON.parse` or a request body) is treated as a value, never as SQL, closing the structural duck-typing bypass
587
+ - **Placeholder integrity**: The `sql` tag rejects any query whose `?` count does not match its bound-value count, catching raw fragments that smuggle or drop placeholders
588
+ - **Safe `sql.join()` separators**: String separators are validated so they cannot introduce string literals, comments, statement terminators, or unbalanced parentheses; use a `SqlFragment` separator to parameterize the connector itself
589
+ - **Stacked Queries**: Queries containing `;` followed by additional SQL are rejected (detection ignores semicolons inside string literals and comments)
590
+ - **Identifier Safety**: `sql.ident()` validates against ANSI identifier rules and caps each part at 255 characters
591
+ - **Length Limits**: Queries exceeding 100KB are rejected; `compileFilter()` enforces the same cap on its output
592
+ - **Pattern Limits**: `like`/`ilike`/`regex` patterns are capped at 1024 characters to bound matching cost at the SQLite layer
430
593
  - **Filter Security**: JSON filters validate operators, identifiers, and enforce limits
431
594
 
432
595
  ### What's Your Responsibility
@@ -534,13 +697,12 @@ app.get('/api/users', (req, res) => {
534
697
  // User sends filter as JSON
535
698
  const filter = req.body.filter || {}
536
699
 
537
- // Safely compile to SQL
538
- const whereClause = compileFilter(filter)
539
-
700
+ // Safely compile to SQL and interpolate directly. Filter values and the
701
+ // LIMIT value are collected in order into query.values.
540
702
  const query = sql`
541
703
  SELECT id, name, email, created_at
542
704
  FROM users
543
- WHERE ${sql.raw(whereClause.text)}
705
+ WHERE ${compileFilter(filter)}
544
706
  ORDER BY created_at DESC
545
707
  LIMIT ${req.query.limit || 20}
546
708
  `
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Common regex patterns and limits used across the codebase
3
+ */
4
+ export declare const MAX_QUERY_LENGTH = 102400;
5
+ export declare const MAX_IDENTIFIER_LENGTH = 255;
6
+ export declare const MAX_PATTERN_LENGTH = 1024;
7
+ export declare const STACKED_QUERY_REGEX: RegExp;
8
+ export declare const QUALIFIED_IDENTIFIER_REGEX: RegExp;
9
+ export declare const SIMPLE_IDENTIFIER_REGEX: RegExp;
10
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,eAAO,MAAM,gBAAgB,SAAS,CAAA;AAKtC,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAMxC,eAAO,MAAM,kBAAkB,OAAO,CAAA;AAMtC,eAAO,MAAM,mBAAmB,QAAe,CAAA;AAG/C,eAAO,MAAM,0BAA0B,QACgB,CAAA;AAGvD,eAAO,MAAM,uBAAuB,QAA6B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,YAAY,EACZ,UAAU,EACX,MAAM,SAAS,CAAA;AA+UhB;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAa9D"}
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAGV,YAAY,EACZ,UAAU,EACX,MAAM,SAAS,CAAA;AAgZhB;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAuB9D"}
@@ -0,0 +1,14 @@
1
+ import type { SqlFragment } from './types';
2
+ /**
3
+ * Create and register an immutable, branded SQL fragment.
4
+ *
5
+ * The returned object is frozen (including its values array) so callers cannot
6
+ * mutate a fragment after the integrity checks that produced it.
7
+ */
8
+ export declare function createFragment(text: string, values: readonly unknown[]): SqlFragment;
9
+ /**
10
+ * Type guard: was this value minted by the library (and therefore trusted to
11
+ * contribute raw SQL text)?
12
+ */
13
+ export declare function isSqlFragment(value: unknown): value is SqlFragment;
14
+ //# sourceMappingURL=fragment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../src/fragment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAkB1C;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,OAAO,EAAE,GACzB,WAAW,CASb;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAMlE"}
package/dist/index.js CHANGED
@@ -1,8 +1,26 @@
1
- // src/sql.ts
1
+ // src/constants.ts
2
2
  var MAX_QUERY_LENGTH = 102400;
3
+ var MAX_IDENTIFIER_LENGTH = 255;
4
+ var MAX_PATTERN_LENGTH = 1024;
3
5
  var STACKED_QUERY_REGEX = /;[\s\S]*\S/;
4
6
  var QUALIFIED_IDENTIFIER_REGEX = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
5
7
  var SIMPLE_IDENTIFIER_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
8
+
9
+ // src/fragment.ts
10
+ var fragmentRegistry = new WeakSet;
11
+ function createFragment(text, values) {
12
+ const fragment = Object.freeze({
13
+ text,
14
+ values: Object.freeze([...values])
15
+ });
16
+ fragmentRegistry.add(fragment);
17
+ return fragment;
18
+ }
19
+ function isSqlFragment(value) {
20
+ return typeof value === "object" && value !== null && fragmentRegistry.has(value);
21
+ }
22
+
23
+ // src/sql.ts
6
24
  function formatDate(date) {
7
25
  const year = date.getFullYear();
8
26
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -31,6 +49,9 @@ function sqlValue(value) {
31
49
  throw new TypeError(`Unsupported value type: ${typeof value}`);
32
50
  }
33
51
  function quoteSingleIdentifier(identifier) {
52
+ if (identifier.length > MAX_IDENTIFIER_LENGTH) {
53
+ throw new TypeError(`Identifier part too long: ${identifier.length} characters (max: ${MAX_IDENTIFIER_LENGTH})`);
54
+ }
34
55
  if (!SIMPLE_IDENTIFIER_REGEX.test(identifier)) {
35
56
  throw new TypeError(`Invalid identifier part: ${identifier}. Must be a valid ANSI identifier.`);
36
57
  }
@@ -50,7 +71,7 @@ function sqlIdent(identifier) {
50
71
  }
51
72
  const fragments = [];
52
73
  for (const item of identifier) {
53
- if (item && typeof item === "object" && "text" in item && "values" in item && typeof item.text === "string" && Array.isArray(item.values)) {
74
+ if (isSqlFragment(item)) {
54
75
  fragments.push(item);
55
76
  } else if (typeof item === "string") {
56
77
  if (!item) {
@@ -59,20 +80,14 @@ function sqlIdent(identifier) {
59
80
  if (!QUALIFIED_IDENTIFIER_REGEX.test(item)) {
60
81
  throw new TypeError(`Invalid identifier: ${item}. Must be a valid identifier or qualified identifier (e.g., table.column)`);
61
82
  }
62
- fragments.push({
63
- text: quoteQualifiedIdentifier(item),
64
- values: []
65
- });
83
+ fragments.push(createFragment(quoteQualifiedIdentifier(item), []));
66
84
  } else {
67
85
  throw new TypeError("Array items must be strings or SQL fragments");
68
86
  }
69
87
  }
70
88
  const text = fragments.map((f) => f.text).join(", ");
71
89
  const values = fragments.flatMap((f) => [...f.values]);
72
- return {
73
- text,
74
- values
75
- };
90
+ return createFragment(text, values);
76
91
  }
77
92
  if (!identifier || typeof identifier !== "string") {
78
93
  throw new TypeError("Identifier must be a non-empty string");
@@ -80,10 +95,7 @@ function sqlIdent(identifier) {
80
95
  if (!QUALIFIED_IDENTIFIER_REGEX.test(identifier)) {
81
96
  throw new TypeError(`Invalid identifier: ${identifier}. Must be a valid identifier or qualified identifier (e.g., table.column)`);
82
97
  }
83
- return {
84
- text: quoteQualifiedIdentifier(identifier),
85
- values: []
86
- };
98
+ return createFragment(quoteQualifiedIdentifier(identifier), []);
87
99
  }
88
100
  function sqlIn(array) {
89
101
  if (!Array.isArray(array)) {
@@ -97,49 +109,153 @@ function sqlIn(array) {
97
109
  }
98
110
  const placeholders = array.map(() => "?").join(",");
99
111
  const values = array.map(sqlValue);
100
- return {
101
- text: `(${placeholders})`,
102
- values
103
- };
112
+ return createFragment(`(${placeholders})`, values);
104
113
  }
105
114
  function sqlRaw(rawSql) {
106
115
  if (typeof rawSql !== "string") {
107
116
  throw new TypeError("sql.raw() requires a string");
108
117
  }
109
- return {
110
- text: rawSql,
111
- values: []
112
- };
118
+ return createFragment(rawSql, []);
113
119
  }
114
120
  function sqlBlob(data) {
115
121
  if (!(data instanceof Buffer) && !(data instanceof Uint8Array)) {
116
122
  throw new TypeError("sql.blob() requires a Buffer or Uint8Array");
117
123
  }
118
- return {
119
- text: "?",
120
- values: [data]
121
- };
124
+ return createFragment("?", [data]);
125
+ }
126
+ var SEPARATOR_FORBIDDEN_TOKENS = [
127
+ "'",
128
+ '"',
129
+ "`",
130
+ "[",
131
+ "]",
132
+ ";",
133
+ "\\",
134
+ "\x00",
135
+ "--",
136
+ "/*",
137
+ "*/"
138
+ ];
139
+ function assertSafeSeparator(separator) {
140
+ for (const token of SEPARATOR_FORBIDDEN_TOKENS) {
141
+ if (separator.includes(token)) {
142
+ throw new TypeError(`Unsafe sql.join() separator: contains forbidden token ${JSON.stringify(token)}. Pass a SqlFragment (e.g. sql.raw) if you need parameterized separators.`);
143
+ }
144
+ }
145
+ let depth = 0;
146
+ for (const char of separator) {
147
+ if (char === "(") {
148
+ depth++;
149
+ } else if (char === ")") {
150
+ depth--;
151
+ if (depth < 0) {
152
+ throw new TypeError("Unsafe sql.join() separator: unbalanced parentheses");
153
+ }
154
+ }
155
+ }
156
+ if (depth !== 0) {
157
+ throw new TypeError("Unsafe sql.join() separator: unbalanced parentheses");
158
+ }
122
159
  }
123
160
  function sqlJoin(fragments, separator = ", ") {
124
161
  if (!Array.isArray(fragments)) {
125
162
  throw new TypeError("sql.join() requires an array of fragments");
126
163
  }
164
+ for (const fragment of fragments) {
165
+ if (!isSqlFragment(fragment)) {
166
+ throw new TypeError("sql.join() requires SQL fragments created by the sql tag or its helpers");
167
+ }
168
+ }
127
169
  if (fragments.length === 0) {
128
- return { text: "", values: [] };
170
+ return createFragment("", []);
171
+ }
172
+ let separatorText;
173
+ let separatorValues = [];
174
+ if (isSqlFragment(separator)) {
175
+ separatorText = separator.text;
176
+ separatorValues = separator.values;
177
+ } else if (typeof separator === "string") {
178
+ assertSafeSeparator(separator);
179
+ separatorText = separator;
180
+ } else {
181
+ throw new TypeError("sql.join() separator must be a string or a SQL fragment");
182
+ }
183
+ let text = "";
184
+ const values = [];
185
+ fragments.forEach((fragment, index) => {
186
+ if (index > 0) {
187
+ text += separatorText;
188
+ values.push(...separatorValues);
189
+ }
190
+ text += fragment.text;
191
+ values.push(...fragment.values);
192
+ });
193
+ return createFragment(text, values);
194
+ }
195
+ function scanSql(text) {
196
+ let placeholderCount = 0;
197
+ let code = "";
198
+ let i = 0;
199
+ const length = text.length;
200
+ while (i < length) {
201
+ const char = text[i];
202
+ const next = text[i + 1];
203
+ if (char === "-" && next === "-") {
204
+ i += 2;
205
+ while (i < length && text[i] !== `
206
+ `) {
207
+ i++;
208
+ }
209
+ continue;
210
+ }
211
+ if (char === "/" && next === "*") {
212
+ i += 2;
213
+ while (i < length && !(text[i] === "*" && text[i + 1] === "/")) {
214
+ i++;
215
+ }
216
+ i += 2;
217
+ continue;
218
+ }
219
+ if (char === "'" || char === '"' || char === "`") {
220
+ const quote = char;
221
+ i++;
222
+ while (i < length) {
223
+ if (text[i] === quote) {
224
+ if (text[i + 1] === quote) {
225
+ i += 2;
226
+ continue;
227
+ }
228
+ i++;
229
+ break;
230
+ }
231
+ i++;
232
+ }
233
+ continue;
234
+ }
235
+ if (char === "[") {
236
+ i++;
237
+ while (i < length && text[i] !== "]") {
238
+ i++;
239
+ }
240
+ i++;
241
+ continue;
242
+ }
243
+ if (char === "?") {
244
+ placeholderCount++;
245
+ }
246
+ code += char;
247
+ i++;
129
248
  }
130
- const text = fragments.map((f) => f.text).join(separator);
131
- const values = fragments.flatMap((f) => [...f.values]);
132
- return { text, values };
249
+ return { placeholderCount, code };
133
250
  }
134
251
  function sql(strings, ...values) {
135
252
  let text = strings[0] || "";
136
253
  const queryValues = [];
137
254
  for (let i = 0;i < values.length; i++) {
138
255
  const value = values[i];
139
- if (value && typeof value === "object" && "text" in value && "values" in value && typeof value.text === "string" && Array.isArray(value.values)) {
140
- const fragment = value;
141
- text += fragment.text;
142
- queryValues.push(...fragment.values);
256
+ if (isSqlFragment(value)) {
257
+ text += value.text;
258
+ queryValues.push(...value.values);
143
259
  } else {
144
260
  text += "?";
145
261
  queryValues.push(sqlValue(value));
@@ -149,13 +265,14 @@ function sql(strings, ...values) {
149
265
  if (text.length > MAX_QUERY_LENGTH) {
150
266
  throw new Error(`Query too long: ${text.length} bytes (max: ${MAX_QUERY_LENGTH})`);
151
267
  }
152
- if (STACKED_QUERY_REGEX.test(text)) {
268
+ const { placeholderCount, code } = scanSql(text);
269
+ if (placeholderCount !== queryValues.length) {
270
+ throw new Error(`Placeholder count (${placeholderCount}) does not match bound value count (${queryValues.length}). ` + 'Did a raw fragment contain a "?" without supplying its value?');
271
+ }
272
+ if (STACKED_QUERY_REGEX.test(code)) {
153
273
  throw new Error("Stacked queries are not allowed");
154
274
  }
155
- return Object.freeze({
156
- text,
157
- values: Object.freeze([...queryValues])
158
- });
275
+ return createFragment(text, queryValues);
159
276
  }
160
277
  sql.value = sqlValue;
161
278
  sql.ident = sqlIdent;
@@ -197,7 +314,15 @@ function compileJsonPath(field) {
197
314
  jsonPath: "$." + jsonPath.join(".")
198
315
  };
199
316
  }
200
- function compileFieldCondition(field, condition, context) {
317
+ function isValidSqlIdentifier(identifier) {
318
+ return identifier.length <= MAX_IDENTIFIER_LENGTH && SIMPLE_IDENTIFIER_REGEX.test(identifier);
319
+ }
320
+ function assertPatternWithinLimit(operator, pattern) {
321
+ if (pattern.length > MAX_PATTERN_LENGTH) {
322
+ throw new RangeError(`${operator} pattern too long: ${pattern.length} characters (max: ${MAX_PATTERN_LENGTH})`);
323
+ }
324
+ }
325
+ function compileFieldCondition(field, condition, context, alias) {
201
326
  if (condition === null || condition === undefined || typeof condition === "string" || typeof condition === "number" || typeof condition === "boolean" || condition instanceof Date) {
202
327
  context.operatorCount++;
203
328
  if (context.operatorCount > MAX_OPERATORS) {
@@ -206,20 +331,22 @@ function compileFieldCondition(field, condition, context) {
206
331
  if (isJsonPath(field)) {
207
332
  const { columnName, jsonPath } = compileJsonPath(field);
208
333
  const identFragment2 = sql.ident(columnName);
334
+ const fullFieldExpr = alias ? `${alias}.${identFragment2.text}` : identFragment2.text;
209
335
  if (condition === null || condition === undefined) {
210
336
  context.values.push(jsonPath);
211
- return `(json_extract(${identFragment2.text}, ?) IS NULL)`;
337
+ return `(json_extract(${fullFieldExpr}, ?) IS NULL)`;
212
338
  } else {
213
339
  context.values.push(jsonPath, sql.value(condition));
214
- return `(json_extract(${identFragment2.text}, ?) = ?)`;
340
+ return `(json_extract(${fullFieldExpr}, ?) = ?)`;
215
341
  }
216
342
  } else {
217
343
  const identFragment2 = sql.ident(field);
344
+ const fullFieldExpr = alias ? `${alias}.${identFragment2.text}` : identFragment2.text;
218
345
  if (condition === null || condition === undefined) {
219
- return `(${identFragment2.text} IS NULL)`;
346
+ return `(${fullFieldExpr} IS NULL)`;
220
347
  } else {
221
348
  context.values.push(sql.value(condition));
222
- return `(${identFragment2.text} = ?)`;
349
+ return `(${fullFieldExpr} = ?)`;
223
350
  }
224
351
  }
225
352
  }
@@ -234,7 +361,8 @@ function compileFieldCondition(field, condition, context) {
234
361
  }
235
362
  }
236
363
  const identFragment = sql.ident(isJsonPath(field) ? compileJsonPath(field).columnName : field);
237
- const fieldExpr = isJsonPath(field) ? `json_extract(${identFragment.text}, ?)` : identFragment.text;
364
+ const baseFieldExpr = alias ? `${alias}.${identFragment.text}` : identFragment.text;
365
+ const fieldExpr = isJsonPath(field) ? `json_extract(${baseFieldExpr}, ?)` : baseFieldExpr;
238
366
  if (isJsonPath(field)) {
239
367
  context.values.push(compileJsonPath(field).jsonPath);
240
368
  }
@@ -309,6 +437,7 @@ function compileFieldCondition(field, condition, context) {
309
437
  if (typeof value !== "string") {
310
438
  throw new TypeError("LIKE operator requires a string pattern");
311
439
  }
440
+ assertPatternWithinLimit("LIKE", value);
312
441
  context.values.push(value);
313
442
  clauses.push(`${fieldExpr} LIKE ?`);
314
443
  break;
@@ -316,6 +445,7 @@ function compileFieldCondition(field, condition, context) {
316
445
  if (typeof value !== "string") {
317
446
  throw new TypeError("ILIKE operator requires a string pattern");
318
447
  }
448
+ assertPatternWithinLimit("ILIKE", value);
319
449
  context.values.push(value);
320
450
  clauses.push(`${fieldExpr} LIKE ? COLLATE NOCASE`);
321
451
  break;
@@ -323,6 +453,7 @@ function compileFieldCondition(field, condition, context) {
323
453
  if (typeof value !== "string") {
324
454
  throw new TypeError("REGEX operator requires a string pattern");
325
455
  }
456
+ assertPatternWithinLimit("REGEX", value);
326
457
  context.values.push(value);
327
458
  clauses.push(`${fieldExpr} REGEXP ?`);
328
459
  break;
@@ -333,7 +464,7 @@ function compileFieldCondition(field, condition, context) {
333
464
  }
334
465
  return clauses.length === 1 ? `(${clauses[0]})` : `(${clauses.join(" AND ")})`;
335
466
  }
336
- function compileFilterRecursive(filter, context) {
467
+ function compileFilterRecursive(filter, context, alias) {
337
468
  if (context.depth >= MAX_NESTING_DEPTH) {
338
469
  throw new RangeError(`Nesting depth too deep (max: ${MAX_NESTING_DEPTH})`);
339
470
  }
@@ -353,7 +484,7 @@ function compileFilterRecursive(filter, context) {
353
484
  if (filter.and.length === 0) {
354
485
  throw new TypeError("AND operator cannot be used with empty arrays");
355
486
  }
356
- const andClauses = filter.and.map((subFilter) => compileFilterRecursive(subFilter, context));
487
+ const andClauses = filter.and.map((subFilter) => compileFilterRecursive(subFilter, context, alias));
357
488
  clauses.push(`(${andClauses.join(" AND ")})`);
358
489
  }
359
490
  if ("or" in filter && filter.or) {
@@ -367,15 +498,24 @@ function compileFilterRecursive(filter, context) {
367
498
  if (filter.or.length === 0) {
368
499
  throw new TypeError("OR operator cannot be used with empty arrays");
369
500
  }
370
- const orClauses = filter.or.map((subFilter) => compileFilterRecursive(subFilter, context));
501
+ const orClauses = filter.or.map((subFilter) => compileFilterRecursive(subFilter, context, alias));
371
502
  clauses.push(`(${orClauses.join(" OR ")})`);
372
503
  }
373
- const fieldEntries = Object.entries(filter).filter(([field, condition]) => field !== "and" && field !== "or" && condition !== undefined);
504
+ const fieldEntries = Object.entries(filter).filter(([field, condition]) => field !== "and" && field !== "or" && !field.startsWith("$") && condition !== undefined);
374
505
  for (const [field, condition] of fieldEntries) {
375
506
  if (Array.isArray(condition)) {
376
507
  throw new SyntaxError(`Field '${field}' cannot have array value. Use logical operators 'and'/'or' instead.`);
377
508
  }
378
- clauses.push(compileFieldCondition(field, condition, context));
509
+ clauses.push(compileFieldCondition(field, condition, context, alias));
510
+ }
511
+ const aliasEntries = Object.entries(filter).filter(([key, value]) => key.startsWith("$") && value !== undefined && typeof value === "object" && value !== null);
512
+ for (const [aliasKey, aliasFilter] of aliasEntries) {
513
+ const aliasName = aliasKey.slice(1);
514
+ if (!isValidSqlIdentifier(aliasName)) {
515
+ throw new SyntaxError(`Invalid alias identifier: ${aliasName}`);
516
+ }
517
+ const aliasClause = compileFilterRecursive(aliasFilter, context, aliasName);
518
+ clauses.push(aliasClause);
379
519
  }
380
520
  context.depth--;
381
521
  if (clauses.length === 0) {
@@ -393,16 +533,16 @@ function compileFilter(filter) {
393
533
  operatorCount: 0,
394
534
  values: []
395
535
  };
396
- const text = compileFilterRecursive(filter, context);
397
- return Object.freeze({
398
- text: `(${text})`,
399
- values: Object.freeze([...context.values])
400
- });
536
+ const text = `(${compileFilterRecursive(filter, context)})`;
537
+ if (text.length > MAX_QUERY_LENGTH) {
538
+ throw new RangeError(`Compiled filter too long: ${text.length} bytes (max: ${MAX_QUERY_LENGTH})`);
539
+ }
540
+ return createFragment(text, context.values);
401
541
  }
402
542
  export {
403
543
  sql,
404
544
  compileFilter
405
545
  };
406
546
 
407
- //# debugId=937C5E8A6E98C05964756E2164756E21
547
+ //# debugId=B02F5A27F175BBBF64756E2164756E21
408
548
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/sql.ts", "../src/filter.ts"],
3
+ "sources": ["../src/constants.ts", "../src/fragment.ts", "../src/sql.ts", "../src/filter.ts"],
4
4
  "sourcesContent": [
5
- "import type { SqlFragment, SqlQuery, SqlValue } from './types'\n\n// Maximum query length (100KB)\nconst MAX_QUERY_LENGTH = 102400\n\n// Regex to detect stacked queries (semicolon followed by non-whitespace)\nconst STACKED_QUERY_REGEX = /;[\\s\\S]*\\S/\n\n// ANSI identifier validation - supports qualified identifiers (e.g., table.column)\nconst QUALIFIED_IDENTIFIER_REGEX =\n /^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$/\n\n// Simple identifier validation (for individual parts)\nconst SIMPLE_IDENTIFIER_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * Format a date for SQLite (YYYY-MM-DD HH:MM:SS)\n */\nfunction formatDate(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n const hours = String(date.getHours()).padStart(2, '0')\n const minutes = String(date.getMinutes()).padStart(2, '0')\n const seconds = String(date.getSeconds()).padStart(2, '0')\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n/**\n * Convert a value to its SQLite representation\n */\nfunction sqlValue(value: SqlValue): unknown {\n if (value === null || value === undefined) {\n return null\n }\n\n if (typeof value === 'string') {\n return value\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return value\n }\n\n if (value instanceof Date) {\n return formatDate(value)\n }\n\n if (value instanceof Buffer || value instanceof Uint8Array) {\n throw new TypeError(\n 'Buffer/Uint8Array values must be used with sql.blob() for safe BLOB handling',\n )\n }\n\n throw new TypeError(`Unsupported value type: ${typeof value}`)\n}\n\n/**\n * Quote a single identifier part\n */\nfunction quoteSingleIdentifier(identifier: string): string {\n if (!SIMPLE_IDENTIFIER_REGEX.test(identifier)) {\n throw new TypeError(\n `Invalid identifier part: ${identifier}. Must be a valid ANSI identifier.`,\n )\n }\n return `\"${identifier}\"`\n}\n\n/**\n * Quote a qualified identifier by splitting on dots and quoting each part\n */\nfunction quoteQualifiedIdentifier(identifier: string): string {\n if (!QUALIFIED_IDENTIFIER_REGEX.test(identifier)) {\n throw new TypeError(\n `Invalid identifier: ${identifier}. Must be a valid identifier or qualified identifier (e.g., table.column)`,\n )\n }\n\n const parts = identifier.split('.')\n return parts.map(quoteSingleIdentifier).join('.')\n}\n\n/**\n * Validate and quote a SQL identifier or array of identifiers/fragments\n */\nfunction sqlIdent(\n identifier: string | readonly (string | SqlFragment)[],\n): SqlFragment {\n // Handle array of identifiers and fragments\n if (Array.isArray(identifier)) {\n if (identifier.length === 0) {\n throw new TypeError('Identifier array cannot be empty')\n }\n\n const fragments: SqlFragment[] = []\n\n for (const item of identifier) {\n // Handle SqlFragment objects (like sql.raw())\n if (\n item &&\n typeof item === 'object' &&\n 'text' in item &&\n 'values' in item &&\n typeof (item as Record<string, unknown>).text === 'string' &&\n Array.isArray((item as Record<string, unknown>).values)\n ) {\n fragments.push(item as SqlFragment)\n } else if (typeof item === 'string') {\n // Handle string identifiers\n if (!item) {\n throw new TypeError('All identifiers must be non-empty strings')\n }\n\n if (!QUALIFIED_IDENTIFIER_REGEX.test(item)) {\n throw new TypeError(\n `Invalid identifier: ${item}. Must be a valid identifier or qualified identifier (e.g., table.column)`,\n )\n }\n\n fragments.push({\n text: quoteQualifiedIdentifier(item),\n values: [],\n })\n } else {\n throw new TypeError('Array items must be strings or SQL fragments')\n }\n }\n\n // Join all fragments\n const text = fragments.map((f) => f.text).join(', ')\n const values = fragments.flatMap((f) => [...f.values])\n\n return {\n text,\n values,\n }\n }\n\n // Handle single identifier (existing behavior)\n if (!identifier || typeof identifier !== 'string') {\n throw new TypeError('Identifier must be a non-empty string')\n }\n\n if (!QUALIFIED_IDENTIFIER_REGEX.test(identifier)) {\n throw new TypeError(\n `Invalid identifier: ${identifier}. Must be a valid identifier or qualified identifier (e.g., table.column)`,\n )\n }\n\n return {\n text: quoteQualifiedIdentifier(identifier),\n values: [],\n }\n}\n\n/**\n * Create SQL IN clause from array\n */\nfunction sqlIn(array: readonly unknown[]): SqlFragment {\n if (!Array.isArray(array)) {\n throw new TypeError('sql.in() requires an array')\n }\n\n if (array.length === 0) {\n throw new TypeError('sql.in() cannot be used with empty arrays')\n }\n\n // Soft warning for large arrays\n if (array.length > 1000) {\n console.warn(\n `sql.in(): Large array with ${array.length} items. Consider using temporary tables for better performance.`,\n )\n }\n\n const placeholders = array.map(() => '?').join(',')\n const values = array.map(sqlValue)\n\n return {\n text: `(${placeholders})`,\n values,\n }\n}\n\n/**\n * Create raw SQL fragment (DANGEROUS - must not contain user input)\n */\nfunction sqlRaw(rawSql: string): SqlFragment {\n if (typeof rawSql !== 'string') {\n throw new TypeError('sql.raw() requires a string')\n }\n\n return {\n text: rawSql,\n values: [],\n }\n}\n\n/**\n * Create SQL fragment for BLOB data (for validated binary data)\n */\nfunction sqlBlob(data: Buffer | Uint8Array): SqlFragment {\n if (!(data instanceof Buffer) && !(data instanceof Uint8Array)) {\n throw new TypeError('sql.blob() requires a Buffer or Uint8Array')\n }\n\n return {\n text: '?',\n values: [data],\n }\n}\n\n/**\n * Join SQL fragments with a separator\n */\nfunction sqlJoin(\n fragments: readonly SqlFragment[],\n separator = ', ',\n): SqlFragment {\n if (!Array.isArray(fragments)) {\n throw new TypeError('sql.join() requires an array of fragments')\n }\n\n if (fragments.length === 0) {\n return { text: '', values: [] }\n }\n\n const text = fragments.map((f: SqlFragment) => f.text).join(separator)\n const values = fragments.flatMap((f: SqlFragment) => [...f.values])\n\n return { text, values }\n}\n\n/**\n * Main SQL tagged template function\n */\nfunction sql(strings: TemplateStringsArray, ...values: unknown[]): SqlQuery {\n // Build the query text and collect values\n let text = strings[0] || ''\n const queryValues: unknown[] = []\n\n for (let i = 0; i < values.length; i++) {\n const value = values[i]\n\n // Handle SqlFragment objects (from helper functions)\n if (\n value &&\n typeof value === 'object' &&\n 'text' in value &&\n 'values' in value &&\n typeof (value as Record<string, unknown>).text === 'string' &&\n Array.isArray((value as Record<string, unknown>).values)\n ) {\n const fragment = value as SqlFragment\n text += fragment.text\n queryValues.push(...fragment.values)\n } else {\n // Regular value - add placeholder and collect value\n text += '?'\n queryValues.push(sqlValue(value as SqlValue))\n }\n\n text += strings[i + 1] || ''\n }\n\n // Security checks\n if (text.length > MAX_QUERY_LENGTH) {\n throw new Error(\n `Query too long: ${text.length} bytes (max: ${MAX_QUERY_LENGTH})`,\n )\n }\n\n if (STACKED_QUERY_REGEX.test(text)) {\n throw new Error('Stacked queries are not allowed')\n }\n\n // Return frozen result\n return Object.freeze({\n text,\n values: Object.freeze([...queryValues]),\n })\n}\n\n// Attach helper functions to sql\nsql.value = sqlValue\nsql.ident = sqlIdent\nsql.in = sqlIn\nsql.raw = sqlRaw\nsql.blob = sqlBlob\nsql.join = sqlJoin\n\nexport { sql }\n",
6
- "import { sql } from './sql'\nimport type {\n ComparisonOperators,\n FieldCondition,\n FilterResult,\n JsonFilter,\n} from './types'\n\n// Limits to prevent DoS attacks\nconst MAX_NESTING_DEPTH = 10\nconst MAX_OPERATORS = 100\n\n// Valid operators for validation\nconst VALID_OPERATORS = new Set([\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'ne',\n 'in',\n 'nin',\n 'like',\n 'ilike',\n 'regex',\n 'exists',\n 'and',\n 'or',\n])\n\n/**\n * Context for tracking compilation state\n */\ninterface CompileContext {\n depth: number\n operatorCount: number\n values: unknown[]\n}\n\n/**\n * Check if a field name represents a JSON path (contains dots)\n */\nfunction isJsonPath(field: string): boolean {\n return field.includes('.')\n}\n\n/**\n * Convert a JSON path field to json_extract expression\n */\nfunction compileJsonPath(field: string): {\n columnName: string\n jsonPath: string\n} {\n const parts = field.split('.')\n const columnName = parts[0]\n const jsonPath = parts.slice(1)\n\n if (!columnName || jsonPath.length === 0 || jsonPath.some((part) => !part)) {\n throw new SyntaxError(`Invalid JSON path: ${field}`)\n }\n\n return {\n columnName,\n jsonPath: '$.' + jsonPath.join('.'),\n }\n}\n\n/**\n * Compile a single field condition to SQL\n */\nfunction compileFieldCondition(\n field: string,\n condition: FieldCondition,\n context: CompileContext,\n): string {\n // Handle direct value (equality)\n if (\n condition === null ||\n condition === undefined ||\n typeof condition === 'string' ||\n typeof condition === 'number' ||\n typeof condition === 'boolean' ||\n condition instanceof Date\n ) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n if (isJsonPath(field)) {\n const { columnName, jsonPath } = compileJsonPath(field)\n const identFragment = sql.ident(columnName)\n\n if (condition === null || condition === undefined) {\n context.values.push(jsonPath)\n return `(json_extract(${identFragment.text}, ?) IS NULL)`\n } else {\n context.values.push(jsonPath, sql.value(condition))\n return `(json_extract(${identFragment.text}, ?) = ?)`\n }\n } else {\n const identFragment = sql.ident(field)\n\n if (condition === null || condition === undefined) {\n return `(${identFragment.text} IS NULL)`\n } else {\n context.values.push(sql.value(condition))\n return `(${identFragment.text} = ?)`\n }\n }\n }\n\n // Handle operator object\n if (typeof condition !== 'object' || condition === null) {\n throw new TypeError('Condition must be a value or operator object')\n }\n\n const operators = condition as ComparisonOperators\n const clauses: string[] = []\n\n // Validate all operators are known\n for (const op of Object.keys(operators)) {\n if (!VALID_OPERATORS.has(op)) {\n throw new SyntaxError(`Unknown operator: ${op}`)\n }\n }\n\n const identFragment = sql.ident(\n isJsonPath(field) ? compileJsonPath(field).columnName : field,\n )\n const fieldExpr = isJsonPath(field)\n ? `json_extract(${identFragment.text}, ?)`\n : identFragment.text\n\n // Add JSON path to values if needed\n if (isJsonPath(field)) {\n context.values.push(compileJsonPath(field).jsonPath)\n }\n\n // Handle exists operator first (it overrides other operators)\n if ('exists' in operators) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n return operators.exists\n ? `(${fieldExpr} IS NOT NULL)`\n : `(${fieldExpr} IS NULL)`\n }\n\n // Handle comparison operators\n for (const [op, value] of Object.entries(operators)) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n switch (op) {\n case 'gt':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} > ?`)\n break\n case 'gte':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} >= ?`)\n break\n case 'lt':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} < ?`)\n break\n case 'lte':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} <= ?`)\n break\n case 'ne':\n if (value === null || value === undefined) {\n clauses.push(`${fieldExpr} IS NOT NULL`)\n } else {\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} <> ?`)\n }\n break\n case 'in': {\n if (!Array.isArray(value)) {\n throw new TypeError('IN operator requires an array')\n }\n if (value.length === 0) {\n throw new TypeError('IN operator cannot be used with empty arrays')\n }\n if (value.length > 999) {\n throw new RangeError(\n 'IN operator cannot be used with arrays larger than 999 items',\n )\n }\n\n const inFragment = sql.in(value)\n context.values.push(...inFragment.values)\n clauses.push(`${fieldExpr} IN ${inFragment.text}`)\n break\n }\n case 'nin': {\n if (!Array.isArray(value)) {\n throw new TypeError('NIN operator requires an array')\n }\n if (value.length === 0) {\n throw new TypeError('NIN operator cannot be used with empty arrays')\n }\n if (value.length > 999) {\n throw new RangeError(\n 'NIN operator cannot be used with arrays larger than 999 items',\n )\n }\n\n const ninFragment = sql.in(value)\n context.values.push(...ninFragment.values)\n clauses.push(`${fieldExpr} NOT IN ${ninFragment.text}`)\n break\n }\n case 'like':\n if (typeof value !== 'string') {\n throw new TypeError('LIKE operator requires a string pattern')\n }\n context.values.push(value)\n clauses.push(`${fieldExpr} LIKE ?`)\n break\n case 'ilike':\n if (typeof value !== 'string') {\n throw new TypeError('ILIKE operator requires a string pattern')\n }\n context.values.push(value)\n clauses.push(`${fieldExpr} LIKE ? COLLATE NOCASE`)\n break\n case 'regex':\n if (typeof value !== 'string') {\n throw new TypeError('REGEX operator requires a string pattern')\n }\n context.values.push(value)\n clauses.push(`${fieldExpr} REGEXP ?`)\n break\n }\n }\n\n if (clauses.length === 0) {\n throw new SyntaxError(\n 'Operator object must contain at least one valid operator',\n )\n }\n\n return clauses.length === 1 ? `(${clauses[0]})` : `(${clauses.join(' AND ')})`\n}\n\n/**\n * Compile a JSON filter to SQL recursively\n */\nfunction compileFilterRecursive(\n filter: JsonFilter,\n context: CompileContext,\n): string {\n if (context.depth >= MAX_NESTING_DEPTH) {\n throw new RangeError(`Nesting depth too deep (max: ${MAX_NESTING_DEPTH})`)\n }\n\n if (typeof filter !== 'object' || filter === null) {\n throw new TypeError('Filter must be an object')\n }\n\n context.depth++\n const clauses: string[] = []\n\n // Handle logical operators first\n if ('and' in filter && filter.and) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n if (!Array.isArray(filter.and)) {\n throw new TypeError('AND operator must be an array')\n }\n if (filter.and.length === 0) {\n throw new TypeError('AND operator cannot be used with empty arrays')\n }\n\n const andClauses = filter.and.map((subFilter) =>\n compileFilterRecursive(subFilter, context),\n )\n clauses.push(`(${andClauses.join(' AND ')})`)\n }\n\n if ('or' in filter && filter.or) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n if (!Array.isArray(filter.or)) {\n throw new TypeError('OR operator must be an array')\n }\n if (filter.or.length === 0) {\n throw new TypeError('OR operator cannot be used with empty arrays')\n }\n\n const orClauses = filter.or.map((subFilter) =>\n compileFilterRecursive(subFilter, context),\n )\n clauses.push(`(${orClauses.join(' OR ')})`)\n }\n\n // Handle field conditions (implicit AND) - preserve original order\n const fieldEntries = Object.entries(filter).filter(\n ([field, condition]) =>\n field !== 'and' && field !== 'or' && condition !== undefined,\n )\n\n for (const [field, condition] of fieldEntries) {\n // Handle array conditions (for 'and'/'or' at field level)\n if (Array.isArray(condition)) {\n throw new SyntaxError(\n `Field '${field}' cannot have array value. Use logical operators 'and'/'or' instead.`,\n )\n }\n\n clauses.push(\n compileFieldCondition(field, condition as FieldCondition, context),\n )\n }\n\n context.depth--\n\n if (clauses.length === 0) {\n throw new SyntaxError('Filter must contain at least one condition')\n }\n\n // Fix: ensure we have at least one clause before accessing clauses[0]\n if (clauses.length === 1) {\n return clauses[0]!\n } else {\n return `(${clauses.join(' AND ')})`\n }\n}\n\n/**\n * Compile a JSON filter to a SQL WHERE clause\n */\nexport function compileFilter(filter: JsonFilter): FilterResult {\n const context: CompileContext = {\n depth: 0,\n operatorCount: 0,\n values: [],\n }\n\n const text = compileFilterRecursive(filter, context)\n\n return Object.freeze({\n text: `(${text})`,\n values: Object.freeze([...context.values]),\n })\n}\n"
5
+ "/**\n * Common regex patterns and limits used across the codebase\n */\n\n// Maximum query length (100KB)\nexport const MAX_QUERY_LENGTH = 102400\n\n// Maximum length of a single identifier part (table/column/alias name).\n// Bounds the amount of attacker-influenced text that can be quoted into a\n// query, preventing identifier-based denial-of-service (huge field names).\nexport const MAX_IDENTIFIER_LENGTH = 255\n\n// Maximum length of a LIKE/ILIKE/REGEXP pattern. These patterns are evaluated\n// by the underlying SQLite engine at query time; bounding their size limits\n// pathological matching cost (e.g. catastrophic backtracking in a REGEXP\n// extension, or quadratic LIKE scans).\nexport const MAX_PATTERN_LENGTH = 1024\n\n// Regex to detect stacked queries (semicolon followed by non-whitespace).\n// Applied to a \"code-only\" view of the query (string literals and comments\n// stripped) so that semicolons inside literals/comments do not cause false\n// positives, and commented-out semicolons cannot smuggle a second statement.\nexport const STACKED_QUERY_REGEX = /;[\\s\\S]*\\S/\n\n// ANSI identifier validation - supports qualified identifiers (e.g., table.column)\nexport const QUALIFIED_IDENTIFIER_REGEX =\n /^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$/\n\n// Simple identifier validation (for individual parts)\nexport const SIMPLE_IDENTIFIER_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/\n",
6
+ "import type { SqlFragment } from './types'\n\n/**\n * Module-private registry of every SQL fragment minted by this library.\n *\n * Trust model: the text of a query is \"code\" (must originate from the\n * developer) and interpolated values are \"data\" (always parameterized). The\n * only way to contribute raw text is through a fragment created by one of the\n * library helpers (sql, sql.raw, sql.ident, sql.in, sql.blob, sql.join,\n * compileFilter). Membership in this WeakSet is the unforgeable proof that an\n * object is such a fragment.\n *\n * A plain object that merely looks like `{ text, values }` (e.g. from\n * JSON.parse or an untrusted request body) is NOT in this set, so it can never\n * be treated as raw SQL. This closes the structural duck-typing bypass.\n */\nconst fragmentRegistry = new WeakSet<object>()\n\n/**\n * Create and register an immutable, branded SQL fragment.\n *\n * The returned object is frozen (including its values array) so callers cannot\n * mutate a fragment after the integrity checks that produced it.\n */\nexport function createFragment(\n text: string,\n values: readonly unknown[],\n): SqlFragment {\n const fragment: SqlFragment = Object.freeze({\n text,\n values: Object.freeze([...values]),\n })\n\n fragmentRegistry.add(fragment)\n\n return fragment\n}\n\n/**\n * Type guard: was this value minted by the library (and therefore trusted to\n * contribute raw SQL text)?\n */\nexport function isSqlFragment(value: unknown): value is SqlFragment {\n return (\n typeof value === 'object' &&\n value !== null &&\n fragmentRegistry.has(value as object)\n )\n}\n",
7
+ "import {\n MAX_IDENTIFIER_LENGTH,\n MAX_QUERY_LENGTH,\n QUALIFIED_IDENTIFIER_REGEX,\n SIMPLE_IDENTIFIER_REGEX,\n STACKED_QUERY_REGEX,\n} from './constants'\nimport { createFragment, isSqlFragment } from './fragment'\nimport type { SqlFragment, SqlQuery, SqlValue } from './types'\n\n/**\n * Format a date for SQLite (YYYY-MM-DD HH:MM:SS)\n */\nfunction formatDate(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n const hours = String(date.getHours()).padStart(2, '0')\n const minutes = String(date.getMinutes()).padStart(2, '0')\n const seconds = String(date.getSeconds()).padStart(2, '0')\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n/**\n * Convert a value to its SQLite representation\n */\nfunction sqlValue(value: SqlValue): unknown {\n if (value === null || value === undefined) {\n return null\n }\n\n if (typeof value === 'string') {\n return value\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return value\n }\n\n if (value instanceof Date) {\n return formatDate(value)\n }\n\n if (value instanceof Buffer || value instanceof Uint8Array) {\n throw new TypeError(\n 'Buffer/Uint8Array values must be used with sql.blob() for safe BLOB handling',\n )\n }\n\n throw new TypeError(`Unsupported value type: ${typeof value}`)\n}\n\n/**\n * Quote a single identifier part\n */\nfunction quoteSingleIdentifier(identifier: string): string {\n if (identifier.length > MAX_IDENTIFIER_LENGTH) {\n throw new TypeError(\n `Identifier part too long: ${identifier.length} characters (max: ${MAX_IDENTIFIER_LENGTH})`,\n )\n }\n if (!SIMPLE_IDENTIFIER_REGEX.test(identifier)) {\n throw new TypeError(\n `Invalid identifier part: ${identifier}. Must be a valid ANSI identifier.`,\n )\n }\n return `\"${identifier}\"`\n}\n\n/**\n * Quote a qualified identifier by splitting on dots and quoting each part\n */\nfunction quoteQualifiedIdentifier(identifier: string): string {\n if (!QUALIFIED_IDENTIFIER_REGEX.test(identifier)) {\n throw new TypeError(\n `Invalid identifier: ${identifier}. Must be a valid identifier or qualified identifier (e.g., table.column)`,\n )\n }\n\n const parts = identifier.split('.')\n return parts.map(quoteSingleIdentifier).join('.')\n}\n\n/**\n * Validate and quote a SQL identifier or array of identifiers/fragments\n */\nfunction sqlIdent(\n identifier: string | readonly (string | SqlFragment)[],\n): SqlFragment {\n // Handle array of identifiers and fragments\n if (Array.isArray(identifier)) {\n if (identifier.length === 0) {\n throw new TypeError('Identifier array cannot be empty')\n }\n\n const fragments: SqlFragment[] = []\n\n for (const item of identifier) {\n // Only fragments minted by this library may pass through unquoted. This\n // prevents a forged `{ text, values }` object (e.g. from untrusted JSON)\n // from being injected as raw SQL via sql.ident().\n if (isSqlFragment(item)) {\n fragments.push(item)\n } else if (typeof item === 'string') {\n // Handle string identifiers\n if (!item) {\n throw new TypeError('All identifiers must be non-empty strings')\n }\n\n if (!QUALIFIED_IDENTIFIER_REGEX.test(item)) {\n throw new TypeError(\n `Invalid identifier: ${item}. Must be a valid identifier or qualified identifier (e.g., table.column)`,\n )\n }\n\n fragments.push(createFragment(quoteQualifiedIdentifier(item), []))\n } else {\n throw new TypeError('Array items must be strings or SQL fragments')\n }\n }\n\n // Join all fragments\n const text = fragments.map((f) => f.text).join(', ')\n const values = fragments.flatMap((f) => [...f.values])\n\n return createFragment(text, values)\n }\n\n // Handle single identifier (existing behavior)\n if (!identifier || typeof identifier !== 'string') {\n throw new TypeError('Identifier must be a non-empty string')\n }\n\n if (!QUALIFIED_IDENTIFIER_REGEX.test(identifier)) {\n throw new TypeError(\n `Invalid identifier: ${identifier}. Must be a valid identifier or qualified identifier (e.g., table.column)`,\n )\n }\n\n return createFragment(quoteQualifiedIdentifier(identifier), [])\n}\n\n/**\n * Create SQL IN clause from array\n */\nfunction sqlIn(array: readonly unknown[]): SqlFragment {\n if (!Array.isArray(array)) {\n throw new TypeError('sql.in() requires an array')\n }\n\n if (array.length === 0) {\n throw new TypeError('sql.in() cannot be used with empty arrays')\n }\n\n // Soft warning for large arrays\n if (array.length > 1000) {\n console.warn(\n `sql.in(): Large array with ${array.length} items. Consider using temporary tables for better performance.`,\n )\n }\n\n const placeholders = array.map(() => '?').join(',')\n const values = array.map(sqlValue)\n\n return createFragment(`(${placeholders})`, values)\n}\n\n/**\n * Create raw SQL fragment (DANGEROUS - must not contain user input).\n *\n * This is the library's single, explicit trust boundary: whatever string is\n * passed here becomes SQL verbatim. Only ever pass developer-authored,\n * constant SQL. Never pass user input. For dynamic WHERE clauses use\n * compileFilter(); for identifiers use sql.ident().\n */\nfunction sqlRaw(rawSql: string): SqlFragment {\n if (typeof rawSql !== 'string') {\n throw new TypeError('sql.raw() requires a string')\n }\n\n return createFragment(rawSql, [])\n}\n\n/**\n * Create SQL fragment for BLOB data (for validated binary data)\n */\nfunction sqlBlob(data: Buffer | Uint8Array): SqlFragment {\n if (!(data instanceof Buffer) && !(data instanceof Uint8Array)) {\n throw new TypeError('sql.blob() requires a Buffer or Uint8Array')\n }\n\n return createFragment('?', [data])\n}\n\n/**\n * Tokens that allow breaking out of a SQL expression context. A join separator\n * is structural SQL (a connector such as `, `, ` AND `, ` OR `). To make\n * arbitrary separators safe regardless of their origin, we forbid the\n * primitives that would let a separator escape the connector role: string and\n * identifier literal delimiters, statement terminators, comment markers, NUL,\n * and backslash escapes.\n */\nconst SEPARATOR_FORBIDDEN_TOKENS = [\n \"'\",\n '\"',\n '`',\n '[',\n ']',\n ';',\n '\\\\',\n '\\0',\n '--',\n '/*',\n '*/',\n] as const\n\n/**\n * Validate a string separator for sql.join(). Allows any structural connector\n * while rejecting the primitives used to inject literals, comments, or extra\n * statements. Parentheses must be balanced so a separator cannot escape the\n * grouping it sits within.\n */\nfunction assertSafeSeparator(separator: string): void {\n for (const token of SEPARATOR_FORBIDDEN_TOKENS) {\n if (separator.includes(token)) {\n throw new TypeError(\n `Unsafe sql.join() separator: contains forbidden token ${JSON.stringify(\n token,\n )}. Pass a SqlFragment (e.g. sql.raw) if you need parameterized separators.`,\n )\n }\n }\n\n let depth = 0\n for (const char of separator) {\n if (char === '(') {\n depth++\n } else if (char === ')') {\n depth--\n if (depth < 0) {\n throw new TypeError(\n 'Unsafe sql.join() separator: unbalanced parentheses',\n )\n }\n }\n }\n if (depth !== 0) {\n throw new TypeError('Unsafe sql.join() separator: unbalanced parentheses')\n }\n}\n\n/**\n * Join SQL fragments with a separator.\n *\n * Fragments must be library-minted (branded) fragments. The separator may be:\n * - a string: treated as a structural connector and validated by\n * assertSafeSeparator() so any connector is supported safely; or\n * - a SqlFragment: its text becomes the connector and its values are\n * interleaved between fragments, allowing fully parameterized separators.\n */\nfunction sqlJoin(\n fragments: readonly SqlFragment[],\n separator: string | SqlFragment = ', ',\n): SqlFragment {\n if (!Array.isArray(fragments)) {\n throw new TypeError('sql.join() requires an array of fragments')\n }\n\n for (const fragment of fragments) {\n if (!isSqlFragment(fragment)) {\n throw new TypeError(\n 'sql.join() requires SQL fragments created by the sql tag or its helpers',\n )\n }\n }\n\n if (fragments.length === 0) {\n return createFragment('', [])\n }\n\n let separatorText: string\n let separatorValues: readonly unknown[] = []\n\n if (isSqlFragment(separator)) {\n separatorText = separator.text\n separatorValues = separator.values\n } else if (typeof separator === 'string') {\n assertSafeSeparator(separator)\n separatorText = separator\n } else {\n throw new TypeError(\n 'sql.join() separator must be a string or a SQL fragment',\n )\n }\n\n let text = ''\n const values: unknown[] = []\n\n fragments.forEach((fragment, index) => {\n if (index > 0) {\n text += separatorText\n values.push(...separatorValues)\n }\n text += fragment.text\n values.push(...fragment.values)\n })\n\n return createFragment(text, values)\n}\n\n/**\n * Scan assembled SQL once to (a) count true placeholders and (b) produce a\n * \"code-only\" view with string/identifier literals and comments removed.\n *\n * Placeholders inside literals/comments are not counted, and semicolons inside\n * literals/comments are not treated as statement separators.\n */\nfunction scanSql(text: string): { placeholderCount: number; code: string } {\n let placeholderCount = 0\n let code = ''\n let i = 0\n const length = text.length\n\n while (i < length) {\n const char = text[i]\n const next = text[i + 1]\n\n // Line comment: -- ... <newline>\n if (char === '-' && next === '-') {\n i += 2\n while (i < length && text[i] !== '\\n') {\n i++\n }\n continue\n }\n\n // Block comment: /* ... */\n if (char === '/' && next === '*') {\n i += 2\n while (i < length && !(text[i] === '*' && text[i + 1] === '/')) {\n i++\n }\n i += 2\n continue\n }\n\n // Quoted string ('...') or quoted identifier (\"...\", `...`), with the SQL\n // convention that the quote char is escaped by doubling it.\n if (char === \"'\" || char === '\"' || char === '`') {\n const quote = char\n i++\n while (i < length) {\n if (text[i] === quote) {\n if (text[i + 1] === quote) {\n i += 2\n continue\n }\n i++\n break\n }\n i++\n }\n continue\n }\n\n // Bracket-quoted identifier: [ ... ]\n if (char === '[') {\n i++\n while (i < length && text[i] !== ']') {\n i++\n }\n i++\n continue\n }\n\n if (char === '?') {\n placeholderCount++\n }\n code += char\n i++\n }\n\n return { placeholderCount, code }\n}\n\n/**\n * Main SQL tagged template function\n */\nfunction sql(strings: TemplateStringsArray, ...values: unknown[]): SqlQuery {\n // Build the query text and collect values\n let text = strings[0] || ''\n const queryValues: unknown[] = []\n\n for (let i = 0; i < values.length; i++) {\n const value = values[i]\n\n // Only library-minted fragments contribute raw text; everything else is\n // parameterized. This blocks forged `{ text, values }` objects.\n if (isSqlFragment(value)) {\n text += value.text\n queryValues.push(...value.values)\n } else {\n // Regular value - add placeholder and collect value\n text += '?'\n queryValues.push(sqlValue(value as SqlValue))\n }\n\n text += strings[i + 1] || ''\n }\n\n // Security checks\n if (text.length > MAX_QUERY_LENGTH) {\n throw new Error(\n `Query too long: ${text.length} bytes (max: ${MAX_QUERY_LENGTH})`,\n )\n }\n\n const { placeholderCount, code } = scanSql(text)\n\n // Integrity: every placeholder must have exactly one bound value and vice\n // versa. Catches raw fragments that smuggle a stray `?` (or, conversely,\n // raw SQL that forgot to carry its values), keeping text and values aligned.\n if (placeholderCount !== queryValues.length) {\n throw new Error(\n `Placeholder count (${placeholderCount}) does not match bound value count (${queryValues.length}). ` +\n 'Did a raw fragment contain a \"?\" without supplying its value?',\n )\n }\n\n if (STACKED_QUERY_REGEX.test(code)) {\n throw new Error('Stacked queries are not allowed')\n }\n\n // Return frozen, branded result so it can be safely composed into other\n // queries (e.g. via sql.join) without being mistaken for a forgery.\n return createFragment(text, queryValues) as SqlQuery\n}\n\n// Attach helper functions to sql\nsql.value = sqlValue\nsql.ident = sqlIdent\nsql.in = sqlIn\nsql.raw = sqlRaw\nsql.blob = sqlBlob\nsql.join = sqlJoin\n\nexport { sql }\n",
8
+ "import {\n MAX_IDENTIFIER_LENGTH,\n MAX_PATTERN_LENGTH,\n MAX_QUERY_LENGTH,\n SIMPLE_IDENTIFIER_REGEX,\n} from './constants'\nimport { createFragment } from './fragment'\nimport { sql } from './sql'\nimport type {\n ComparisonOperators,\n FieldCondition,\n FilterResult,\n JsonFilter,\n} from './types'\n\n// Limits to prevent DoS attacks\nconst MAX_NESTING_DEPTH = 10\nconst MAX_OPERATORS = 100\n\n// Valid operators for validation\nconst VALID_OPERATORS = new Set([\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'ne',\n 'in',\n 'nin',\n 'like',\n 'ilike',\n 'regex',\n 'exists',\n 'and',\n 'or',\n])\n\n/**\n * Context for tracking compilation state\n */\ninterface CompileContext {\n depth: number\n operatorCount: number\n values: unknown[]\n}\n\n/**\n * Check if a field name represents a JSON path (contains dots)\n */\nfunction isJsonPath(field: string): boolean {\n return field.includes('.')\n}\n\n/**\n * Convert a JSON path field to json_extract expression\n */\nfunction compileJsonPath(field: string): {\n columnName: string\n jsonPath: string\n} {\n const parts = field.split('.')\n const columnName = parts[0]\n const jsonPath = parts.slice(1)\n\n if (!columnName || jsonPath.length === 0 || jsonPath.some((part) => !part)) {\n throw new SyntaxError(`Invalid JSON path: ${field}`)\n }\n\n return {\n columnName,\n jsonPath: '$.' + jsonPath.join('.'),\n }\n}\n\n/**\n * Validate if a string is a valid SQL identifier\n */\nfunction isValidSqlIdentifier(identifier: string): boolean {\n return (\n identifier.length <= MAX_IDENTIFIER_LENGTH &&\n SIMPLE_IDENTIFIER_REGEX.test(identifier)\n )\n}\n\n/**\n * Validate a LIKE/ILIKE/REGEXP pattern, bounding its length to limit\n * pathological matching cost at the SQLite layer.\n */\nfunction assertPatternWithinLimit(operator: string, pattern: string): void {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n throw new RangeError(\n `${operator} pattern too long: ${pattern.length} characters (max: ${MAX_PATTERN_LENGTH})`,\n )\n }\n}\n\n/**\n * Compile a single field condition to SQL\n */\nfunction compileFieldCondition(\n field: string,\n condition: FieldCondition,\n context: CompileContext,\n alias?: string,\n): string {\n // Handle direct value (equality)\n if (\n condition === null ||\n condition === undefined ||\n typeof condition === 'string' ||\n typeof condition === 'number' ||\n typeof condition === 'boolean' ||\n condition instanceof Date\n ) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n if (isJsonPath(field)) {\n const { columnName, jsonPath } = compileJsonPath(field)\n const identFragment = sql.ident(columnName)\n const fullFieldExpr = alias\n ? `${alias}.${identFragment.text}`\n : identFragment.text\n\n if (condition === null || condition === undefined) {\n context.values.push(jsonPath)\n return `(json_extract(${fullFieldExpr}, ?) IS NULL)`\n } else {\n context.values.push(jsonPath, sql.value(condition))\n return `(json_extract(${fullFieldExpr}, ?) = ?)`\n }\n } else {\n const identFragment = sql.ident(field)\n const fullFieldExpr = alias\n ? `${alias}.${identFragment.text}`\n : identFragment.text\n\n if (condition === null || condition === undefined) {\n return `(${fullFieldExpr} IS NULL)`\n } else {\n context.values.push(sql.value(condition))\n return `(${fullFieldExpr} = ?)`\n }\n }\n }\n\n // Handle operator object\n if (typeof condition !== 'object' || condition === null) {\n throw new TypeError('Condition must be a value or operator object')\n }\n\n const operators = condition as ComparisonOperators\n const clauses: string[] = []\n\n // Validate all operators are known\n for (const op of Object.keys(operators)) {\n if (!VALID_OPERATORS.has(op)) {\n throw new SyntaxError(`Unknown operator: ${op}`)\n }\n }\n\n const identFragment = sql.ident(\n isJsonPath(field) ? compileJsonPath(field).columnName : field,\n )\n const baseFieldExpr = alias\n ? `${alias}.${identFragment.text}`\n : identFragment.text\n const fieldExpr = isJsonPath(field)\n ? `json_extract(${baseFieldExpr}, ?)`\n : baseFieldExpr\n\n // Add JSON path to values if needed\n if (isJsonPath(field)) {\n context.values.push(compileJsonPath(field).jsonPath)\n }\n\n // Handle exists operator first (it overrides other operators)\n if ('exists' in operators) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n return operators.exists\n ? `(${fieldExpr} IS NOT NULL)`\n : `(${fieldExpr} IS NULL)`\n }\n\n // Handle comparison operators\n for (const [op, value] of Object.entries(operators)) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n switch (op) {\n case 'gt':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} > ?`)\n break\n case 'gte':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} >= ?`)\n break\n case 'lt':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} < ?`)\n break\n case 'lte':\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} <= ?`)\n break\n case 'ne':\n if (value === null || value === undefined) {\n clauses.push(`${fieldExpr} IS NOT NULL`)\n } else {\n context.values.push(sql.value(value))\n clauses.push(`${fieldExpr} <> ?`)\n }\n break\n case 'in': {\n if (!Array.isArray(value)) {\n throw new TypeError('IN operator requires an array')\n }\n if (value.length === 0) {\n throw new TypeError('IN operator cannot be used with empty arrays')\n }\n if (value.length > 999) {\n throw new RangeError(\n 'IN operator cannot be used with arrays larger than 999 items',\n )\n }\n\n const inFragment = sql.in(value)\n context.values.push(...inFragment.values)\n clauses.push(`${fieldExpr} IN ${inFragment.text}`)\n break\n }\n case 'nin': {\n if (!Array.isArray(value)) {\n throw new TypeError('NIN operator requires an array')\n }\n if (value.length === 0) {\n throw new TypeError('NIN operator cannot be used with empty arrays')\n }\n if (value.length > 999) {\n throw new RangeError(\n 'NIN operator cannot be used with arrays larger than 999 items',\n )\n }\n\n const ninFragment = sql.in(value)\n context.values.push(...ninFragment.values)\n clauses.push(`${fieldExpr} NOT IN ${ninFragment.text}`)\n break\n }\n case 'like':\n if (typeof value !== 'string') {\n throw new TypeError('LIKE operator requires a string pattern')\n }\n assertPatternWithinLimit('LIKE', value)\n context.values.push(value)\n clauses.push(`${fieldExpr} LIKE ?`)\n break\n case 'ilike':\n if (typeof value !== 'string') {\n throw new TypeError('ILIKE operator requires a string pattern')\n }\n assertPatternWithinLimit('ILIKE', value)\n context.values.push(value)\n clauses.push(`${fieldExpr} LIKE ? COLLATE NOCASE`)\n break\n case 'regex':\n if (typeof value !== 'string') {\n throw new TypeError('REGEX operator requires a string pattern')\n }\n assertPatternWithinLimit('REGEX', value)\n context.values.push(value)\n clauses.push(`${fieldExpr} REGEXP ?`)\n break\n }\n }\n\n if (clauses.length === 0) {\n throw new SyntaxError(\n 'Operator object must contain at least one valid operator',\n )\n }\n\n return clauses.length === 1 ? `(${clauses[0]})` : `(${clauses.join(' AND ')})`\n}\n\n/**\n * Compile a JSON filter to SQL recursively\n */\nfunction compileFilterRecursive(\n filter: JsonFilter,\n context: CompileContext,\n alias?: string,\n): string {\n if (context.depth >= MAX_NESTING_DEPTH) {\n throw new RangeError(`Nesting depth too deep (max: ${MAX_NESTING_DEPTH})`)\n }\n\n if (typeof filter !== 'object' || filter === null) {\n throw new TypeError('Filter must be an object')\n }\n\n context.depth++\n const clauses: string[] = []\n\n // Handle logical operators first\n if ('and' in filter && filter.and) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n if (!Array.isArray(filter.and)) {\n throw new TypeError('AND operator must be an array')\n }\n if (filter.and.length === 0) {\n throw new TypeError('AND operator cannot be used with empty arrays')\n }\n\n const andClauses = filter.and.map((subFilter) =>\n compileFilterRecursive(subFilter, context, alias),\n )\n clauses.push(`(${andClauses.join(' AND ')})`)\n }\n\n if ('or' in filter && filter.or) {\n context.operatorCount++\n if (context.operatorCount > MAX_OPERATORS) {\n throw new RangeError(`Too many operators (max: ${MAX_OPERATORS})`)\n }\n\n if (!Array.isArray(filter.or)) {\n throw new TypeError('OR operator must be an array')\n }\n if (filter.or.length === 0) {\n throw new TypeError('OR operator cannot be used with empty arrays')\n }\n\n const orClauses = filter.or.map((subFilter) =>\n compileFilterRecursive(subFilter, context, alias),\n )\n clauses.push(`(${orClauses.join(' OR ')})`)\n }\n\n // Handle field conditions (implicit AND) first - preserve original order\n const fieldEntries = Object.entries(filter).filter(\n ([field, condition]) =>\n field !== 'and' &&\n field !== 'or' &&\n !field.startsWith('$') &&\n condition !== undefined,\n )\n\n for (const [field, condition] of fieldEntries) {\n // Handle array conditions (for 'and'/'or' at field level)\n if (Array.isArray(condition)) {\n throw new SyntaxError(\n `Field '${field}' cannot have array value. Use logical operators 'and'/'or' instead.`,\n )\n }\n\n clauses.push(\n compileFieldCondition(field, condition as FieldCondition, context, alias),\n )\n }\n\n // Handle alias blocks (keys starting with $) after regular fields\n const aliasEntries = Object.entries(filter).filter(\n ([key, value]) =>\n key.startsWith('$') &&\n value !== undefined &&\n typeof value === 'object' &&\n value !== null,\n )\n\n for (const [aliasKey, aliasFilter] of aliasEntries) {\n const aliasName = aliasKey.slice(1) // Remove the $ prefix\n\n // Validate alias name\n if (!isValidSqlIdentifier(aliasName)) {\n throw new SyntaxError(`Invalid alias identifier: ${aliasName}`)\n }\n\n // Recursively compile the alias block with the alias context\n const aliasClause = compileFilterRecursive(\n aliasFilter as JsonFilter,\n context,\n aliasName,\n )\n clauses.push(aliasClause)\n }\n\n context.depth--\n\n if (clauses.length === 0) {\n throw new SyntaxError('Filter must contain at least one condition')\n }\n\n // Fix: ensure we have at least one clause before accessing clauses[0]\n if (clauses.length === 1) {\n return clauses[0]!\n } else {\n return `(${clauses.join(' AND ')})`\n }\n}\n\n/**\n * Compile a JSON filter to a SQL WHERE clause\n */\nexport function compileFilter(filter: JsonFilter): FilterResult {\n const context: CompileContext = {\n depth: 0,\n operatorCount: 0,\n values: [],\n }\n\n const text = `(${compileFilterRecursive(filter, context)})`\n\n // Backstop against output-size denial-of-service. Identifier-length and\n // operator-count limits keep individual pieces bounded; this guards the\n // aggregate so compileFilter() cannot emit an oversized clause even when used\n // standalone (the sql tag enforces the same cap when fragments are composed).\n if (text.length > MAX_QUERY_LENGTH) {\n throw new RangeError(\n `Compiled filter too long: ${text.length} bytes (max: ${MAX_QUERY_LENGTH})`,\n )\n }\n\n // Returned as a branded fragment so it can be interpolated directly into a\n // sql`` template with its values automatically collected — no sql.raw()\n // needed, which removes the value-binding footgun.\n return createFragment(text, context.values) as FilterResult\n}\n"
7
9
  ],
8
- "mappings": "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",
9
- "debugId": "937C5E8A6E98C05964756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "B02F5A27F175BBBF64756E2164756E21",
10
12
  "names": []
11
13
  }
package/dist/sql.d.ts CHANGED
@@ -12,7 +12,12 @@ declare function sqlIdent(identifier: string | readonly (string | SqlFragment)[]
12
12
  */
13
13
  declare function sqlIn(array: readonly unknown[]): SqlFragment;
14
14
  /**
15
- * Create raw SQL fragment (DANGEROUS - must not contain user input)
15
+ * Create raw SQL fragment (DANGEROUS - must not contain user input).
16
+ *
17
+ * This is the library's single, explicit trust boundary: whatever string is
18
+ * passed here becomes SQL verbatim. Only ever pass developer-authored,
19
+ * constant SQL. Never pass user input. For dynamic WHERE clauses use
20
+ * compileFilter(); for identifiers use sql.ident().
16
21
  */
17
22
  declare function sqlRaw(rawSql: string): SqlFragment;
18
23
  /**
@@ -20,9 +25,15 @@ declare function sqlRaw(rawSql: string): SqlFragment;
20
25
  */
21
26
  declare function sqlBlob(data: Buffer | Uint8Array): SqlFragment;
22
27
  /**
23
- * Join SQL fragments with a separator
28
+ * Join SQL fragments with a separator.
29
+ *
30
+ * Fragments must be library-minted (branded) fragments. The separator may be:
31
+ * - a string: treated as a structural connector and validated by
32
+ * assertSafeSeparator() so any connector is supported safely; or
33
+ * - a SqlFragment: its text becomes the connector and its values are
34
+ * interleaved between fragments, allowing fully parameterized separators.
24
35
  */
25
- declare function sqlJoin(fragments: readonly SqlFragment[], separator?: string): SqlFragment;
36
+ declare function sqlJoin(fragments: readonly SqlFragment[], separator?: string | SqlFragment): SqlFragment;
26
37
  /**
27
38
  * Main SQL tagged template function
28
39
  */
package/dist/sql.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AA6B9D;;GAEG;AACH,iBAAS,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAwB1C;AA4BD;;GAEG;AACH,iBAAS,QAAQ,CACf,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,GACrD,WAAW,CAkEb;AAED;;GAEG;AACH,iBAAS,KAAK,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,CAuBrD;AAED;;GAEG;AACH,iBAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAS3C;AAED;;GAEG;AACH,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,CASvD;AAED;;GAEG;AACH,iBAAS,OAAO,CACd,SAAS,EAAE,SAAS,WAAW,EAAE,EACjC,SAAS,SAAO,GACf,WAAW,CAab;AAED;;GAEG;AACH,iBAAS,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CA6C1E;kBA7CQ,GAAG;;;;;;;;;AAuDZ,OAAO,EAAE,GAAG,EAAE,CAAA"}
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAgB9D;;GAEG;AACH,iBAAS,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAwB1C;AAiCD;;GAEG;AACH,iBAAS,QAAQ,CACf,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,GACrD,WAAW,CAoDb;AAED;;GAEG;AACH,iBAAS,KAAK,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,CAoBrD;AAED;;;;;;;GAOG;AACH,iBAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAM3C;AAED;;GAEG;AACH,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,CAMvD;AA2DD;;;;;;;;GAQG;AACH,iBAAS,OAAO,CACd,SAAS,EAAE,SAAS,WAAW,EAAE,EACjC,SAAS,GAAE,MAAM,GAAG,WAAkB,GACrC,WAAW,CA6Cb;AA6ED;;GAEG;AACH,iBAAS,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CAgD1E;kBAhDQ,GAAG;;;;;;;;;AA0DZ,OAAO,EAAE,GAAG,EAAE,CAAA"}
package/dist/types.d.ts CHANGED
@@ -58,10 +58,11 @@ export interface LogicalOperators {
58
58
  }
59
59
  /**
60
60
  * A JSON filter for compiling to SQL WHERE clauses
61
- * Can be a field-to-condition mapping with implicit AND, or explicit logical operators
61
+ * Can be a field-to-condition mapping with implicit AND, explicit logical operators,
62
+ * or alias blocks (keys starting with $) containing nested filters
62
63
  */
63
64
  export type JsonFilter = LogicalOperators & {
64
- [field: string]: FieldCondition | readonly JsonFilter[] | undefined;
65
+ [field: string]: FieldCondition | readonly JsonFilter[] | JsonFilter | undefined;
65
66
  };
66
67
  /**
67
68
  * Result of compiling a JSON filter to SQL
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,IAAI,GACJ,MAAM,GACN,UAAU,CAAA;AAEd;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,mBAAmB;IACnB,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,4BAA4B;IAC5B,GAAG,CAAC,EAAE,QAAQ,CAAA;IACd,gBAAgB;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,yBAAyB;IACzB,GAAG,CAAC,EAAE,QAAQ,CAAA;IACd,gBAAgB;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,eAAe;IACf,EAAE,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;IACxB,mBAAmB;IACnB,GAAG,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;IACzB,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,mBAAmB,CAAA;AAE3D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,GAAG,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;IAC3B,+BAA+B;IAC/B,EAAE,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG;IAC1C,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,UAAU,EAAE,GAAG,SAAS,CAAA;CACpE,CAAA;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,IAAI,GACJ,MAAM,GACN,UAAU,CAAA;AAEd;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,mBAAmB;IACnB,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,4BAA4B;IAC5B,GAAG,CAAC,EAAE,QAAQ,CAAA;IACd,gBAAgB;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,yBAAyB;IACzB,GAAG,CAAC,EAAE,QAAQ,CAAA;IACd,gBAAgB;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,eAAe;IACf,EAAE,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;IACxB,mBAAmB;IACnB,GAAG,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;IACzB,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,mBAAmB,CAAA;AAE3D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,GAAG,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;IAC3B,+BAA+B;IAC/B,EAAE,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;CAC3B;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG;IAC1C,CAAC,KAAK,EAAE,MAAM,GACV,cAAc,GACd,SAAS,UAAU,EAAE,GACrB,UAAU,GACV,SAAS,CAAA;CACd,CAAA;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CACpC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truto/sqlite-builder",
3
- "version": "1.0.3",
3
+ "version": "2.0.1",
4
4
  "description": "Safe, zero-dependency template-literal tag for SQLite queries in any JS environment",
5
5
  "keywords": [
6
6
  "sqlite",