@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.
Files changed (168) hide show
  1. package/CHANGELOG.md +417 -0
  2. package/LICENSE +21 -0
  3. package/README.md +75 -0
  4. package/dist/commands/main.d.ts +19 -0
  5. package/dist/commands/main.d.ts.map +1 -0
  6. package/dist/commands/main.js +19 -0
  7. package/dist/commands/main.js.map +1 -0
  8. package/dist/commands/make_filter.d.ts +18 -0
  9. package/dist/commands/make_filter.d.ts.map +1 -0
  10. package/dist/commands/make_filter.js +32 -0
  11. package/dist/commands/make_filter.js.map +1 -0
  12. package/dist/commands/make_filter_client.d.ts +35 -0
  13. package/dist/commands/make_filter_client.d.ts.map +1 -0
  14. package/dist/commands/make_filter_client.js +85 -0
  15. package/dist/commands/make_filter_client.js.map +1 -0
  16. package/dist/configure.d.ts +15 -0
  17. package/dist/configure.d.ts.map +1 -0
  18. package/dist/configure.js +20 -0
  19. package/dist/configure.js.map +1 -0
  20. package/dist/providers/filter_provider.d.ts +28 -0
  21. package/dist/providers/filter_provider.d.ts.map +1 -0
  22. package/dist/providers/filter_provider.js +33 -0
  23. package/dist/providers/filter_provider.js.map +1 -0
  24. package/dist/src/aggregate.d.ts +70 -0
  25. package/dist/src/aggregate.d.ts.map +1 -0
  26. package/dist/src/aggregate.js +195 -0
  27. package/dist/src/aggregate.js.map +1 -0
  28. package/dist/src/aggregate_path.d.ts +25 -0
  29. package/dist/src/aggregate_path.d.ts.map +1 -0
  30. package/dist/src/aggregate_path.js +35 -0
  31. package/dist/src/aggregate_path.js.map +1 -0
  32. package/dist/src/apply_from_request.d.ts +85 -0
  33. package/dist/src/apply_from_request.d.ts.map +1 -0
  34. package/dist/src/apply_from_request.js +164 -0
  35. package/dist/src/apply_from_request.js.map +1 -0
  36. package/dist/src/base_filter.d.ts +49 -0
  37. package/dist/src/base_filter.d.ts.map +1 -0
  38. package/dist/src/base_filter.js +33 -0
  39. package/dist/src/base_filter.js.map +1 -0
  40. package/dist/src/base_model_filter.d.ts +90 -0
  41. package/dist/src/base_model_filter.d.ts.map +1 -0
  42. package/dist/src/base_model_filter.js +85 -0
  43. package/dist/src/base_model_filter.js.map +1 -0
  44. package/dist/src/coerce_value.d.ts +26 -0
  45. package/dist/src/coerce_value.d.ts.map +1 -0
  46. package/dist/src/coerce_value.js +82 -0
  47. package/dist/src/coerce_value.js.map +1 -0
  48. package/dist/src/cursor.d.ts +92 -0
  49. package/dist/src/cursor.d.ts.map +1 -0
  50. package/dist/src/cursor.js +107 -0
  51. package/dist/src/cursor.js.map +1 -0
  52. package/dist/src/custom_filter.d.ts +36 -0
  53. package/dist/src/custom_filter.d.ts.map +1 -0
  54. package/dist/src/custom_filter.js +127 -0
  55. package/dist/src/custom_filter.js.map +1 -0
  56. package/dist/src/decorator_metadata.d.ts +41 -0
  57. package/dist/src/decorator_metadata.d.ts.map +1 -0
  58. package/dist/src/decorator_metadata.js +83 -0
  59. package/dist/src/decorator_metadata.js.map +1 -0
  60. package/dist/src/decorators.d.ts +73 -0
  61. package/dist/src/decorators.d.ts.map +1 -0
  62. package/dist/src/decorators.js +118 -0
  63. package/dist/src/decorators.js.map +1 -0
  64. package/dist/src/escape-like.d.ts +10 -0
  65. package/dist/src/escape-like.d.ts.map +1 -0
  66. package/dist/src/escape-like.js +15 -0
  67. package/dist/src/escape-like.js.map +1 -0
  68. package/dist/src/field_aliases.d.ts +30 -0
  69. package/dist/src/field_aliases.d.ts.map +1 -0
  70. package/dist/src/field_aliases.js +65 -0
  71. package/dist/src/field_aliases.js.map +1 -0
  72. package/dist/src/filter_class.d.ts +46 -0
  73. package/dist/src/filter_class.d.ts.map +1 -0
  74. package/dist/src/filter_class.js +244 -0
  75. package/dist/src/filter_class.js.map +1 -0
  76. package/dist/src/filter_spec.d.ts +231 -0
  77. package/dist/src/filter_spec.d.ts.map +1 -0
  78. package/dist/src/filter_spec.js +182 -0
  79. package/dist/src/filter_spec.js.map +1 -0
  80. package/dist/src/filterable_mixin.d.ts +49 -0
  81. package/dist/src/filterable_mixin.d.ts.map +1 -0
  82. package/dist/src/filterable_mixin.js +47 -0
  83. package/dist/src/filterable_mixin.js.map +1 -0
  84. package/dist/src/generate_client.d.ts +107 -0
  85. package/dist/src/generate_client.d.ts.map +1 -0
  86. package/dist/src/generate_client.js +164 -0
  87. package/dist/src/generate_client.js.map +1 -0
  88. package/dist/src/group_by_count.d.ts +67 -0
  89. package/dist/src/group_by_count.d.ts.map +1 -0
  90. package/dist/src/group_by_count.js +71 -0
  91. package/dist/src/group_by_count.js.map +1 -0
  92. package/dist/src/in_memory.d.ts +279 -0
  93. package/dist/src/in_memory.d.ts.map +1 -0
  94. package/dist/src/in_memory.js +810 -0
  95. package/dist/src/in_memory.js.map +1 -0
  96. package/dist/src/index.d.ts +51 -0
  97. package/dist/src/index.d.ts.map +1 -0
  98. package/dist/src/index.js +33 -0
  99. package/dist/src/index.js.map +1 -0
  100. package/dist/src/lucid_adapter.d.ts +311 -0
  101. package/dist/src/lucid_adapter.d.ts.map +1 -0
  102. package/dist/src/lucid_adapter.js +469 -0
  103. package/dist/src/lucid_adapter.js.map +1 -0
  104. package/dist/src/lucid_macros.d.ts +87 -0
  105. package/dist/src/lucid_macros.d.ts.map +1 -0
  106. package/dist/src/lucid_macros.js +67 -0
  107. package/dist/src/lucid_macros.js.map +1 -0
  108. package/dist/src/normalizer.d.ts +21 -0
  109. package/dist/src/normalizer.d.ts.map +1 -0
  110. package/dist/src/normalizer.js +61 -0
  111. package/dist/src/normalizer.js.map +1 -0
  112. package/dist/src/operators.d.ts +37 -0
  113. package/dist/src/operators.d.ts.map +1 -0
  114. package/dist/src/operators.js +43 -0
  115. package/dist/src/operators.js.map +1 -0
  116. package/dist/src/paginate.d.ts +12 -0
  117. package/dist/src/paginate.d.ts.map +1 -0
  118. package/dist/src/paginate.js +88 -0
  119. package/dist/src/paginate.js.map +1 -0
  120. package/dist/src/pagination_stash.d.ts +4 -0
  121. package/dist/src/pagination_stash.d.ts.map +1 -0
  122. package/dist/src/pagination_stash.js +19 -0
  123. package/dist/src/pagination_stash.js.map +1 -0
  124. package/dist/src/parse_request.d.ts +50 -0
  125. package/dist/src/parse_request.d.ts.map +1 -0
  126. package/dist/src/parse_request.js +220 -0
  127. package/dist/src/parse_request.js.map +1 -0
  128. package/dist/src/runner.d.ts +65 -0
  129. package/dist/src/runner.d.ts.map +1 -0
  130. package/dist/src/runner.js +371 -0
  131. package/dist/src/runner.js.map +1 -0
  132. package/dist/src/source_resolver.d.ts +18 -0
  133. package/dist/src/source_resolver.d.ts.map +1 -0
  134. package/dist/src/source_resolver.js +64 -0
  135. package/dist/src/source_resolver.js.map +1 -0
  136. package/dist/src/spatie_parser.d.ts +39 -0
  137. package/dist/src/spatie_parser.d.ts.map +1 -0
  138. package/dist/src/spatie_parser.js +134 -0
  139. package/dist/src/spatie_parser.js.map +1 -0
  140. package/dist/src/testing.d.ts +73 -0
  141. package/dist/src/testing.d.ts.map +1 -0
  142. package/dist/src/testing.js +136 -0
  143. package/dist/src/testing.js.map +1 -0
  144. package/dist/src/types.d.ts +226 -0
  145. package/dist/src/types.d.ts.map +1 -0
  146. package/dist/src/types.js +2 -0
  147. package/dist/src/types.js.map +1 -0
  148. package/dist/src/validate-column-filter.d.ts +30 -0
  149. package/dist/src/validate-column-filter.d.ts.map +1 -0
  150. package/dist/src/validate-column-filter.js +148 -0
  151. package/dist/src/validate-column-filter.js.map +1 -0
  152. package/dist/src/value-shape.d.ts +18 -0
  153. package/dist/src/value-shape.d.ts.map +1 -0
  154. package/dist/src/value-shape.js +36 -0
  155. package/dist/src/value-shape.js.map +1 -0
  156. package/dist/stubs/main.d.ts +3 -0
  157. package/dist/stubs/main.d.ts.map +1 -0
  158. package/dist/stubs/main.js +4 -0
  159. package/dist/stubs/main.js.map +1 -0
  160. package/dist/stubs/make/filter/main.stub +45 -0
  161. package/package.json +102 -0
  162. package/skills/filter-basics/SKILL.md +285 -0
  163. package/skills/filter-codegen/SKILL.md +246 -0
  164. package/skills/filter-definitions/SKILL.md +340 -0
  165. package/skills/filter-querying/SKILL.md +319 -0
  166. package/skills/filter-safety/SKILL.md +304 -0
  167. package/stubs/main.ts +4 -0
  168. package/stubs/make/filter/main.stub +45 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate_client.d.ts","sourceRoot":"","sources":["../../src/generate_client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAC;AAEjE;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,wFAAwF;IACxF,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,kGAAkG;IAClG,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC1C,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,gDAAgD;AAChD,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACjD,wFAAwF;IACxF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,SAAgB,GAAG,MAAM,EAAE,CAEzF;AAED,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,SAAgB,GAAG,MAAM,EAAE,CAEvF;AA0FD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,2BAA2B,GACnC,MAAM,CAsFR;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,IAAI,EAAE,UAAU,CAAC;IACjB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACjD,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qFAAqF;AACrF,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAErE,8FAA8F;AAC9F,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,qBAAqB,EAAE,CAW7F"}
