@adonis-agora/filter 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/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/commands/main.d.ts +17 -0
- package/dist/commands/main.d.ts.map +1 -0
- package/dist/commands/main.js +17 -0
- package/dist/commands/main.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/src/apply_from_request.d.ts +69 -0
- package/dist/src/apply_from_request.d.ts.map +1 -0
- package/dist/src/apply_from_request.js +115 -0
- package/dist/src/apply_from_request.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/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 +28 -0
- package/dist/src/field_aliases.d.ts.map +1 -0
- package/dist/src/field_aliases.js +61 -0
- package/dist/src/field_aliases.js.map +1 -0
- package/dist/src/filter_spec.d.ts +146 -0
- package/dist/src/filter_spec.d.ts.map +1 -0
- package/dist/src/filter_spec.js +140 -0
- package/dist/src/filter_spec.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 +154 -0
- package/dist/src/generate_client.js.map +1 -0
- package/dist/src/index.d.ts +28 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lucid_adapter.d.ts +213 -0
- package/dist/src/lucid_adapter.d.ts.map +1 -0
- package/dist/src/lucid_adapter.js +303 -0
- package/dist/src/lucid_adapter.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/parse_request.d.ts +22 -0
- package/dist/src/parse_request.d.ts.map +1 -0
- package/dist/src/parse_request.js +107 -0
- package/dist/src/parse_request.js.map +1 -0
- package/dist/src/runner.d.ts +46 -0
- package/dist/src/runner.d.ts.map +1 -0
- package/dist/src/runner.js +190 -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 +38 -0
- package/dist/src/spatie_parser.d.ts.map +1 -0
- package/dist/src/spatie_parser.js +130 -0
- package/dist/src/spatie_parser.js.map +1 -0
- package/dist/src/testing.d.ts +62 -0
- package/dist/src/testing.d.ts.map +1 -0
- package/dist/src/testing.js +113 -0
- package/dist/src/testing.js.map +1 -0
- package/dist/src/types.d.ts +130 -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/package.json +71 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type CursorParams, type ResolvedCursor } from './cursor.js';
|
|
2
|
+
import { type QueryBuilderLike } from './lucid_adapter.js';
|
|
3
|
+
import type { FilterConfig, FilterInput } from './types.js';
|
|
4
|
+
/** The resolved offset pagination to hand to Lucid's `query.paginate(page, size)`. */
|
|
5
|
+
export interface ResolvedPagination {
|
|
6
|
+
page: number;
|
|
7
|
+
size: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Apply a parsed {@link FilterInput} to a Lucid query builder under a
|
|
11
|
+
* {@link FilterConfig} policy, and return the resolved offset pagination.
|
|
12
|
+
*
|
|
13
|
+
* The allow-lists are the security boundary — fields not in `allowed`/`sortable`/
|
|
14
|
+
* `searchable` are dropped (or rejected with `throwOnInvalid`). Pagination is
|
|
15
|
+
* returned (not applied) so the caller drives Lucid's `query.paginate()`:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const { page, size } = applyFilter(Users.query(), input, { allowed: ['name', 'age'] })
|
|
19
|
+
* const result = await Users.query()... // or: await query.paginate(page, size)
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function applyFilter(qb: QueryBuilderLike, input: FilterInput, config: FilterConfig): ResolvedPagination;
|
|
23
|
+
/** Per-call policy for {@link applyCursor} — a {@link FilterConfig} plus the keyset tiebreaker. */
|
|
24
|
+
export interface CursorConfig extends FilterConfig {
|
|
25
|
+
/** Primary-key column appended to the keyset as a stable tiebreaker. Default `'id'`. */
|
|
26
|
+
primaryKey?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Apply a parsed {@link FilterInput} plus {@link CursorParams} to a Lucid query
|
|
30
|
+
* builder as a **keyset (cursor) page** under a {@link CursorConfig} policy.
|
|
31
|
+
*
|
|
32
|
+
* Filters/search go through the same allow-list boundary as {@link applyFilter}.
|
|
33
|
+
* The effective (allow-listed) sort, plus the primary-key tiebreaker, forms the
|
|
34
|
+
* keyset; a supplied `after`/`before` cursor becomes a row-value seek predicate,
|
|
35
|
+
* and the builder is ordered + limited to `size + 1` (one extra row to detect a
|
|
36
|
+
* further page). Feed the fetched rows and the returned {@link ResolvedCursor}
|
|
37
|
+
* to {@link buildCursorPage} to assemble the page and its boundary cursors:
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* const resolved = applyCursor(Users.query(), input, { allowed: ['name'], primaryKey: 'id' })
|
|
41
|
+
* const rows = await Users.query()...exec()
|
|
42
|
+
* const page = buildCursorPage(rows, resolved)
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function applyCursor(qb: QueryBuilderLike, input: FilterInput & CursorParams, config: CursorConfig): ResolvedCursor;
|
|
46
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EAIpB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,KAAK,gBAAgB,EAOtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAa,YAAY,EAAE,WAAW,EAAY,MAAM,YAAY,CAAC;AAGjF,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AA8HD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,gBAAgB,EACpB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,GACnB,kBAAkB,CA+BpB;AAED,mGAAmG;AACnG,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,gBAAgB,EACpB,KAAK,EAAE,WAAW,GAAG,YAAY,EACjC,MAAM,EAAE,YAAY,GACnB,cAAc,CA0BhB"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { buildKeyset, decodeCursor, reverseKeyset, } from './cursor.js';
|
|
2
|
+
import { remapFilterAliases, remapSortAliases } from './field_aliases.js';
|
|
3
|
+
import { applyColumnFilters, applyFullTextSearch, applyKeyset, applySearch, applySort, applyVectorSimilarity, } from './lucid_adapter.js';
|
|
4
|
+
import { InvalidColumnFilterError, validateColumnFilters } from './validate-column-filter.js';
|
|
5
|
+
function isAllowed(field, allow) {
|
|
6
|
+
if (allow === '*')
|
|
7
|
+
return true;
|
|
8
|
+
if (typeof allow === 'function')
|
|
9
|
+
return allow(field);
|
|
10
|
+
return allow.includes(field);
|
|
11
|
+
}
|
|
12
|
+
function clamp(n, min, max) {
|
|
13
|
+
return Math.min(max, Math.max(min, n));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Recursively prune a filter against the allow-list. A leaf on a disallowed field
|
|
17
|
+
* is dropped (or throws when `throwOnInvalid`). Group nodes keep only their
|
|
18
|
+
* surviving children; a group left empty is itself dropped (returns `null`).
|
|
19
|
+
*/
|
|
20
|
+
function prune(filter, allowed, throwOnInvalid) {
|
|
21
|
+
const hasField = typeof filter.field === 'string' && filter.field.length > 0;
|
|
22
|
+
if (hasField && !isAllowed(filter.field, allowed)) {
|
|
23
|
+
if (throwOnInvalid) {
|
|
24
|
+
throw new InvalidColumnFilterError(`Field "${filter.field}" is not filterable.`);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const next = { field: filter.field, operator: filter.operator };
|
|
29
|
+
if (filter.value !== undefined)
|
|
30
|
+
next.value = filter.value;
|
|
31
|
+
if (filter.AND) {
|
|
32
|
+
const kept = filter.AND.map((f) => prune(f, allowed, throwOnInvalid)).filter((f) => f !== null);
|
|
33
|
+
if (kept.length > 0)
|
|
34
|
+
next.AND = kept;
|
|
35
|
+
}
|
|
36
|
+
if (filter.OR) {
|
|
37
|
+
const kept = filter.OR.map((f) => prune(f, allowed, throwOnInvalid)).filter((f) => f !== null);
|
|
38
|
+
if (kept.length > 0)
|
|
39
|
+
next.OR = kept;
|
|
40
|
+
}
|
|
41
|
+
// A pure group node (no field of its own) with no surviving children is empty.
|
|
42
|
+
if (!hasField && next.AND === undefined && next.OR === undefined) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return next;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve aliases, structurally validate, and prune `input.filters`/`input.search`
|
|
49
|
+
* against the allow-lists, applying the survivors to the builder. Shared by both
|
|
50
|
+
* {@link applyFilter} (offset) and {@link applyCursor} (keyset) so the security
|
|
51
|
+
* boundary is identical for either pagination style.
|
|
52
|
+
*/
|
|
53
|
+
function applyFilterConditions(qb, input, config) {
|
|
54
|
+
const throwOnInvalid = config.throwOnInvalid ?? false;
|
|
55
|
+
if (input.filters && input.filters.length > 0) {
|
|
56
|
+
// Alias resolution runs FIRST — the allow-list, validation and query builder
|
|
57
|
+
// all see the resolved target column, never the client-facing alias key.
|
|
58
|
+
const aliased = config.aliases
|
|
59
|
+
? input.filters.map((f) => remapFilterAliases(f, config.aliases))
|
|
60
|
+
: input.filters;
|
|
61
|
+
// Structural validation next (operator/value shape, depth, field charset).
|
|
62
|
+
validateColumnFilters(aliased);
|
|
63
|
+
const safe = aliased
|
|
64
|
+
.map((f) => prune(f, config.allowed, throwOnInvalid))
|
|
65
|
+
.filter((f) => f !== null);
|
|
66
|
+
if (safe.length > 0) {
|
|
67
|
+
applyColumnFilters(qb, safe);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (input.search) {
|
|
71
|
+
const term = input.search.trim();
|
|
72
|
+
if (term.length > 0) {
|
|
73
|
+
// tsvector full-text search is the primary path when the policy declares
|
|
74
|
+
// it; otherwise fall back to the portable ILIKE scan across `searchable`.
|
|
75
|
+
if (config.fullText) {
|
|
76
|
+
applyFullTextSearch(qb, {
|
|
77
|
+
query: term,
|
|
78
|
+
column: config.fullText.column,
|
|
79
|
+
...(config.fullText.language !== undefined && { language: config.fullText.language }),
|
|
80
|
+
...(config.fullText.rank !== undefined && { rank: config.fullText.rank }),
|
|
81
|
+
...(config.fullText.columnKind !== undefined && {
|
|
82
|
+
columnKind: config.fullText.columnKind,
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else if (config.searchable && config.searchable.length > 0) {
|
|
87
|
+
applySearch(qb, term, config.searchable);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Resolve the alias-mapped, allow-listed sort directives for the request. */
|
|
93
|
+
function resolveSafeSort(sort, config) {
|
|
94
|
+
const throwOnInvalid = config.throwOnInvalid ?? false;
|
|
95
|
+
const sortable = config.sortable ?? config.allowed;
|
|
96
|
+
const aliased = config.aliases
|
|
97
|
+
? remapSortAliases(sort, config.aliases)
|
|
98
|
+
: sort;
|
|
99
|
+
const safe = [];
|
|
100
|
+
for (const item of aliased) {
|
|
101
|
+
if (isAllowed(item.field, sortable)) {
|
|
102
|
+
safe.push(item);
|
|
103
|
+
}
|
|
104
|
+
else if (throwOnInvalid) {
|
|
105
|
+
throw new InvalidColumnFilterError(`Field "${item.field}" is not sortable.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return safe;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Apply a parsed {@link FilterInput} to a Lucid query builder under a
|
|
112
|
+
* {@link FilterConfig} policy, and return the resolved offset pagination.
|
|
113
|
+
*
|
|
114
|
+
* The allow-lists are the security boundary — fields not in `allowed`/`sortable`/
|
|
115
|
+
* `searchable` are dropped (or rejected with `throwOnInvalid`). Pagination is
|
|
116
|
+
* returned (not applied) so the caller drives Lucid's `query.paginate()`:
|
|
117
|
+
*
|
|
118
|
+
* ```ts
|
|
119
|
+
* const { page, size } = applyFilter(Users.query(), input, { allowed: ['name', 'age'] })
|
|
120
|
+
* const result = await Users.query()... // or: await query.paginate(page, size)
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function applyFilter(qb, input, config) {
|
|
124
|
+
applyFilterConditions(qb, input, config);
|
|
125
|
+
// Embedding-similarity ranking (opt-in, additive): only when the policy declares
|
|
126
|
+
// a similarity column AND the request carries a query embedding. Applied before
|
|
127
|
+
// the user sort so nearest-first distance is the primary ordering; any allowed
|
|
128
|
+
// sort then acts as a tiebreaker.
|
|
129
|
+
if (config.vectorSimilarity && input.vectorSimilarity && input.vectorSimilarity.length > 0) {
|
|
130
|
+
applyVectorSimilarity(qb, {
|
|
131
|
+
column: config.vectorSimilarity.column,
|
|
132
|
+
vector: input.vectorSimilarity,
|
|
133
|
+
...(config.vectorSimilarity.metric !== undefined && {
|
|
134
|
+
metric: config.vectorSimilarity.metric,
|
|
135
|
+
}),
|
|
136
|
+
...(config.vectorSimilarity.threshold !== undefined && {
|
|
137
|
+
threshold: config.vectorSimilarity.threshold,
|
|
138
|
+
}),
|
|
139
|
+
...(config.vectorSimilarity.topK !== undefined && { topK: config.vectorSimilarity.topK }),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (input.sort && input.sort.length > 0) {
|
|
143
|
+
const safeSort = resolveSafeSort(input.sort, config);
|
|
144
|
+
if (safeSort.length > 0) {
|
|
145
|
+
applySort(qb, safeSort);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const size = clamp(input.size ?? config.defaultSize ?? 25, 1, config.maxSize ?? 100);
|
|
149
|
+
const page = Math.max(1, input.page ?? 1);
|
|
150
|
+
return { page, size };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Apply a parsed {@link FilterInput} plus {@link CursorParams} to a Lucid query
|
|
154
|
+
* builder as a **keyset (cursor) page** under a {@link CursorConfig} policy.
|
|
155
|
+
*
|
|
156
|
+
* Filters/search go through the same allow-list boundary as {@link applyFilter}.
|
|
157
|
+
* The effective (allow-listed) sort, plus the primary-key tiebreaker, forms the
|
|
158
|
+
* keyset; a supplied `after`/`before` cursor becomes a row-value seek predicate,
|
|
159
|
+
* and the builder is ordered + limited to `size + 1` (one extra row to detect a
|
|
160
|
+
* further page). Feed the fetched rows and the returned {@link ResolvedCursor}
|
|
161
|
+
* to {@link buildCursorPage} to assemble the page and its boundary cursors:
|
|
162
|
+
*
|
|
163
|
+
* ```ts
|
|
164
|
+
* const resolved = applyCursor(Users.query(), input, { allowed: ['name'], primaryKey: 'id' })
|
|
165
|
+
* const rows = await Users.query()...exec()
|
|
166
|
+
* const page = buildCursorPage(rows, resolved)
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export function applyCursor(qb, input, config) {
|
|
170
|
+
applyFilterConditions(qb, input, config);
|
|
171
|
+
const safeSort = input.sort && input.sort.length > 0 ? resolveSafeSort(input.sort, config) : [];
|
|
172
|
+
const baseKeyset = buildKeyset(safeSort, config.primaryKey ?? 'id');
|
|
173
|
+
const backward = input.before !== undefined && input.after === undefined;
|
|
174
|
+
const cursorStr = input.after ?? input.before;
|
|
175
|
+
const requested = backward ? input.last : input.first;
|
|
176
|
+
const size = clamp(requested ?? config.defaultSize ?? 25, 1, config.maxSize ?? 100);
|
|
177
|
+
// For backward paging, reverse keyset directions so the boundary seek and
|
|
178
|
+
// ordering walk the other way; buildCursorPage re-reverses the rows.
|
|
179
|
+
const queryKeyset = backward ? reverseKeyset(baseKeyset) : baseKeyset;
|
|
180
|
+
if (cursorStr !== undefined) {
|
|
181
|
+
const values = decodeCursor(cursorStr);
|
|
182
|
+
if (values && values.length === queryKeyset.length) {
|
|
183
|
+
applyKeyset(qb, queryKeyset, values);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
applySort(qb, queryKeyset);
|
|
187
|
+
qb.limit(size + 1);
|
|
188
|
+
return { keyset: baseKeyset, size, backward, hasCursor: cursorStr !== undefined };
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { InputSource } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the raw structured input from a request according to an
|
|
4
|
+
* {@link InputSource}:
|
|
5
|
+
*
|
|
6
|
+
* - a function — a custom extractor, called with the request.
|
|
7
|
+
* - `'query'` / `'body'` — always that container.
|
|
8
|
+
* - `'auto'` (or any other bare token) — query on reads (GET/HEAD), query+body
|
|
9
|
+
* merged (body wins) on writes (POST/PUT/PATCH/DELETE).
|
|
10
|
+
* - a dot-path (e.g. `'body.filters'`) — the nested object at that path, or `{}`
|
|
11
|
+
* when the path is missing or does not resolve to an object.
|
|
12
|
+
*
|
|
13
|
+
* The `req` is treated structurally (`{ method, query, body }`), so an AdonisJS
|
|
14
|
+
* `HttpContext.request` — whose `qs()`/`body()` you pass in — or a plain object
|
|
15
|
+
* both work. The result is always a fresh shallow copy.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveInputFromRequest(req: unknown, source: InputSource): Record<string, unknown>;
|
|
18
|
+
//# sourceMappingURL=source_resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source_resolver.d.ts","sourceRoot":"","sources":["../../src/source_resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAW9C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,WAAW,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+BzB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const WRITE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
|
2
|
+
const READ_METHODS = new Set(['GET', 'HEAD']);
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the raw structured input from a request according to an
|
|
5
|
+
* {@link InputSource}:
|
|
6
|
+
*
|
|
7
|
+
* - a function — a custom extractor, called with the request.
|
|
8
|
+
* - `'query'` / `'body'` — always that container.
|
|
9
|
+
* - `'auto'` (or any other bare token) — query on reads (GET/HEAD), query+body
|
|
10
|
+
* merged (body wins) on writes (POST/PUT/PATCH/DELETE).
|
|
11
|
+
* - a dot-path (e.g. `'body.filters'`) — the nested object at that path, or `{}`
|
|
12
|
+
* when the path is missing or does not resolve to an object.
|
|
13
|
+
*
|
|
14
|
+
* The `req` is treated structurally (`{ method, query, body }`), so an AdonisJS
|
|
15
|
+
* `HttpContext.request` — whose `qs()`/`body()` you pass in — or a plain object
|
|
16
|
+
* both work. The result is always a fresh shallow copy.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveInputFromRequest(req, source) {
|
|
19
|
+
if (typeof source === 'function') {
|
|
20
|
+
const r = source(req);
|
|
21
|
+
return { ...(r ?? {}) };
|
|
22
|
+
}
|
|
23
|
+
const r = (req ?? {});
|
|
24
|
+
const query = (r.query ?? {});
|
|
25
|
+
const body = (r.body ?? {});
|
|
26
|
+
switch (source) {
|
|
27
|
+
case 'query':
|
|
28
|
+
return { ...query };
|
|
29
|
+
case 'body':
|
|
30
|
+
return { ...body };
|
|
31
|
+
case 'auto': {
|
|
32
|
+
const method = (r.method ?? 'GET').toUpperCase();
|
|
33
|
+
if (READ_METHODS.has(method))
|
|
34
|
+
return { ...query };
|
|
35
|
+
if (WRITE_METHODS.has(method))
|
|
36
|
+
return { ...query, ...body };
|
|
37
|
+
return { ...query };
|
|
38
|
+
}
|
|
39
|
+
default: {
|
|
40
|
+
if (source.includes('.')) {
|
|
41
|
+
const resolved = resolveDotPath(r, source);
|
|
42
|
+
return { ...(resolved ?? {}) };
|
|
43
|
+
}
|
|
44
|
+
const method = (r.method ?? 'GET').toUpperCase();
|
|
45
|
+
if (READ_METHODS.has(method))
|
|
46
|
+
return { ...query };
|
|
47
|
+
if (WRITE_METHODS.has(method))
|
|
48
|
+
return { ...query, ...body };
|
|
49
|
+
return { ...query };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function resolveDotPath(obj, path) {
|
|
54
|
+
let current = obj;
|
|
55
|
+
for (const segment of path.split('.')) {
|
|
56
|
+
if (current == null || typeof current !== 'object')
|
|
57
|
+
return undefined;
|
|
58
|
+
current = current[segment];
|
|
59
|
+
}
|
|
60
|
+
if (current == null || typeof current !== 'object')
|
|
61
|
+
return undefined;
|
|
62
|
+
return current;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=source_resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source_resolver.js","sourceRoot":"","sources":["../../src/source_resolver.ts"],"names":[],"mappings":"AAEA,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAClE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAQ9C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAY,EACZ,MAAmB;IAEnB,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAA6B,CAAC;IACrD,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAY,CAAC;IACjC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAC;IACzD,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;IAEvD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO;YACV,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;YAClD,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,CAAC;YAC5D,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;QACtB,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;gBAC3C,OAAO,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;YACjC,CAAC;YACD,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;YAClD,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,CAAC;YAC5D,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,GAAY,EAAE,IAAY;IAChD,IAAI,OAAO,GAAY,GAAG,CAAC;IAC3B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACrE,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACrE,OAAO,OAAkC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CursorParams } from './cursor.js';
|
|
2
|
+
import type { FilterInput } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The richer Spatie / JSON:API input shape produced by {@link parseSpatieRequest}.
|
|
5
|
+
* A superset of {@link FilterInput}: it adds cursor pagination
|
|
6
|
+
* ({@link CursorParams}), JSON:API sparse fieldsets (`select`) and relation
|
|
7
|
+
* includes (`include`). Ready to hand to `applyFilter` (offset) or `applyCursor`
|
|
8
|
+
* (keyset) — the extra fields are ignored by whichever runner does not use them.
|
|
9
|
+
*/
|
|
10
|
+
export interface SpatieInput extends FilterInput, CursorParams {
|
|
11
|
+
/** JSON:API sparse fieldsets (`fields[resource]=a,b`), flattened + de-duped. */
|
|
12
|
+
select?: string[];
|
|
13
|
+
/** Relation includes (`include=posts,comments`). */
|
|
14
|
+
include?: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse a decoded, Spatie-laravel-query-builder / JSON:API-style query object
|
|
18
|
+
* into a {@link SpatieInput}. This is the richer, additive counterpart to
|
|
19
|
+
* {@link parseFilterRequest}: it understands the same `filter`/`sort`/`search`
|
|
20
|
+
* shapes plus JSON:API `include`, sparse `fields[resource]` sets, and cursor
|
|
21
|
+
* pagination (`page[after]`/`page[before]`).
|
|
22
|
+
*
|
|
23
|
+
* | Query string | Decoded input | Mapped to |
|
|
24
|
+
* | ------------------------------- | ------------------------------------ | ------------------------------------------ |
|
|
25
|
+
* | `filter[name]=Al` | `{ filter: { name: 'Al' } }` | `equals` column filter |
|
|
26
|
+
* | `filter[id]=1,2,3` | `{ filter: { id: '1,2,3' } }` | `in` column filter |
|
|
27
|
+
* | `filter[age][gte]=18` | `{ filter: { age: { gte: '18' } } }` | operator column filter |
|
|
28
|
+
* | `sort=-createdAt,name` | `{ sort: '-createdAt,name' }` | sort items |
|
|
29
|
+
* | `include=posts,comments` | `{ include: 'posts,comments' }` | `include: ['posts', 'comments']` |
|
|
30
|
+
* | `fields[users]=id,name` | `{ fields: { users: 'id,name' } }` | `select: ['id', 'name']` |
|
|
31
|
+
* | `page[number]=2&page[size]=10` | `{ page: { number, size } }` | offset (`page`/`size`) |
|
|
32
|
+
* | `page[after]=cur&page[size]=10`| `{ page: { after, size } }` | cursor (`after`/`first`) |
|
|
33
|
+
*
|
|
34
|
+
* A pure reshape — no validation or allow-listing here; that happens downstream
|
|
35
|
+
* in `applyFilter` / `applyCursor` against the `FilterConfig`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseSpatieRequest(qs: unknown): SpatieInput;
|
|
38
|
+
//# sourceMappingURL=spatie_parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spatie_parser.d.ts","sourceRoot":"","sources":["../../src/spatie_parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,WAAW,WAAY,SAAQ,WAAW,EAAE,YAAY;IAC5D,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAWD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,WAAW,CA2B3D"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { 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
|
+
* | `include=posts,comments` | `{ include: 'posts,comments' }` | `include: ['posts', 'comments']` |
|
|
26
|
+
* | `fields[users]=id,name` | `{ fields: { users: 'id,name' } }` | `select: ['id', 'name']` |
|
|
27
|
+
* | `page[number]=2&page[size]=10` | `{ page: { number, size } }` | offset (`page`/`size`) |
|
|
28
|
+
* | `page[after]=cur&page[size]=10`| `{ page: { after, size } }` | cursor (`after`/`first`) |
|
|
29
|
+
*
|
|
30
|
+
* A pure reshape — no validation or allow-listing here; that happens downstream
|
|
31
|
+
* in `applyFilter` / `applyCursor` against the `FilterConfig`.
|
|
32
|
+
*/
|
|
33
|
+
export function parseSpatieRequest(qs) {
|
|
34
|
+
if (qs == null || typeof qs !== 'object')
|
|
35
|
+
return {};
|
|
36
|
+
const src = qs;
|
|
37
|
+
const out = {};
|
|
38
|
+
if (src.filter != null && typeof src.filter === 'object' && !Array.isArray(src.filter)) {
|
|
39
|
+
const filters = [];
|
|
40
|
+
for (const [field, value] of Object.entries(src.filter)) {
|
|
41
|
+
filters.push(...toColumnFilters(field, value));
|
|
42
|
+
}
|
|
43
|
+
if (filters.length > 0)
|
|
44
|
+
out.filters = filters;
|
|
45
|
+
}
|
|
46
|
+
const sort = parseSort(src.sort);
|
|
47
|
+
if (sort.length > 0)
|
|
48
|
+
out.sort = sort;
|
|
49
|
+
if (typeof src.search === 'string' && src.search.length > 0)
|
|
50
|
+
out.search = src.search;
|
|
51
|
+
const include = parseInclude(src.include);
|
|
52
|
+
if (include.length > 0)
|
|
53
|
+
out.include = include;
|
|
54
|
+
const select = parseSparseFieldsets(src.fields);
|
|
55
|
+
if (select.length > 0)
|
|
56
|
+
out.select = select;
|
|
57
|
+
applyPage(src.page, out);
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
/** Parse `include=posts,comments` (string or array) into a relation-name list. */
|
|
61
|
+
function parseInclude(include) {
|
|
62
|
+
if (typeof include === 'string') {
|
|
63
|
+
return include
|
|
64
|
+
.split(',')
|
|
65
|
+
.map((s) => s.trim())
|
|
66
|
+
.filter((s) => s.length > 0);
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(include)) {
|
|
69
|
+
return include.filter((s) => typeof s === 'string' && s.length > 0);
|
|
70
|
+
}
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Flatten JSON:API sparse fieldsets (`fields[resource]=a,b`) into a single
|
|
75
|
+
* order-preserving, de-duplicated list of column names. Per-resource keys are
|
|
76
|
+
* collapsed because a single SELECT applies to the primary entity.
|
|
77
|
+
*/
|
|
78
|
+
function parseSparseFieldsets(fields) {
|
|
79
|
+
if (fields == null || typeof fields !== 'object' || Array.isArray(fields))
|
|
80
|
+
return [];
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const value of Object.values(fields)) {
|
|
84
|
+
const cols = typeof value === 'string'
|
|
85
|
+
? value.split(',')
|
|
86
|
+
: Array.isArray(value)
|
|
87
|
+
? value.filter((s) => typeof s === 'string')
|
|
88
|
+
: [];
|
|
89
|
+
for (const raw of cols) {
|
|
90
|
+
const col = raw.trim();
|
|
91
|
+
if (col.length > 0 && !seen.has(col)) {
|
|
92
|
+
seen.add(col);
|
|
93
|
+
out.push(col);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Map a JSON:API `page` object onto `out`: `page[after]`/`page[before]` →
|
|
101
|
+
* cursor (`after`/`first`, `before`/`last`); otherwise `page[number]`/`page[size]`
|
|
102
|
+
* → offset (`page`/`size`). `after` wins if both cursor bounds are present.
|
|
103
|
+
*/
|
|
104
|
+
function applyPage(page, out) {
|
|
105
|
+
if (page == null || typeof page !== 'object' || Array.isArray(page))
|
|
106
|
+
return;
|
|
107
|
+
const p = page;
|
|
108
|
+
const after = typeof p.after === 'string' ? p.after : undefined;
|
|
109
|
+
const before = typeof p.before === 'string' ? p.before : undefined;
|
|
110
|
+
const size = toInt(p.size);
|
|
111
|
+
if (after !== undefined || before !== undefined) {
|
|
112
|
+
if (after !== undefined) {
|
|
113
|
+
out.after = after;
|
|
114
|
+
if (size !== undefined)
|
|
115
|
+
out.first = size;
|
|
116
|
+
}
|
|
117
|
+
else if (before !== undefined) {
|
|
118
|
+
out.before = before;
|
|
119
|
+
if (size !== undefined)
|
|
120
|
+
out.last = size;
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const number = toInt(p.number);
|
|
125
|
+
if (number !== undefined)
|
|
126
|
+
out.page = number;
|
|
127
|
+
if (size !== undefined)
|
|
128
|
+
out.size = size;
|
|
129
|
+
}
|
|
130
|
+
//# 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,62 @@
|
|
|
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(column: string, value: unknown): this;
|
|
35
|
+
whereIn(column: string, values: unknown[]): this;
|
|
36
|
+
whereNotIn(column: string, values: unknown[]): this;
|
|
37
|
+
whereNull(column: string): this;
|
|
38
|
+
whereNotNull(column: string): this;
|
|
39
|
+
whereBetween(column: string, range: [unknown, unknown]): this;
|
|
40
|
+
whereNotBetween(column: string, range: [unknown, unknown]): this;
|
|
41
|
+
whereILike(column: string, value: string): this;
|
|
42
|
+
orWhereILike(column: string, value: string): this;
|
|
43
|
+
orderBy(column: string, direction: 'asc' | 'desc'): this;
|
|
44
|
+
/** Record a raw predicate: `args` are `[sql, bindings]` (bindings default `[]`). */
|
|
45
|
+
whereRaw(sql: string, bindings?: readonly unknown[]): this;
|
|
46
|
+
/** Record a raw ordering: `args` are `[sql, bindings]` (bindings default `[]`). */
|
|
47
|
+
orderByRaw(sql: string, bindings?: readonly unknown[]): this;
|
|
48
|
+
limit(count: number): this;
|
|
49
|
+
/**
|
|
50
|
+
* Every recorded call across this builder and all nested groups and relation
|
|
51
|
+
* subqueries (excludes the anonymous group markers, but keeps `whereHas` since
|
|
52
|
+
* its recorded arg is the relation name, not a marker).
|
|
53
|
+
*/
|
|
54
|
+
flatten(): RecordedCall[];
|
|
55
|
+
/** Find the first flattened call matching `method`. */
|
|
56
|
+
find(method: string): RecordedCall | undefined;
|
|
57
|
+
/** Every flattened call matching `method` (handy for asserting relation subqueries). */
|
|
58
|
+
findAll(method: string): RecordedCall[];
|
|
59
|
+
}
|
|
60
|
+
/** Factory for a fresh {@link MockQueryBuilder} — the recording Lucid stand-in. */
|
|
61
|
+
export declare function makeMockQueryBuilder(): MockQueryBuilder;
|
|
62
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,oEAAoE;AACpE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,gBAAiB,YAAW,gBAAgB;IACvD,KAAK,EAAE,YAAY,EAAE,CAAM;IAC3B,QAAQ,EAAE,gBAAgB,EAAE,CAAM;IAElC,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,KAAK;IASb,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI;IAIrC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IAGjD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IAOpE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAG9C,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IAGhD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IAGnD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAG/B,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAGlC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI;IAG7D,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI;IAGhE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAG/C,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAGjD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI;IAGxD,oFAAoF;IACpF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,SAAS,OAAO,EAAO,GAAG,IAAI;IAG9D,mFAAmF;IACnF,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,SAAS,OAAO,EAAO,GAAG,IAAI;IAGhE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1B;;;;OAIG;IACH,OAAO,IAAI,YAAY,EAAE;IAKzB,uDAAuD;IACvD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAI9C,wFAAwF;IACxF,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;CAGxC;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,IAAI,gBAAgB,CAEvD"}
|