@ballistix.digital/odata-query-builder 0.0.0-stage → 0.1.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/README.md +166 -2
- package/dist/index.d.ts +31 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/module/common/domain/exceptionCode.model.d.ts +12 -0
- package/dist/module/common/domain/exceptionCode.model.js +17 -0
- package/dist/module/common/domain/exceptionCode.model.js.map +1 -0
- package/dist/module/common/domain/exceptionDetail.model.d.ts +14 -0
- package/dist/module/common/domain/exceptionDetail.model.js +11 -0
- package/dist/module/common/domain/exceptionDetail.model.js.map +1 -0
- package/dist/module/common/domain/scalar.model.d.ts +15 -0
- package/dist/module/common/domain/scalar.model.js +14 -0
- package/dist/module/common/domain/scalar.model.js.map +1 -0
- package/dist/module/common/exception/queryBuilder.exception.d.ts +27 -0
- package/dist/module/common/exception/queryBuilder.exception.js +37 -0
- package/dist/module/common/exception/queryBuilder.exception.js.map +1 -0
- package/dist/module/common/util/escape.util.d.ts +55 -0
- package/dist/module/common/util/escape.util.js +83 -0
- package/dist/module/common/util/escape.util.js.map +1 -0
- package/dist/module/query/domain/chain/chain.model.d.ts +33 -0
- package/dist/module/query/domain/chain/chain.model.js +3 -0
- package/dist/module/query/domain/chain/chain.model.js.map +1 -0
- package/dist/module/query/domain/chain/expandNamespace.model.d.ts +56 -0
- package/dist/module/query/domain/chain/expandNamespace.model.js +3 -0
- package/dist/module/query/domain/chain/expandNamespace.model.js.map +1 -0
- package/dist/module/query/domain/chain/itemChain.model.d.ts +19 -0
- package/dist/module/query/domain/chain/itemChain.model.js +3 -0
- package/dist/module/query/domain/chain/itemChain.model.js.map +1 -0
- package/dist/module/query/domain/chain/projectChain.model.d.ts +28 -0
- package/dist/module/query/domain/chain/projectChain.model.js +3 -0
- package/dist/module/query/domain/chain/projectChain.model.js.map +1 -0
- package/dist/module/query/domain/filter/booleanExpr.model.d.ts +13 -0
- package/dist/module/query/domain/filter/booleanExpr.model.js +3 -0
- package/dist/module/query/domain/filter/booleanExpr.model.js.map +1 -0
- package/dist/module/query/domain/filter/castable.model.d.ts +17 -0
- package/dist/module/query/domain/filter/castable.model.js +3 -0
- package/dist/module/query/domain/filter/castable.model.js.map +1 -0
- package/dist/module/query/domain/filter/comparable.model.d.ts +32 -0
- package/dist/module/query/domain/filter/comparable.model.js +3 -0
- package/dist/module/query/domain/filter/comparable.model.js.map +1 -0
- package/dist/module/query/domain/filter/dateExpr.model.d.ts +35 -0
- package/dist/module/query/domain/filter/dateExpr.model.js +3 -0
- package/dist/module/query/domain/filter/dateExpr.model.js.map +1 -0
- package/dist/module/query/domain/filter/node.model.d.ts +21 -0
- package/dist/module/query/domain/filter/node.model.js +10 -0
- package/dist/module/query/domain/filter/node.model.js.map +1 -0
- package/dist/module/query/domain/filter/numberExpr.model.d.ts +22 -0
- package/dist/module/query/domain/filter/numberExpr.model.js +3 -0
- package/dist/module/query/domain/filter/numberExpr.model.js.map +1 -0
- package/dist/module/query/domain/filter/operators.model.d.ts +25 -0
- package/dist/module/query/domain/filter/operators.model.js +3 -0
- package/dist/module/query/domain/filter/operators.model.js.map +1 -0
- package/dist/module/query/domain/filter/scope.model.d.ts +94 -0
- package/dist/module/query/domain/filter/scope.model.js +3 -0
- package/dist/module/query/domain/filter/scope.model.js.map +1 -0
- package/dist/module/query/domain/filter/stringExpr.model.d.ts +33 -0
- package/dist/module/query/domain/filter/stringExpr.model.js +3 -0
- package/dist/module/query/domain/filter/stringExpr.model.js.map +1 -0
- package/dist/module/query/domain/queryState.model.d.ts +37 -0
- package/dist/module/query/domain/queryState.model.js +3 -0
- package/dist/module/query/domain/queryState.model.js.map +1 -0
- package/dist/module/query/exception/invalidFilter.exception.d.ts +4 -0
- package/dist/module/query/exception/invalidFilter.exception.js +21 -0
- package/dist/module/query/exception/invalidFilter.exception.js.map +1 -0
- package/dist/module/query/exception/reservedName.exception.d.ts +5 -0
- package/dist/module/query/exception/reservedName.exception.js +27 -0
- package/dist/module/query/exception/reservedName.exception.js.map +1 -0
- package/dist/module/query/service/emit/emit.facade.d.ts +8 -0
- package/dist/module/query/service/emit/emit.facade.js +31 -0
- package/dist/module/query/service/emit/emit.facade.js.map +1 -0
- package/dist/module/query/service/filter/filter.facade.d.ts +22 -0
- package/dist/module/query/service/filter/filter.facade.js +79 -0
- package/dist/module/query/service/filter/filter.facade.js.map +1 -0
- package/dist/module/query/service/queryChain.service.d.ts +53 -0
- package/dist/module/query/service/queryChain.service.js +97 -0
- package/dist/module/query/service/queryChain.service.js.map +1 -0
- package/dist/module/query/util/emit/queryParts.util.d.ts +16 -0
- package/dist/module/query/util/emit/queryParts.util.js +56 -0
- package/dist/module/query/util/emit/queryParts.util.js.map +1 -0
- package/dist/module/query/util/filter/filterFunction.util.d.ts +9 -0
- package/dist/module/query/util/filter/filterFunction.util.js +111 -0
- package/dist/module/query/util/filter/filterFunction.util.js.map +1 -0
- package/dist/module/query/util/filter/literal.util.d.ts +23 -0
- package/dist/module/query/util/filter/literal.util.js +81 -0
- package/dist/module/query/util/filter/literal.util.js.map +1 -0
- package/dist/module/query/util/filter/predicate.util.d.ts +21 -0
- package/dist/module/query/util/filter/predicate.util.js +35 -0
- package/dist/module/query/util/filter/predicate.util.js.map +1 -0
- package/dist/module/query/util/filter/scopeProxy.util.d.ts +22 -0
- package/dist/module/query/util/filter/scopeProxy.util.js +93 -0
- package/dist/module/query/util/filter/scopeProxy.util.js.map +1 -0
- package/package.json +65 -4
package/README.md
CHANGED
|
@@ -1,3 +1,167 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ballistix.digital/odata-query-builder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A typed, fluent builder for the OData v4 query strings that
|
|
4
|
+
`@ballistix.digital/typeorm-extensions` serves.
|
|
5
|
+
|
|
6
|
+
It builds a **query string**. It does not fetch, and it does not know about the
|
|
7
|
+
`_embedded` / `_page` response envelope. It has **zero runtime dependencies**.
|
|
8
|
+
|
|
9
|
+
Full documentation lives in [`docs/`](docs/README.md) — the developer entry
|
|
10
|
+
point, with a module map and one page per pipeline stage.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { odataQuery, ProjectStatusEnum } from '@ballistix.digital/odata-query-builder';
|
|
14
|
+
|
|
15
|
+
const query = odataQuery<ProjectDto>()
|
|
16
|
+
.filter((v, o) =>
|
|
17
|
+
o.and(
|
|
18
|
+
v.archived.isFalse(),
|
|
19
|
+
v.status.in([ProjectStatusEnum.DRAFT, ProjectStatusEnum.ACTIVE]),
|
|
20
|
+
v.children.any((a) => a.priority.ge(2)),
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
.select('id', 'name', 'status')
|
|
24
|
+
.expand.parent((p) => p.select('name').expand.owner((u) => u.select('fullName')))
|
|
25
|
+
.orderBy('name', 'desc')
|
|
26
|
+
.top(25)
|
|
27
|
+
.count()
|
|
28
|
+
.toQueryString();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## What makes it typed
|
|
32
|
+
|
|
33
|
+
The scope a `filter()` callback receives is derived from the DTO type alone:
|
|
34
|
+
|
|
35
|
+
- an own scalar column becomes an expression typed by the column's own type, so
|
|
36
|
+
an enum column takes enum members and a number column refuses a string;
|
|
37
|
+
- a `_embedded` to-one relation becomes a nested scope, emitted with `/`;
|
|
38
|
+
- a `_embedded` to-many relation becomes a lambda namespace, so `any` and `all`
|
|
39
|
+
are the only things reachable on it — which is what the server accepts;
|
|
40
|
+
- a primitive array column (`text[]`) becomes a lambda namespace over the
|
|
41
|
+
element.
|
|
42
|
+
|
|
43
|
+
`expand.<relation>` lists exactly the relations `_embedded` declares. There is
|
|
44
|
+
no code generation, no `$metadata` document, and no configuration: the DTO
|
|
45
|
+
convention is `_embedded`, and the library assumes it.
|
|
46
|
+
|
|
47
|
+
## A property read is never a method
|
|
48
|
+
|
|
49
|
+
`v.year` is ambiguous at runtime: the `year()` function, or a column called
|
|
50
|
+
`year`. Reading a property therefore never resolves a function. Read further
|
|
51
|
+
from it and it was a path segment; call it and it was a function. Both roles
|
|
52
|
+
work in one expression:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
v.year.eq(v.date.asDate().year());
|
|
56
|
+
// year eq year(date)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## The surface
|
|
60
|
+
|
|
61
|
+
| Verb | Emits |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `filter(cb)` | `$filter`; repeated calls AND together |
|
|
64
|
+
| `search(term, cols)` | `$search` + the mandatory `$searchColumns` |
|
|
65
|
+
| `select(...)` | `$select`; the own columns plus embedded leaf paths; omit it and nothing is narrowed, at any depth |
|
|
66
|
+
| `expand.<rel>(cb?)` | `$expand`, with nested `$filter`/`$select`/`$expand` |
|
|
67
|
+
| `orderBy(key, direction?)` | `$orderby`, ascending by default; `key` may be a to-one relation path or an embedded leaf path |
|
|
68
|
+
| `top` / `skip` / `count` | `$top` / `$skip` / `$count=true` |
|
|
69
|
+
| `toQueryString()` | the whole string, already URL-safe |
|
|
70
|
+
| `toParams()` | the same options as a map, escaping undone |
|
|
71
|
+
|
|
72
|
+
`odataItem<T>()` is the single-row variant: `$select` and `$expand` only.
|
|
73
|
+
|
|
74
|
+
An embedded object column (a DTO property that is a plain object) reads as a
|
|
75
|
+
namespace of columns, so `v.address.geo.lat` filters, selects and sorts as
|
|
76
|
+
`address/geo/lat`.
|
|
77
|
+
|
|
78
|
+
Inside a filter callback the second argument carries the combinators —
|
|
79
|
+
`o.and`, `o.or`, `o.not`, `o.now`, `o.maxdatetime`, `o.mindatetime`, `o.raw` —
|
|
80
|
+
and the same functions are importable. A single predicate needs no combinator.
|
|
81
|
+
|
|
82
|
+
`isNull()` and `isNotNull()` are how you compare against null; `eq(null)` is a
|
|
83
|
+
compile error, because "no filter" and "is null" must not look alike.
|
|
84
|
+
|
|
85
|
+
## Filtered expansions
|
|
86
|
+
|
|
87
|
+
From `@ballistix.digital/typeorm-extensions` 3.8.0 the server accepts a nested
|
|
88
|
+
`$filter`, so the nested chain has `filter()`:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
odataQuery<ProjectDto>()
|
|
92
|
+
.expand.children((c) => c.filter((x) => x.priority.ge(2023)).select('priority'));
|
|
93
|
+
// $expand=children($filter=priority ge 2023;$select=priority)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The filter narrows the EXPANSION only. Every root row still comes back: a row
|
|
97
|
+
whose collection matched nothing arrives with an empty collection, a to-one
|
|
98
|
+
expansion that fails the filter arrives as null, and `$count` and the paging
|
|
99
|
+
window are unchanged.
|
|
100
|
+
|
|
101
|
+
`$orderby`, `$top`, `$skip`, `$count` and `$levels` inside `$expand` are still
|
|
102
|
+
rejected by the server, so they are still absent from the nested chain.
|
|
103
|
+
|
|
104
|
+
## Escaping
|
|
105
|
+
|
|
106
|
+
`toQueryString()` is already URL-safe. **Do not** run `encodeURIComponent` over
|
|
107
|
+
it — that turns `%25` into `%2525`. Six rules, each measured against the
|
|
108
|
+
server's own parser:
|
|
109
|
+
|
|
110
|
+
| In a literal | Emitted as | Why |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `'` | `''` | the OData string escape |
|
|
113
|
+
| `%` | `%25` | the query string is percent-decoded before parsing |
|
|
114
|
+
| `+` | `%2B` | a bare `+` is read as whitespace |
|
|
115
|
+
| `&` | `%26` | a bare `&` starts another query option |
|
|
116
|
+
| `#` | `%23` | a bare `#` starts the URL fragment |
|
|
117
|
+
| space | `%20` | only inside literals; the spaces between tokens stay raw |
|
|
118
|
+
|
|
119
|
+
Use `toParams()` when the caller has its own serialiser (`URLSearchParams`,
|
|
120
|
+
ky): it hands back the raw values so that serialiser's own encoding is the only
|
|
121
|
+
one applied.
|
|
122
|
+
|
|
123
|
+
## Tests
|
|
124
|
+
|
|
125
|
+
The suite is split into five Jest projects, each runnable on its own.
|
|
126
|
+
|
|
127
|
+
| Command | Needs Postgres | What it covers |
|
|
128
|
+
| --- | --- | --- |
|
|
129
|
+
| `npm run test:unit` | no | Golden strings for the whole emission surface, the combination matrix, `toParams()` equivalence, every runtime guard, and a cross-check that feeds each emitted string through the real server-side parser |
|
|
130
|
+
| `npm run test:types` | no | The compile gate: every `.ok` fixture compiles clean and every `// @errorline TSxxxx` marker in an `.err` fixture errors with that code, on TypeScript 5.9, 6.0 and 7.0 |
|
|
131
|
+
| `npm run test:packaging` | no | `npm pack --dry-run`: only `dist/**` ships |
|
|
132
|
+
| `npm run test:integration` | yes | The emitted strings executed against real Postgres through the published `typeorm-extensions`, asserting rows |
|
|
133
|
+
| `npm run test:e2e` | yes | The same strings sent to a real NestJS app, asserting the response envelope and the 400 body for each rejected construct |
|
|
134
|
+
| `npm run test:coverage` | no | `unit`, with the coverage gate |
|
|
135
|
+
| `npm test` | yes | `npm run build`, then all five projects |
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm run db:up # postgres:17-alpine on port 5557
|
|
139
|
+
npm test
|
|
140
|
+
npm run db:down
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The database-backed tiers skip themselves with a clear message when the
|
|
144
|
+
sidecar is down. See [Testing](docs/testing.md) for the shared test setup,
|
|
145
|
+
the fixtures, the seed data, and how to add a test to each tier.
|
|
146
|
+
|
|
147
|
+
## Layout
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
src/index.ts the public API: odataQuery, odataItem, and every exported type
|
|
151
|
+
src/module/common/ what two or more areas share; it imports nothing
|
|
152
|
+
src/module/common/domain/ ExceptionCode, ExceptionDetail, Scalar and Unwrap
|
|
153
|
+
src/module/common/exception/ QueryBuilderException
|
|
154
|
+
src/module/common/util/ escape.util — the one literal-escaping mechanism
|
|
155
|
+
src/module/query/ the builder: three stages, QueryChainService, shared state, the two error classes
|
|
156
|
+
src/module/query/domain/ QueryState, and one folder per stage: chain/, filter/
|
|
157
|
+
src/module/query/exception/ InvalidFilterError, ReservedNameError
|
|
158
|
+
src/module/query/service/ queryChain.service.ts (QueryChainService) and one facade per stage: filter/, emit/
|
|
159
|
+
src/module/query/util/ pure-function folders for two stages: filter/, emit/
|
|
160
|
+
test/unit/ no database
|
|
161
|
+
test/types/ the compile gate, three TypeScript versions
|
|
162
|
+
test/integration/ executes queries against seeded Postgres
|
|
163
|
+
test/e2e/ a real NestJS app, the response envelope, the rejected constructs
|
|
164
|
+
test/packaging/ tarball guard
|
|
165
|
+
test/fixtures/ DTO twins, entities, the seed data
|
|
166
|
+
test/support/ shared test setup: datasource, app, env
|
|
167
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one entry point of the package: the complete public API.
|
|
3
|
+
*
|
|
4
|
+
* Imports and re-exports only. Every name below is public — it keeps its name
|
|
5
|
+
* and signature across minor versions. A construct the server answers with a
|
|
6
|
+
* 400 is deliberately absent, so it is a compile error rather than a runtime
|
|
7
|
+
* failure.
|
|
8
|
+
*/
|
|
9
|
+
import { QueryBuilderExceptionCode } from './module/common/domain/exceptionCode.model';
|
|
10
|
+
import type { ExceptionDetail } from './module/common/domain/exceptionDetail.model';
|
|
11
|
+
import type { Scalar, Unwrap } from './module/common/domain/scalar.model';
|
|
12
|
+
import { QueryBuilderException } from './module/common/exception/queryBuilder.exception';
|
|
13
|
+
import type { Chain, QueryParams } from './module/query/domain/chain/chain.model';
|
|
14
|
+
import type { EmbeddedPathKeys, ExpandKey, ExpandNamespace, FieldKey, OrderKey } from './module/query/domain/chain/expandNamespace.model';
|
|
15
|
+
import type { ItemChain } from './module/query/domain/chain/itemChain.model';
|
|
16
|
+
import type { ProjectChain } from './module/query/domain/chain/projectChain.model';
|
|
17
|
+
import type { BooleanExpr } from './module/query/domain/filter/booleanExpr.model';
|
|
18
|
+
import type { Castable, CastResult, EdmTarget } from './module/query/domain/filter/castable.model';
|
|
19
|
+
import type { Comparable } from './module/query/domain/filter/comparable.model';
|
|
20
|
+
import type { DateExpr, DateInput } from './module/query/domain/filter/dateExpr.model';
|
|
21
|
+
import type { Node, Predicate } from './module/query/domain/filter/node.model';
|
|
22
|
+
import type { NumberExpr } from './module/query/domain/filter/numberExpr.model';
|
|
23
|
+
import type { Operators } from './module/query/domain/filter/operators.model';
|
|
24
|
+
import type { EmbeddedObjectKeys, EmbeddedOf, ExprFor, LambdaNamespace, OwnKeys, PrimitiveArrayKeys, PrimitiveLambdaNamespace, Relation, ScalarFieldKeys, Scope, ToManyKeys, ToOneKeys } from './module/query/domain/filter/scope.model';
|
|
25
|
+
import type { StringExpr } from './module/query/domain/filter/stringExpr.model';
|
|
26
|
+
import { InvalidFilterError } from './module/query/exception/invalidFilter.exception';
|
|
27
|
+
import { ReservedNameError } from './module/query/exception/reservedName.exception';
|
|
28
|
+
import { and, maxdatetime, mindatetime, not, now, or, raw } from './module/query/service/filter/filter.facade';
|
|
29
|
+
import { odataItem, odataQuery } from './module/query/service/queryChain.service';
|
|
30
|
+
export { InvalidFilterError, QueryBuilderException, QueryBuilderExceptionCode, ReservedNameError, and, maxdatetime, mindatetime, not, now, odataItem, odataQuery, or, raw, };
|
|
31
|
+
export type { BooleanExpr, CastResult, Castable, Chain, Comparable, DateExpr, DateInput, EdmTarget, EmbeddedObjectKeys, EmbeddedOf, EmbeddedPathKeys, ExceptionDetail, ExpandKey, ExpandNamespace, ExprFor, FieldKey, ItemChain, LambdaNamespace, Node, NumberExpr, Operators, OrderKey, OwnKeys, Predicate, PrimitiveArrayKeys, PrimitiveLambdaNamespace, ProjectChain, QueryParams, Relation, Scalar, ScalarFieldKeys, Scope, StringExpr, ToManyKeys, ToOneKeys, Unwrap, };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The one entry point of the package: the complete public API.
|
|
4
|
+
*
|
|
5
|
+
* Imports and re-exports only. Every name below is public — it keeps its name
|
|
6
|
+
* and signature across minor versions. A construct the server answers with a
|
|
7
|
+
* 400 is deliberately absent, so it is a compile error rather than a runtime
|
|
8
|
+
* failure.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.raw = exports.or = exports.odataQuery = exports.odataItem = exports.now = exports.not = exports.mindatetime = exports.maxdatetime = exports.and = exports.ReservedNameError = exports.QueryBuilderExceptionCode = exports.QueryBuilderException = exports.InvalidFilterError = void 0;
|
|
12
|
+
const exceptionCode_model_1 = require("./module/common/domain/exceptionCode.model");
|
|
13
|
+
Object.defineProperty(exports, "QueryBuilderExceptionCode", { enumerable: true, get: function () { return exceptionCode_model_1.QueryBuilderExceptionCode; } });
|
|
14
|
+
const queryBuilder_exception_1 = require("./module/common/exception/queryBuilder.exception");
|
|
15
|
+
Object.defineProperty(exports, "QueryBuilderException", { enumerable: true, get: function () { return queryBuilder_exception_1.QueryBuilderException; } });
|
|
16
|
+
const invalidFilter_exception_1 = require("./module/query/exception/invalidFilter.exception");
|
|
17
|
+
Object.defineProperty(exports, "InvalidFilterError", { enumerable: true, get: function () { return invalidFilter_exception_1.InvalidFilterError; } });
|
|
18
|
+
const reservedName_exception_1 = require("./module/query/exception/reservedName.exception");
|
|
19
|
+
Object.defineProperty(exports, "ReservedNameError", { enumerable: true, get: function () { return reservedName_exception_1.ReservedNameError; } });
|
|
20
|
+
const filter_facade_1 = require("./module/query/service/filter/filter.facade");
|
|
21
|
+
Object.defineProperty(exports, "and", { enumerable: true, get: function () { return filter_facade_1.and; } });
|
|
22
|
+
Object.defineProperty(exports, "maxdatetime", { enumerable: true, get: function () { return filter_facade_1.maxdatetime; } });
|
|
23
|
+
Object.defineProperty(exports, "mindatetime", { enumerable: true, get: function () { return filter_facade_1.mindatetime; } });
|
|
24
|
+
Object.defineProperty(exports, "not", { enumerable: true, get: function () { return filter_facade_1.not; } });
|
|
25
|
+
Object.defineProperty(exports, "now", { enumerable: true, get: function () { return filter_facade_1.now; } });
|
|
26
|
+
Object.defineProperty(exports, "or", { enumerable: true, get: function () { return filter_facade_1.or; } });
|
|
27
|
+
Object.defineProperty(exports, "raw", { enumerable: true, get: function () { return filter_facade_1.raw; } });
|
|
28
|
+
const queryChain_service_1 = require("./module/query/service/queryChain.service");
|
|
29
|
+
Object.defineProperty(exports, "odataItem", { enumerable: true, get: function () { return queryChain_service_1.odataItem; } });
|
|
30
|
+
Object.defineProperty(exports, "odataQuery", { enumerable: true, get: function () { return queryChain_service_1.odataQuery; } });
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,oFAAuF;AAiEtF,0GAjEQ,+CAAyB,OAiER;AA9D1B,6FAAyF;AA6DxF,sGA7DQ,8CAAqB,OA6DR;AAlBtB,8FAAsF;AAiBrF,mGAjBQ,4CAAkB,OAiBR;AAhBnB,4FAAoF;AAmBnF,kGAnBQ,0CAAiB,OAmBR;AAlBlB,+EAQqD;AAWpD,oFAlBA,mBAAG,OAkBA;AACH,4FAlBA,2BAAW,OAkBA;AACX,4FAlBA,2BAAW,OAkBA;AACX,oFAlBA,mBAAG,OAkBA;AACH,oFAlBA,mBAAG,OAkBA;AAGH,mFApBA,kBAAE,OAoBA;AACF,oFApBA,mBAAG,OAoBA;AAlBJ,kFAGmD;AAYlD,0FAdA,8BAAS,OAcA;AACT,2FAdA,+BAAU,OAcA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stable machine-readable codes this library reports.
|
|
3
|
+
*
|
|
4
|
+
* A code never changes meaning: a caller may branch on it, and a value here is
|
|
5
|
+
* part of the public API. `QueryBuilderExceptionCode` is namespaced away from
|
|
6
|
+
* the server's own `ExceptionCode` on purpose — the two enums are unrelated
|
|
7
|
+
* even where a name looks similar.
|
|
8
|
+
*/
|
|
9
|
+
export declare enum QueryBuilderExceptionCode {
|
|
10
|
+
RESERVED_NAME = "RESERVED_NAME",
|
|
11
|
+
INVALID_FILTER = "INVALID_FILTER"
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The stable machine-readable codes this library reports.
|
|
4
|
+
*
|
|
5
|
+
* A code never changes meaning: a caller may branch on it, and a value here is
|
|
6
|
+
* part of the public API. `QueryBuilderExceptionCode` is namespaced away from
|
|
7
|
+
* the server's own `ExceptionCode` on purpose — the two enums are unrelated
|
|
8
|
+
* even where a name looks similar.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.QueryBuilderExceptionCode = void 0;
|
|
12
|
+
var QueryBuilderExceptionCode;
|
|
13
|
+
(function (QueryBuilderExceptionCode) {
|
|
14
|
+
QueryBuilderExceptionCode["RESERVED_NAME"] = "RESERVED_NAME";
|
|
15
|
+
QueryBuilderExceptionCode["INVALID_FILTER"] = "INVALID_FILTER";
|
|
16
|
+
})(QueryBuilderExceptionCode || (exports.QueryBuilderExceptionCode = QueryBuilderExceptionCode = {}));
|
|
17
|
+
//# sourceMappingURL=exceptionCode.model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exceptionCode.model.js","sourceRoot":"","sources":["../../../../src/module/common/domain/exceptionCode.model.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,IAAY,yBAGX;AAHD,WAAY,yBAAyB;IACpC,4DAA+B,CAAA;IAC/B,8DAAiC,CAAA;AAClC,CAAC,EAHW,yBAAyB,yCAAzB,yBAAyB,QAGpC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The machine-readable payload behind an error message. One entry of
|
|
3
|
+
* `QueryBuilderException.errors` is one of these, and `errors[0].reason` is
|
|
4
|
+
* the exception message.
|
|
5
|
+
*
|
|
6
|
+
* Plain serializable data: an adapter can put the array straight into an HTTP
|
|
7
|
+
* response body.
|
|
8
|
+
*/
|
|
9
|
+
export interface ExceptionDetail {
|
|
10
|
+
/** What went wrong, in one sentence, for a human reading a log. */
|
|
11
|
+
reason: string;
|
|
12
|
+
/** The name, path or value the reason is about, where there is one. */
|
|
13
|
+
subject?: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The machine-readable payload behind an error message. One entry of
|
|
4
|
+
* `QueryBuilderException.errors` is one of these, and `errors[0].reason` is
|
|
5
|
+
* the exception message.
|
|
6
|
+
*
|
|
7
|
+
* Plain serializable data: an adapter can put the array straight into an HTTP
|
|
8
|
+
* response body.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
//# sourceMappingURL=exceptionDetail.model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exceptionDetail.model.js","sourceRoot":"","sources":["../../../../src/module/common/domain/exceptionDetail.model.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two type primitives every other module reads a DTO with.
|
|
3
|
+
*
|
|
4
|
+
* They live in `common` because both the filter module (which turns a DTO into
|
|
5
|
+
* a filter scope) and the query module (which turns a DTO into a select/expand
|
|
6
|
+
* namespace) need the same answer to "is this a column or a relation".
|
|
7
|
+
*
|
|
8
|
+
* `Scalar` admits only the five shapes the dialect can compare, sort or
|
|
9
|
+
* select directly; an object or array field never matches it, so those two
|
|
10
|
+
* modules route it through `Unwrap` and the embedding types instead.
|
|
11
|
+
*/
|
|
12
|
+
/** A DTO property this dialect can compare, sort or select. */
|
|
13
|
+
export type Scalar = string | number | boolean | bigint | Date;
|
|
14
|
+
/** The element type of an array property; anything else is returned as-is. */
|
|
15
|
+
export type Unwrap<T> = T extends readonly (infer U)[] ? U : T;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The two type primitives every other module reads a DTO with.
|
|
4
|
+
*
|
|
5
|
+
* They live in `common` because both the filter module (which turns a DTO into
|
|
6
|
+
* a filter scope) and the query module (which turns a DTO into a select/expand
|
|
7
|
+
* namespace) need the same answer to "is this a column or a relation".
|
|
8
|
+
*
|
|
9
|
+
* `Scalar` admits only the five shapes the dialect can compare, sort or
|
|
10
|
+
* select directly; an object or array field never matches it, so those two
|
|
11
|
+
* modules route it through `Unwrap` and the embedding types instead.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
//# sourceMappingURL=scalar.model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scalar.model.js","sourceRoot":"","sources":["../../../../src/module/common/domain/scalar.model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The base of every error this library throws.
|
|
3
|
+
*
|
|
4
|
+
* It carries the same `{status, code, errors}` SHAPE
|
|
5
|
+
* `@ballistix.digital/typeorm-extensions` puts on its own errors — an
|
|
6
|
+
* HTTP-ish `status`, a stable machine-readable `code`, and a list of details —
|
|
7
|
+
* so a caller's existing handling of the server's errors (reading those three
|
|
8
|
+
* fields) also handles the builder's. It is field-compatible, not a shared
|
|
9
|
+
* type: that package exports its own, unrelated `LibraryException` and
|
|
10
|
+
* `ExceptionCode`, so this class is never an `instanceof` that one.
|
|
11
|
+
*
|
|
12
|
+
* The envelope lives in `common` because every area raises it. Nothing here
|
|
13
|
+
* depends on a framework, and nothing here depends on another area.
|
|
14
|
+
*/
|
|
15
|
+
import type { QueryBuilderExceptionCode } from '../domain/exceptionCode.model';
|
|
16
|
+
import type { ExceptionDetail } from '../domain/exceptionDetail.model';
|
|
17
|
+
export declare abstract class QueryBuilderException extends Error {
|
|
18
|
+
/** Suggested HTTP status for adapters. Always 400. */
|
|
19
|
+
readonly status: number;
|
|
20
|
+
/** The same value, under the name Nest and Axios both use. */
|
|
21
|
+
readonly statusCode: number;
|
|
22
|
+
/** Stable machine-readable code, e.g. "RESERVED_NAME". */
|
|
23
|
+
readonly code: QueryBuilderExceptionCode;
|
|
24
|
+
/** Every detail behind this error; `errors[0]` fills the message. */
|
|
25
|
+
readonly errors: ExceptionDetail[];
|
|
26
|
+
protected constructor(code: QueryBuilderExceptionCode, message: string, errors: ExceptionDetail[]);
|
|
27
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The base of every error this library throws.
|
|
4
|
+
*
|
|
5
|
+
* It carries the same `{status, code, errors}` SHAPE
|
|
6
|
+
* `@ballistix.digital/typeorm-extensions` puts on its own errors — an
|
|
7
|
+
* HTTP-ish `status`, a stable machine-readable `code`, and a list of details —
|
|
8
|
+
* so a caller's existing handling of the server's errors (reading those three
|
|
9
|
+
* fields) also handles the builder's. It is field-compatible, not a shared
|
|
10
|
+
* type: that package exports its own, unrelated `LibraryException` and
|
|
11
|
+
* `ExceptionCode`, so this class is never an `instanceof` that one.
|
|
12
|
+
*
|
|
13
|
+
* The envelope lives in `common` because every area raises it. Nothing here
|
|
14
|
+
* depends on a framework, and nothing here depends on another area.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.QueryBuilderException = void 0;
|
|
18
|
+
class QueryBuilderException extends Error {
|
|
19
|
+
/** Suggested HTTP status for adapters. Always 400. */
|
|
20
|
+
status;
|
|
21
|
+
/** The same value, under the name Nest and Axios both use. */
|
|
22
|
+
statusCode;
|
|
23
|
+
/** Stable machine-readable code, e.g. "RESERVED_NAME". */
|
|
24
|
+
code;
|
|
25
|
+
/** Every detail behind this error; `errors[0]` fills the message. */
|
|
26
|
+
errors;
|
|
27
|
+
constructor(code, message, errors) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = new.target.name;
|
|
30
|
+
this.status = 400;
|
|
31
|
+
this.statusCode = 400;
|
|
32
|
+
this.code = code;
|
|
33
|
+
this.errors = errors;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.QueryBuilderException = QueryBuilderException;
|
|
37
|
+
//# sourceMappingURL=queryBuilder.exception.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryBuilder.exception.js","sourceRoot":"","sources":["../../../../src/module/common/exception/queryBuilder.exception.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;AAKH,MAAsB,qBAAsB,SAAQ,KAAK;IACxD,sDAAsD;IAC7C,MAAM,CAAS;IACxB,8DAA8D;IACrD,UAAU,CAAS;IAC5B,0DAA0D;IACjD,IAAI,CAA4B;IACzC,qEAAqE;IAC5D,MAAM,CAAoB;IAEnC,YACC,IAA+B,EAC/B,OAAe,EACf,MAAyB;QAEzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;QAClB,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;CACD;AAtBD,sDAsBC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal escaping: the one mechanism that makes a value safe inside a query
|
|
3
|
+
* string. It sits in `common` because two areas need the same answer — filter
|
|
4
|
+
* quotes literals and search terms with it, and emit undoes its escapes for
|
|
5
|
+
* `toParams()`.
|
|
6
|
+
*
|
|
7
|
+
* These seven rules are EMITTER GUARANTEES, each one measured by feeding the
|
|
8
|
+
* emitted string back through the server's own parser and reading the literal
|
|
9
|
+
* that came out:
|
|
10
|
+
*
|
|
11
|
+
* ' -> '' OData string escape. `name eq 'It''s'` parses to `It's`.
|
|
12
|
+
* % -> %25 the query string is percent-DECODED before parsing, so a
|
|
13
|
+
* literal `a%20b` would arrive as `a b` unless the % is
|
|
14
|
+
* escaped first.
|
|
15
|
+
* + -> %2B the grammar reads a bare `+` as whitespace: `name eq 'a+b'`
|
|
16
|
+
* parses to the value `a b`. This is the rule that silently
|
|
17
|
+
* corrupts data if it is missing.
|
|
18
|
+
* & -> %26 a bare `&` splits the query string into another option, so
|
|
19
|
+
* `name eq 'a&b'` is a 400, not a wrong value.
|
|
20
|
+
* # -> %23 a bare `#` starts the URL fragment.
|
|
21
|
+
* ' ' -> %20 only inside literals; the spaces BETWEEN tokens stay raw so
|
|
22
|
+
* the query string is readable.
|
|
23
|
+
* a C0 control code point (below 0x20) or DEL (0x7F) -> %XX
|
|
24
|
+
* a full WHATWG URL parse (the trip a caller's own HTTP
|
|
25
|
+
* client makes when it builds a request URL from the
|
|
26
|
+
* emitted string) deletes a raw tab, LF or CR from the query
|
|
27
|
+
* string outright, before any percent-decoding happens. A
|
|
28
|
+
* literal carrying one of those bytes raw would arrive at
|
|
29
|
+
* the server silently shortened.
|
|
30
|
+
*
|
|
31
|
+
* `%` is replaced first, otherwise the escapes introduced afterwards would be
|
|
32
|
+
* escaped again. The control-code pass runs after the other percent-escapes
|
|
33
|
+
* and walks the string by code point rather than by regex, so it never has to
|
|
34
|
+
* name an unprintable character inside a pattern.
|
|
35
|
+
*
|
|
36
|
+
* The output of `toQueryString()` is therefore already URL-safe. Do NOT run
|
|
37
|
+
* `encodeURIComponent` over it — that turns every `%25` into `%2525`.
|
|
38
|
+
*/
|
|
39
|
+
/** Every rule except the quote doubling, for values that are not quoted (`$search`). */
|
|
40
|
+
export declare const escapeBareValue: (value: string) => string;
|
|
41
|
+
/**
|
|
42
|
+
* Every rule: `escapeBareValue()`, then the quote doubling. No other rule adds
|
|
43
|
+
* or removes a quote, so the order does not change the result.
|
|
44
|
+
*/
|
|
45
|
+
export declare const escapeLiteral: (value: string) => string;
|
|
46
|
+
export declare const quote: (value: string) => string;
|
|
47
|
+
/**
|
|
48
|
+
* Undo the percent-escapes, for `toParams()`.
|
|
49
|
+
*
|
|
50
|
+
* A caller that hands the parts to `URLSearchParams` or ky runs its own
|
|
51
|
+
* encoder over each value, so the OData-safety escaping has to come off first
|
|
52
|
+
* or every `%25` ships as `%2525`. The OData quote doubling is NOT undone —
|
|
53
|
+
* that one belongs to the expression grammar, not to the transport.
|
|
54
|
+
*/
|
|
55
|
+
export declare const unescapeParamValue: (value: string) => string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Literal escaping: the one mechanism that makes a value safe inside a query
|
|
4
|
+
* string. It sits in `common` because two areas need the same answer — filter
|
|
5
|
+
* quotes literals and search terms with it, and emit undoes its escapes for
|
|
6
|
+
* `toParams()`.
|
|
7
|
+
*
|
|
8
|
+
* These seven rules are EMITTER GUARANTEES, each one measured by feeding the
|
|
9
|
+
* emitted string back through the server's own parser and reading the literal
|
|
10
|
+
* that came out:
|
|
11
|
+
*
|
|
12
|
+
* ' -> '' OData string escape. `name eq 'It''s'` parses to `It's`.
|
|
13
|
+
* % -> %25 the query string is percent-DECODED before parsing, so a
|
|
14
|
+
* literal `a%20b` would arrive as `a b` unless the % is
|
|
15
|
+
* escaped first.
|
|
16
|
+
* + -> %2B the grammar reads a bare `+` as whitespace: `name eq 'a+b'`
|
|
17
|
+
* parses to the value `a b`. This is the rule that silently
|
|
18
|
+
* corrupts data if it is missing.
|
|
19
|
+
* & -> %26 a bare `&` splits the query string into another option, so
|
|
20
|
+
* `name eq 'a&b'` is a 400, not a wrong value.
|
|
21
|
+
* # -> %23 a bare `#` starts the URL fragment.
|
|
22
|
+
* ' ' -> %20 only inside literals; the spaces BETWEEN tokens stay raw so
|
|
23
|
+
* the query string is readable.
|
|
24
|
+
* a C0 control code point (below 0x20) or DEL (0x7F) -> %XX
|
|
25
|
+
* a full WHATWG URL parse (the trip a caller's own HTTP
|
|
26
|
+
* client makes when it builds a request URL from the
|
|
27
|
+
* emitted string) deletes a raw tab, LF or CR from the query
|
|
28
|
+
* string outright, before any percent-decoding happens. A
|
|
29
|
+
* literal carrying one of those bytes raw would arrive at
|
|
30
|
+
* the server silently shortened.
|
|
31
|
+
*
|
|
32
|
+
* `%` is replaced first, otherwise the escapes introduced afterwards would be
|
|
33
|
+
* escaped again. The control-code pass runs after the other percent-escapes
|
|
34
|
+
* and walks the string by code point rather than by regex, so it never has to
|
|
35
|
+
* name an unprintable character inside a pattern.
|
|
36
|
+
*
|
|
37
|
+
* The output of `toQueryString()` is therefore already URL-safe. Do NOT run
|
|
38
|
+
* `encodeURIComponent` over it — that turns every `%25` into `%2525`.
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.unescapeParamValue = exports.quote = exports.escapeLiteral = exports.escapeBareValue = void 0;
|
|
42
|
+
const encodeControlCharacters = (value) => Array.from(value)
|
|
43
|
+
.map((character) => {
|
|
44
|
+
const code = character.charCodeAt(0);
|
|
45
|
+
// A C0 control code (below 0x20) or DEL (0x7F).
|
|
46
|
+
return code < 0x20 || code === 0x7f
|
|
47
|
+
? `%${code.toString(16).toUpperCase().padStart(2, '0')}`
|
|
48
|
+
: character;
|
|
49
|
+
})
|
|
50
|
+
.join('');
|
|
51
|
+
/** Every rule except the quote doubling, for values that are not quoted (`$search`). */
|
|
52
|
+
const escapeBareValue = (value) => encodeControlCharacters(value
|
|
53
|
+
.replace(/%/g, '%25')
|
|
54
|
+
.replace(/\+/g, '%2B')
|
|
55
|
+
.replace(/&/g, '%26')
|
|
56
|
+
.replace(/#/g, '%23')
|
|
57
|
+
.replace(/ /g, '%20'));
|
|
58
|
+
exports.escapeBareValue = escapeBareValue;
|
|
59
|
+
/**
|
|
60
|
+
* Every rule: `escapeBareValue()`, then the quote doubling. No other rule adds
|
|
61
|
+
* or removes a quote, so the order does not change the result.
|
|
62
|
+
*/
|
|
63
|
+
const escapeLiteral = (value) => (0, exports.escapeBareValue)(value).replace(/'/g, "''");
|
|
64
|
+
exports.escapeLiteral = escapeLiteral;
|
|
65
|
+
const quote = (value) => `'${(0, exports.escapeLiteral)(value)}'`;
|
|
66
|
+
exports.quote = quote;
|
|
67
|
+
/**
|
|
68
|
+
* Undo the percent-escapes, for `toParams()`.
|
|
69
|
+
*
|
|
70
|
+
* A caller that hands the parts to `URLSearchParams` or ky runs its own
|
|
71
|
+
* encoder over each value, so the OData-safety escaping has to come off first
|
|
72
|
+
* or every `%25` ships as `%2525`. The OData quote doubling is NOT undone —
|
|
73
|
+
* that one belongs to the expression grammar, not to the transport.
|
|
74
|
+
*/
|
|
75
|
+
const unescapeParamValue = (value) => value
|
|
76
|
+
.replace(/%(0[0-9A-Fa-f]|1[0-9A-Fa-f]|7[Ff])/g, (_match, hexDigits) => String.fromCharCode(parseInt(hexDigits, 16)))
|
|
77
|
+
.replace(/%2B/g, '+')
|
|
78
|
+
.replace(/%26/g, '&')
|
|
79
|
+
.replace(/%23/g, '#')
|
|
80
|
+
.replace(/%20/g, ' ')
|
|
81
|
+
.replace(/%25/g, '%');
|
|
82
|
+
exports.unescapeParamValue = unescapeParamValue;
|
|
83
|
+
//# sourceMappingURL=escape.util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.util.js","sourceRoot":"","sources":["../../../../src/module/common/util/escape.util.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;;;AAEH,MAAM,uBAAuB,GAAG,CAAC,KAAa,EAAU,EAAE,CACzD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;KACf,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;IAClB,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACrC,gDAAgD;IAChD,OAAO,IAAI,GAAG,IAAI,IAAI,IAAI,KAAK,IAAI;QAClC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;QACxD,CAAC,CAAC,SAAS,CAAC;AACd,CAAC,CAAC;KACD,IAAI,CAAC,EAAE,CAAC,CAAC;AAEZ,wFAAwF;AACjF,MAAM,eAAe,GAAG,CAAC,KAAa,EAAU,EAAE,CACxD,uBAAuB,CACtB,KAAK;KACH,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;KACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;KACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;KACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CACtB,CAAC;AARU,QAAA,eAAe,mBAQzB;AAEH;;;GAGG;AACI,MAAM,aAAa,GAAG,CAAC,KAAa,EAAU,EAAE,CACtD,IAAA,uBAAe,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAD/B,QAAA,aAAa,iBACkB;AAErC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,IAAI,IAAA,qBAAa,EAAC,KAAK,CAAC,GAAG,CAAC;AAA/D,QAAA,KAAK,SAA0D;AAE5E;;;;;;;GAOG;AACI,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAU,EAAE,CAC3D,KAAK;KACH,OAAO,CACP,qCAAqC,EACrC,CAAC,MAAM,EAAE,SAAiB,EAAE,EAAE,CAC7B,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAC7C;KACA,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;KACpB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;KACpB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;KACpB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;KACpB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAXX,QAAA,kBAAkB,sBAWP"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The root chain: every system query option the dialect serves.
|
|
3
|
+
*
|
|
4
|
+
* The builder produces a query STRING. It does not fetch, and it does not know
|
|
5
|
+
* about the `_embedded` / `_page` response envelope.
|
|
6
|
+
*/
|
|
7
|
+
import type { Predicate } from '../filter/node.model';
|
|
8
|
+
import type { Operators } from '../filter/operators.model';
|
|
9
|
+
import type { Scope } from '../filter/scope.model';
|
|
10
|
+
import type { StringExpr } from '../filter/stringExpr.model';
|
|
11
|
+
import type { ExpandNamespace, FieldKey, OrderKey } from './expandNamespace.model';
|
|
12
|
+
/** A flat `{ $option: value }` map, for a caller that serialises its own URL. */
|
|
13
|
+
export type QueryParams = Record<string, string>;
|
|
14
|
+
export interface Chain<T> {
|
|
15
|
+
readonly expand: ExpandNamespace<T, Chain<T>>;
|
|
16
|
+
select(...keys: FieldKey<T>[]): Chain<T>;
|
|
17
|
+
/** Repeated calls AND together. */
|
|
18
|
+
filter(build: (scope: Scope<T>, operators: Operators) => Predicate): Chain<T>;
|
|
19
|
+
/** `$search` + its mandatory `$searchColumns`, as one verb. */
|
|
20
|
+
search(term: string, columns: (scope: Scope<T>) => StringExpr | readonly StringExpr[]): Chain<T>;
|
|
21
|
+
/** Sorts on an own column, an embedded leaf, or a to-one relation path. */
|
|
22
|
+
orderBy(key: OrderKey<T>, direction?: 'asc' | 'desc'): Chain<T>;
|
|
23
|
+
top(count: number): Chain<T>;
|
|
24
|
+
skip(count: number): Chain<T>;
|
|
25
|
+
count(on?: boolean): Chain<T>;
|
|
26
|
+
/** The whole query string, already URL-safe. Never re-encode it. */
|
|
27
|
+
toQueryString(): string;
|
|
28
|
+
/**
|
|
29
|
+
* The same query as separate parts, with the transport escaping undone, for
|
|
30
|
+
* a caller that hands them to `URLSearchParams` or ky and lets that encode.
|
|
31
|
+
*/
|
|
32
|
+
toParams(): QueryParams;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain.model.js","sourceRoot":"","sources":["../../../../../src/module/query/domain/chain/chain.model.ts"],"names":[],"mappings":""}
|