@zeroxsolutions/db 9.1.0 → 9.2.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 +28 -0
- package/README.md +26 -0
- package/dist/utils/filter.d.ts +7 -0
- package/dist/utils/filter.d.ts.map +1 -1
- package/dist/utils/filter.js +15 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## 9.2.0 (2026-09-28)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **db:** resolve a relationship filter against its foreign-key column ([32903ba](https://github.com/zeroxsolutions/zeroxsolutions/commit/32903ba))
|
|
6
|
+
|
|
7
|
+
### ❤️ Thank You
|
|
8
|
+
|
|
9
|
+
- Claude Opus 5.5 (1M context)
|
|
10
|
+
- Lương Văn Tú
|
|
11
|
+
|
|
12
|
+
## 9.1.0 (2026-09-27)
|
|
13
|
+
|
|
14
|
+
### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- **db:** order by a path no column holds, and admit resolved names ([bc10882](https://github.com/zeroxsolutions/zeroxsolutions/commit/bc10882))
|
|
17
|
+
- **db:** resolve a filter path no column holds through its own expression ([4627742](https://github.com/zeroxsolutions/zeroxsolutions/commit/4627742))
|
|
18
|
+
|
|
19
|
+
### 🩹 Fixes
|
|
20
|
+
|
|
21
|
+
- **db:** parenthesize a resolved condition and read resolvers by own key ([56bb1e4](https://github.com/zeroxsolutions/zeroxsolutions/commit/56bb1e4))
|
|
22
|
+
|
|
23
|
+
### ❤️ Thank You
|
|
24
|
+
|
|
25
|
+
- Claude Opus 5.5 (1M context)
|
|
26
|
+
- Claude Sonnet 5
|
|
27
|
+
- Lương Văn Tú
|
|
28
|
+
|
|
1
29
|
## 9.0.1 (2026-09-27)
|
|
2
30
|
|
|
3
31
|
### 🩹 Fixes
|
package/README.md
CHANGED
|
@@ -361,6 +361,32 @@ All 15, as `filterOperatorSchema` declares them:
|
|
|
361
361
|
|
|
362
362
|
A `%` or `_` in a text value is escaped, so it matches literally rather than as a wildcard.
|
|
363
363
|
|
|
364
|
+
#### Relationship Filters
|
|
365
|
+
|
|
366
|
+
A relationship named in a permit list is no column - its id sits in a foreign-key column instead, which
|
|
367
|
+
`buildFilterConditions` cannot resolve on its own. Declare the permit through `permitsFor`'s own
|
|
368
|
+
`relationship(name, operators)` (`@zeroxsolutions/query`), then hand `columnResolver` the foreign-key
|
|
369
|
+
column under that same name in `resolvers`:
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
import { permitsFor } from '@zeroxsolutions/query';
|
|
373
|
+
import { buildFilterConditions, columnResolver } from '@zeroxsolutions/db/utils';
|
|
374
|
+
|
|
375
|
+
const placesQueryPermits = permitsFor(placeAttributes, { relationships: places.relationships }).relationship(
|
|
376
|
+
'region',
|
|
377
|
+
['equals', 'in'],
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const condition = buildFilterConditions({
|
|
381
|
+
table: places,
|
|
382
|
+
filter: query.filter,
|
|
383
|
+
resolvers: { region: columnResolver(places.regionId) },
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
`columnResolver` answers `equals` and `in` - the only two operators a relationship filter grants -
|
|
388
|
+
comparing the id against the column it closes over, and throws on any other operator.
|
|
389
|
+
|
|
364
390
|
### Order Builder
|
|
365
391
|
|
|
366
392
|
```ts
|
package/dist/utils/filter.d.ts
CHANGED
|
@@ -104,6 +104,13 @@ export type FilterResolver = (leaf: FilterValue) => SQL | undefined;
|
|
|
104
104
|
export type FilterResolvers<TTable extends FilterableTable, P> = {
|
|
105
105
|
readonly [F in Exclude<keyof FilterableOf<P> & string, keyof TTable['_']['columns'] & string>]: FilterResolver;
|
|
106
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* The resolver for a relationship filter: the relationship's name is no column, and the id it compares is held in its
|
|
109
|
+
* foreign-key column. `{ region: columnResolver(places.regionId) }` declares one.
|
|
110
|
+
*
|
|
111
|
+
* @throws Error on an operator other than `equals` and `in`, the only two a relationship filter grants
|
|
112
|
+
*/
|
|
113
|
+
export declare function columnResolver(column: Column): FilterResolver;
|
|
107
114
|
/** What a filter may reach: the table to resolve fields against, the tree to build, and the paths no column holds. */
|
|
108
115
|
export interface FilterQuery<TTable extends FilterableTable = FilterableTable> {
|
|
109
116
|
table: TTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/utils/filter.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/utils/filter.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/filter.js
CHANGED
|
@@ -88,6 +88,21 @@ export class UnknownQueryField extends Error {
|
|
|
88
88
|
super(`No column resolves the field "${field}".`);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The resolver for a relationship filter: the relationship's name is no column, and the id it compares is held in its
|
|
93
|
+
* foreign-key column. `{ region: columnResolver(places.regionId) }` declares one.
|
|
94
|
+
*
|
|
95
|
+
* @throws Error on an operator other than `equals` and `in`, the only two a relationship filter grants
|
|
96
|
+
*/
|
|
97
|
+
export function columnResolver(column) {
|
|
98
|
+
return (leaf) => {
|
|
99
|
+
if (leaf.operator === 'equals')
|
|
100
|
+
return eq(column, leaf.value);
|
|
101
|
+
if (leaf.operator === 'in')
|
|
102
|
+
return leaf.value.length > 0 ? inArray(column, leaf.value) : undefined;
|
|
103
|
+
throw new Error(`A relationship filter answers equals and in, not "${leaf.operator}".`);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
91
106
|
/**
|
|
92
107
|
* The character that makes the next one literal. It is its own escape, so the character class below
|
|
93
108
|
* must list it first: escaping `%` before `\` would re-escape the backslash the first pass added.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroxsolutions/db",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.2.0",
|
|
4
4
|
"description": "Generic Drizzle toolkit - the id column and audit timestamps a table takes, a base repository, the unit-of-work and transactional outbox impls, and the filter/sort/pagination query helpers, for PostgreSQL (postgres-js) and Cloudflare D1; ships no domain schema. Shared across ZeroXSolutions services.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"tslib": "^2.3.0",
|
|
39
39
|
"uuid": "^14.0.1",
|
|
40
40
|
"@zeroxsolutions/cosmic": "0.3.4",
|
|
41
|
-
"@zeroxsolutions/query": "0.
|
|
41
|
+
"@zeroxsolutions/query": "0.7.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@testcontainers/postgresql": "^12.1.0",
|