@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,154 @@
|
|
|
1
|
+
/** Enumerate the filterable field paths a spec admits, capped at `maxDepth` relation hops. */
|
|
2
|
+
export function filterableFieldPaths(spec, maxDepth = spec.maxDepth) {
|
|
3
|
+
return collectPaths(spec, 'filterable', maxDepth);
|
|
4
|
+
}
|
|
5
|
+
/** Enumerate the sortable field paths a spec admits, capped at `maxDepth` relation hops. */
|
|
6
|
+
export function sortableFieldPaths(spec, maxDepth = spec.maxDepth) {
|
|
7
|
+
return collectPaths(spec, 'sortable', maxDepth);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Walk the spec's base allow-list + relation whitelist into a flat, de-duped list
|
|
11
|
+
* of (dotted) field paths, bounded by `maxDepth` relation hops. A `'*'` allow-list
|
|
12
|
+
* cannot be enumerated (any column), so it contributes no concrete base names —
|
|
13
|
+
* the emitter then falls back to a permissive `string` field union.
|
|
14
|
+
*/
|
|
15
|
+
function collectPaths(spec, kind, maxDepth) {
|
|
16
|
+
const out = [];
|
|
17
|
+
const base = kind === 'filterable' ? spec.filterable : spec.sortable;
|
|
18
|
+
if (Array.isArray(base))
|
|
19
|
+
out.push(...base);
|
|
20
|
+
const walk = (relations, prefix, depth) => {
|
|
21
|
+
if (depth > maxDepth)
|
|
22
|
+
return;
|
|
23
|
+
for (const [relName, rel] of Object.entries(relations)) {
|
|
24
|
+
const cols = kind === 'filterable' ? rel.filterable : (rel.sortable ?? rel.filterable);
|
|
25
|
+
if (Array.isArray(cols)) {
|
|
26
|
+
for (const col of cols)
|
|
27
|
+
out.push([...prefix, relName, col].join('.'));
|
|
28
|
+
}
|
|
29
|
+
if (rel.relations)
|
|
30
|
+
walk(rel.relations, [...prefix, relName], depth + 1);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
walk(spec.relations, [], 1);
|
|
34
|
+
return [...new Set(out)];
|
|
35
|
+
}
|
|
36
|
+
/** `foo-bar_baz` → `FooBarBaz`. Strips non-identifier chars, PascalCases the rest. */
|
|
37
|
+
function toPascalCase(name) {
|
|
38
|
+
const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
39
|
+
const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
|
|
40
|
+
return /^[0-9]/.test(pascal) ? `_${pascal}` : pascal || 'Filter';
|
|
41
|
+
}
|
|
42
|
+
/** PascalCase but a lowercased first char — the value-identifier form (`peopleFilterQuery`). */
|
|
43
|
+
function toCamelCase(name) {
|
|
44
|
+
const pascal = toPascalCase(name);
|
|
45
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
46
|
+
}
|
|
47
|
+
/** `BlogPost`/`blog-post` → `blog_post` — the AdonisJS file-name convention. */
|
|
48
|
+
function toSnakeCase(name) {
|
|
49
|
+
return toPascalCase(name)
|
|
50
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
51
|
+
.toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
/** Map a field's type info to a TS type literal. Mirrors the NestJS `kindToTs` emitter. */
|
|
54
|
+
function fieldTypeToTs(info) {
|
|
55
|
+
if (info.typeRef)
|
|
56
|
+
return info.typeRef;
|
|
57
|
+
if (info.enumValues && info.enumValues.length > 0) {
|
|
58
|
+
return info.enumValues
|
|
59
|
+
.map((v) => (typeof v === 'number' ? String(v) : JSON.stringify(v)))
|
|
60
|
+
.join(' | ');
|
|
61
|
+
}
|
|
62
|
+
switch (info.kind) {
|
|
63
|
+
case 'string':
|
|
64
|
+
return 'string';
|
|
65
|
+
case 'number':
|
|
66
|
+
return 'number';
|
|
67
|
+
case 'boolean':
|
|
68
|
+
return 'boolean';
|
|
69
|
+
case 'date':
|
|
70
|
+
return 'Date';
|
|
71
|
+
case 'json':
|
|
72
|
+
return 'Record<string, unknown>';
|
|
73
|
+
default:
|
|
74
|
+
return 'unknown';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** The classifier kind stored in runtime meta (`typeRef`/enum collapse to their base bucket). */
|
|
78
|
+
function metaKind(info) {
|
|
79
|
+
return info.kind ?? 'unknown';
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Generate a typed filter client module for one {@link FilterSpec} — a pure
|
|
83
|
+
* string transform. The emitted module exports, for a spec named `people`:
|
|
84
|
+
*
|
|
85
|
+
* - `type PeopleFilterFields` — the union of filterable field paths (the security
|
|
86
|
+
* boundary, as concrete string literals);
|
|
87
|
+
* - `interface PeopleFilterFieldTypes` — the per-field value-type map (only when
|
|
88
|
+
* `fieldTypes` is supplied), which drives the client's operator/value narrowing;
|
|
89
|
+
* - `const peopleFilterMeta` — runtime metadata (filterable/sortable/searchable
|
|
90
|
+
* fields, whitelisted relations, per-field kinds, and the default sort / page
|
|
91
|
+
* size / max size + cursor keyset the runner paginates by);
|
|
92
|
+
* - `function peopleFilterQuery()` — a `filterQueryTyped<Fields, FieldTypes>()`
|
|
93
|
+
* factory returning a type-safe builder scoped to this spec.
|
|
94
|
+
*/
|
|
95
|
+
export function generateFilterClient(spec, options) {
|
|
96
|
+
const pascal = toPascalCase(options.name);
|
|
97
|
+
const camel = toCamelCase(options.name);
|
|
98
|
+
const clientModule = options.clientModule ?? '@adonis-agora/filter-client';
|
|
99
|
+
const maxDepth = options.maxDepth ?? spec.maxDepth;
|
|
100
|
+
const fieldTypes = options.fieldTypes ?? {};
|
|
101
|
+
const hasTypes = Object.keys(fieldTypes).length > 0;
|
|
102
|
+
const fields = filterableFieldPaths(spec, maxDepth);
|
|
103
|
+
const sortable = sortableFieldPaths(spec, maxDepth);
|
|
104
|
+
const relations = Object.keys(spec.relations);
|
|
105
|
+
const cursorKeyset = spec.defaultSort.map((s) => s.field);
|
|
106
|
+
const fieldsUnion = fields.length > 0 ? fields.map((f) => JSON.stringify(f)).join(' | ') : 'string';
|
|
107
|
+
const typesTypeName = `${pascal}FilterFieldTypes`;
|
|
108
|
+
const fieldsTypeName = `${pascal}FilterFields`;
|
|
109
|
+
const typeArgs = hasTypes ? `${fieldsTypeName}, ${typesTypeName}` : fieldsTypeName;
|
|
110
|
+
const lines = [];
|
|
111
|
+
if (options.banner ?? true) {
|
|
112
|
+
lines.push('// Code generated by @adonis-agora/filter. DO NOT EDIT.', `// Source: filter spec "${options.name}".`, '');
|
|
113
|
+
}
|
|
114
|
+
lines.push(`import { filterQueryTyped as _filterQueryTyped } from '${clientModule}';`, '', `/** Filterable field paths for \`${options.name}\` — the client's field-name allow-list. */`, `export type ${fieldsTypeName} = ${fieldsUnion};`, '');
|
|
115
|
+
if (hasTypes) {
|
|
116
|
+
lines.push(`/** Per-field value types for \`${options.name}\` (drives operator narrowing). */`);
|
|
117
|
+
lines.push(`export interface ${typesTypeName} {`);
|
|
118
|
+
for (const [field, info] of Object.entries(fieldTypes)) {
|
|
119
|
+
let ts = fieldTypeToTs(info);
|
|
120
|
+
if (info.nullable)
|
|
121
|
+
ts = `${ts} | null`;
|
|
122
|
+
lines.push(` ${JSON.stringify(field)}: ${ts};`);
|
|
123
|
+
}
|
|
124
|
+
lines.push('}', '');
|
|
125
|
+
}
|
|
126
|
+
const typesMeta = hasTypes
|
|
127
|
+
? `{ ${Object.entries(fieldTypes)
|
|
128
|
+
.map(([field, info]) => `${JSON.stringify(field)}: ${JSON.stringify(metaKind(info))}`)
|
|
129
|
+
.join(', ')} }`
|
|
130
|
+
: '{}';
|
|
131
|
+
lines.push(`/** Runtime filter metadata for \`${options.name}\`. */`, `export const ${camel}FilterMeta = {`, ` fields: [${fields.map((f) => JSON.stringify(f)).join(', ')}],`, ` sortable: [${sortable.map((f) => JSON.stringify(f)).join(', ')}],`, ` searchable: [${spec.searchable.map((f) => JSON.stringify(f)).join(', ')}],`, ` relations: [${relations.map((r) => JSON.stringify(r)).join(', ')}],`, ` types: ${typesMeta},`, ` defaultSort: [${spec.defaultSort
|
|
132
|
+
.map((s) => `{ field: ${JSON.stringify(s.field)}, direction: ${JSON.stringify(s.direction)} }`)
|
|
133
|
+
.join(', ')}],`, ` cursorKeyset: [${cursorKeyset.map((f) => JSON.stringify(f)).join(', ')}],`, ` defaultSize: ${spec.defaultSize ?? 'undefined'},`, ` maxSize: ${spec.maxSize ?? 'undefined'},`, '} as const;', '', `/** A type-safe filter-query builder scoped to \`${options.name}\`'s filterable fields. */`, `export function ${camel}FilterQuery() {`, ` return _filterQueryTyped<${typeArgs}>();`, '}', '');
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Expand a whole {@link FilterClientManifest} into per-spec generated modules —
|
|
138
|
+
* the pure core the `make:filter-client` ace command writes to disk. Kept pure
|
|
139
|
+
* (manifest in, modules out) so the command stays a thin IO wrapper and the
|
|
140
|
+
* expansion is itself testable without a running app.
|
|
141
|
+
*/
|
|
142
|
+
export function generateFilterClients(manifest) {
|
|
143
|
+
return Object.entries(manifest).map(([name, entry]) => ({
|
|
144
|
+
name,
|
|
145
|
+
filename: `${toSnakeCase(name)}_filter_client.ts`,
|
|
146
|
+
code: generateFilterClient(entry.spec, {
|
|
147
|
+
name,
|
|
148
|
+
...(entry.fieldTypes && { fieldTypes: entry.fieldTypes }),
|
|
149
|
+
...(entry.clientModule && { clientModule: entry.clientModule }),
|
|
150
|
+
...(entry.maxDepth !== undefined && { maxDepth: entry.maxDepth }),
|
|
151
|
+
}),
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=generate_client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate_client.js","sourceRoot":"","sources":["../../src/generate_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
+
export declare const VERSION = "0.1.0";
|
|
3
|
+
export type { ColumnFilter, FilterOperator, FilterOperatorAlias, FilterOperatorInput, } from './operators.js';
|
|
4
|
+
export { FILTER_OPERATORS, OPERATOR_ALIASES } from './operators.js';
|
|
5
|
+
export { InvalidColumnFilterError, MAX_FILTER_DEPTH, normalizeOperator, validateColumnFilter, validateColumnFilters, } from './validate-column-filter.js';
|
|
6
|
+
export { isOperatorObject, valueToColumnFilters } from './value-shape.js';
|
|
7
|
+
export { escapeLike } from './escape-like.js';
|
|
8
|
+
export type { AllowList, FilterConfig, FilterInput, FullTextSearchConfig, InputNormalizer, InputSource, SortItem, VectorSimilarityConfig, } from './types.js';
|
|
9
|
+
export { defineFilter, FilterDefinitionError, specToFilterConfig, } from './filter_spec.js';
|
|
10
|
+
export { filterableFieldPaths, generateFilterClient, generateFilterClients, sortableFieldPaths, } from './generate_client.js';
|
|
11
|
+
export type { FilterClientEntry, FilterClientManifest, FilterFieldKind, FilterFieldTypeInfo, GeneratedFilterClient, GenerateFilterClientOptions, } from './generate_client.js';
|
|
12
|
+
export type { DefineFilterOptions, FilterSpec, RelationColumns, RelationSpec, TenantResolver, TenantScopeSpec, } from './filter_spec.js';
|
|
13
|
+
export { applyCursorFromRequest, applyFilterFromRequest } from './apply_from_request.js';
|
|
14
|
+
export type { ApplyCursorFromRequestOptions, ApplyFromRequestOptions, FilterRequestContext, } from './apply_from_request.js';
|
|
15
|
+
export { parseFilterRequest, parseSort, toColumnFilters } from './parse_request.js';
|
|
16
|
+
export { parseSpatieRequest } from './spatie_parser.js';
|
|
17
|
+
export type { SpatieInput } from './spatie_parser.js';
|
|
18
|
+
export { applyCursor, applyFilter } from './runner.js';
|
|
19
|
+
export type { CursorConfig, ResolvedPagination } from './runner.js';
|
|
20
|
+
export { applyColumnFilters, applyFullTextSearch, applyKeyset, applySearch, applySort, applyVectorSimilarity, type FullTextSearchOptions, type QueryBuilderLike, type VectorDistanceMetric, type VectorSimilarityOptions, } from './lucid_adapter.js';
|
|
21
|
+
export { buildCursorPage, buildKeyset, decodeCursor, encodeCursor, extractCursorValues, reverseKeyset, } from './cursor.js';
|
|
22
|
+
export type { CursorPage, CursorParams, CursorValues, ResolvedCursor } from './cursor.js';
|
|
23
|
+
export { remapFilterAliases, remapSortAliases, resolveFieldAlias } from './field_aliases.js';
|
|
24
|
+
export type { FieldAliases } from './field_aliases.js';
|
|
25
|
+
export { normalizeInput } from './normalizer.js';
|
|
26
|
+
export type { NormalizeOptions } from './normalizer.js';
|
|
27
|
+
export { resolveInputFromRequest } from './source_resolver.js';
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,YAAY,EACV,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,QAAQ,EACR,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,YAAY,EACZ,cAAc,EACd,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACzF,YAAY,EACV,6BAA6B,EAC7B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,SAAS,EACT,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
+
export const VERSION = '0.1.0';
|
|
3
|
+
export { FILTER_OPERATORS, OPERATOR_ALIASES } from './operators.js';
|
|
4
|
+
export { InvalidColumnFilterError, MAX_FILTER_DEPTH, normalizeOperator, validateColumnFilter, validateColumnFilters, } from './validate-column-filter.js';
|
|
5
|
+
export { isOperatorObject, valueToColumnFilters } from './value-shape.js';
|
|
6
|
+
export { escapeLike } from './escape-like.js';
|
|
7
|
+
export { defineFilter, FilterDefinitionError, specToFilterConfig, } from './filter_spec.js';
|
|
8
|
+
export { filterableFieldPaths, generateFilterClient, generateFilterClients, sortableFieldPaths, } from './generate_client.js';
|
|
9
|
+
export { applyCursorFromRequest, applyFilterFromRequest } from './apply_from_request.js';
|
|
10
|
+
export { parseFilterRequest, parseSort, toColumnFilters } from './parse_request.js';
|
|
11
|
+
export { parseSpatieRequest } from './spatie_parser.js';
|
|
12
|
+
export { applyCursor, applyFilter } from './runner.js';
|
|
13
|
+
export { applyColumnFilters, applyFullTextSearch, applyKeyset, applySearch, applySort, applyVectorSimilarity, } from './lucid_adapter.js';
|
|
14
|
+
export { buildCursorPage, buildKeyset, decodeCursor, encodeCursor, extractCursorValues, reverseKeyset, } from './cursor.js';
|
|
15
|
+
export { remapFilterAliases, remapSortAliases, resolveFieldAlias } from './field_aliases.js';
|
|
16
|
+
export { normalizeInput } from './normalizer.js';
|
|
17
|
+
export { resolveInputFromRequest } from './source_resolver.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAQ/B,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAW9C,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAMzF,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,SAAS,EACT,qBAAqB,GAKtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE7F,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { ColumnFilter } from './operators.js';
|
|
2
|
+
import type { SortItem } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The structural subset of a Lucid `ModelQueryBuilder` / `DatabaseQueryBuilder`
|
|
5
|
+
* the adapter drives. An `@adonisjs/lucid` query builder satisfies it — declared
|
|
6
|
+
* locally so the adapter never hard-imports Lucid (and stays unit-testable with a
|
|
7
|
+
* recording mock). The nested-callback `where`/`orWhere` overloads model Lucid's
|
|
8
|
+
* grouping closures used for AND/OR composition.
|
|
9
|
+
*/
|
|
10
|
+
export interface QueryBuilderLike {
|
|
11
|
+
where(callback: (qb: QueryBuilderLike) => void): QueryBuilderLike;
|
|
12
|
+
where(column: string, value: unknown): QueryBuilderLike;
|
|
13
|
+
where(column: string, operator: string, value: unknown): QueryBuilderLike;
|
|
14
|
+
orWhere(callback: (qb: QueryBuilderLike) => void): QueryBuilderLike;
|
|
15
|
+
/**
|
|
16
|
+
* Constrain the query to rows whose `relation` has at least one related row
|
|
17
|
+
* matching the nested conditions — Lucid's `whereHas`. Used to translate a
|
|
18
|
+
* dotted relation-path filter (`posts.title = x`) into a real subquery
|
|
19
|
+
* (`whereHas('posts', (q) => q.where('title', x))`) instead of a dotted
|
|
20
|
+
* column reference. Optional on real Lucid builders; the recording mock
|
|
21
|
+
* implements it so relation translation stays unit-testable.
|
|
22
|
+
*/
|
|
23
|
+
whereHas(relation: string, callback: (qb: QueryBuilderLike) => void): QueryBuilderLike;
|
|
24
|
+
whereNot(column: string, value: unknown): QueryBuilderLike;
|
|
25
|
+
whereIn(column: string, values: unknown[]): QueryBuilderLike;
|
|
26
|
+
whereNotIn(column: string, values: unknown[]): QueryBuilderLike;
|
|
27
|
+
whereNull(column: string): QueryBuilderLike;
|
|
28
|
+
whereNotNull(column: string): QueryBuilderLike;
|
|
29
|
+
whereBetween(column: string, range: [unknown, unknown]): QueryBuilderLike;
|
|
30
|
+
whereNotBetween(column: string, range: [unknown, unknown]): QueryBuilderLike;
|
|
31
|
+
whereILike(column: string, value: string): QueryBuilderLike;
|
|
32
|
+
orWhereILike(column: string, value: string): QueryBuilderLike;
|
|
33
|
+
orderBy(column: string, direction: 'asc' | 'desc'): QueryBuilderLike;
|
|
34
|
+
/**
|
|
35
|
+
* Add a raw SQL predicate with positional bindings — Lucid's `whereRaw`. The
|
|
36
|
+
* escape hatch for constraints no structured method can express, used here for
|
|
37
|
+
* pgvector similarity (`embedding <=> ?::vector < ?`). `sql` is server-authored
|
|
38
|
+
* (never client text); every user value travels as a `?` binding. Optional on
|
|
39
|
+
* real Lucid builders; the recording mock implements it so vector translation
|
|
40
|
+
* stays unit-testable and the package stays framework-free.
|
|
41
|
+
*/
|
|
42
|
+
whereRaw(sql: string, bindings?: readonly unknown[]): QueryBuilderLike;
|
|
43
|
+
/**
|
|
44
|
+
* Add a raw SQL ordering with positional bindings — Lucid's `orderByRaw`. Used
|
|
45
|
+
* to order by a pgvector distance expression (`embedding <=> ?::vector asc`),
|
|
46
|
+
* which no column-name `orderBy` can express. Same seam contract as
|
|
47
|
+
* {@link QueryBuilderLike.whereRaw}.
|
|
48
|
+
*/
|
|
49
|
+
orderByRaw(sql: string, bindings?: readonly unknown[]): QueryBuilderLike;
|
|
50
|
+
limit(count: number): QueryBuilderLike;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A pgvector distance metric → the operator applied between the vector column and
|
|
54
|
+
* the query embedding:
|
|
55
|
+
*
|
|
56
|
+
* - `'cosine'` — cosine distance, pgvector `<=>` (default; the usual choice for
|
|
57
|
+
* normalized embeddings).
|
|
58
|
+
* - `'l2'` — Euclidean / L2 distance, pgvector `<->`.
|
|
59
|
+
* - `'innerProduct'` — negative inner product, pgvector `<#>`.
|
|
60
|
+
*
|
|
61
|
+
* All three are *distance* operators (smaller = more similar), so nearest-first
|
|
62
|
+
* ranking is always ascending order.
|
|
63
|
+
*/
|
|
64
|
+
export type VectorDistanceMetric = 'cosine' | 'l2' | 'innerProduct';
|
|
65
|
+
/**
|
|
66
|
+
* Options for {@link applyVectorSimilarity} — an **embedding similarity** ordering
|
|
67
|
+
* (pgvector) against a `vector` column. This is DISTINCT from full-text search
|
|
68
|
+
* ({@link applyFullTextSearch}): here a query *embedding* is ranked against the
|
|
69
|
+
* column by a distance metric, optionally filtered by a distance threshold and
|
|
70
|
+
* truncated to the top-K nearest rows. (Full-text search matches a text *query
|
|
71
|
+
* string* against a tsvector document.)
|
|
72
|
+
*/
|
|
73
|
+
export interface VectorSimilarityOptions {
|
|
74
|
+
/** The pgvector column to compare the query embedding against (e.g. `'embedding'`). */
|
|
75
|
+
column: string;
|
|
76
|
+
/** The query embedding — the vector rows are ranked by similarity to. */
|
|
77
|
+
vector: readonly number[];
|
|
78
|
+
/** Distance metric → pgvector operator. Default `'cosine'` (`<=>`). */
|
|
79
|
+
metric?: VectorDistanceMetric;
|
|
80
|
+
/**
|
|
81
|
+
* Keep only rows whose distance to the query embedding is strictly below this
|
|
82
|
+
* value (a `WHERE embedding <=> ? < threshold`). Omitted → no distance filter.
|
|
83
|
+
*/
|
|
84
|
+
threshold?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Limit the result to the K nearest rows (a `LIMIT`). Omitted → no limit is
|
|
87
|
+
* added here (the caller's pagination still applies).
|
|
88
|
+
*/
|
|
89
|
+
topK?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Order by ascending distance (nearest first). Default `true`. Set `false` to
|
|
92
|
+
* apply only a threshold filter without changing the query's ordering.
|
|
93
|
+
*/
|
|
94
|
+
order?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Apply an **embedding similarity** ordering (pgvector) to a Lucid query builder.
|
|
98
|
+
*
|
|
99
|
+
* This is *not* full-text search — it ranks rows by distance between a stored
|
|
100
|
+
* embedding column and a query *embedding vector*. For matching a text query
|
|
101
|
+
* string, use {@link applyFullTextSearch} instead.
|
|
102
|
+
*
|
|
103
|
+
* pgvector expresses similarity through raw distance operators (`<=>`, `<->`,
|
|
104
|
+
* `<#>`) that no structured query-builder method covers, so this drives the
|
|
105
|
+
* adapter's raw seam ({@link QueryBuilderLike.whereRaw}/`orderByRaw`) with the
|
|
106
|
+
* query embedding passed as a positional binding — the column name is the only
|
|
107
|
+
* interpolated fragment, and it is validated against a strict identifier charset
|
|
108
|
+
* so it can never carry injection. The distance expression is
|
|
109
|
+
* `<column> <op> ?::vector`, cast to `vector` so a text binding compares against
|
|
110
|
+
* the column.
|
|
111
|
+
*
|
|
112
|
+
* A no-op when the embedding is empty. When `order` is not `false`, rows are
|
|
113
|
+
* ordered nearest-first (ascending distance); `threshold` adds a max-distance
|
|
114
|
+
* filter; `topK` truncates to the K nearest.
|
|
115
|
+
*/
|
|
116
|
+
export declare function applyVectorSimilarity(qb: QueryBuilderLike, opts: VectorSimilarityOptions): void;
|
|
117
|
+
/**
|
|
118
|
+
* Options for {@link applyFullTextSearch} — a Postgres **tsvector full-text
|
|
119
|
+
* search** (the parity behavior of the NestJS reference's `applyVectorSearch`).
|
|
120
|
+
* The user query string is matched against a text-search document with
|
|
121
|
+
* `websearch_to_tsquery` and the `@@` operator, optionally ranked by `ts_rank`.
|
|
122
|
+
*
|
|
123
|
+
* This is DISTINCT from {@link VectorSimilarityOptions} (embedding similarity):
|
|
124
|
+
* here the input is arbitrary *text*, tokenized by a language config; there the
|
|
125
|
+
* input is a numeric *embedding vector*.
|
|
126
|
+
*/
|
|
127
|
+
export interface FullTextSearchOptions {
|
|
128
|
+
/**
|
|
129
|
+
* The user's raw search text. Always passed as a positional binding to
|
|
130
|
+
* `websearch_to_tsquery` — never interpolated into SQL. A no-op when blank.
|
|
131
|
+
*/
|
|
132
|
+
query: string;
|
|
133
|
+
/**
|
|
134
|
+
* The document to match against. Either a single precomputed `tsvector`
|
|
135
|
+
* column (matched directly with `@@`), or one-or-more plain text columns that
|
|
136
|
+
* are wrapped in `to_tsvector(<language>, ...)` at query time (see
|
|
137
|
+
* {@link FullTextSearchOptions.columnKind}). Passing multiple columns implies
|
|
138
|
+
* `'text'`.
|
|
139
|
+
*/
|
|
140
|
+
column: string | readonly string[];
|
|
141
|
+
/**
|
|
142
|
+
* Postgres text-search config / language used for both `websearch_to_tsquery`
|
|
143
|
+
* and any `to_tsvector` wrapping (e.g. `'english'`, `'simple'`). Default
|
|
144
|
+
* `'english'`. Validated against the strict identifier charset before it is
|
|
145
|
+
* spliced into SQL.
|
|
146
|
+
*/
|
|
147
|
+
language?: string;
|
|
148
|
+
/**
|
|
149
|
+
* Add relevance ordering for matched rows (`ORDER BY ts_rank(...) DESC`). Off
|
|
150
|
+
* by default because it changes the query's default ordering; opt in for
|
|
151
|
+
* best-match-first results.
|
|
152
|
+
*/
|
|
153
|
+
rank?: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* How {@link FullTextSearchOptions.column} is treated:
|
|
156
|
+
* - `'tsvector'` — a precomputed `tsvector` column, matched directly (`col @@ ...`);
|
|
157
|
+
* - `'text'` — plain text column(s), wrapped in `to_tsvector(<language>, col ...)`.
|
|
158
|
+
*
|
|
159
|
+
* Defaults to `'tsvector'` for a single column and `'text'` when multiple
|
|
160
|
+
* columns are given.
|
|
161
|
+
*/
|
|
162
|
+
columnKind?: 'tsvector' | 'text';
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Apply a Postgres **tsvector full-text search** to a Lucid query builder — the
|
|
166
|
+
* parity port of the NestJS reference's `applyVectorSearch` (tsvector), NOT the
|
|
167
|
+
* embedding similarity {@link applyVectorSimilarity}.
|
|
168
|
+
*
|
|
169
|
+
* Injection-safety: the user `query` string ALWAYS travels as a positional
|
|
170
|
+
* binding (`websearch_to_tsquery('<lang>', ?)`); it is never interpolated.
|
|
171
|
+
* `websearch_to_tsquery` (not the raw `to_tsquery`) parses arbitrary user input
|
|
172
|
+
* — multi-word text, `"quoted phrases"`, `-exclude`, `or` — without throwing a
|
|
173
|
+
* syntax error. The only spliced fragments are the column name(s) and the
|
|
174
|
+
* language config, each validated against the strict identifier charset
|
|
175
|
+
* ({@link SAFE_IDENTIFIER}) before use, so neither can carry injection.
|
|
176
|
+
*
|
|
177
|
+
* The match predicate is `<document> @@ websearch_to_tsquery('<lang>', ?)` where
|
|
178
|
+
* `<document>` is either the tsvector column directly, or
|
|
179
|
+
* `to_tsvector('<lang>', coalesce(col1,'') || ' ' || coalesce(col2,''))` for
|
|
180
|
+
* text columns. When `rank` is set, rows are additionally ordered by descending
|
|
181
|
+
* `ts_rank(<document>, websearch_to_tsquery('<lang>', ?))`. A no-op for a blank
|
|
182
|
+
* query.
|
|
183
|
+
*/
|
|
184
|
+
export declare function applyFullTextSearch(qb: QueryBuilderLike, opts: FullTextSearchOptions): void;
|
|
185
|
+
/** Apply an array of column filters (combined with AND) to a Lucid query builder. */
|
|
186
|
+
export declare function applyColumnFilters(qb: QueryBuilderLike, filters: ColumnFilter[]): void;
|
|
187
|
+
/** Apply sort directives to a Lucid query builder, in order. */
|
|
188
|
+
export declare function applySort(qb: QueryBuilderLike, sorts: SortItem[]): void;
|
|
189
|
+
/**
|
|
190
|
+
* Apply a keyset (cursor) seek predicate to a Lucid query builder for row-value
|
|
191
|
+
* comparison across the keyset columns.
|
|
192
|
+
*
|
|
193
|
+
* Given a keyset (the active sort columns plus a primary-key tiebreaker, each
|
|
194
|
+
* with a direction) and a boundary row's values, this constrains the query to
|
|
195
|
+
* rows strictly *after* the boundary in keyset order. The row-value comparison
|
|
196
|
+
* is expanded into the portable "OR of AND tiers" form that works across all
|
|
197
|
+
* SQL dialects:
|
|
198
|
+
*
|
|
199
|
+
* ```text
|
|
200
|
+
* (c0 OP0 v0)
|
|
201
|
+
* OR (c0 = v0 AND c1 OP1 v1)
|
|
202
|
+
* OR (c0 = v0 AND c1 = v1 AND c2 OP2 v2) ...
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* where `OPi` is `>` for an `asc` column and `<` for a `desc` column. The whole
|
|
206
|
+
* predicate is wrapped in one AND-group so it composes with any existing
|
|
207
|
+
* `where` conditions. A no-op when the keyset is empty or `values` does not line
|
|
208
|
+
* up positionally with it.
|
|
209
|
+
*/
|
|
210
|
+
export declare function applyKeyset(qb: QueryBuilderLike, keyset: SortItem[], values: unknown[]): void;
|
|
211
|
+
/** Apply a free-text ILIKE search across `columns` (OR-combined) to a Lucid query builder. */
|
|
212
|
+
export declare function applySearch(qb: QueryBuilderLike, term: string, columns: string[]): void;
|
|
213
|
+
//# sourceMappingURL=lucid_adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lucid_adapter.d.ts","sourceRoot":"","sources":["../../src/lucid_adapter.ts"],"names":[],"mappings":"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"}
|