@dcupl/common 2.0.0-beta.20 → 2.0.0-beta.22
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/dist/esm/helper.d.ts +10 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +506 -331
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/query-builder.d.ts +13 -3
- package/dist/esm/queryData.d.ts +51 -1
- package/dist/esm/redact-url.d.ts +9 -0
- package/dist/esm/types/facets.types.d.ts +8 -0
- package/dist/esm/types/list.types.d.ts +5 -0
- package/dist/esm/types/query.types.d.ts +98 -12
- package/dist/esm/types/suggestion.types.d.ts +5 -0
- package/dist/node/index.cjs +2 -2
- package/dist/node/index.cjs.map +1 -1
- package/package.json +1 -1
|
@@ -69,7 +69,8 @@ export declare class DcuplQueryBuilder {
|
|
|
69
69
|
* - {@link replaceQuery} - Replace complete query
|
|
70
70
|
*
|
|
71
71
|
* @param queryToApply - Query, condition, or array to apply
|
|
72
|
-
* @param options - Mode: 'add' (default) or 'set' (replace)
|
|
72
|
+
* @param options - Mode: 'add' (default) or 'set' (replace). In 'add' mode a group
|
|
73
|
+
* without `groupKey` is always appended as a new group, never merged into another.
|
|
73
74
|
* @param relevantQuery - Target query (defaults to current)
|
|
74
75
|
* @returns Updated query
|
|
75
76
|
*/
|
|
@@ -187,7 +188,10 @@ export declare class DcuplQueryBuilder {
|
|
|
187
188
|
/**
|
|
188
189
|
* Add query condition(s) to the current query.
|
|
189
190
|
* Accepts single condition or array of conditions.
|
|
190
|
-
* Each condition is added to its respective attribute group
|
|
191
|
+
* Each condition is added to its respective attribute group (the group whose
|
|
192
|
+
* groupKey equals the condition's attribute; created as an `'or'` group if missing).
|
|
193
|
+
* To combine conditions in an unkeyed group, use {@link addGroup} — unkeyed groups
|
|
194
|
+
* are always appended as new groups and never merged.
|
|
191
195
|
*
|
|
192
196
|
* Clearer alternative to `applyQuery(condition, { mode: 'add' })`.
|
|
193
197
|
*
|
|
@@ -209,7 +213,13 @@ export declare class DcuplQueryBuilder {
|
|
|
209
213
|
addCondition(condition: DcuplQuery | DcuplQuery[]): DcuplGlobalQueryOptions<any>;
|
|
210
214
|
/**
|
|
211
215
|
* Add a query group to the current query.
|
|
212
|
-
*
|
|
216
|
+
* - Keyed group (`groupKey` set): if a group with the same groupKey exists, its queries
|
|
217
|
+
* are extended and its groupType is replaced by the given one (if set); otherwise a new
|
|
218
|
+
* group is created.
|
|
219
|
+
* - Unkeyed group (no `groupKey`): always appended as a new group; it is never merged
|
|
220
|
+
* into another group, so each unkeyed group keeps its own groupType.
|
|
221
|
+
*
|
|
222
|
+
* A newly created group without `groupType` is stored as `'or'`.
|
|
213
223
|
*
|
|
214
224
|
* @param group - Query group to add
|
|
215
225
|
* @returns Updated query
|
package/dist/esm/queryData.d.ts
CHANGED
|
@@ -12,9 +12,59 @@ export declare class QueryManager {
|
|
|
12
12
|
registerCustomOperator(operator: string, fn: CustomOperatorFn): void;
|
|
13
13
|
transformValue(value: any, transformers: DcuplQueryTransformOptions[]): any;
|
|
14
14
|
private evaluateQuery;
|
|
15
|
+
/** A missing value never compares — in JS `null >= 0` is true. */
|
|
16
|
+
private compareValue;
|
|
17
|
+
/**
|
|
18
|
+
* The value a condition compares against. A dotted path that runs through a
|
|
19
|
+
* list (a multi-valued reference, e.g. `memberships.role`) fans out into the
|
|
20
|
+
* list of leaf values, the same values the attribute's inverse index holds —
|
|
21
|
+
* `lodash.get` cannot step into an array and would answer `undefined`. An
|
|
22
|
+
* index (`tags.0`) or `length` still reads the list itself, as with `get`.
|
|
23
|
+
*/
|
|
24
|
+
private getAttributeValue;
|
|
25
|
+
/**
|
|
26
|
+
* Applies `predicate` to a single value, or to each value of a list per
|
|
27
|
+
* `arrayValueHandling`. `every` needs at least one value: an empty list
|
|
28
|
+
* matches nothing (and so matches under `invert`).
|
|
29
|
+
*/
|
|
30
|
+
private matchValues;
|
|
31
|
+
/**
|
|
32
|
+
* `eq` against a list compares each value, as the inverse index does; a
|
|
33
|
+
* query value that is itself a list still compares the whole list.
|
|
34
|
+
*/
|
|
35
|
+
private queryEq;
|
|
36
|
+
/**
|
|
37
|
+
* `in`: a value matches if it equals (as `eq` compares) any listed value; a
|
|
38
|
+
* list of values matches per `arrayValueHandling`. An empty list matches nothing.
|
|
39
|
+
*/
|
|
40
|
+
private queryIn;
|
|
41
|
+
/**
|
|
42
|
+
* The inverse-index key a query value looks up: a string, or the key of a
|
|
43
|
+
* `{ key }` object. Anything else has no key and is compared by a scan.
|
|
44
|
+
*/
|
|
45
|
+
private getIndexKey;
|
|
46
|
+
/**
|
|
47
|
+
* Equality the way the inverse index keys values: a string (or `{ key }`)
|
|
48
|
+
* query value matches a reference by its key and a date by its ISO string.
|
|
49
|
+
*/
|
|
50
|
+
private equalsQueryValue;
|
|
51
|
+
/**
|
|
52
|
+
* An inverse index answers `eq` as "some value equals", untransformed — only
|
|
53
|
+
* use it when that is the question.
|
|
54
|
+
*/
|
|
55
|
+
private canUseIndex;
|
|
15
56
|
private evaluateEqOperator;
|
|
57
|
+
/**
|
|
58
|
+
* `in` from the inverse index when it can answer: the union of the index
|
|
59
|
+
* sets of the listed values. A value the index cannot look up (not a string
|
|
60
|
+
* or `{ key }`, or not in the index) is answered by a scan of the rest, so
|
|
61
|
+
* the result equals the scan's.
|
|
62
|
+
*/
|
|
63
|
+
private evaluateInOperator;
|
|
64
|
+
/** Evaluates `query` on each item, resolving a deep path's references first. */
|
|
65
|
+
private scanDataset;
|
|
16
66
|
private equalRegexFn;
|
|
17
|
-
queryFind(entryValue: any | any[], queryValue: any, arrayValueHandling: 'some' | 'every'): boolean;
|
|
67
|
+
queryFind(entryValue: any | any[], queryValue: any, arrayValueHandling: 'some' | 'every', regexFlags?: string): boolean;
|
|
18
68
|
private validateQuery;
|
|
19
69
|
private getEvaluatedQueryDataset;
|
|
20
70
|
private handleArrayStarQuery;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `url` with the values of secret query parameters replaced by `<redacted>`.
|
|
3
|
+
*
|
|
4
|
+
* For error messages and log lines only — the request itself keeps the real values. The app
|
|
5
|
+
* loader appends the project `api-key` to every dcupl CDN request, and a failed fetch used to
|
|
6
|
+
* carry that URL verbatim into the log output and the quality error. Works on the raw string so
|
|
7
|
+
* nothing else is re-encoded or reordered. Same parameter set as console-api's `redactUrl`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function redactUrl(url: string): string;
|
|
@@ -11,6 +11,14 @@ export type DcuplFacetOptions = {
|
|
|
11
11
|
* When omitted the fast (insertion-order) path is preserved.
|
|
12
12
|
*/
|
|
13
13
|
sort?: 'size-desc' | 'size-asc' | 'value-asc' | 'value-desc';
|
|
14
|
+
/**
|
|
15
|
+
* Key of the query group to leave out when counting (disjunctive facets):
|
|
16
|
+
* each facet value is counted under every other group of the current query,
|
|
17
|
+
* so selecting one value does not collapse its siblings.
|
|
18
|
+
* Defaults to the group keyed by `attribute`. A key that matches no group
|
|
19
|
+
* leaves nothing out. `selected` reflects this group's conditions on `attribute`.
|
|
20
|
+
*/
|
|
21
|
+
excludeGroup?: string;
|
|
14
22
|
};
|
|
15
23
|
export type DcuplFacet = {
|
|
16
24
|
key: string;
|
|
@@ -38,6 +38,11 @@ export type DcuplFilterOptions = CatalogApiOptions & {
|
|
|
38
38
|
* last. `value-desc` keeps `undefined` last as well.
|
|
39
39
|
*/
|
|
40
40
|
sort?: 'size-desc' | 'size-asc' | 'value-asc' | 'value-desc';
|
|
41
|
+
/**
|
|
42
|
+
* Key of the query group left out when counting facet entries.
|
|
43
|
+
* Defaults to the group keyed by the faceted attribute. See `DcuplFacetOptions.excludeGroup`.
|
|
44
|
+
*/
|
|
45
|
+
excludeGroup?: string;
|
|
41
46
|
};
|
|
42
47
|
export type DcuplFiltersOptions = DcuplFilterOptions & {
|
|
43
48
|
filterKeys?: string[];
|
|
@@ -7,12 +7,13 @@ import { Projection, SortingProjection } from './projection.types.js';
|
|
|
7
7
|
* - `removeWhitespace`: Remove all whitespace characters
|
|
8
8
|
*/
|
|
9
9
|
export type DcuplQueryTransformOptions = 'lowercase' | 'trim' | 'removeWhitespace';
|
|
10
|
-
export declare const DcuplQueryOperatorsAsArray: readonly ["eq", "find", "gt", "gte", "lt", "lte", "typeof", "isTruthy", "size"];
|
|
10
|
+
export declare const DcuplQueryOperatorsAsArray: readonly ["eq", "in", "find", "gt", "gte", "lt", "lte", "typeof", "isTruthy", "size"];
|
|
11
11
|
/**
|
|
12
12
|
* Query operators for filtering data.
|
|
13
13
|
*
|
|
14
14
|
* Available operators:
|
|
15
15
|
* - `eq`: Equality check - matches exact values
|
|
16
|
+
* - `in`: Membership check - matches if the value equals any value of a list (`eq`'s equality)
|
|
16
17
|
* - `find`: Regex / exact / object match - bare string is exact-equality, `/regex/` for substring/pattern, object for property match
|
|
17
18
|
* - `gt`: Greater than - numeric/date comparison (exclusive)
|
|
18
19
|
* - `gte`: Greater than or equal - numeric/date comparison (inclusive)
|
|
@@ -27,6 +28,9 @@ export declare const DcuplQueryOperatorsAsArray: readonly ["eq", "find", "gt", "
|
|
|
27
28
|
* // Equality
|
|
28
29
|
* { operator: 'eq', attribute: 'status', value: 'active' }
|
|
29
30
|
*
|
|
31
|
+
* // Any of several values
|
|
32
|
+
* { operator: 'in', attribute: 'status', value: ['active', 'pending'] }
|
|
33
|
+
*
|
|
30
34
|
* // Numeric comparison
|
|
31
35
|
* { operator: 'gte', attribute: 'price', value: 100 }
|
|
32
36
|
*
|
|
@@ -79,7 +83,13 @@ type DcuplQueryBase = {
|
|
|
79
83
|
*/
|
|
80
84
|
transform?: DcuplQueryTransformOptions[];
|
|
81
85
|
/**
|
|
82
|
-
* Invert the query result (NOT operation).
|
|
86
|
+
* Invert the query result (NOT operation). Applies to every built-in operator.
|
|
87
|
+
* A missing (`undefined`/`null`) value matches neither way, except for `eq`,
|
|
88
|
+
* `in`, `typeof` and `isTruthy`, which compare against it. A list of values (an
|
|
89
|
+
* `Array<…>` property or a dotted path through a multi-valued reference) is
|
|
90
|
+
* checked per value instead: an empty list, or a `null` in it, simply does not
|
|
91
|
+
* match, so `invert` does match it (`memberships.user.daysInactive gte 60`
|
|
92
|
+
* inverted includes items with no memberships).
|
|
83
93
|
* @example
|
|
84
94
|
* ```typescript
|
|
85
95
|
* // Find items where status is NOT 'active'
|
|
@@ -88,9 +98,14 @@ type DcuplQueryBase = {
|
|
|
88
98
|
*/
|
|
89
99
|
invert?: boolean;
|
|
90
100
|
/**
|
|
91
|
-
* When querying array values, determines match behavior.
|
|
101
|
+
* When querying array values, determines match behavior. Array values are
|
|
102
|
+
* `Array<…>` properties and dotted paths through a multi-valued reference
|
|
103
|
+
* (`memberships.role` compares the `role` of every membership).
|
|
92
104
|
* - `some`: Match if at least one array element matches (default)
|
|
93
|
-
* - `every`: Match only if all array elements match
|
|
105
|
+
* - `every`: Match only if all array elements match — and there is at least
|
|
106
|
+
* one: an empty array (or a reference with no items) never matches
|
|
107
|
+
*
|
|
108
|
+
* On a reference path, leaves that are `undefined` are skipped.
|
|
94
109
|
*
|
|
95
110
|
* @example
|
|
96
111
|
* ```typescript
|
|
@@ -127,14 +142,58 @@ type DcuplQueryEq = DcuplQueryBase & {
|
|
|
127
142
|
*/
|
|
128
143
|
value: any;
|
|
129
144
|
};
|
|
145
|
+
/**
|
|
146
|
+
* Membership query - matches if the attribute's value equals **any** value of
|
|
147
|
+
* the list, using the same equality as `eq`: a string (or `{ key }`) matches a
|
|
148
|
+
* reference by its key and a date by its ISO string. It answers like an `or`
|
|
149
|
+
* group of one `eq` condition per listed value.
|
|
150
|
+
*
|
|
151
|
+
* - On an `Array<…>` property or a dotted path through a multi-valued
|
|
152
|
+
* reference, `arrayValueHandling: 'some'` (default) matches if at least one
|
|
153
|
+
* value is listed; `'every'` if every value is listed (and there is at least one).
|
|
154
|
+
* - `options.invert` is NOT IN; like `eq`, a missing value then matches.
|
|
155
|
+
* - `options.transform` applies to the attribute value and to every listed value.
|
|
156
|
+
* - `null` in the list matches `null`, like `eq` with `null`.
|
|
157
|
+
* - An empty list matches nothing (and everything under `invert`).
|
|
158
|
+
* - `value` must be an array; anything else throws. (`eq` with an array value
|
|
159
|
+
* compares the whole array instead.)
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* // Items whose status is 'active' or 'pending'
|
|
164
|
+
* { operator: 'in', attribute: 'status', value: ['active', 'pending'] }
|
|
165
|
+
*
|
|
166
|
+
* // Through references (2 hops)
|
|
167
|
+
* { operator: 'in', attribute: 'customer.address.city', value: ['Vienna', 'Graz'] }
|
|
168
|
+
*
|
|
169
|
+
* // Reference keys, as strings or { key }
|
|
170
|
+
* { operator: 'in', attribute: 'category', value: ['cat-1', { key: 'cat-2' }] }
|
|
171
|
+
*
|
|
172
|
+
* // Every tag is one of the listed ones
|
|
173
|
+
* { operator: 'in', attribute: 'tags', value: ['sale', 'new'], options: { arrayValueHandling: 'every' } }
|
|
174
|
+
*
|
|
175
|
+
* // NOT IN
|
|
176
|
+
* { operator: 'in', attribute: 'status', value: ['archived', 'deleted'], options: { invert: true } }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
type DcuplQueryIn = DcuplQueryBase & {
|
|
180
|
+
operator: 'in';
|
|
181
|
+
/**
|
|
182
|
+
* The values to match; any one of them matching is enough.
|
|
183
|
+
* Note: For indexed search optimization, use string or { key: string } values.
|
|
184
|
+
*/
|
|
185
|
+
value: unknown[];
|
|
186
|
+
};
|
|
130
187
|
/**
|
|
131
188
|
* Find query - regex / exact / object match.
|
|
132
189
|
*
|
|
133
190
|
* Matching depends on the shape of `value`:
|
|
134
191
|
* - **Bare string** (e.g. `'search'`) — exact-equality against the *whole* field
|
|
135
192
|
* value, NOT a substring. `find` with `'Ball'` does not match `'Soccer Ball'`.
|
|
136
|
-
* - **Slash-delimited regex** (e.g. `'/search/'`, `'/^foo/
|
|
137
|
-
* use this for substring and pattern matching.
|
|
193
|
+
* - **Slash-delimited regex** (e.g. `'/search/'`, `'/^foo/'`) — the text between the
|
|
194
|
+
* slashes is a JS regex; use this for substring and pattern matching. Flags go in
|
|
195
|
+
* `options.regexFlags` (`'/^foo/'` + `regexFlags: 'i'`), not after the closing
|
|
196
|
+
* slash: `'/^foo/i'` is a bare string and matches exactly.
|
|
138
197
|
* - **Object** (e.g. `{ key: 'value' }`) — property matching against the field.
|
|
139
198
|
*
|
|
140
199
|
* @example
|
|
@@ -142,6 +201,9 @@ type DcuplQueryEq = DcuplQueryBase & {
|
|
|
142
201
|
* // Substring search — slash-delimited regex (bare string would be exact-match)
|
|
143
202
|
* { operator: 'find', attribute: 'description', value: '/search/' }
|
|
144
203
|
*
|
|
204
|
+
* // Case-insensitive substring search
|
|
205
|
+
* { operator: 'find', attribute: 'description', value: '/search/', options: { regexFlags: 'i' } }
|
|
206
|
+
*
|
|
145
207
|
* // Exact-match the whole field value
|
|
146
208
|
* { operator: 'find', attribute: 'status', value: 'active' }
|
|
147
209
|
*
|
|
@@ -155,6 +217,13 @@ type DcuplQueryFind = DcuplQueryBase & {
|
|
|
155
217
|
* Bare string (exact-match), `/regex/` (substring/pattern), or object (property match).
|
|
156
218
|
*/
|
|
157
219
|
value: Record<string, unknown> | string;
|
|
220
|
+
options?: {
|
|
221
|
+
/**
|
|
222
|
+
* Flags for a `/regex/` value, e.g. `'i'` for case-insensitive. Ignored for any
|
|
223
|
+
* other value; invalid flags match nothing.
|
|
224
|
+
*/
|
|
225
|
+
regexFlags?: string;
|
|
226
|
+
};
|
|
158
227
|
};
|
|
159
228
|
/**
|
|
160
229
|
* Numeric/date comparison queries.
|
|
@@ -260,7 +329,7 @@ type DcuplQuerySize = DcuplQueryBase & {
|
|
|
260
329
|
* };
|
|
261
330
|
* ```
|
|
262
331
|
*/
|
|
263
|
-
export type DcuplQuery = DcuplQueryEq | DcuplQueryNumbers | DcuplQueryTypeOf | DcuplQueryIsTruthy | DcuplQuerySize | DcuplQueryFind;
|
|
332
|
+
export type DcuplQuery = DcuplQueryEq | DcuplQueryIn | DcuplQueryNumbers | DcuplQueryTypeOf | DcuplQueryIsTruthy | DcuplQuerySize | DcuplQueryFind;
|
|
264
333
|
/**
|
|
265
334
|
* Query group - combines multiple conditions with AND/OR logic.
|
|
266
335
|
* Groups can be nested to create complex filter expressions.
|
|
@@ -311,13 +380,24 @@ export type DcuplQueryGroup<ChildQuery extends DcuplQuery | DcuplQueryGroup = an
|
|
|
311
380
|
*/
|
|
312
381
|
__type?: 'group';
|
|
313
382
|
/**
|
|
314
|
-
* Combination logic for conditions in this group.
|
|
315
|
-
*
|
|
383
|
+
* Combination logic for conditions in this group. Set it explicitly; when omitted,
|
|
384
|
+
* the effective behaviour depends on where the group ends up:
|
|
385
|
+
* - Groups created by the query builder (`addGroup` / `setGroup` / `apply(group)` for a
|
|
386
|
+
* group not yet present) are stored as `'or'`. When `addGroup` extends an existing keyed
|
|
387
|
+
* group, that group keeps its current groupType.
|
|
388
|
+
* - The root query (`groupKey: 'root'`) is evaluated as `'and'`.
|
|
389
|
+
* - A nested group that reaches evaluation without a groupType (e.g. passed inside a full
|
|
390
|
+
* query via `apply({ groupKey: 'root', ... })`, `init` or `execute`, or stored by
|
|
391
|
+
* `setGroup` replacing an existing group) is intersected like `'and'`, with one
|
|
392
|
+
* difference: if the running intersection becomes empty, the matches of the next
|
|
393
|
+
* condition replace it instead of the result staying empty.
|
|
316
394
|
*/
|
|
317
395
|
groupType?: DcuplQueryType;
|
|
318
396
|
/**
|
|
319
397
|
* Optional identifier for this group.
|
|
320
|
-
* Useful for tracking or removing specific query groups.
|
|
398
|
+
* Useful for tracking or removing specific query groups. Adding a group with an
|
|
399
|
+
* existing groupKey extends that group; a group without groupKey is always added
|
|
400
|
+
* as a new group.
|
|
321
401
|
*/
|
|
322
402
|
groupKey?: string;
|
|
323
403
|
/**
|
|
@@ -412,7 +492,10 @@ export type DcuplGlobalManyItemOptions<T> = {
|
|
|
412
492
|
};
|
|
413
493
|
/**
|
|
414
494
|
* Options for executing queries on a list instance.
|
|
415
|
-
* Combines query conditions with data retrieval options.
|
|
495
|
+
* Combines optional query conditions with data retrieval options.
|
|
496
|
+
*
|
|
497
|
+
* `queries` is optional: without it the list's own (live) query is used, so
|
|
498
|
+
* item options alone (`projection`, `start`, `count`, `sort`) are valid.
|
|
416
499
|
*
|
|
417
500
|
* @see ListQueryOptions - Type alias with clearer naming
|
|
418
501
|
*
|
|
@@ -426,9 +509,12 @@ export type DcuplGlobalManyItemOptions<T> = {
|
|
|
426
509
|
* count: 10
|
|
427
510
|
* };
|
|
428
511
|
* const results = list.catalog.query.execute(options);
|
|
512
|
+
*
|
|
513
|
+
* // The list's own query, projected
|
|
514
|
+
* const projected = list.catalog.query.execute({ projection: { $: false, name: true } });
|
|
429
515
|
* ```
|
|
430
516
|
*/
|
|
431
|
-
export type DcuplListQueryOptions<T> = DcuplItemOptions<T> & DcuplQueryGroup
|
|
517
|
+
export type DcuplListQueryOptions<T> = DcuplItemOptions<T> & Partial<DcuplQueryGroup>;
|
|
432
518
|
/**
|
|
433
519
|
* Options for retrieving a specific item from a list instance.
|
|
434
520
|
*/
|
|
@@ -7,6 +7,11 @@ type SuggestionOptionsBase = {
|
|
|
7
7
|
excludeUndefineds?: boolean;
|
|
8
8
|
excludeNulls?: boolean;
|
|
9
9
|
transform?: DcuplQueryTransformOptions[];
|
|
10
|
+
/**
|
|
11
|
+
* Flags for a `/pattern/` value, e.g. `'i'` for case-insensitive. Ignored for any
|
|
12
|
+
* other value; invalid flags suggest nothing.
|
|
13
|
+
*/
|
|
14
|
+
regexFlags?: string;
|
|
10
15
|
};
|
|
11
16
|
type SuggestionOptionsFiltered = SuggestionOptionsBase & {
|
|
12
17
|
relevantData?: 'filtered';
|