@uql/core 3.11.1 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,9 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [3.11.1](https://github.com/rogerpadilla/uql/compare/@uql/core@3.11.0...@uql/core@3.11.1) (2026-02-26)
6
+ # [3.12.0](https://github.com/rogerpadilla/uql/compare/@uql/core@3.11.1...@uql/core@3.12.0) (2026-03-05)
7
7
 
8
- **Note:** Version bump only for package @uql/core
8
+
9
+ ### Features
10
+
11
+ * introduce JSONB dot-notation filtering in `where` clauses and fix `raw()` expression prefixing in logical operators. ([669257d](https://github.com/rogerpadilla/uql/commit/669257d497e08729094a9f178685ca6665b8b20b))
9
12
 
10
13
 
11
14
 
@@ -17,6 +20,40 @@ All notable changes to this project will be documented in this file. Please add
17
20
 
18
21
  date format is [yyyy-mm-dd]
19
22
 
23
+ ## [3.12.0] - 2026-03-05
24
+ ### New Features
25
+ - **JSONB Dot-Notation Operators**: Filter by nested JSON field paths directly in `$where` with full operator support (`$eq`, `$ne`, `$gt`, `$lt`, `$like`, `$ilike`, `$in`, `$nin`, `$regex`, etc.). Works across PostgreSQL, MySQL, and SQLite.
26
+ ```ts
27
+ await querier.findMany(User, {
28
+ $where: { 'settings.isArchived': { $ne: true } },
29
+ });
30
+ ```
31
+ - **Relation Filtering**: Filter by ManyToMany and OneToMany relations using automatic EXISTS subqueries. No more manual `raw()` joins.
32
+ ```ts
33
+ await querier.findMany(Item, {
34
+ $where: { tags: { name: 'important' } },
35
+ });
36
+ ```
37
+ - **`Json<T>` Marker Type**: Wrap JSONB field types with `Json<T>` to ensure they are classified as `FieldKey` (not `RelationKey`), enabling type-safe usage in `$where`, `$select`, and `$sort`.
38
+ ```ts
39
+ @Field({ type: 'jsonb' })
40
+ settings?: Json<{ isArchived?: boolean }>;
41
+ ```
42
+ - **`JsonFieldPaths<E>` Autocompletion**: Template literal type that derives valid dot-notation paths from `Json<T>` fields (e.g., `'kind.public' | 'kind.private'`). Provides IDE autocompletion for JSONB `$where` queries without restricting arbitrary string paths.
43
+
44
+ ### Bug Fixes
45
+ - **raw() String Prefix Fix**: String-based `raw()` values in `$and`/`$or` are no longer incorrectly table-prefixed (e.g., `raw("kind IS NOT NULL")` previously produced `resource.kind IS NOT NULL` instead of `kind IS NOT NULL`).
46
+
47
+ ### Improvements & Refactoring
48
+ - **DRY JSON Config**: Extracted `getBaseJsonConfig()` in each dialect — `$elemMatch` and dot-notation now compose from a single config source, eliminating ~20 lines of duplication.
49
+ - **Extracted `normalizeWhereValue()`**: Deduplicated the `Array→$in / object→passthrough / scalar→$eq` normalization used by both regular field and JSON path comparisons.
50
+ - **Cleaner Dot-Notation Detection**: Uses `indexOf`+`slice` instead of two `split('.')` calls for efficient dot-path parsing.
51
+ - **Relation Safety Guard**: `compareRelation()` now throws a descriptive `TypeError` if `rel.references` is missing, instead of a cryptic undefined crash.
52
+ - **TypeScript 6 Compatibility**: Fixed `QueryWhereMap` circular type reference and expanded `QueryWhereOptions.clause` union.
53
+
54
+ ### Test Coverage
55
+ - **46 new tests** across 3 dialects (base, PostgreSQL, SQLite) covering all new features and edge cases. Total: **1561 tests passing**.
56
+
20
57
  ## [3.11.1] - 2026-02-26
21
58
  ### Improvements
22
59
  - **Expanded ColumnType Aliases**: Added `integer`, `tinyint`, `bool`, `datetime`, and `smallserial` as first-class `ColumnType` values (aliases for `int`, `boolean`, `timestamp`, `smallserial`, and `serial` respectively). Users can now use standard SQL keywords interchangeably (e.g., `integer` or `int`, `bool` or `boolean`, `datetime` or `timestamp`).
package/README.md CHANGED
@@ -112,7 +112,7 @@ UQL separates the **intent** of your data from its **storage**. Both properties
112
112
  externalId?: string;
113
113
 
114
114
  @Field({ type: 'json' }) // → JSONB (Postgres), JSON (MySQL), TEXT (SQLite)
115
- metadata?: Record<string, unknown>;
115
+ metadata?: Json<{ theme?: string }>;
116
116
 
117
117
  // Logical types with constraints - portable with control
118
118
  @Field({ type: 'varchar', length: 500 })
@@ -131,7 +131,7 @@ statusCode?: number;
131
131
 
132
132
  ```ts
133
133
  import { v7 as uuidv7 } from 'uuid';
134
- import { Entity, Id, Field, OneToOne, OneToMany, ManyToOne, ManyToMany, type Relation } from '@uql/core';
134
+ import { Entity, Id, Field, OneToOne, OneToMany, ManyToOne, ManyToMany, type Relation, type Json } from '@uql/core';
135
135
 
136
136
  @Entity()
137
137
  export class User {
@@ -314,6 +314,38 @@ export class Item {
314
314
 
315
315
  &nbsp;
316
316
 
317
+ ### JSONB Operators & Relation Filtering
318
+
319
+ Query nested JSON fields using **type-safe dot-notation** with full operator support. Wrap fields with `Json<T>` to get IDE autocompletion for valid paths. UQL generates the correct SQL for each dialect.
320
+
321
+ ```ts
322
+ // Filter by nested JSONB field paths
323
+ const items = await querier.findMany(Company, {
324
+ $where: {
325
+ 'settings.isArchived': { $ne: true },
326
+ 'settings.priority': { $gte: 5 },
327
+ },
328
+ });
329
+ ```
330
+
331
+ **PostgreSQL:** `WHERE ("settings"->>'isArchived') <> $1 AND (("settings"->>'priority'))::numeric >= $2`
332
+ **SQLite:** `WHERE json_extract("settings", '$.isArchived') <> ? AND CAST(json_extract("settings", '$.priority') AS REAL) >= ?`
333
+
334
+ Filter parent entities by their **ManyToMany** or **OneToMany** relations using automatic EXISTS subqueries:
335
+
336
+ ```ts
337
+ // Find posts that have a tag named 'typescript'
338
+ const posts = await querier.findMany(Post, {
339
+ $where: { tags: { name: 'typescript' } },
340
+ });
341
+ ```
342
+
343
+ **PostgreSQL:** `WHERE EXISTS (SELECT 1 FROM "PostTag" WHERE "PostTag"."postId" = "Post"."id" AND "PostTag"."tagId" IN (SELECT "Tag"."id" FROM "Tag" WHERE "Tag"."name" = $1))`
344
+
345
+ > **Pro Tip**: Wrap JSONB field types with `Json<T>` (e.g., `settings?: Json<{ isArchived?: boolean }>`) to get IDE autocompletion for dot-notation paths.
346
+
347
+ &nbsp;
348
+
317
349
  ### Thread-Safe Transactions
318
350
 
319
351
  UQL is one of the few ORMs with a **centralized serialization engine**. Transactions are guaranteed to be race-condition free.