@adonis-agora/filter 0.0.0-beta-20261110131309
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 +417 -0
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/dist/commands/main.d.ts +19 -0
- package/dist/commands/main.d.ts.map +1 -0
- package/dist/commands/main.js +19 -0
- package/dist/commands/main.js.map +1 -0
- package/dist/commands/make_filter.d.ts +18 -0
- package/dist/commands/make_filter.d.ts.map +1 -0
- package/dist/commands/make_filter.js +32 -0
- package/dist/commands/make_filter.js.map +1 -0
- package/dist/commands/make_filter_client.d.ts +35 -0
- package/dist/commands/make_filter_client.d.ts.map +1 -0
- package/dist/commands/make_filter_client.js +85 -0
- package/dist/commands/make_filter_client.js.map +1 -0
- package/dist/configure.d.ts +15 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +20 -0
- package/dist/configure.js.map +1 -0
- package/dist/providers/filter_provider.d.ts +28 -0
- package/dist/providers/filter_provider.d.ts.map +1 -0
- package/dist/providers/filter_provider.js +33 -0
- package/dist/providers/filter_provider.js.map +1 -0
- package/dist/src/aggregate.d.ts +70 -0
- package/dist/src/aggregate.d.ts.map +1 -0
- package/dist/src/aggregate.js +195 -0
- package/dist/src/aggregate.js.map +1 -0
- package/dist/src/aggregate_path.d.ts +25 -0
- package/dist/src/aggregate_path.d.ts.map +1 -0
- package/dist/src/aggregate_path.js +35 -0
- package/dist/src/aggregate_path.js.map +1 -0
- package/dist/src/apply_from_request.d.ts +85 -0
- package/dist/src/apply_from_request.d.ts.map +1 -0
- package/dist/src/apply_from_request.js +164 -0
- package/dist/src/apply_from_request.js.map +1 -0
- package/dist/src/base_filter.d.ts +49 -0
- package/dist/src/base_filter.d.ts.map +1 -0
- package/dist/src/base_filter.js +33 -0
- package/dist/src/base_filter.js.map +1 -0
- package/dist/src/base_model_filter.d.ts +90 -0
- package/dist/src/base_model_filter.d.ts.map +1 -0
- package/dist/src/base_model_filter.js +85 -0
- package/dist/src/base_model_filter.js.map +1 -0
- package/dist/src/coerce_value.d.ts +26 -0
- package/dist/src/coerce_value.d.ts.map +1 -0
- package/dist/src/coerce_value.js +82 -0
- package/dist/src/coerce_value.js.map +1 -0
- package/dist/src/cursor.d.ts +92 -0
- package/dist/src/cursor.d.ts.map +1 -0
- package/dist/src/cursor.js +107 -0
- package/dist/src/cursor.js.map +1 -0
- package/dist/src/custom_filter.d.ts +36 -0
- package/dist/src/custom_filter.d.ts.map +1 -0
- package/dist/src/custom_filter.js +127 -0
- package/dist/src/custom_filter.js.map +1 -0
- package/dist/src/decorator_metadata.d.ts +41 -0
- package/dist/src/decorator_metadata.d.ts.map +1 -0
- package/dist/src/decorator_metadata.js +83 -0
- package/dist/src/decorator_metadata.js.map +1 -0
- package/dist/src/decorators.d.ts +73 -0
- package/dist/src/decorators.d.ts.map +1 -0
- package/dist/src/decorators.js +118 -0
- package/dist/src/decorators.js.map +1 -0
- package/dist/src/escape-like.d.ts +10 -0
- package/dist/src/escape-like.d.ts.map +1 -0
- package/dist/src/escape-like.js +15 -0
- package/dist/src/escape-like.js.map +1 -0
- package/dist/src/field_aliases.d.ts +30 -0
- package/dist/src/field_aliases.d.ts.map +1 -0
- package/dist/src/field_aliases.js +65 -0
- package/dist/src/field_aliases.js.map +1 -0
- package/dist/src/filter_class.d.ts +46 -0
- package/dist/src/filter_class.d.ts.map +1 -0
- package/dist/src/filter_class.js +244 -0
- package/dist/src/filter_class.js.map +1 -0
- package/dist/src/filter_spec.d.ts +231 -0
- package/dist/src/filter_spec.d.ts.map +1 -0
- package/dist/src/filter_spec.js +182 -0
- package/dist/src/filter_spec.js.map +1 -0
- package/dist/src/filterable_mixin.d.ts +49 -0
- package/dist/src/filterable_mixin.d.ts.map +1 -0
- package/dist/src/filterable_mixin.js +47 -0
- package/dist/src/filterable_mixin.js.map +1 -0
- package/dist/src/generate_client.d.ts +107 -0
- package/dist/src/generate_client.d.ts.map +1 -0
- package/dist/src/generate_client.js +164 -0
- package/dist/src/generate_client.js.map +1 -0
- package/dist/src/group_by_count.d.ts +67 -0
- package/dist/src/group_by_count.d.ts.map +1 -0
- package/dist/src/group_by_count.js +71 -0
- package/dist/src/group_by_count.js.map +1 -0
- package/dist/src/in_memory.d.ts +279 -0
- package/dist/src/in_memory.d.ts.map +1 -0
- package/dist/src/in_memory.js +810 -0
- package/dist/src/in_memory.js.map +1 -0
- package/dist/src/index.d.ts +51 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +33 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lucid_adapter.d.ts +311 -0
- package/dist/src/lucid_adapter.d.ts.map +1 -0
- package/dist/src/lucid_adapter.js +469 -0
- package/dist/src/lucid_adapter.js.map +1 -0
- package/dist/src/lucid_macros.d.ts +87 -0
- package/dist/src/lucid_macros.d.ts.map +1 -0
- package/dist/src/lucid_macros.js +67 -0
- package/dist/src/lucid_macros.js.map +1 -0
- package/dist/src/normalizer.d.ts +21 -0
- package/dist/src/normalizer.d.ts.map +1 -0
- package/dist/src/normalizer.js +61 -0
- package/dist/src/normalizer.js.map +1 -0
- package/dist/src/operators.d.ts +37 -0
- package/dist/src/operators.d.ts.map +1 -0
- package/dist/src/operators.js +43 -0
- package/dist/src/operators.js.map +1 -0
- package/dist/src/paginate.d.ts +12 -0
- package/dist/src/paginate.d.ts.map +1 -0
- package/dist/src/paginate.js +88 -0
- package/dist/src/paginate.js.map +1 -0
- package/dist/src/pagination_stash.d.ts +4 -0
- package/dist/src/pagination_stash.d.ts.map +1 -0
- package/dist/src/pagination_stash.js +19 -0
- package/dist/src/pagination_stash.js.map +1 -0
- package/dist/src/parse_request.d.ts +50 -0
- package/dist/src/parse_request.d.ts.map +1 -0
- package/dist/src/parse_request.js +220 -0
- package/dist/src/parse_request.js.map +1 -0
- package/dist/src/runner.d.ts +65 -0
- package/dist/src/runner.d.ts.map +1 -0
- package/dist/src/runner.js +371 -0
- package/dist/src/runner.js.map +1 -0
- package/dist/src/source_resolver.d.ts +18 -0
- package/dist/src/source_resolver.d.ts.map +1 -0
- package/dist/src/source_resolver.js +64 -0
- package/dist/src/source_resolver.js.map +1 -0
- package/dist/src/spatie_parser.d.ts +39 -0
- package/dist/src/spatie_parser.d.ts.map +1 -0
- package/dist/src/spatie_parser.js +134 -0
- package/dist/src/spatie_parser.js.map +1 -0
- package/dist/src/testing.d.ts +73 -0
- package/dist/src/testing.d.ts.map +1 -0
- package/dist/src/testing.js +136 -0
- package/dist/src/testing.js.map +1 -0
- package/dist/src/types.d.ts +226 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/validate-column-filter.d.ts +30 -0
- package/dist/src/validate-column-filter.d.ts.map +1 -0
- package/dist/src/validate-column-filter.js +148 -0
- package/dist/src/validate-column-filter.js.map +1 -0
- package/dist/src/value-shape.d.ts +18 -0
- package/dist/src/value-shape.d.ts.map +1 -0
- package/dist/src/value-shape.js +36 -0
- package/dist/src/value-shape.js.map +1 -0
- package/dist/stubs/main.d.ts +3 -0
- package/dist/stubs/main.d.ts.map +1 -0
- package/dist/stubs/main.js +4 -0
- package/dist/stubs/main.js.map +1 -0
- package/dist/stubs/make/filter/main.stub +45 -0
- package/package.json +102 -0
- package/skills/filter-basics/SKILL.md +285 -0
- package/skills/filter-codegen/SKILL.md +246 -0
- package/skills/filter-definitions/SKILL.md +340 -0
- package/skills/filter-querying/SKILL.md +319 -0
- package/skills/filter-safety/SKILL.md +304 -0
- package/stubs/main.ts +4 -0
- package/stubs/make/filter/main.stub +45 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { parseDistinct, parseSort, toColumnFilters } from './parse_request.js';
|
|
2
|
+
function toInt(value) {
|
|
3
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
4
|
+
return value;
|
|
5
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
6
|
+
const n = Number(value);
|
|
7
|
+
if (Number.isFinite(n))
|
|
8
|
+
return n;
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse a decoded, Spatie-laravel-query-builder / JSON:API-style query object
|
|
14
|
+
* into a {@link SpatieInput}. This is the richer, additive counterpart to
|
|
15
|
+
* {@link parseFilterRequest}: it understands the same `filter`/`sort`/`search`
|
|
16
|
+
* shapes plus JSON:API `include`, sparse `fields[resource]` sets, and cursor
|
|
17
|
+
* pagination (`page[after]`/`page[before]`).
|
|
18
|
+
*
|
|
19
|
+
* | Query string | Decoded input | Mapped to |
|
|
20
|
+
* | ------------------------------- | ------------------------------------ | ------------------------------------------ |
|
|
21
|
+
* | `filter[name]=Al` | `{ filter: { name: 'Al' } }` | `equals` column filter |
|
|
22
|
+
* | `filter[id]=1,2,3` | `{ filter: { id: '1,2,3' } }` | `in` column filter |
|
|
23
|
+
* | `filter[age][gte]=18` | `{ filter: { age: { gte: '18' } } }` | operator column filter |
|
|
24
|
+
* | `sort=-createdAt,name` | `{ sort: '-createdAt,name' }` | sort items |
|
|
25
|
+
* | `distinct=city,tier` | `{ distinct: 'city,tier' }` | `distinct: ['city', 'tier']` |
|
|
26
|
+
* | `include=posts,comments` | `{ include: 'posts,comments' }` | `include: ['posts', 'comments']` |
|
|
27
|
+
* | `fields[users]=id,name` | `{ fields: { users: 'id,name' } }` | `select: ['id', 'name']` |
|
|
28
|
+
* | `page[number]=2&page[size]=10` | `{ page: { number, size } }` | offset (`page`/`size`) |
|
|
29
|
+
* | `page[after]=cur&page[size]=10`| `{ page: { after, size } }` | cursor (`after`/`first`) |
|
|
30
|
+
*
|
|
31
|
+
* A pure reshape — no validation or allow-listing here; that happens downstream
|
|
32
|
+
* in `applyFilter` / `applyCursor` against the `FilterConfig`.
|
|
33
|
+
*/
|
|
34
|
+
export function parseSpatieRequest(qs) {
|
|
35
|
+
if (qs == null || typeof qs !== 'object')
|
|
36
|
+
return {};
|
|
37
|
+
const src = qs;
|
|
38
|
+
const out = {};
|
|
39
|
+
if (src.filter != null && typeof src.filter === 'object' && !Array.isArray(src.filter)) {
|
|
40
|
+
const filters = [];
|
|
41
|
+
for (const [field, value] of Object.entries(src.filter)) {
|
|
42
|
+
filters.push(...toColumnFilters(field, value));
|
|
43
|
+
}
|
|
44
|
+
if (filters.length > 0)
|
|
45
|
+
out.filters = filters;
|
|
46
|
+
}
|
|
47
|
+
const sort = parseSort(src.sort);
|
|
48
|
+
if (sort.length > 0)
|
|
49
|
+
out.sort = sort;
|
|
50
|
+
const distinct = parseDistinct(src.distinct);
|
|
51
|
+
if (distinct.length > 0)
|
|
52
|
+
out.distinct = distinct;
|
|
53
|
+
if (typeof src.search === 'string' && src.search.length > 0)
|
|
54
|
+
out.search = src.search;
|
|
55
|
+
const include = parseInclude(src.include);
|
|
56
|
+
if (include.length > 0)
|
|
57
|
+
out.include = include;
|
|
58
|
+
const select = parseSparseFieldsets(src.fields);
|
|
59
|
+
if (select.length > 0)
|
|
60
|
+
out.select = select;
|
|
61
|
+
applyPage(src.page, out);
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
/** Parse `include=posts,comments` (string or array) into a relation-name list. */
|
|
65
|
+
function parseInclude(include) {
|
|
66
|
+
if (typeof include === 'string') {
|
|
67
|
+
return include
|
|
68
|
+
.split(',')
|
|
69
|
+
.map((s) => s.trim())
|
|
70
|
+
.filter((s) => s.length > 0);
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(include)) {
|
|
73
|
+
return include.filter((s) => typeof s === 'string' && s.length > 0);
|
|
74
|
+
}
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Flatten JSON:API sparse fieldsets (`fields[resource]=a,b`) into a single
|
|
79
|
+
* order-preserving, de-duplicated list of column names. Per-resource keys are
|
|
80
|
+
* collapsed because a single SELECT applies to the primary entity.
|
|
81
|
+
*/
|
|
82
|
+
function parseSparseFieldsets(fields) {
|
|
83
|
+
if (fields == null || typeof fields !== 'object' || Array.isArray(fields))
|
|
84
|
+
return [];
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const value of Object.values(fields)) {
|
|
88
|
+
const cols = typeof value === 'string'
|
|
89
|
+
? value.split(',')
|
|
90
|
+
: Array.isArray(value)
|
|
91
|
+
? value.filter((s) => typeof s === 'string')
|
|
92
|
+
: [];
|
|
93
|
+
for (const raw of cols) {
|
|
94
|
+
const col = raw.trim();
|
|
95
|
+
if (col.length > 0 && !seen.has(col)) {
|
|
96
|
+
seen.add(col);
|
|
97
|
+
out.push(col);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Map a JSON:API `page` object onto `out`: `page[after]`/`page[before]` →
|
|
105
|
+
* cursor (`after`/`first`, `before`/`last`); otherwise `page[number]`/`page[size]`
|
|
106
|
+
* → offset (`page`/`size`). `after` wins if both cursor bounds are present.
|
|
107
|
+
*/
|
|
108
|
+
function applyPage(page, out) {
|
|
109
|
+
if (page == null || typeof page !== 'object' || Array.isArray(page))
|
|
110
|
+
return;
|
|
111
|
+
const p = page;
|
|
112
|
+
const after = typeof p.after === 'string' ? p.after : undefined;
|
|
113
|
+
const before = typeof p.before === 'string' ? p.before : undefined;
|
|
114
|
+
const size = toInt(p.size);
|
|
115
|
+
if (after !== undefined || before !== undefined) {
|
|
116
|
+
if (after !== undefined) {
|
|
117
|
+
out.after = after;
|
|
118
|
+
if (size !== undefined)
|
|
119
|
+
out.first = size;
|
|
120
|
+
}
|
|
121
|
+
else if (before !== undefined) {
|
|
122
|
+
out.before = before;
|
|
123
|
+
if (size !== undefined)
|
|
124
|
+
out.last = size;
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const number = toInt(p.number);
|
|
129
|
+
if (number !== undefined)
|
|
130
|
+
out.page = number;
|
|
131
|
+
if (size !== undefined)
|
|
132
|
+
out.size = size;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=spatie_parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spatie_parser.js","sourceRoot":"","sources":["../../src/spatie_parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { QueryBuilderLike } from './lucid_adapter.js';
|
|
2
|
+
/** A recorded query-builder call: method name + its scalar args. */
|
|
3
|
+
export interface RecordedCall {
|
|
4
|
+
method: string;
|
|
5
|
+
args: unknown[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A recording stand-in for a Lucid query builder, published under
|
|
9
|
+
* `@adonis-agora/filter/testing` so consumers can unit-test their own filter
|
|
10
|
+
* definitions without a database. It satisfies {@link QueryBuilderLike}, so any
|
|
11
|
+
* of the adapter/runner helpers (or {@link applyFilterFromRequest}) can be
|
|
12
|
+
* driven against it and the resulting translation asserted.
|
|
13
|
+
*
|
|
14
|
+
* Nested `where`/`orWhere` closures run against child recorders, and a dotted
|
|
15
|
+
* relation-path filter's `whereHas` records the relation name plus runs its
|
|
16
|
+
* nested callback against a child. {@link flatten} collects every leaf call
|
|
17
|
+
* (across all groups and relation subqueries) so tests can assert a translation
|
|
18
|
+
* appeared without caring about the exact grouping nesting.
|
|
19
|
+
*/
|
|
20
|
+
export declare class MockQueryBuilder implements QueryBuilderLike {
|
|
21
|
+
calls: RecordedCall[];
|
|
22
|
+
children: MockQueryBuilder[];
|
|
23
|
+
private record;
|
|
24
|
+
private group;
|
|
25
|
+
where(a: any, b?: any, c?: any): this;
|
|
26
|
+
orWhere(cb: (qb: QueryBuilderLike) => void): this;
|
|
27
|
+
/**
|
|
28
|
+
* Record a relation subquery: the `whereHas` call is captured with the
|
|
29
|
+
* relation name (so `find('whereHas')?.args[0]` is the relation), and the
|
|
30
|
+
* nested callback runs against a child recorder whose calls surface via
|
|
31
|
+
* {@link flatten}.
|
|
32
|
+
*/
|
|
33
|
+
whereHas(relation: string, cb: (qb: QueryBuilderLike) => void): this;
|
|
34
|
+
whereNot(callback: (qb: QueryBuilderLike) => void): this;
|
|
35
|
+
whereNot(column: string, value: unknown): this;
|
|
36
|
+
whereIn(column: string, values: unknown[]): this;
|
|
37
|
+
whereNotIn(column: string, values: unknown[]): this;
|
|
38
|
+
whereNull(column: string): this;
|
|
39
|
+
whereNotNull(column: string): this;
|
|
40
|
+
whereBetween(column: string, range: [unknown, unknown]): this;
|
|
41
|
+
whereNotBetween(column: string, range: [unknown, unknown]): this;
|
|
42
|
+
whereILike(column: string, value: string): this;
|
|
43
|
+
orWhereILike(column: string, value: string): this;
|
|
44
|
+
orderBy(column: string, direction: 'asc' | 'desc'): this;
|
|
45
|
+
/** Record a raw predicate: `args` are `[sql, bindings]` (bindings default `[]`). */
|
|
46
|
+
whereRaw(sql: string, bindings?: readonly unknown[]): this;
|
|
47
|
+
/** Record a raw ordering: `args` are `[sql, bindings]` (bindings default `[]`). */
|
|
48
|
+
orderByRaw(sql: string, bindings?: readonly unknown[]): this;
|
|
49
|
+
limit(count: number): this;
|
|
50
|
+
/** Record a row skip: `args` are `[n]`. Only the group-by-count aggregation pages this way. */
|
|
51
|
+
offset(n: number): this;
|
|
52
|
+
/** Record a projection: `args` are the select expressions (aggregates included). */
|
|
53
|
+
select(...columns: string[]): this;
|
|
54
|
+
/** Record an aggregation: `args` are `[column]` (e.g. `'* AS count'`). */
|
|
55
|
+
count(column: string): this;
|
|
56
|
+
/** Record a grouping: `args` are the group columns. */
|
|
57
|
+
groupBy(...columns: string[]): this;
|
|
58
|
+
/** Record a DISTINCT projection: `args` are the distinct column names. */
|
|
59
|
+
distinct(...columns: string[]): this;
|
|
60
|
+
/**
|
|
61
|
+
* Every recorded call across this builder and all nested groups and relation
|
|
62
|
+
* subqueries (excludes the anonymous group markers, but keeps `whereHas` since
|
|
63
|
+
* its recorded arg is the relation name, not a marker).
|
|
64
|
+
*/
|
|
65
|
+
flatten(): RecordedCall[];
|
|
66
|
+
/** Find the first flattened call matching `method`. */
|
|
67
|
+
find(method: string): RecordedCall | undefined;
|
|
68
|
+
/** Every flattened call matching `method` (handy for asserting relation subqueries). */
|
|
69
|
+
findAll(method: string): RecordedCall[];
|
|
70
|
+
}
|
|
71
|
+
/** Factory for a fresh {@link MockQueryBuilder} — the recording Lucid stand-in. */
|
|
72
|
+
export declare function makeMockQueryBuilder(): MockQueryBuilder;
|
|
73
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A recording stand-in for a Lucid query builder, published under
|
|
3
|
+
* `@adonis-agora/filter/testing` so consumers can unit-test their own filter
|
|
4
|
+
* definitions without a database. It satisfies {@link QueryBuilderLike}, so any
|
|
5
|
+
* of the adapter/runner helpers (or {@link applyFilterFromRequest}) can be
|
|
6
|
+
* driven against it and the resulting translation asserted.
|
|
7
|
+
*
|
|
8
|
+
* Nested `where`/`orWhere` closures run against child recorders, and a dotted
|
|
9
|
+
* relation-path filter's `whereHas` records the relation name plus runs its
|
|
10
|
+
* nested callback against a child. {@link flatten} collects every leaf call
|
|
11
|
+
* (across all groups and relation subqueries) so tests can assert a translation
|
|
12
|
+
* appeared without caring about the exact grouping nesting.
|
|
13
|
+
*/
|
|
14
|
+
export class MockQueryBuilder {
|
|
15
|
+
calls = [];
|
|
16
|
+
children = [];
|
|
17
|
+
record(method, ...args) {
|
|
18
|
+
this.calls.push({ method, args });
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
group(method, cb) {
|
|
22
|
+
const child = new MockQueryBuilder();
|
|
23
|
+
this.children.push(child);
|
|
24
|
+
this.calls.push({ method, args: ['<group>'] });
|
|
25
|
+
cb(child);
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
// biome-ignore lint/suspicious/noExplicitAny: structural overloads for the mock.
|
|
29
|
+
where(a, b, c) {
|
|
30
|
+
if (typeof a === 'function')
|
|
31
|
+
return this.group('where', a);
|
|
32
|
+
return c !== undefined ? this.record('where', a, b, c) : this.record('where', a, b);
|
|
33
|
+
}
|
|
34
|
+
orWhere(cb) {
|
|
35
|
+
return this.group('orWhere', cb);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Record a relation subquery: the `whereHas` call is captured with the
|
|
39
|
+
* relation name (so `find('whereHas')?.args[0]` is the relation), and the
|
|
40
|
+
* nested callback runs against a child recorder whose calls surface via
|
|
41
|
+
* {@link flatten}.
|
|
42
|
+
*/
|
|
43
|
+
whereHas(relation, cb) {
|
|
44
|
+
const child = new MockQueryBuilder();
|
|
45
|
+
this.children.push(child);
|
|
46
|
+
this.calls.push({ method: 'whereHas', args: [relation] });
|
|
47
|
+
cb(child);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
// biome-ignore lint/suspicious/noExplicitAny: structural overloads for the mock.
|
|
51
|
+
whereNot(a, b) {
|
|
52
|
+
if (typeof a === 'function')
|
|
53
|
+
return this.group('whereNot', a);
|
|
54
|
+
return this.record('whereNot', a, b);
|
|
55
|
+
}
|
|
56
|
+
whereIn(column, values) {
|
|
57
|
+
return this.record('whereIn', column, values);
|
|
58
|
+
}
|
|
59
|
+
whereNotIn(column, values) {
|
|
60
|
+
return this.record('whereNotIn', column, values);
|
|
61
|
+
}
|
|
62
|
+
whereNull(column) {
|
|
63
|
+
return this.record('whereNull', column);
|
|
64
|
+
}
|
|
65
|
+
whereNotNull(column) {
|
|
66
|
+
return this.record('whereNotNull', column);
|
|
67
|
+
}
|
|
68
|
+
whereBetween(column, range) {
|
|
69
|
+
return this.record('whereBetween', column, range);
|
|
70
|
+
}
|
|
71
|
+
whereNotBetween(column, range) {
|
|
72
|
+
return this.record('whereNotBetween', column, range);
|
|
73
|
+
}
|
|
74
|
+
whereILike(column, value) {
|
|
75
|
+
return this.record('whereILike', column, value);
|
|
76
|
+
}
|
|
77
|
+
orWhereILike(column, value) {
|
|
78
|
+
return this.record('orWhereILike', column, value);
|
|
79
|
+
}
|
|
80
|
+
orderBy(column, direction) {
|
|
81
|
+
return this.record('orderBy', column, direction);
|
|
82
|
+
}
|
|
83
|
+
/** Record a raw predicate: `args` are `[sql, bindings]` (bindings default `[]`). */
|
|
84
|
+
whereRaw(sql, bindings = []) {
|
|
85
|
+
return this.record('whereRaw', sql, bindings);
|
|
86
|
+
}
|
|
87
|
+
/** Record a raw ordering: `args` are `[sql, bindings]` (bindings default `[]`). */
|
|
88
|
+
orderByRaw(sql, bindings = []) {
|
|
89
|
+
return this.record('orderByRaw', sql, bindings);
|
|
90
|
+
}
|
|
91
|
+
limit(count) {
|
|
92
|
+
return this.record('limit', count);
|
|
93
|
+
}
|
|
94
|
+
/** Record a row skip: `args` are `[n]`. Only the group-by-count aggregation pages this way. */
|
|
95
|
+
offset(n) {
|
|
96
|
+
return this.record('offset', n);
|
|
97
|
+
}
|
|
98
|
+
/** Record a projection: `args` are the select expressions (aggregates included). */
|
|
99
|
+
select(...columns) {
|
|
100
|
+
return this.record('select', ...columns);
|
|
101
|
+
}
|
|
102
|
+
/** Record an aggregation: `args` are `[column]` (e.g. `'* AS count'`). */
|
|
103
|
+
count(column) {
|
|
104
|
+
return this.record('count', column);
|
|
105
|
+
}
|
|
106
|
+
/** Record a grouping: `args` are the group columns. */
|
|
107
|
+
groupBy(...columns) {
|
|
108
|
+
return this.record('groupBy', ...columns);
|
|
109
|
+
}
|
|
110
|
+
/** Record a DISTINCT projection: `args` are the distinct column names. */
|
|
111
|
+
distinct(...columns) {
|
|
112
|
+
return this.record('distinct', ...columns);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Every recorded call across this builder and all nested groups and relation
|
|
116
|
+
* subqueries (excludes the anonymous group markers, but keeps `whereHas` since
|
|
117
|
+
* its recorded arg is the relation name, not a marker).
|
|
118
|
+
*/
|
|
119
|
+
flatten() {
|
|
120
|
+
const own = this.calls.filter((c) => c.args[0] !== '<group>');
|
|
121
|
+
return [...own, ...this.children.flatMap((c) => c.flatten())];
|
|
122
|
+
}
|
|
123
|
+
/** Find the first flattened call matching `method`. */
|
|
124
|
+
find(method) {
|
|
125
|
+
return this.flatten().find((c) => c.method === method);
|
|
126
|
+
}
|
|
127
|
+
/** Every flattened call matching `method` (handy for asserting relation subqueries). */
|
|
128
|
+
findAll(method) {
|
|
129
|
+
return this.flatten().filter((c) => c.method === method);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** Factory for a fresh {@link MockQueryBuilder} — the recording Lucid stand-in. */
|
|
133
|
+
export function makeMockQueryBuilder() {
|
|
134
|
+
return new MockQueryBuilder();
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type { FieldAliases } from './field_aliases.js';
|
|
2
|
+
import type { FullTextSearchOptions, VectorDistanceMetric } from './lucid_adapter.js';
|
|
3
|
+
import type { ColumnFilter } from './operators.js';
|
|
4
|
+
/**
|
|
5
|
+
* A field's classified value kind — mirrors `@adonis-agora/filter-client`'s `FieldTypeKind`.
|
|
6
|
+
*
|
|
7
|
+
* Lives here rather than next to the client codegen because it is no longer codegen-only: the same
|
|
8
|
+
* declaration drives server-side value coercion (see {@link FilterConfig.fieldTypes}). Re-exported
|
|
9
|
+
* from `generate_client.ts` for backwards compatibility.
|
|
10
|
+
*/
|
|
11
|
+
export type FilterFieldKind = 'string' | 'number' | 'boolean' | 'date' | 'json' | 'unknown';
|
|
12
|
+
/** A sort directive: field + direction. */
|
|
13
|
+
export interface SortItem {
|
|
14
|
+
field: string;
|
|
15
|
+
direction: 'asc' | 'desc';
|
|
16
|
+
}
|
|
17
|
+
/** Context handed to a {@link ComputedSource} function — the root table alias. */
|
|
18
|
+
export interface ComputedContext {
|
|
19
|
+
/**
|
|
20
|
+
* The root table's SQL alias — for Lucid this is the model's table name (the
|
|
21
|
+
* main table carries no generated alias), taken from {@link FilterConfig.table}.
|
|
22
|
+
* A correlated subquery references the outer row through it, e.g.
|
|
23
|
+
* `` ({ alias }) => `(SELECT COUNT(*) FROM posts WHERE posts.author_id = ${alias}.id)` ``.
|
|
24
|
+
*/
|
|
25
|
+
alias: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A dev-declared virtual/computed field's SQL source. Two forms:
|
|
29
|
+
*
|
|
30
|
+
* - **verbatim string** — inlined exactly as written (`"first || ' ' || last"`).
|
|
31
|
+
* No token substitution; a correlated subquery needing the outer alias must
|
|
32
|
+
* use the function form.
|
|
33
|
+
* - **function** `({ alias }) => sql` — receives the root table alias
|
|
34
|
+
* ({@link ComputedContext}) so it can emit a correlated subquery referencing
|
|
35
|
+
* the outer row.
|
|
36
|
+
*
|
|
37
|
+
* SAFETY: the source is authored by the developer at declaration time, never by
|
|
38
|
+
* the client. The client only references the declared alias name; its filter
|
|
39
|
+
* VALUE is always a bound parameter. Only this dev expression is inlined.
|
|
40
|
+
*/
|
|
41
|
+
export type ComputedSource = string | ((ctx: ComputedContext) => string);
|
|
42
|
+
/** A map of computed-field alias → its dev-declared {@link ComputedSource}. */
|
|
43
|
+
export type ComputedFields = Record<string, ComputedSource>;
|
|
44
|
+
/**
|
|
45
|
+
* Where a request's structured input is read from, for {@link resolveInputFromRequest}:
|
|
46
|
+
*
|
|
47
|
+
* - `'auto'` — query on reads (GET/HEAD), query+body (body wins) on writes.
|
|
48
|
+
* - `'query'` / `'body'` — always that container.
|
|
49
|
+
* - a dot-path (e.g. `'body.filters'`) — the nested object at that path.
|
|
50
|
+
* - a function — a custom extractor receiving the raw request.
|
|
51
|
+
*/
|
|
52
|
+
export type InputSource = 'auto' | 'query' | 'body' | (string & {}) | ((req: unknown) => Record<string, unknown> | undefined);
|
|
53
|
+
/**
|
|
54
|
+
* How incoming field-name keys are normalized before matching against the
|
|
55
|
+
* allow-list — a built-in case transform or a custom mapping function.
|
|
56
|
+
*/
|
|
57
|
+
export type InputNormalizer = 'camelCase' | 'snakeCase' | ((key: string) => string);
|
|
58
|
+
/**
|
|
59
|
+
* A group-by-count aggregation request: the distinct values of `field` with counts, over the
|
|
60
|
+
* rows the active filters select. `limit` bounds the rows (highest count first — tag cardinality
|
|
61
|
+
* grows with the data, so the unbounded answer is a listing); `offset` pages that bound;
|
|
62
|
+
* `search` narrows to values containing the text, server-side, so a rare value outside the first
|
|
63
|
+
* page stays reachable by typing.
|
|
64
|
+
*/
|
|
65
|
+
export interface GroupByCountRequest {
|
|
66
|
+
field: string;
|
|
67
|
+
limit?: number | undefined;
|
|
68
|
+
offset?: number | undefined;
|
|
69
|
+
search?: string | undefined;
|
|
70
|
+
}
|
|
71
|
+
/** Bounds for a group-by-count answer: how many group rows come back, from where, matching what. */
|
|
72
|
+
export interface GroupByCountOptions {
|
|
73
|
+
limit?: number | undefined;
|
|
74
|
+
offset?: number | undefined;
|
|
75
|
+
/** Narrow to groups whose value contains this text (case-insensitive). */
|
|
76
|
+
search?: string | undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The parsed, structured input a {@link applyFilter} call consumes — produced by
|
|
80
|
+
* {@link parseFilterRequest} from a request query string, or built directly.
|
|
81
|
+
*/
|
|
82
|
+
export interface FilterInput {
|
|
83
|
+
/** Column filter conditions (with optional AND/OR composition). */
|
|
84
|
+
filters?: ColumnFilter[];
|
|
85
|
+
/** Sort directives, applied in order. */
|
|
86
|
+
sort?: SortItem[];
|
|
87
|
+
/**
|
|
88
|
+
* Select DISTINCT values of the given field(s) — the active filters, search,
|
|
89
|
+
* sort and pagination still apply. Useful for populating a filter dropdown
|
|
90
|
+
* with the distinct values of a column. Fields are alias-resolved and checked
|
|
91
|
+
* against {@link FilterConfig.allowed} exactly like a `where` field; unknown
|
|
92
|
+
* fields are dropped (or rejected under {@link FilterConfig.throwOnInvalid}).
|
|
93
|
+
*/
|
|
94
|
+
distinct?: string[];
|
|
95
|
+
/**
|
|
96
|
+
* Free-text search term. Routed through Postgres tsvector full-text search
|
|
97
|
+
* when the policy declares {@link FilterConfig.fullText}, otherwise a portable
|
|
98
|
+
* ILIKE scan across {@link FilterConfig.searchable}.
|
|
99
|
+
*/
|
|
100
|
+
search?: string;
|
|
101
|
+
/** 1-based page number for offset pagination. */
|
|
102
|
+
page?: number;
|
|
103
|
+
/** Page size for offset pagination. */
|
|
104
|
+
size?: number;
|
|
105
|
+
/**
|
|
106
|
+
* A group-by-count aggregation replacing entity-row output: the distinct values of one field
|
|
107
|
+
* with counts — what populates a filter dropdown. Parsed from the `groupByCount[field]`,
|
|
108
|
+
* `groupByCount[limit]`, `groupByCount[offset]` and `groupByCount[search]` params the client
|
|
109
|
+
* builder emits.
|
|
110
|
+
*/
|
|
111
|
+
groupByCount?: GroupByCountRequest;
|
|
112
|
+
/**
|
|
113
|
+
* A query embedding to rank rows by pgvector *similarity* (distinct from the
|
|
114
|
+
* text `search` above). Applied only when the policy declares a similarity
|
|
115
|
+
* column (see {@link FilterConfig.vectorSimilarity}) — ignored otherwise, so
|
|
116
|
+
* non-similarity requests are unchanged. Typically the controller computes
|
|
117
|
+
* this from an embedding service, not the query string.
|
|
118
|
+
*/
|
|
119
|
+
vectorSimilarity?: readonly number[];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Declares a Postgres tsvector full-text search on a policy — the primary
|
|
123
|
+
* `search` path when set. The request's `search` string is matched against the
|
|
124
|
+
* configured document via `websearch_to_tsquery`, optionally ranked by
|
|
125
|
+
* `ts_rank`. Omitting `column`-related fields is a compile error; a policy
|
|
126
|
+
* without this config falls back to the ILIKE {@link FilterConfig.searchable}
|
|
127
|
+
* scan. Additive — carries the same shape as {@link FullTextSearchOptions}
|
|
128
|
+
* minus the per-request `query`.
|
|
129
|
+
*/
|
|
130
|
+
export type FullTextSearchConfig = Omit<FullTextSearchOptions, 'query'>;
|
|
131
|
+
/**
|
|
132
|
+
* Declares a pgvector **embedding similarity** ordering on a policy — distinct
|
|
133
|
+
* from full-text `search`. When a request also carries a query embedding
|
|
134
|
+
* ({@link FilterInput.vectorSimilarity}), the runner ranks rows by ascending
|
|
135
|
+
* distance to it. Additive — a policy without this behaves exactly as before.
|
|
136
|
+
*/
|
|
137
|
+
export interface VectorSimilarityConfig {
|
|
138
|
+
/** The pgvector column ranked against the query embedding (e.g. `'embedding'`). */
|
|
139
|
+
column: string;
|
|
140
|
+
/** Distance metric → pgvector operator. Default `'cosine'` (`<=>`). */
|
|
141
|
+
metric?: VectorDistanceMetric;
|
|
142
|
+
/** Optional max-distance filter — drop rows farther than this from the embedding. */
|
|
143
|
+
threshold?: number;
|
|
144
|
+
/** Optional top-K truncation — keep only the K nearest rows. */
|
|
145
|
+
topK?: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* An allow-list of field names — the security boundary for filter/sort. One of:
|
|
149
|
+
*
|
|
150
|
+
* - `'*'` — allow any field (use with care);
|
|
151
|
+
* - `string[]` — allow exactly these field names;
|
|
152
|
+
* - a predicate `(field) => boolean` — allow fields for which it returns true.
|
|
153
|
+
* The predicate form lets a policy express rules a flat list can't (e.g.
|
|
154
|
+
* relation-path whitelisting with a depth cap). It is evaluated against the
|
|
155
|
+
* already alias-resolved target field, never the client-facing alias key.
|
|
156
|
+
*/
|
|
157
|
+
export type AllowList = '*' | string[] | ((field: string) => boolean);
|
|
158
|
+
/**
|
|
159
|
+
* Per-call filter policy. The allow-lists are the security boundary: only fields
|
|
160
|
+
* named here can be filtered/sorted/searched, so client input can never probe
|
|
161
|
+
* arbitrary columns.
|
|
162
|
+
*/
|
|
163
|
+
export interface FilterConfig {
|
|
164
|
+
/** Columns clients may filter on. `'*'` allows any (use with care). */
|
|
165
|
+
allowed: AllowList;
|
|
166
|
+
/** Columns clients may sort on. Defaults to {@link FilterConfig.allowed}. */
|
|
167
|
+
sortable?: AllowList;
|
|
168
|
+
/**
|
|
169
|
+
* Columns the free-text `search` term scans with a portable ILIKE — the
|
|
170
|
+
* default search path when {@link FilterConfig.fullText} is not set.
|
|
171
|
+
*/
|
|
172
|
+
searchable?: string[];
|
|
173
|
+
/**
|
|
174
|
+
* Declared column value kinds, keyed by field. A declared field has its filter value coerced to
|
|
175
|
+
* that kind; a value that can't be coerced is treated exactly like a disallowed field (dropped,
|
|
176
|
+
* or thrown under {@link FilterConfig.throwOnInvalid}). Guards the column from an uncastable
|
|
177
|
+
* value that Postgres would otherwise reject at query time as a 500. Undeclared fields are
|
|
178
|
+
* passed through uncoerced.
|
|
179
|
+
*/
|
|
180
|
+
fieldTypes?: Readonly<Record<string, {
|
|
181
|
+
kind?: FilterFieldKind;
|
|
182
|
+
}>>;
|
|
183
|
+
/**
|
|
184
|
+
* Opt-in Postgres tsvector full-text search. When set, the request `search`
|
|
185
|
+
* string routes through `websearch_to_tsquery`/`@@` (and optional `ts_rank`)
|
|
186
|
+
* instead of the ILIKE {@link FilterConfig.searchable} scan. Omitted → ILIKE.
|
|
187
|
+
*/
|
|
188
|
+
fullText?: FullTextSearchConfig;
|
|
189
|
+
/** Default page size when none is given. Default 25. */
|
|
190
|
+
defaultSize?: number;
|
|
191
|
+
/** Hard cap on page size. Default 100. */
|
|
192
|
+
maxSize?: number;
|
|
193
|
+
/** Throw `InvalidColumnFilterError` on a disallowed/invalid field instead of dropping it. Default false (drop). */
|
|
194
|
+
throwOnInvalid?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Declarative field-name remapping applied to filter and sort fields BEFORE
|
|
197
|
+
* allow-listing — an alias key resolves to its target column, which is what
|
|
198
|
+
* the allow-list, validation and query builder then see. Aliases do not
|
|
199
|
+
* cascade. See {@link resolveFieldAlias}.
|
|
200
|
+
*/
|
|
201
|
+
aliases?: FieldAliases;
|
|
202
|
+
/**
|
|
203
|
+
* Opt-in pgvector embedding-similarity ordering (distinct from text `search`).
|
|
204
|
+
* When set and the request carries a query embedding
|
|
205
|
+
* ({@link FilterInput.vectorSimilarity}), rows are ranked nearest-first by
|
|
206
|
+
* distance to it. Omitted → no similarity ordering (default).
|
|
207
|
+
*/
|
|
208
|
+
vectorSimilarity?: VectorSimilarityConfig;
|
|
209
|
+
/**
|
|
210
|
+
* Virtual/computed fields — a map of alias → dev-declared SQL expression
|
|
211
|
+
* ({@link ComputedSource}). A declared alias becomes filterable and sortable
|
|
212
|
+
* as if it were a real column; its declaration IS its allow-list (computed
|
|
213
|
+
* fields bypass the column `allowed`/`sortable` lists — they are not real
|
|
214
|
+
* columns). The client value is always parameterized; only the dev expression
|
|
215
|
+
* is inlined.
|
|
216
|
+
*/
|
|
217
|
+
computed?: ComputedFields;
|
|
218
|
+
/**
|
|
219
|
+
* The root table's SQL alias, surfaced to {@link ComputedSource} functions as
|
|
220
|
+
* {@link ComputedContext.alias}. For Lucid this is the model's table name.
|
|
221
|
+
* Required for function-form computed fields (and to-many aggregate fields)
|
|
222
|
+
* whose correlated subqueries must reference the outer row.
|
|
223
|
+
*/
|
|
224
|
+
table?: string;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ColumnFilter, type FilterOperator } from './operators.js';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes an operator string to its canonical {@link FilterOperator},
|
|
4
|
+
* resolving SQL-symbol aliases (`=` -> `equals`, `!=` -> `notEquals`, ...).
|
|
5
|
+
* Canonical operators (and unknown strings) pass through unchanged.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeOperator(operator: string): FilterOperator;
|
|
8
|
+
/**
|
|
9
|
+
* Maximum allowed nesting depth for AND/OR filter composition.
|
|
10
|
+
* Prevents stack overflow from maliciously deep payloads (DoS).
|
|
11
|
+
*/
|
|
12
|
+
export declare const MAX_FILTER_DEPTH = 10;
|
|
13
|
+
export declare class InvalidColumnFilterError extends Error {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validates a ColumnFilter at runtime:
|
|
18
|
+
* - field must be a non-empty string with no SQL-unsafe characters
|
|
19
|
+
* - operator must be a known FilterOperator
|
|
20
|
+
* - value is required for non-unary operators
|
|
21
|
+
* - 'between' requires a 2-element array
|
|
22
|
+
* - 'in'/'isAnyOf' require arrays
|
|
23
|
+
* - AND/OR arrays are validated recursively
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateColumnFilter(filter: ColumnFilter, depth?: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* Validates an array of ColumnFilter objects.
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateColumnFilters(filters: ColumnFilter[]): void;
|
|
30
|
+
//# sourceMappingURL=validate-column-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-column-filter.d.ts","sourceRoot":"","sources":["../../src/validate-column-filter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,cAAc,EAEpB,MAAM,gBAAgB,CAAC;AAIxB;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAElE;AAyBD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,SAAI,GAAG,IAAI,CAsG1E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAOnE"}
|