@@ -0,0 +1,164 @@
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
+ // Computed/virtual fields are both filterable and sortable — surface their
21
+ // declared aliases in the client's field union so a generated client can
22
+ // reference `fullName`/`postCount` exactly like a real column.
23
+ if (spec.computed)
24
+ out.push(...Object.keys(spec.computed));
25
+ const walk = (relations, prefix, depth) => {
26
+ if (depth > maxDepth)
27
+ return;
28
+ for (const [relName, rel] of Object.entries(relations)) {
29
+ const cols = kind === 'filterable' ? rel.filterable : (rel.sortable ?? rel.filterable);
30
+ if (Array.isArray(cols)) {
31
+ for (const col of cols)
32
+ out.push([...prefix, relName, col].join('.'));
33
+ }
34
+ if (rel.relations)
35
+ walk(rel.relations, [...prefix, relName], depth + 1);
36
+ }
37
+ };
38
+ walk(spec.relations, [], 1);
39
+ return [...new Set(out)];
40
+ }
41
+ /** `foo-bar_baz` → `FooBarBaz`. Strips non-identifier chars, PascalCases the rest. */
42
+ function toPascalCase(name) {
43
+ const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean);
44
+ const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
45
+ return /^[0-9]/.test(pascal) ? `_${pascal}` : pascal || 'Filter';
46
+ }
47
+ /** PascalCase but a lowercased first char — the value-identifier form (`peopleFilterQuery`). */
48
+ function toCamelCase(name) {
49
+ const pascal = toPascalCase(name);
50
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
51
+ }
52
+ /** `BlogPost`/`blog-post` → `blog_post` — the AdonisJS file-name convention. */
53
+ function toSnakeCase(name) {
54
+ return toPascalCase(name)
55
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
56
+ .toLowerCase();
57
+ }
58
+ /** Map a field's type info to a TS type literal. Mirrors the NestJS `kindToTs` emitter. */
59
+ function fieldTypeToTs(info) {
60
+ if (info.typeRef)
61
+ return info.typeRef;
62
+ if (info.enumValues && info.enumValues.length > 0) {
63
+ return info.enumValues
64
+ .map((v) => (typeof v === 'number' ? String(v) : JSON.stringify(v)))
65
+ .join(' | ');
66
+ }
67
+ switch (info.kind) {
68
+ case 'string':
69
+ return 'string';
70
+ case 'number':
71
+ return 'number';
72
+ case 'boolean':
73
+ return 'boolean';
74
+ case 'date':
75
+ return 'Date';
76
+ case 'json':
77
+ return 'Record<string, unknown>';
78
+ default:
79
+ return 'unknown';
80
+ }
81
+ }
82
+ /** The classifier kind stored in runtime meta (`typeRef`/enum collapse to their base bucket). */
83
+ function metaKind(info) {
84
+ return info.kind ?? 'unknown';
85
+ }
86
+ /**
87
+ * Generate a typed filter client module for one {@link FilterSpec} — a pure
88
+ * string transform. The emitted module exports, for a spec named `people`:
89
+ *
90
+ * - `type PeopleFilterFields` — the union of filterable field paths (the security
91
+ * boundary, as concrete string literals);
92
+ * - `interface PeopleFilterFieldTypes` — the per-field value-type map (only when
93
+ * `fieldTypes` is supplied), which drives the client's operator/value narrowing;
94
+ * - `const peopleFilterMeta` — runtime metadata (filterable/sortable/searchable
95
+ * fields, whitelisted relations, per-field kinds, and the default sort / page
96
+ * size / max size + cursor keyset the runner paginates by);
97
+ * - `function peopleFilterQuery()` — a `filterQueryTyped<Fields, FieldTypes>()`
98
+ * factory returning a type-safe builder scoped to this spec.
99
+ */
100
+ export function generateFilterClient(spec, options) {
101
+ const pascal = toPascalCase(options.name);
102
+ const camel = toCamelCase(options.name);
103
+ const clientModule = options.clientModule ?? '@adonis-agora/filter-client';
104
+ const maxDepth = options.maxDepth ?? spec.maxDepth;
105
+ // The spec's own `fieldTypes` are the default source: declaring a kind there already drives
106
+ // server-side value coercion, so inheriting it here means one declaration feeds both ends
107
+ // instead of asking callers to repeat themselves in the manifest. An explicit `options.fieldTypes`
108
+ // still wins, for a caller that wants richer codegen-only info (enumValues/typeRef) than the
109
+ // server needs.
110
+ const fieldTypes = options.fieldTypes ?? spec.fieldTypes ?? {};
111
+ const hasTypes = Object.keys(fieldTypes).length > 0;
112
+ const fields = filterableFieldPaths(spec, maxDepth);
113
+ const sortable = sortableFieldPaths(spec, maxDepth);
114
+ const relations = Object.keys(spec.relations);
115
+ const cursorKeyset = spec.defaultSort.map((s) => s.field);
116
+ const fieldsUnion = fields.length > 0 ? fields.map((f) => JSON.stringify(f)).join(' | ') : 'string';
117
+ const typesTypeName = `${pascal}FilterFieldTypes`;
118
+ const fieldsTypeName = `${pascal}FilterFields`;
119
+ const typeArgs = hasTypes ? `${fieldsTypeName}, ${typesTypeName}` : fieldsTypeName;
120
+ const lines = [];
121
+ if (options.banner ?? true) {
122
+ lines.push('// Code generated by @adonis-agora/filter. DO NOT EDIT.', `// Source: filter spec "${options.name}".`, '');
123
+ }
124
+ 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};`, '');
125
+ if (hasTypes) {
126
+ lines.push(`/** Per-field value types for \`${options.name}\` (drives operator narrowing). */`);
127
+ lines.push(`export interface ${typesTypeName} {`);
128
+ for (const [field, info] of Object.entries(fieldTypes)) {
129
+ let ts = fieldTypeToTs(info);
130
+ if (info.nullable)
131
+ ts = `${ts} | null`;
132
+ lines.push(` ${JSON.stringify(field)}: ${ts};`);
133
+ }
134
+ lines.push('}', '');
135
+ }
136
+ const typesMeta = hasTypes
137
+ ? `{ ${Object.entries(fieldTypes)
138
+ .map(([field, info]) => `${JSON.stringify(field)}: ${JSON.stringify(metaKind(info))}`)
139
+ .join(', ')} }`
140
+ : '{}';
141
+ 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
142
+ .map((s) => `{ field: ${JSON.stringify(s.field)}, direction: ${JSON.stringify(s.direction)} }`)
143
+ .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}>();`, '}', '');
144
+ return lines.join('\n');
145
+ }
146
+ /**
147
+ * Expand a whole {@link FilterClientManifest} into per-spec generated modules —
148
+ * the pure core the `make:filter-client` ace command writes to disk. Kept pure
149
+ * (manifest in, modules out) so the command stays a thin IO wrapper and the
150
+ * expansion is itself testable without a running app.
151
+ */
152
+ export function generateFilterClients(manifest) {
153
+ return Object.entries(manifest).map(([name, entry]) => ({
154
+ name,
155
+ filename: `${toSnakeCase(name)}_filter_client.ts`,
156
+ code: generateFilterClient(entry.spec, {
157
+ name,
158
+ ...(entry.fieldTypes && { fieldTypes: entry.fieldTypes }),
159
+ ...(entry.clientModule && { clientModule: entry.clientModule }),
160
+ ...(entry.maxDepth !== undefined && { maxDepth: entry.maxDepth }),
161
+ }),
162
+ }));
163
+ }
164
+ //# 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,67 @@
1
+ import { type FilterRequestContext } from './apply_from_request.js';
2
+ import type { CustomFilterClass } from './custom_filter.js';
3
+ import { type FilterClass } from './filter_class.js';
4
+ import type { FilterSpec } from './filter_spec.js';
5
+ import type { QueryBuilderLike } from './lucid_adapter.js';
6
+ import type { FilterInput, GroupByCountOptions } from './types.js';
7
+ /** One enumerated value and how many matching rows carry it — the rows behind a picker. */
8
+ export interface GroupByCountRow {
9
+ value: unknown;
10
+ count: number;
11
+ }
12
+ /**
13
+ * Count the distinct values of one field over a CUSTOM backend — the seam a non-Lucide store
14
+ * implements so its consoles get pickers too. The helper runs the filter class over the active
15
+ * scope first (unknown fields fail there, loudly), then hands the narrowed draft over: the
16
+ * adapter never parses a query string, it only counts.
17
+ */
18
+ export interface GroupByCountAdapter<D> {
19
+ groupByCount(field: string, draft: D, opts: GroupByCountOptions): Promise<GroupByCountRow[]>;
20
+ }
21
+ /** Options for {@link groupByCountFromRequest}. */
22
+ export interface GroupByCountFromRequestOptions<D = unknown> {
23
+ /**
24
+ * Pre-parsed input to use instead of reading `ctx.request.qs()`. Useful for non-HTTP callers
25
+ * and tests.
26
+ */
27
+ input?: FilterInput;
28
+ /** The axis to enumerate, when the request does not carry a `groupByCount[field]`. */
29
+ field?: string;
30
+ /** Cap the returned groups (highest count first). */
31
+ limit?: number;
32
+ /** Skip groups — pages the fixed count-desc/value-asc ordering. */
33
+ offset?: number;
34
+ /** Narrow to groups whose value contains this text. */
35
+ search?: string;
36
+ /**
37
+ * Custom-backend counting: the narrowed draft is handed to this adapter instead of running a
38
+ * `GROUP BY` on the builder. Required when the first argument is a draft rather than a Lucid
39
+ * builder.
40
+ */
41
+ adapter?: GroupByCountAdapter<D>;
42
+ }
43
+ /**
44
+ * Terminal group-by-count aggregation — the value pickers' query: the distinct values of one
45
+ * field with counts, over the rows the active filters select.
46
+ *
47
+ * Lucid mode (a query builder, no adapter): the server scope plus the request filters and search
48
+ * apply on the builder, then a `GROUP BY` replaces entity-row output — most groups first, so the
49
+ * answer is pageable. Sort/pagination/distinct of the listing do not apply: this mode replaces
50
+ * entity rows.
51
+ *
52
+ * Custom mode (a draft + `adapter`): the filter class narrows the draft over the same scope,
53
+ * then the adapter counts it — a Lucid builder never enters the picture.
54
+ *
55
+ * ```ts
56
+ * // Lucid — in a controller:
57
+ * const rows = await groupByCountFromRequest(User.query(), UserFilter, ctx);
58
+ *
59
+ * // custom backend — same shape, a draft instead of a builder:
60
+ * const rows = await groupByCountFromRequest(draft, RunFilter, ctx, { adapter });
61
+ * ```
62
+ */
63
+ export declare function groupByCountFromRequest(qb: QueryBuilderLike, spec: FilterSpec | FilterClass, ctx: FilterRequestContext | undefined, opts?: GroupByCountFromRequestOptions<never>): Promise<GroupByCountRow[]>;
64
+ export declare function groupByCountFromRequest<D>(draft: D, cls: CustomFilterClass<D>, ctx: FilterRequestContext | undefined, opts: GroupByCountFromRequestOptions<D> & {
65
+ adapter: GroupByCountAdapter<D>;
66
+ }): Promise<GroupByCountRow[]>;
67
+ //# sourceMappingURL=group_by_count.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group_by_count.d.ts","sourceRoot":"","sources":["../../src/group_by_count.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,KAAK,WAAW,EAAsC,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,OAAO,KAAK,EAAgB,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGjF,2FAA2F;AAC3F,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CAC9F;AAED,mDAAmD;AACnD,MAAM,WAAW,8BAA8B,CAAC,CAAC,GAAG,OAAO;IACzD;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;CAClC;AAmBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,gBAAgB,EACpB,IAAI,EAAE,UAAU,GAAG,WAAW,EAC9B,GAAG,EAAE,oBAAoB,GAAG,SAAS,EACrC,IAAI,CAAC,EAAE,8BAA8B,CAAC,KAAK,CAAC,GAC3C,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;AAC9B,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,KAAK,EAAE,CAAC,EACR,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,EACzB,GAAG,EAAE,oBAAoB,GAAG,SAAS,EACrC,IAAI,EAAE,8BAA8B,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAA;CAAE,GAC5E,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { applyServerScope } from './apply_from_request.js';
2
+ import { applyCustomFilter } from './custom_filter.js';
3
+ import { isFilterClass, specFromFilterClass } from './filter_class.js';
4
+ import { specToFilterConfig } from './filter_spec.js';
5
+ import { applyGroupByCount } from './lucid_adapter.js';
6
+ import { parseFilterRequest } from './parse_request.js';
7
+ import { applyFilterConditions, resolveSafeDistinct } from './runner.js';
8
+ import { InvalidColumnFilterError } from './validate-column-filter.js';
9
+ /**
10
+ * Check a grouping field against the allow-list — the same boundary `sort`/`distinct` enforce
11
+ * (aliases resolve first; relation paths never project). Unknown fields are rejected outright,
12
+ * never silently ignored: the grouping column IS the whole query, so there is no safe default
13
+ * for a typo.
14
+ */
15
+ function resolveGroupField(field, config) {
16
+ if (!field || typeof field !== 'string' || field.trim() === '') {
17
+ throw new InvalidColumnFilterError('groupByCount needs a `field` — `groupByCount[field]=tag`.');
18
+ }
19
+ const [column] = resolveSafeDistinct([field], { ...config, throwOnInvalid: false });
20
+ if (column === undefined) {
21
+ throw new InvalidColumnFilterError(`Cannot group by unlisted field: "${field}".`);
22
+ }
23
+ return column;
24
+ }
25
+ export async function groupByCountFromRequest(qbOrDraft, specOrClass, ctx, opts = {}) {
26
+ const raw = ctx?.request?.qs?.() ?? {};
27
+ const parsed = opts.input ?? parseFilterRequest(raw);
28
+ const limit = opts.limit ?? parsed.groupByCount?.limit;
29
+ const offset = opts.offset ?? parsed.groupByCount?.offset;
30
+ const search = opts.search ?? parsed.groupByCount?.search;
31
+ if (opts.adapter) {
32
+ const draft = qbOrDraft;
33
+ await applyCustomFilter(draft, specOrClass, ctx, { input: parsed });
34
+ const field = opts.field ?? parsed.groupByCount?.field;
35
+ if (!field || typeof field !== 'string' || field.trim() === '') {
36
+ throw new InvalidColumnFilterError('groupByCount needs a `field` — `groupByCount[field]=tag`.');
37
+ }
38
+ return opts.adapter.groupByCount(field, draft, {
39
+ ...(limit !== undefined && { limit }),
40
+ ...(offset !== undefined && { offset }),
41
+ ...(search !== undefined && { search }),
42
+ });
43
+ }
44
+ const spec = typeof specOrClass === 'function' && isFilterClass(specOrClass)
45
+ ? specFromFilterClass(specOrClass)
46
+ : specOrClass;
47
+ const config = specToFilterConfig(spec);
48
+ const column = resolveGroupField(opts.field ?? parsed.groupByCount?.field, config);
49
+ const qb = qbOrDraft;
50
+ applyServerScope(qb, spec, ctx);
51
+ applyFilterConditions(qb, {
52
+ ...(parsed.filters !== undefined && { filters: parsed.filters }),
53
+ ...(parsed.search !== undefined && { search: parsed.search }),
54
+ }, config);
55
+ applyGroupByCount(qb, column, {
56
+ ...(limit !== undefined && { limit }),
57
+ ...(offset !== undefined && { offset }),
58
+ ...(search !== undefined && { search }),
59
+ });
60
+ // Lucid builders are thenable (awaiting runs the query) — outside the structural seam's
61
+ // vocabulary, hence the cast at the single point that executes. Model queries hydrate rows
62
+ // into model instances, where ad-hoc selects land in `$extras` rather than as direct props;
63
+ // plain builders return the raw `{ value, count }` rows. Both shapes map to the same answer.
64
+ const rows = (await qb);
65
+ return rows.map((row) => {
66
+ const value = row.value ?? row.$extras?.value ?? null;
67
+ const count = row.count ?? row.$extras?.count;
68
+ return { value, count: Number(count) };
69
+ });
70
+ }
71
+ //# sourceMappingURL=group_by_count.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group_by_count.js","sourceRoot":"","sources":["../../src/group_by_count.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,279 @@
1
+ import { type CursorPage, type CursorParams } from './cursor.js';
2
+ import type { QueryBuilderLike } from './lucid_adapter.js';
3
+ import type { FilterConfig, FilterFieldKind, FilterInput } from './types.js';
4
+ /**
5
+ * The in-memory adapter: a {@link QueryBuilderLike} that evaluates against a plain JS array.
6
+ *
7
+ * The runner (`applyFilter`, `applyCursor`, `applyFilterFromRequest`, `groupByCountFromRequest`)
8
+ * does exactly what it does for a Lucid query — parse, alias-resolve, validate, allow-list, coerce —
9
+ * and then drives the same builder calls it would hand Lucid. This builder records those calls and
10
+ * evaluates them over rows instead of compiling SQL, so an in-memory listing speaks the same wire
11
+ * format, operators and error shapes as a database one, and a filter spec written for a model
12
+ * works unchanged over rows assembled in memory (several sources merged, a static catalog, a
13
+ * remote API page).
14
+ *
15
+ * The evaluation mirrors what Postgres does with the SQL the Lucid adapter emits — including the
16
+ * places that surprise people: three-valued NULL logic (a NULL value fails `notEquals`/`notIn`/
17
+ * `notBetween`, `NOT IN` a list holding NULL matches nothing), ILIKE for every LIKE-style operator,
18
+ * and NULLS LAST on ascending / NULLS FIRST on descending order.
19
+ */
20
+ /** A scalar array kind — an in-memory extension; see {@link InMemoryFieldType}. */
21
+ export type InMemoryArrayKind = 'string[]' | 'number[]' | 'boolean[]' | 'date[]';
22
+ /**
23
+ * A declared field type. The scalar kinds are the lib's own {@link FilterFieldKind}; the array
24
+ * kinds declare a list-valued field, on which a scalar operator matches when ANY element matches
25
+ * and a negated operator (`notEquals`, `notIn`, `notBetween`) matches when NO element does.
26
+ */
27
+ export type InMemoryFieldType = FilterFieldKind | InMemoryArrayKind;
28
+ /** A virtual field: a value computed from the row — the in-memory counterpart of `computed`. */
29
+ export interface InMemoryVirtualField<T> {
30
+ type: InMemoryFieldType;
31
+ get: (row: T) => unknown;
32
+ }
33
+ /** One entry of {@link DefineCollectionOptions.fields}: a stored field's type, or a virtual field. */
34
+ export type InMemoryFieldDeclaration<T> = InMemoryFieldType | InMemoryVirtualField<T>;
35
+ /** Relation cardinality. `*-to-one` reads a row (or null); `*-to-many` reads an array. */
36
+ export type InMemoryRelationKind = 'one-to-one' | 'many-to-one' | 'one-to-many' | 'many-to-many';
37
+ /**
38
+ * A relation a dotted filter path (`posts.title`) can cross. The runner turns such a path into a
39
+ * `whereHas`, evaluated here with EXISTS semantics: the row matches when at least one related row
40
+ * satisfies the nested conditions.
41
+ */
42
+ export interface InMemoryRelation<T> {
43
+ kind: InMemoryRelationKind;
44
+ /** The related collection — a thunk so two collections can reference each other. */
45
+ target: () => InMemoryCollection<any>;
46
+ /** Read the related row(s) off a row. `null`/`undefined` means no related row. */
47
+ get: (row: T) => unknown;
48
+ }
49
+ /** Options for {@link defineCollection}. */
50
+ export interface DefineCollectionOptions<T> {
51
+ /**
52
+ * The field declarations — what each field is, so filter values are compared as that kind
53
+ * (`'3'` equals `3` on a `number` field, `'true'` equals `true` on a `boolean`, ISO strings
54
+ * compare as instants on a `date`). A field that is not declared is read as an own property and
55
+ * compared by the kind its value has at runtime.
56
+ */
57
+ fields: Record<string, InMemoryFieldDeclaration<T>>;
58
+ /** The keyset tiebreaker for cursor pagination. Default `'id'`. */
59
+ primaryKey?: string;
60
+ /** Relations dotted filter paths may cross. */
61
+ relations?: Record<string, InMemoryRelation<T>>;
62
+ /**
63
+ * The collection's "table" name. A `distinct`/sort field qualified with it (`users.name`, the
64
+ * form {@link FilterConfig.table} makes projectable) reads the root field `name`.
65
+ */
66
+ name?: string;
67
+ }
68
+ interface ResolvedField<T> {
69
+ kind: FilterFieldKind;
70
+ array: boolean;
71
+ get: ((row: T) => unknown) | undefined;
72
+ }
73
+ /** A declared, reusable row source. Create one with {@link defineCollection}. */
74
+ export interface InMemoryCollection<T extends object> {
75
+ readonly name: string | undefined;
76
+ readonly primaryKey: string;
77
+ /**
78
+ * The declared fields as the colocated `filterable` map `defineFilter` accepts
79
+ * (`defineFilter({ filterable: users.filterable })`) — array kinds map to their element kind,
80
+ * which is what the runner coerces a filter value to.
81
+ */
82
+ readonly filterable: Readonly<Record<string, FilterFieldKind>>;
83
+ /** The declared fields as {@link FilterConfig.fieldTypes}, for `applyFilter` callers. */
84
+ readonly fieldTypes: Readonly<Record<string, {
85
+ kind: FilterFieldKind;
86
+ }>>;
87
+ /** Start a query over `rows` — hand the builder to the runner like a Lucid query. */
88
+ query(rows: Iterable<T>): InMemoryQueryBuilder<T>;
89
+ /** @internal */
90
+ readonly $fields: Readonly<Record<string, ResolvedField<T>>>;
91
+ /** @internal */
92
+ readonly $relations: Readonly<Record<string, InMemoryRelation<T>>>;
93
+ }
94
+ /**
95
+ * Thrown when the runner asks the in-memory builder for something only SQL can do: a raw
96
+ * predicate or ordering (policy `computed` fields, `fullText`, `vectorSimilarity`) — declare a
97
+ * virtual field instead — or a relation/field path the collection does not declare.
98
+ */
99
+ export declare class InMemoryQueryError extends Error {
100
+ constructor(message: string);
101
+ }
102
+ /**
103
+ * Declare a collection: its fields (and their types), virtual fields, primary key and relations.
104
+ *
105
+ * ```ts
106
+ * const posts = defineCollection<Post>({ fields: { id: 'number', title: 'string' } })
107
+ * const users = defineCollection<User>({
108
+ * fields: {
109
+ * id: 'number',
110
+ * name: 'string',
111
+ * age: 'number',
112
+ * tags: 'string[]',
113
+ * fullName: { type: 'string', get: (u) => `${u.first} ${u.last}` },
114
+ * },
115
+ * relations: { posts: { kind: 'one-to-many', target: () => posts, get: (u) => u.posts } },
116
+ * })
117
+ *
118
+ * const qb = users.query(rows)
119
+ * const { page, size } = applyFilter(qb, input, { allowed: ['name', 'posts.title'] })
120
+ * return qb.paginate(page, size)
121
+ * ```
122
+ */
123
+ export declare function defineCollection<T extends object>(options: DefineCollectionOptions<T>): InMemoryCollection<T>;
124
+ /** The pagination meta {@link InMemoryQueryBuilder.paginate} returns — Lucid's paginator fields. */
125
+ export interface InMemoryPageMeta {
126
+ total: number;
127
+ perPage: number;
128
+ currentPage: number;
129
+ lastPage: number;
130
+ firstPage: number;
131
+ }
132
+ /** One offset page — the shape of a serialized Lucid paginator (`{ meta, data }`). */
133
+ export interface InMemoryPage<T> {
134
+ data: T[];
135
+ meta: InMemoryPageMeta;
136
+ }
137
+ /**
138
+ * A {@link QueryBuilderLike} over an array. Created by {@link InMemoryCollection.query}; hand it to
139
+ * any runner entry point exactly as you would a Lucid query, then read the result with
140
+ * {@link all}, {@link paginate}, or by awaiting it (it is thenable, like a Lucid query — which is
141
+ * what lets `groupByCountFromRequest` run over it unchanged).
142
+ */
143
+ export declare class InMemoryQueryBuilder<T extends object> implements QueryBuilderLike {
144
+ readonly collection: InMemoryCollection<T>;
145
+ private readonly rows;
146
+ private readonly clauses;
147
+ private readonly sorts;
148
+ private readonly selects;
149
+ private readonly groups;
150
+ private readonly groupSearches;
151
+ private distinctFields;
152
+ private countAlias;
153
+ private limitCount;
154
+ private offsetCount;
155
+ /** Lazily-built readers for to-one relation hops, so a dotted read does not allocate per row. */
156
+ private relationReaders;
157
+ constructor(collection: InMemoryCollection<T>, rows?: readonly T[]);
158
+ private field;
159
+ /** Strip a leading `<collection name>.` qualifier. */
160
+ private unqualify;
161
+ /**
162
+ * Read a (possibly dotted) path off a row. A dotted path crosses a to-one relation; crossing a
163
+ * to-many relation is ambiguous outside a `whereHas` (which the runner uses for filters) and is
164
+ * rejected.
165
+ */
166
+ read(row: T, rawPath: string): unknown;
167
+ /** The (memoized) empty builder over a relation's target collection, used to read through it. */
168
+ private relationReader;
169
+ /** The declared kind of a path (following to-one relations), or `unknown`. */
170
+ private kindOf;
171
+ /**
172
+ * Build a leaf predicate. `match` tests one non-null element; `negated` flips the element-level
173
+ * result into "no element matches" — with SQL's rule that a NULL value (or an uncastable
174
+ * comparison) satisfies neither a condition nor its negation.
175
+ */
176
+ private leaf;
177
+ private equalsLeaf;
178
+ private compareLeaf;
179
+ private inLeaf;
180
+ private betweenLeaf;
181
+ private likeLeaf;
182
+ private add;
183
+ private group;
184
+ /**
185
+ * Does `row` satisfy every recorded condition? Clauses chain left to right the way knex joins
186
+ * them — `a AND b OR c` — so AND binds tighter than OR, exactly as the emitted SQL would.
187
+ */
188
+ matches(row: T): boolean;
189
+ where(callback: (qb: QueryBuilderLike) => void): this;
190
+ where(column: string, value: unknown): this;
191
+ where(column: string, operator: string, value: unknown): this;
192
+ orWhere(callback: (qb: QueryBuilderLike) => void): this;
193
+ whereHas(relation: any, callback: (qb: QueryBuilderLike) => void): this;
194
+ whereNot(callback: (qb: QueryBuilderLike) => void): this;
195
+ whereNot(column: string, value: unknown): this;
196
+ whereIn(column: string, values: unknown[]): this;
197
+ whereNotIn(column: string, values: unknown[]): this;
198
+ whereNull(column: string): this;
199
+ whereNotNull(column: string): this;
200
+ whereBetween(column: string, range: [unknown, unknown]): this;
201
+ whereNotBetween(column: string, range: [unknown, unknown]): this;
202
+ whereILike(column: string, value: string): this;
203
+ orWhereILike(column: string, value: string): this;
204
+ orderBy(column: string, direction: 'asc' | 'desc'): this;
205
+ /**
206
+ * Only the group-by-count search predicate (`LOWER(??) LIKE ?`) is understood; any other raw
207
+ * SQL — a policy's `computed` fields, `fullText`, `vectorSimilarity` — throws
208
+ * {@link InMemoryQueryError}. Declare a virtual field on the collection instead of `computed`.
209
+ */
210
+ whereRaw(sql: string, bindings?: readonly unknown[]): this;
211
+ orderByRaw(sql: string, _bindings?: readonly unknown[]): this;
212
+ distinct(...columns: string[]): this;
213
+ limit(count: number): this;
214
+ offset(n: number): this;
215
+ select(...columns: string[]): this;
216
+ count(column: string): this;
217
+ groupBy(...columns: string[]): this;
218
+ /**
219
+ * Sort rows by the recorded `orderBy`s. Each row's sort values are read at most once per field
220
+ * (memoized lazily, so a field is only read for rows whose earlier fields tied — exactly when a
221
+ * pairwise comparator would read it) instead of twice per comparison, turning O(n log n) reads
222
+ * (virtual getters, relation hops) into O(n).
223
+ */
224
+ private sortRows;
225
+ /** Ascending comparison with NULLs sorting last (so NULLS FIRST once a desc sort negates it). */
226
+ private compareValues;
227
+ /** Filter + sort (+ distinct / projection / aggregation), before limit/offset. */
228
+ private evaluate;
229
+ /**
230
+ * `GROUP BY` over the matched rows. An array-typed grouping field is unnested — each element is
231
+ * its own group — which is what a tag picker wants (Postgres would group whole arrays).
232
+ */
233
+ private aggregate;
234
+ private window;
235
+ /**
236
+ * Execute: the matching rows in order, honouring `limit`/`offset` (which is what a cursor page
237
+ * sets). Under a `distinct`, a `select` or a `groupBy`, the rows are projection objects — just
238
+ * as a Lucid query returns them.
239
+ */
240
+ all(): T[];
241
+ /** How many rows (or distinct tuples / groups) match, ignoring `limit`/`offset`. */
242
+ total(): number;
243
+ /**
244
+ * One offset page — the in-memory `query.paginate(page, size)`. Feed it the `{ page, size }`
245
+ * the runner resolved (clamped and defaulted), never raw request values.
246
+ */
247
+ paginate(page: number, perPage: number): InMemoryPage<T>;
248
+ /**
249
+ * Thenable, like a Lucid query: `await qb` resolves to {@link all}. This is what lets helpers
250
+ * that execute a builder by awaiting it (`groupByCountFromRequest`) run over an array.
251
+ */
252
+ then<R1 = T[], R2 = never>(onfulfilled?: ((rows: T[]) => R1 | PromiseLike<R1>) | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null): Promise<R1 | R2>;
253
+ }
254
+ /**
255
+ * Filter, search, sort and offset-paginate `rows` in one call: {@link applyFilter} over a fresh
256
+ * builder, then {@link InMemoryQueryBuilder.paginate} with the resolved page. A `distinct` request
257
+ * pages the distinct tuples instead of rows.
258
+ *
259
+ * ```ts
260
+ * const page = filterInMemory(users, rows, parseFilterRequest(ctx.request.qs()), {
261
+ * allowed: ['name', 'age', 'posts.title'],
262
+ * searchable: ['name'],
263
+ * fieldTypes: users.fieldTypes,
264
+ * })
265
+ * // → { data, meta: { total, perPage, currentPage, lastPage, firstPage } }
266
+ * ```
267
+ */
268
+ export declare function filterInMemory<T extends object>(collection: InMemoryCollection<T>, rows: Iterable<T>, input: FilterInput, config: FilterConfig): InMemoryPage<T>;
269
+ /**
270
+ * The keyset (cursor) counterpart of {@link filterInMemory}: {@link applyCursor} over a fresh
271
+ * builder (the primary key defaults to the collection's), then `buildCursorPage`. Keyset values
272
+ * are read through the collection, so a virtual field or a to-one relation path works as a cursor
273
+ * sort field.
274
+ */
275
+ export declare function cursorInMemory<T extends object>(collection: InMemoryCollection<T>, rows: Iterable<T>, input: FilterInput & CursorParams, config: FilterConfig & {
276
+ primaryKey?: string;
277
+ }): CursorPage<T>;
278
+ export {};
279
+ //# sourceMappingURL=in_memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in_memory.d.ts","sourceRoot":"","sources":["../../src/in_memory.ts"],"names":[],"mappings":"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"}