@uql/core 3.11.1 → 3.12.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/CHANGELOG.md +58 -2
- package/README.md +34 -2
- package/dist/browser/uql-browser.min.js.map +1 -1
- package/dist/dialect/abstractSqlDialect.d.ts +37 -6
- package/dist/dialect/abstractSqlDialect.d.ts.map +1 -1
- package/dist/dialect/abstractSqlDialect.js +140 -10
- package/dist/dialect/abstractSqlDialect.js.map +1 -1
- package/dist/postgres/postgresDialect.d.ts +12 -2
- package/dist/postgres/postgresDialect.d.ts.map +1 -1
- package/dist/postgres/postgresDialect.js +6 -3
- package/dist/postgres/postgresDialect.js.map +1 -1
- package/dist/querier/abstractQuerier.d.ts.map +1 -1
- package/dist/querier/abstractQuerier.js.map +1 -1
- package/dist/sqlite/sqliteDialect.d.ts +23 -2
- package/dist/sqlite/sqliteDialect.d.ts.map +1 -1
- package/dist/sqlite/sqliteDialect.js +10 -10
- package/dist/sqlite/sqliteDialect.js.map +1 -1
- package/dist/type/entity.d.ts +19 -3
- package/dist/type/entity.d.ts.map +1 -1
- package/dist/type/query.d.ts +14 -5
- package/dist/type/query.d.ts.map +1 -1
- package/dist/type/query.js.map +1 -1
- package/dist/type/utility.d.ts +14 -0
- package/dist/type/utility.d.ts.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,17 @@
|
|
|
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.
|
|
6
|
+
## [3.12.1](https://github.com/rogerpadilla/uql/compare/@uql/core@3.12.0...@uql/core@3.12.1) (2026-03-05)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Make JSONB `$ne` operator null-safe by using `IS DISTINCT FROM` / `IS NOT` and refine query map type definitions. ([2bb2023](https://github.com/rogerpadilla/uql/commit/2bb202310d5a1a7fa2bc0ff7948c892afa4ba4f2))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* Enhance `QueryWhereMap` type for improved JSONB dot-notation and relation filtering, eliminating `as any` casts in tests. ([2092e11](https://github.com/rogerpadilla/uql/commit/2092e113d858130941a0d0b2807026b73c852946))
|
|
9
17
|
|
|
10
18
|
|
|
11
19
|
|
|
@@ -17,6 +25,54 @@ All notable changes to this project will be documented in this file. Please add
|
|
|
17
25
|
|
|
18
26
|
date format is [yyyy-mm-dd]
|
|
19
27
|
|
|
28
|
+
## [3.12.1] - 2026-03-05
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
- **Null-Safe JSONB `$ne`**: JSONB dot-notation `$ne` now uses null-safe operators (`IS DISTINCT FROM` on PostgreSQL, `IS NOT` on SQLite) so that absent keys (which return SQL `NULL`) are correctly included in results. Previously, `{ 'settings.isArchived': { $ne: true } }` would silently exclude rows where the key didn't exist.
|
|
31
|
+
- **JSONB `$eq`/`$ne` with `null`**: `$eq: null` and `$ne: null` on JSONB paths now correctly generate `IS NULL` / `IS NOT NULL` instead of `= NULL` / `<> NULL`.
|
|
32
|
+
|
|
33
|
+
### Improvements & Refactoring
|
|
34
|
+
- **`QueryWhereMap` Type Safety**: Replaced the overly permissive `Record<string, ...>` catch-all with explicit typed unions: template literal `` `${string}.${string}` `` for dot-paths, `RelationKey<E>` for relation filtering, and `JsonFieldPaths<E>` for IDE autocompletion.
|
|
35
|
+
- **`DeepJsonKeys` Recursive Type**: `JsonFieldPaths<E>` now derives dot-notation paths up to 5 levels deep (previously only 1 level), enabling autocompletion for nested JSONB structures like `'kind.theme.color'`.
|
|
36
|
+
- **DRY `compare()` Signature**: Simplified `compare()` from `<E, K extends keyof QueryWhereMap<E>>(key: K, val: QueryWhereMap<E>[K])` to `<E>(key: string, val: unknown)` across all dialect overrides, removing redundant generic constraints.
|
|
37
|
+
- **DRY SQLite Config**: SQLite's `getBaseJsonConfig()` now spreads from `super` instead of duplicating 4 identical fields.
|
|
38
|
+
|
|
39
|
+
### Test Coverage
|
|
40
|
+
- Removed ~20 `as any` casts from tests (now unnecessary with improved types). Added null-safe `$ne` tests across all dialects. Total: **1,563 tests passing**.
|
|
41
|
+
|
|
42
|
+
## [3.12.0] - 2026-03-05
|
|
43
|
+
### New Features
|
|
44
|
+
- **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.
|
|
45
|
+
```ts
|
|
46
|
+
await querier.findMany(User, {
|
|
47
|
+
$where: { 'settings.isArchived': { $ne: true } },
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
- **Relation Filtering**: Filter by ManyToMany and OneToMany relations using automatic EXISTS subqueries. No more manual `raw()` joins.
|
|
51
|
+
```ts
|
|
52
|
+
await querier.findMany(Item, {
|
|
53
|
+
$where: { tags: { name: 'important' } },
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
- **`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`.
|
|
57
|
+
```ts
|
|
58
|
+
@Field({ type: 'jsonb' })
|
|
59
|
+
settings?: Json<{ isArchived?: boolean }>;
|
|
60
|
+
```
|
|
61
|
+
- **`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.
|
|
62
|
+
|
|
63
|
+
### Bug Fixes
|
|
64
|
+
- **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`).
|
|
65
|
+
|
|
66
|
+
### Improvements & Refactoring
|
|
67
|
+
- **DRY JSON Config**: Extracted `getBaseJsonConfig()` in each dialect — `$elemMatch` and dot-notation now compose from a single config source, eliminating ~20 lines of duplication.
|
|
68
|
+
- **Extracted `normalizeWhereValue()`**: Deduplicated the `Array→$in / object→passthrough / scalar→$eq` normalization used by both regular field and JSON path comparisons.
|
|
69
|
+
- **Cleaner Dot-Notation Detection**: Uses `indexOf`+`slice` instead of two `split('.')` calls for efficient dot-path parsing.
|
|
70
|
+
- **Relation Safety Guard**: `compareRelation()` now throws a descriptive `TypeError` if `rel.references` is missing, instead of a cryptic undefined crash.
|
|
71
|
+
- **TypeScript 6 Compatibility**: Fixed `QueryWhereMap` circular type reference and expanded `QueryWhereOptions.clause` union.
|
|
72
|
+
|
|
73
|
+
### Test Coverage
|
|
74
|
+
- **46 new tests** across 3 dialects (base, PostgreSQL, SQLite) covering all new features and edge cases. Total: **1561 tests passing**.
|
|
75
|
+
|
|
20
76
|
## [3.11.1] - 2026-02-26
|
|
21
77
|
### Improvements
|
|
22
78
|
- **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?:
|
|
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
|
|
|
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') IS DISTINCT FROM $1 AND (("settings"->>'priority'))::numeric >= $2`
|
|
332
|
+
**SQLite:** `WHERE json_extract("settings", '$.isArchived') IS NOT ? 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
|
+
|
|
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.
|