queryable-array 1.0.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.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright © 2026 Kip Price
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Queryable Arrays
2
+
3
+ [![Test Coverage](https://github.com/kipprice/queryable-array/actions/workflows/coverage.yml/badge.svg)](https://github.com/kipprice/queryable-array/actions/workflows/coverage.yml)
4
+ [![Build](https://github.com/kipprice/queryable-array/actions/workflows/build.yml/badge.svg)](https://github.com/kipprice/queryable-array/actions/workflows/build.yml)
5
+ [![Benchmark](https://github.com/kipprice/queryable-array/actions/workflows/benchmark.yml/badge.svg)](https://github.com/kipprice/queryable-array/actions/workflows/benchmark.yml)
6
+
7
+
8
+ A lightweight (14kb) TS-first querying language designed to make it easy to work with arrays that need significant filtering, joining, sorting, or mapping. This library has no external dependencies, and performance of the queryable array is within an order of magnitude of standard arrays, ensuring that if you could perform a query via standard array functions, you can probably use a queryable array to do the same, albeit a little more human readable.
9
+
10
+ ## Why use this instead of standard array functions, `lodash`, etc.?
11
+
12
+ This library has a narrow scope that really optimizes for readability (without sacrificing too much in performance). It is designed for teams that are doing a significant amount of filtering and/or joining in JS / TS code (client-side or server-side) and want the queries they are writing to be somewhat self documenting. For example, consider:
13
+
14
+ ```ts
15
+ const authors = [
16
+ { id: 'a1', name: 'Kip', contributorSince: '2008-01-01' },
17
+ // ...
18
+ ]
19
+ const songs = [
20
+ {
21
+ id: 's1',
22
+ name: 'My Song',
23
+ tags: [{ id: 't1', label: 'rad'}],
24
+ authorIds: ['a1'],
25
+ lengthInSeconds: 800
26
+ },
27
+ // ...
28
+ ]
29
+
30
+ // standard array version
31
+ const radLongSongsWithAuthors = songs
32
+ .filter((s) => s.length > 500)
33
+ .filter((s) => s.tags.some((t) => t.name === 'rad'))
34
+ .map((s) => ({ ...s, authors: s.authorIds.map((aId) =>
35
+ authors.find((a) => a.id === s.authorId)
36
+ )}))
37
+
38
+ // queryable array version
39
+ const queryableSongs = queryable(songs)
40
+ .where('length').greaterThan(500)
41
+ .and.where('tags').some('tag').its('label').is('rad')
42
+ .joinWith(authors).whereMy('authorIds').referencesTheir('id').storedTo('authors')
43
+ ```
44
+
45
+ ## How does it work?
46
+
47
+ Under the hood, a queryable array extends a standard array, so you can still perform standard array functions on it. Any standard array method that would normally return an array (either as a new instance or by modifying the current array) returns a pre-wrapped queryable array so you can continue chaining off of it. A queryable array respects the same behavior as the underlying array methods, swapping in place when the array method would do so.
48
+
49
+ ```ts
50
+ const songs = [
51
+ {
52
+ id: 's1',
53
+ name: 'My Song',
54
+ tags: [{ id: 't1', label: 'rad'}],
55
+ authorId: 'a1',
56
+ lengthInSeconds: 800
57
+ },
58
+ // ...
59
+ ]
60
+
61
+ const tags = songs
62
+ .flatMap((s) => s.tags)
63
+ .uniqueBy('id')
64
+ .where('label').is('rad')
65
+ ```
66
+
67
+ ## Types
68
+
69
+ Everything in the queryable array library is written in Typescript and infers the shape of objects automatically to present appropriate methods and properties depending on where in the chain a given queryable array is. Performing `where` queries are non-modifying, meaning the underlying array wrapped by the queryable array remains as it was even as the queryable array version gets successively more narrowed.
70
+
71
+ ## Bug Reports
72
+
73
+ Please report any found bugs on the library's GH [Issues](https://github.com/kipprice/queryable-array/issues) page.
74
+
75
+ ## Contributing
76
+
77
+ If you're interested in helping improve this library, feel free to open a PR against the [repo](https://github.com/kipprice/queryable-array/pulls).
@@ -0,0 +1,414 @@
1
+ export declare interface ArrayQueryClause<T extends Array<unknown>, E extends T extends Array<infer E> ? E : never = T extends Array<infer E> ? E : never, R = QueryClauseResult<T>> extends BaseQueryClause<T, R> {
2
+ /**
3
+ * considers this array resolved if at least one element in the array resolves
4
+ * to true in the subsequent logic calls
5
+ *
6
+ * @param label
7
+ * Functionally unused, but allows deeply nested calls to `some` to
8
+ * be more human readable by labeling the entity being looped over
9
+ */
10
+ some: (label?: string) => UnionToIntersection<QueryClause<E, R>>;
11
+ /**
12
+ * considers this array resolved if every element in the array resolves to
13
+ * true in the subsequent logic calls before including this element into the
14
+ * query result
15
+ *
16
+ * @param label
17
+ * Functionally unused, but allows deeply nested calls to `every` to
18
+ * be more human readable by labeling the entity being looped over
19
+ */
20
+ every: (label?: string) => UnionToIntersection<QueryClause<E, R>>;
21
+ /**
22
+ * determines whether the provided value is findable within the current value
23
+ * array. Similar to 'is' and 'in', this does a string equality check for
24
+ * the specified value; if a deep match is required, it's preferred to use
25
+ * a combination of 'some' / 'every' and 'matches' (or further filtering
26
+ * logic)
27
+ *
28
+ * @param t
29
+ * The element that should be contained within this array
30
+ */
31
+ includes: (t: E) => R;
32
+ /** determins if the array has no elements */
33
+ isEmpty: () => R;
34
+ }
35
+
36
+ /**-------------------------------------------------------------------------
37
+ * BaseQueryClause
38
+ * -------------------------------------------------------------------------
39
+ * A foundational object that tracks a current value and creates a set of
40
+ * resolving functions to determine whether that current value is considered
41
+ * a match for the query that spawned it.
42
+ *
43
+ * A Query Clause can only be resolved once, but depending on the value under
44
+ * test, it may undergo several chained methods before it is resolved to
45
+ * narrow the specificity of the query.
46
+ *
47
+ * -------------------------------------------------------------------------
48
+ */
49
+ export declare interface BaseQueryClause<T, R = QueryClauseResult<T>> {
50
+ /** whether this clause has been resolved. */
51
+ isResolved: boolean;
52
+ /** whether this clause will resolve to 'true' for non-matches rather than matches */
53
+ isNegated: boolean;
54
+ /** the current value under evaluation */
55
+ value?: T;
56
+ /** when resolved, treat a 'false' result as a match and a 'true' result as a non-match */
57
+ not: QueryClause<T, R>;
58
+ /**
59
+ * check for equality between the current value and the provided comparison
60
+ * value. For objects and arrays, this compares via stringified equality, not
61
+ * reference equality.
62
+ **/
63
+ is: (value: T | null | undefined) => R;
64
+ /** check for equality between the current value and the provided comparison value; synonym for 'is' */
65
+ equals: (value: T | null | undefined) => R;
66
+ /** check for equality between the current value and the provided comparison value; synonym for 'is */
67
+ eq: (value: T | null | undefined) => R;
68
+ /** check if the current value is null */
69
+ isNull: () => R;
70
+ /** check if the current value is undefined */
71
+ isUndefined: () => R;
72
+ /** check if the current value is null or undefined */
73
+ isNullish: () => R;
74
+ /**
75
+ * check if the current value is contained within the provided values. For
76
+ * objects and aeeats, this compares via stringified equality, not reference
77
+ * equality
78
+ */
79
+ in: (value: T[]) => R;
80
+ /**
81
+ * check if the current value, when passed into the provided function,
82
+ * resolves to 'true'
83
+ **/
84
+ satisfies: (fn: (t: T) => boolean) => R;
85
+ }
86
+
87
+ export declare interface ComparableQueryClause<T extends string | number, R = QueryClauseResult<T>> extends BaseQueryClause<T, R> {
88
+ /** determines whether the current value is greater than the provided value */
89
+ gt: (value: T) => R;
90
+ /** determines whether the current value is greater than the provided value; synonym for 'gt' */
91
+ greaterThan: (value: T) => R;
92
+ /** determines whether the current value is greater than or equal to the provided value */
93
+ gte: (value: T) => R;
94
+ /** determines whether the current value is greater than or equal to the provided value; synonym for 'gte' */
95
+ greaterThanOrEqualTo: (value: T) => R;
96
+ /** determines whether the current value is less than the provided value */
97
+ lt: (value: T) => R;
98
+ /** determines whether the current value is less than the provided value; synonym for 'lt' */
99
+ lessThan: (value: T) => R;
100
+ /** determines whether the current value is less than or equal to the provided value */
101
+ lte: (value: T) => R;
102
+ /** determines whether the current value is less than or equal to the provided value; synonym for 'lte' */
103
+ lessThanOrEqualTo: (value: T) => R;
104
+ }
105
+
106
+ export declare type ElemType<T> = T extends Array<infer E> ? E : never;
107
+
108
+ export declare type Key = string | number | symbol;
109
+
110
+ export declare type NestedPartial<T> = {
111
+ [K in keyof T]?: NestedPartial<T[K]>;
112
+ };
113
+
114
+ export declare interface ObjectQueryClause<T extends object, R = QueryClauseResult<T>> extends BaseQueryClause<T, R> {
115
+ /**
116
+ * extracts a particular property from the current value and treats that
117
+ * value at that property as the new current value for subsequent calls
118
+ */
119
+ its: <K extends keyof T>(k: K) => UnionToIntersection<QueryClause<T[K], R>>;
120
+ /**
121
+ * determines if the current value contains all of the data specified in
122
+ * the provided partial value. Unlike `is`, this performs a deep equality
123
+ * check between two objects
124
+ */
125
+ matches: (t: NestedPartial<T>) => R;
126
+ /**
127
+ * determines if the current value is an exact match for the provided value.
128
+ * This is stricter than `matches` in that it requires the provided value to
129
+ * also be full instance of the model
130
+ */
131
+ deepEquals: (t: T) => R;
132
+ /** determins if the object has no keys */
133
+ isEmpty: () => R;
134
+ }
135
+
136
+ export declare const ql: typeof queryable;
137
+
138
+ /**----------------------------------------------------------------------------
139
+ * queryable
140
+ * ----------------------------------------------------------------------------
141
+ * Augment an array of data elements (or a single data element, which will
142
+ * subsequently be wrapped in an array) with query helpers that allow for more
143
+ * human-readable filtering and map logic.
144
+ *
145
+ * @param toBeQueryable
146
+ * If an array, augments the array with additional functionality that
147
+ * can be called to directly filter the array. If not an array, first
148
+ * wraps the element in an array, then augments it to have the same
149
+ * query functions.
150
+ *
151
+ * @returns An Array-like object that can be used in all places that an array
152
+ * can be used, but can also be directly filtered.
153
+ *
154
+ * @example
155
+ * // returns an new array containing just the second element
156
+ * queryable([{ id: 1 }, { id: 2 }, { id: 3 }])
157
+ * .where('id').greaterThan(1)
158
+ * .and.where('id').lessThan(3)
159
+ * .and.where('id').satisfies((id) => id % 2 === 0)
160
+ *
161
+ * -------------------------------------------------------------------------
162
+ */
163
+ export declare function queryable<T, E extends T extends Array<infer E> ? E : T>(toBeQueryable: T): QueryableArray<E>;
164
+
165
+ /**-------------------------------------------------------------------------
166
+ * QueryableArray
167
+ * -------------------------------------------------------------------------
168
+ * Helper to be able to take an array of data and turn it into a user-friendly
169
+ * queryable object. This is an actual class as opposed to a wrapped object
170
+ * so we can continue to take advantage of native array functionality,
171
+ *
172
+ * -------------------------------------------------------------------------
173
+ */
174
+ export declare class QueryableArray<T> extends Array<T> {
175
+ protected _currentLogic: "and" | "or";
176
+ /** the set of currently included elems, if relevant -- used to improve performance of OR queries */
177
+ protected _currentSet: Set<T>;
178
+ protected _currentData: T[];
179
+ protected _originalData: T[];
180
+ [idx: number]: T;
181
+ /** ensure that we instantiate intermediate versions of query arrays (e.g.
182
+ * through .filter or .map) as a straight array instance -- we will always
183
+ * explicitly wrap the result in a QueryableArray when appropriate */
184
+ static get [Symbol.species](): ArrayConstructor;
185
+ get data(): T[];
186
+ constructor(elements: T[], originalData?: T[], _currentLogic?: "and" | "or",
187
+ /** the set of currently included elems, if relevant -- used to improve performance of OR queries */
188
+ _currentSet?: Set<T>);
189
+ /**
190
+ * ensure that we have the elements from _currentData also populated into our
191
+ * numeric indices, so that you can extract data as you would from a regular
192
+ * array
193
+ **/
194
+ protected _populateInnerStore(): void;
195
+ /**
196
+ * get the first element in the query array, based on the current query
197
+ */
198
+ get first(): T | undefined;
199
+ /**
200
+ * get the last element in the query array, based on the current query
201
+ */
202
+ get last(): T | undefined;
203
+ /**
204
+ * apply a new query to the array, enusring that only elements that match
205
+ * both the current query and the new query are included
206
+ */
207
+ get and(): this;
208
+ /**
209
+ * apply a new query to the array, ensuring that elements that match either
210
+ * the current query or the new query are included (without duplicates)
211
+ */
212
+ get or(): this;
213
+ /**
214
+ * Remove any duplicate members of the array, via deep-equality comparison
215
+ *
216
+ * @returns This QueryableArray sans duplicate members
217
+ */
218
+ unique(): QueryableArray<T>;
219
+ /**
220
+ * Remove any members of the array that are considered a duplicate via the
221
+ * provided key getter.
222
+ *
223
+ * @param keyGetter
224
+ * Either a property name or a function that can be used to get the
225
+ * value that should be compared for uniqueness
226
+ *
227
+ * @returns This QueryableArray sans duplicate members
228
+ */
229
+ uniqueBy<X>(keyGetter: keyof T | ((t: T) => X)): QueryableArray<T>;
230
+ /**
231
+ * sort the query array by either the value in the specified property or via
232
+ * a function evaluator.
233
+ *
234
+ * @param sortKeyGetter
235
+ * The property or function that can retrieve a sortable value from
236
+ * a given element
237
+ *
238
+ * @param direction
239
+ * Whether we should perform an ascending or descending search
240
+ *
241
+ * @returns A sorted version of this query array
242
+ */
243
+ sortBy(sortKeyGetter: keyof T | ((t: T) => string | number), direction?: "asc" | "desc"): QueryableArray<T>;
244
+ /**
245
+ * turn this query array into a map, with keys based on the provided key
246
+ * getter.
247
+ *
248
+ * This assumes that the key retrieved is unique for the element in
249
+ * question; if multiple elements could return the same key, use `groupBy`
250
+ * instead
251
+ *
252
+ * @param keyGetter
253
+ * A function or name of a property that can extract an appropriate
254
+ * key for any given element.
255
+ *
256
+ * @returns A map of the query array, keyed by the results of the keyGetter
257
+ */
258
+ indexBy(keyGetter: keyof T | ((t: T) => Key)): Record<Key, T>;
259
+ /**
260
+ * group all elements in the query array by a given key
261
+ *
262
+ * @param keyGetter
263
+ * The property name or function to retrieve the value
264
+ *
265
+ * @returns A map with keys based on the results of keyGetter and an array
266
+ * of values that returned that key
267
+ */
268
+ groupBy(keyGetter: keyof T | ((t: T) => Key | Key[])): Record<Key, T[]>;
269
+ /**
270
+ * extract inner data from this query array and turn it into a QueryableArray of
271
+ * its own. Will automatically flatten data if the result of the key getter
272
+ * is an array.
273
+ *
274
+ * @param keyGetter
275
+ * The key to extract to get the appropriate inner data
276
+ */
277
+ extract<K extends keyof T, X extends T[K]>(keyGetter: K): QueryableArray<X extends Array<unknown> ? ElemType<X> : X>;
278
+ /**
279
+ * extract inner data from this query array and turn it into a QueryableArray of
280
+ * its own. Will automatically flatten data if the result of the key getter
281
+ * is an array.
282
+ *
283
+ * @param keyGetter
284
+ * The function through which to get the appropriate inner data
285
+ */
286
+ extract<X>(keyGetter: (t: T) => X): QueryableArray<X extends Array<unknown> ? ElemType<X> : X>;
287
+ joinWith<U>(them: U[]): {
288
+ /**
289
+ * a key on the current array that references a value in the provided
290
+ * array. This can be either a value or an array of values
291
+ *
292
+ * @param myKeyGetter
293
+ * A direct property name or function by which the referenceable
294
+ * id(s) can be extracted via from an element in my array
295
+ *
296
+ * @returns A chainable joiner that can be used to set a new key to the
297
+ * result of the join
298
+ **/
299
+ whereMy: <KT extends keyof T, X>(myKeyGetter: KT | ((t: T) => X)) => {
300
+ /**
301
+ * a key on the provided array that will have the same value as the
302
+ * result of retrieving the above key
303
+ *
304
+ * @param theirKeyGetter
305
+ * A direct property name or function by which the referenceable
306
+ * id(s) can be extracted via from an element in their array
307
+ *
308
+ * @returns A chainable joiner that can be used to set a new key to the
309
+ * result of the join
310
+ **/
311
+ referencesTheir: <KU extends keyof U, Y>(theirKeyGetter: KU | ((u: U) => Y)) => {
312
+ /**
313
+ * what key on the original element the joined value / values
314
+ * should be persisted to
315
+ *
316
+ * @param joinKey
317
+ * A new or existing key for type T that the data that
318
+ * was joined will be added to
319
+ *
320
+ * @param options
321
+ * Any additional tweaks to how this join should be
322
+ * performed.
323
+ *
324
+ * @returns A new QueryableArray with objects that contain the joined
325
+ * data
326
+ **/
327
+ storedTo: <K extends string | number, TU extends T & { [k in K]?: T[KT] extends Array<unknown> ? U[] : U; } = T & { [k in K]?: T[KT] extends unknown[] ? U[] : U; }>(joinKey: K, { keepUndefinedReferences, }?: {
328
+ /** if true, retains `undefined` as elements in a joined array
329
+ * element being referenced couldn't be found */
330
+ keepUndefinedReferences?: boolean;
331
+ }) => QueryableArray<TU>;
332
+ };
333
+ };
334
+ };
335
+ /**
336
+ * start filtering the array to elements that have a particular value for the
337
+ * specified property name.
338
+ *
339
+ * @param propertyName
340
+ * The property to extract from each element to perform further
341
+ * queries on
342
+ *
343
+ * @returns A chainable QueryClause that can be resolved to filter the array
344
+ */
345
+ where<K extends keyof T, X extends T[K]>(propertyName: K): UnionToIntersection<QueryClause<X, QueryableArray<T>>>;
346
+ /**
347
+ * start filtering the array to elements that have a particualr value when
348
+ * passed through the specified extract function.
349
+ *
350
+ * @param extractFn
351
+ * The function to perform on each element in the array to get the
352
+ * value that can have further queries performed on
353
+ *
354
+ * @returns A chainable QueryClause that can be resolved to filter the array
355
+ */
356
+ where<X>(extractFn: (t: T) => X): UnionToIntersection<QueryClause<X, QueryableArray<T>>>;
357
+ map<U>(fn: (t: T, idx: number, arr: T[]) => U): QueryableArray<U>;
358
+ filter(...args: Parameters<Array<T>["filter"]>): QueryableArray<T>;
359
+ concat(...args: Parameters<Array<T>["concat"]>): QueryableArray<T>;
360
+ slice(...args: Parameters<Array<T>["slice"]>): QueryableArray<T>;
361
+ flat<A, D extends number = 1>(depth?: D | undefined): QueryableArray<FlatArray<A, D>>;
362
+ flatMap<U>(callback: (value: T, index: number, array: T[]) => U | readonly U[]): QueryableArray<U>;
363
+ with(...args: Parameters<Array<T>["with"]>): QueryableArray<T>;
364
+ toSorted(...args: Parameters<Array<T>["toSorted"]>): QueryableArray<T>;
365
+ toReversed(): QueryableArray<T>;
366
+ toSpliced(startIdx: number, deleteCount?: number, replaceItem?: T): QueryableArray<T>;
367
+ fill(...args: Parameters<Array<T>["fill"]>): this;
368
+ sort(...args: Parameters<Array<T>["sort"]>): this;
369
+ reverse(): this;
370
+ splice(...args: Parameters<Array<T>["splice"]>): QueryableArray<T>;
371
+ copyWithin(target: number, start: number, end?: number): this;
372
+ includes(...args: Parameters<Array<T>["includes"]>): boolean;
373
+ find<U>(predicate: (value: T, index: number, obj: T[]) => U): T | undefined;
374
+ findIndex<U>(predicate: (value: T, index: number, obj: T[]) => U): number;
375
+ findLast<U>(predicate: (value: T, index: number, obj: T[]) => U): T | undefined;
376
+ findLastIndex<U>(predicate: (value: T, index: number, obj: T[]) => U): number;
377
+ indexOf(...args: Parameters<Array<T>["indexOf"]>): number;
378
+ lastIndexOf(...args: Parameters<Array<T>["lastIndexOf"]>): number;
379
+ forEach(...args: Parameters<Array<T>["forEach"]>): void;
380
+ every(predicate: (t: T) => boolean): boolean;
381
+ every<S extends T>(predicate: (t: T) => t is S): this is S[];
382
+ some(...args: Parameters<Array<T>["some"]>): boolean;
383
+ reduce<U = T, RT extends U extends Array<infer E> ? QueryableArray<E> : U = U extends Array<infer E> ? QueryableArray<E> : U>(callbackfn: (acc: U, cur: T, idx: number, arr: T[]) => U, initialValue?: U): RT;
384
+ reduceRight<U = T, RT extends U extends Array<infer E> ? QueryableArray<E> : U = U extends Array<infer E> ? QueryableArray<E> : U>(callbackfn: (acc: U, cur: T, idx: number, arr: T[]) => U, initialValue?: U): RT;
385
+ pop(): T | undefined;
386
+ shift(): T | undefined;
387
+ push(...args: Parameters<Array<T>["push"]>): number;
388
+ unshift(...args: Parameters<Array<T>["unshift"]>): number;
389
+ at(index: number): T | undefined;
390
+ entries(): ArrayIterator<[number, T]>;
391
+ values(): ArrayIterator<T>;
392
+ keys(): ArrayIterator<number>;
393
+ join(separator?: string): string;
394
+ toString(): string;
395
+ toLocaleString(): string;
396
+ toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions & Intl.DateTimeFormatOptions): string;
397
+ }
398
+
399
+ export declare type QueryClause<T, R = QueryClauseResult<T>> = T extends Array<unknown> ? ArrayQueryClause<T, ElemType<T>, R> : T extends object ? ObjectQueryClause<T, R> : T extends string | number ? ComparableQueryClause<T, R> : BaseQueryClause<T, R>;
400
+
401
+ export declare interface QueryClauseResult<T> {
402
+ isResolved: true;
403
+ result: boolean;
404
+ }
405
+
406
+ export declare enum SortOrder {
407
+ A_BEFORE_B = -1,
408
+ B_BEFORE_A = 1,
409
+ EQUAL = 0
410
+ }
411
+
412
+ export declare type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
413
+
414
+ export { }
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var w=(e=>(e[e.A_BEFORE_B=-1]="A_BEFORE_B",e[e.B_BEFORE_A=1]="B_BEFORE_A",e[e.EQUAL=0]="EQUAL",e))(w||{});function v(e){return typeof e=="string"}function B(e){return typeof e=="number"}function L(e){return typeof e=="boolean"}function M(e){const t=typeof e;return t==="string"||t==="number"||t==="boolean"}function N(e){return typeof e=="symbol"}function d(e){return e instanceof Array}function X(e){return typeof e=="object"&&!d(e)&&e!==null}function E(e){return typeof e=="function"}function g(e){return typeof e=="object"&&e!==null}function R(e){return!(e===void 0||e===null)}function U(e,t="expected value to be true"){if(!e)throw new Error(t)}const q=e=>g(e)?Object.keys(e):[],m=(e,t)=>e===t?!0:M(e)||M(t)?!1:N(e)&&N(t)?e.toString()===t.toString():JSON.stringify(e)===JSON.stringify(t),A=(e,t)=>q(t).every(s=>g(t[s])&&g(e[s])?A(e[s],t[s]):m(e[s],t[s])),V=(e,t)=>!g(e)||!g(t)?m(e,t):[...q(e),...q(t)].every(s=>g(e[s])&&g(t[s])?V(e[s],t[s]):m(e[s],t[s])),F=Symbol("["),W=Symbol("]"),j=e=>{if(!R(e))return e;const t=[];for(const n of e){if(!d(n)){t.push(n);continue}t.push(F),n.forEach(s=>t.push(s)),t.push(W)}return t},Y=(e,t,n)=>{const s=[[]];let u=0,a=-1;if(e[0]!==F)return e.length>0&&e[t[0]](o=>n(o));for(let o=0;o<e.length;o+=1){const D=e[o];if(D===F){if(a>=0){a+=1;continue}u+=1,s[u]=[]}else if(D===W){if(a>0){a-=1;continue}a=-1;const p=t[u];if(!p)throw new Error("too few array logics passed");const h=s.pop(),c=h.length>0&&(p==="some"?h.some(r=>L(r)?r:n(r)):h.every(r=>L(r)?r:n(r)));u-=1,s[u].push(c);for(let r=u;r>=0;r-=1){const i=t[r];if(i==="some"&&c||i==="every"&&!c){if(r===0)return c;a+=1}else break}}else{if(a>=0)continue;s[u].push(D)}}U(s.length==1);const _=t[0],l=s[0];return l&&l.length>0&&(_==="some"?l.some(o=>o):l.every(o=>o))},Z=(e,t,n,s="and",u,a)=>{const _=(o,D=[],p=!1)=>{const h=r=>{const i=s==="and",J=(i?t:n).filter(O=>{if(!i&&u.has(O))return!0;const I=O?o(O):void 0,H=d(I)&&D.length>0;let T;H?T=Y(I,D,r):T=r(I);const x=p?!T:T;return x&&u.add(O),x});return a(J)},c={is:r=>g(r)?h(i=>m(i,r)):h(i=>r===i),eq:r=>c.is(r),equals:r=>c.is(r),isNull:()=>h(r=>r===null),isUndefined:()=>h(r=>r===void 0),isNullish:()=>h(r=>r==null),isEmpty:()=>h(r=>r instanceof Array?r.length===0:X(r)?Object.keys(r).length===0:!1),in:r=>h(i=>!!r.find(S=>S===i?!0:m(i,S))),satisfies:r=>h(r),greaterThan:r=>h(i=>B(i)||v(i)?i>r:!1),gt:r=>c.greaterThan(r),greaterThanOrEqualTo:r=>h(i=>B(i)||v(i)?i>=r:!1),gte:r=>c.greaterThanOrEqualTo(r),lessThan:r=>h(i=>B(i)||v(i)?i<r:!1),lt:r=>c.lessThan(r),lessThanOrEqualTo:r=>h(i=>B(i)||v(i)?i<=r:!1),lte:r=>c.lessThanOrEqualTo(r),matches:r=>h(i=>g(i)&&g(r)?A(i,r):!1),deepEquals:r=>h(i=>g(i)&&g(r)?V(i,r):!1),includes:r=>h(i=>d(i)?!!i.find(S=>m(r,S)):!1)};return c},l=(o,D=[],p=!1)=>({..._(o,D,p),get not(){return l(o,D,!p)},its:c=>l(r=>{const i=o(r);return d(i)&&D?i.map(S=>S?.[c]):i?.[c]},D,p),some:()=>l(c=>D.length==0?o(c):j(o(c)),[...D,"some"],p),every:()=>l(c=>D.length==0?o(c):j(o(c)),[...D,"every"],p)});return l(e)};class f extends Array{constructor(t,n,s="and",u=new Set){U(d(t)),super(t.length),this._currentLogic=s,this._currentSet=u,this._currentData=t,this._originalData=n??t.slice(0,t.length),this._populateInnerStore()}_currentData;_originalData;static get[Symbol.species](){return Array}get data(){return this._currentData}_populateInnerStore(){this.forEach((t,n)=>{const s=this._currentData[n];R(s)?this[n]=s:delete this[n]}),this.length=this._currentData.length}get first(){return this._currentData[0]}get last(){return this._currentData[this._currentData.length-1]}get and(){return this._currentLogic="and",this._currentSet=new Set,this}get or(){return this._currentLogic="or",this}unique(){return this.uniqueBy(t=>t)}uniqueBy(t){const n=s=>{const u={},a=[];return s.forEach(_=>{const l=E(t)?t(_):_[t],o=JSON.stringify(l);u[o]||(u[o]=!0,a.push(_))}),a};return new f(n(this._currentData),n(this._originalData),this._currentLogic,this._currentSet)}sortBy(t,n="asc"){const s=a=>E(t)?t(a):a[t],u=(a,_)=>{const l=s(a),o=s(_);return l<o?n==="asc"?w.A_BEFORE_B:w.B_BEFORE_A:l>o?n==="asc"?w.B_BEFORE_A:w.A_BEFORE_B:w.EQUAL};return new f([...this._currentData].sort(u),[...this._originalData].sort(u),this._currentLogic,this._currentSet)}indexBy(t){const n={},s=u=>E(t)?t(u):u?.[t];return this._currentData.forEach(u=>{n[s(u)]=u}),n}groupBy(t){const n={},s=u=>E(t)?t(u):u?.[t];return this._currentData.forEach(u=>{const a=s(u),_=d(a)?a:[a];for(const l of _)n[l]||(n[l]=[]),n[l].push(u)}),n}extract(t){return new f(this._currentData.flatMap(n=>E(t)?t(n):n[t]).filter(n=>!!n),this._originalData.flatMap(n=>E(t)?t(n):n[t]).filter(n=>!!n))}joinWith(t){return{whereMy:n=>{const s=E(n)?u=>n(u):u=>u[n];return{referencesTheir:u=>{const a=E(u)?_=>u(_):_=>_[u];return{storedTo:(_,{keepUndefinedReferences:l}={})=>{const o=D=>D.map(p=>{const h=s(p);if(d(h)){const c=h.map(r=>t.find(i=>a(i)===r));return{...p,[_]:l?c:c.filter(r=>!!r)}}else{const c=t.find(r=>a(r)===h);return{...p,[_]:c}}});return new f(o(this._currentData),o(this._originalData))}}}}}}}where(t){return Z(E(t)?n=>t(n):n=>n[t],this._currentData,this._originalData,this._currentLogic,this._currentSet,n=>new f(n,this._originalData,this._currentLogic,this._currentSet))}map(t){const n=this._currentData.map(t);return new f(n)}filter(...t){return new f(this._currentData.filter(...t),this._originalData,this._currentLogic,this._currentSet)}concat(...t){return new f(this._currentData.concat(...t))}slice(...t){return new f(this._currentData.slice(...t))}flat(t){return new f(this._currentData.flat(t))}flatMap(t){return new f(this._currentData.flatMap(t))}with(...t){return new f(this._currentData.with(...t))}toSorted(...t){return new f(this._currentData.toSorted(...t),this._originalData.toSorted(...t),this._currentLogic,this._currentSet)}toReversed(){return new f(this._currentData.toReversed(),this._originalData.toReversed(),this._currentLogic,this._currentSet)}toSpliced(t,n,s){return new f(R(n)&&R(s)?this._currentData.toSpliced(t,n,s):this._currentData.toSpliced(t,n),this._originalData,this._currentLogic,this._currentSet)}fill(...t){return this._currentData.fill(...t),this._populateInnerStore(),this}sort(...t){return this._currentData.sort(...t),this._populateInnerStore(),this._originalData.sort(...t),this}reverse(){return this._currentData.reverse(),this._populateInnerStore(),this._originalData.reverse(),this}splice(...t){const n=this._currentData.splice(...t);return this._populateInnerStore(),new f(n)}copyWithin(t,n,s){return this._currentData.copyWithin(t,n,s),this._populateInnerStore(),this}includes(...t){return this._currentData.includes(...t)}find(t){return this._currentData.find(t)}findIndex(t){return this._currentData.findIndex(t)}findLast(t){return this._currentData.findLast(t)}findLastIndex(t){return this._currentData.findLastIndex(t)}indexOf(...t){return this._currentData.indexOf(...t)}lastIndexOf(...t){return this._currentData.lastIndexOf(...t)}forEach(...t){return this._currentData.forEach(...t)}every(t){return this._currentData.every(t)}some(...t){return this._currentData.some(...t)}reduce(t,n){const s=this._currentData.reduce(t,n);return d(s)?new f(s):s}reduceRight(t,n){const s=this._currentData.reduceRight(t,n);return d(s)?new f(s):s}pop(){const t=this._currentData.pop();return this._populateInnerStore(),t}shift(){const t=this._currentData.shift();return this._populateInnerStore(),t}push(...t){const n=this._currentData.push(...t);return this._populateInnerStore(),n}unshift(...t){const n=this._currentData.unshift(...t);return this._populateInnerStore(),n}at(t){return this._currentData.at(t)}entries(){return this._currentData.entries()}values(){return this._currentData.values()}keys(){return this._currentData.keys()}join(t){return this._currentData.join(t)}toString(){return this._currentData.toString()}toLocaleString(t,n){return t?this._currentData.toLocaleString(t,n):this._currentData.toLocaleString()}}function P(e){return d(e)?new f(e):new f([e],[e])}const $=P;exports.ql=$;exports.queryable=P;
@@ -0,0 +1,626 @@
1
+ var w = /* @__PURE__ */ ((e) => (e[e.A_BEFORE_B = -1] = "A_BEFORE_B", e[e.B_BEFORE_A = 1] = "B_BEFORE_A", e[e.EQUAL = 0] = "EQUAL", e))(w || {});
2
+ function R(e) {
3
+ return typeof e == "string";
4
+ }
5
+ function T(e) {
6
+ return typeof e == "number";
7
+ }
8
+ function L(e) {
9
+ return typeof e == "boolean";
10
+ }
11
+ function M(e) {
12
+ const t = typeof e;
13
+ return t === "string" || t === "number" || t === "boolean";
14
+ }
15
+ function N(e) {
16
+ return typeof e == "symbol";
17
+ }
18
+ function d(e) {
19
+ return e instanceof Array;
20
+ }
21
+ function H(e) {
22
+ return typeof e == "object" && !d(e) && e !== null;
23
+ }
24
+ function E(e) {
25
+ return typeof e == "function";
26
+ }
27
+ function g(e) {
28
+ return typeof e == "object" && e !== null;
29
+ }
30
+ function v(e) {
31
+ return !(e === void 0 || e === null);
32
+ }
33
+ function U(e, t = "expected value to be true") {
34
+ if (!e)
35
+ throw new Error(t);
36
+ }
37
+ const F = (e) => g(e) ? Object.keys(e) : [], m = (e, t) => e === t ? !0 : M(e) || M(t) ? !1 : N(e) && N(t) ? e.toString() === t.toString() : JSON.stringify(e) === JSON.stringify(t), A = (e, t) => F(t).every((s) => g(t[s]) && g(e[s]) ? A(e[s], t[s]) : m(e[s], t[s])), V = (e, t) => !g(e) || !g(t) ? m(e, t) : [...F(e), ...F(t)].every((s) => g(e[s]) && g(t[s]) ? V(e[s], t[s]) : m(e[s], t[s])), x = /* @__PURE__ */ Symbol("["), W = /* @__PURE__ */ Symbol("]"), j = (e) => {
38
+ if (!v(e))
39
+ return e;
40
+ const t = [];
41
+ for (const n of e) {
42
+ if (!d(n)) {
43
+ t.push(n);
44
+ continue;
45
+ }
46
+ t.push(x), n.forEach((s) => t.push(s)), t.push(W);
47
+ }
48
+ return t;
49
+ }, X = (e, t, n) => {
50
+ const s = [[]];
51
+ let u = 0, a = -1;
52
+ if (e[0] !== x)
53
+ return e.length > 0 && e[t[0]]((o) => n(o));
54
+ for (let o = 0; o < e.length; o += 1) {
55
+ const D = e[o];
56
+ if (D === x) {
57
+ if (a >= 0) {
58
+ a += 1;
59
+ continue;
60
+ }
61
+ u += 1, s[u] = [];
62
+ } else if (D === W) {
63
+ if (a > 0) {
64
+ a -= 1;
65
+ continue;
66
+ }
67
+ a = -1;
68
+ const p = t[u];
69
+ if (!p)
70
+ throw new Error("too few array logics passed");
71
+ const h = s.pop(), c = h.length > 0 && (p === "some" ? h.some((r) => L(r) ? r : n(r)) : h.every((r) => L(r) ? r : n(r)));
72
+ u -= 1, s[u].push(c);
73
+ for (let r = u; r >= 0; r -= 1) {
74
+ const i = t[r];
75
+ if (
76
+ // any true value will succeed in a 'some' evaluation
77
+ i === "some" && c || // any false value will fail in an 'every' evaluation
78
+ i === "every" && !c
79
+ ) {
80
+ if (r === 0)
81
+ return c;
82
+ a += 1;
83
+ } else
84
+ break;
85
+ }
86
+ } else {
87
+ if (a >= 0)
88
+ continue;
89
+ s[u].push(D);
90
+ }
91
+ }
92
+ U(s.length == 1);
93
+ const _ = t[0], l = s[0];
94
+ return l && l.length > 0 && (_ === "some" ? l.some((o) => o) : l.every((o) => o));
95
+ }, Y = (e, t, n, s = "and", u, a) => {
96
+ const _ = (o, D = [], p = !1) => {
97
+ const h = (r) => {
98
+ const i = s === "and", J = (i ? t : n).filter((B) => {
99
+ if (!i && u.has(B))
100
+ return !0;
101
+ const I = B ? o(B) : void 0, P = d(I) && D.length > 0;
102
+ let O;
103
+ P ? O = X(
104
+ I,
105
+ D,
106
+ r
107
+ ) : O = r(I);
108
+ const q = p ? !O : O;
109
+ return q && u.add(B), q;
110
+ });
111
+ return a(J);
112
+ }, c = {
113
+ is: (r) => g(r) ? h((i) => m(i, r)) : h((i) => r === i),
114
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
115
+ eq: (r) => c.is(r),
116
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
117
+ equals: (r) => c.is(r),
118
+ isNull: () => h((r) => r === null),
119
+ isUndefined: () => h((r) => r === void 0),
120
+ isNullish: () => h((r) => r == null),
121
+ isEmpty: () => h(
122
+ (r) => r instanceof Array ? r.length === 0 : H(r) ? Object.keys(r).length === 0 : !1
123
+ ),
124
+ in: (r) => h((i) => !!r.find((S) => S === i ? !0 : m(i, S))),
125
+ satisfies: (r) => h(r),
126
+ greaterThan: (r) => h((i) => T(i) || R(i) ? i > r : !1),
127
+ gt: (r) => c.greaterThan(
128
+ r
129
+ ),
130
+ greaterThanOrEqualTo: (r) => h((i) => T(i) || R(i) ? i >= r : !1),
131
+ gte: (r) => c.greaterThanOrEqualTo(r),
132
+ lessThan: (r) => h((i) => T(i) || R(i) ? i < r : !1),
133
+ lt: (r) => c.lessThan(
134
+ r
135
+ ),
136
+ lessThanOrEqualTo: (r) => h((i) => T(i) || R(i) ? i <= r : !1),
137
+ lte: (r) => c.lessThanOrEqualTo(r),
138
+ matches: (r) => h((i) => g(i) && g(r) ? A(i, r) : !1),
139
+ deepEquals: (r) => h((i) => g(i) && g(r) ? V(i, r) : !1),
140
+ includes: (r) => h((i) => d(i) ? !!i.find((S) => m(r, S)) : !1)
141
+ };
142
+ return c;
143
+ }, l = (o, D = [], p = !1) => ({
144
+ ..._(o, D, p),
145
+ get not() {
146
+ return l(o, D, !p);
147
+ },
148
+ its: (c) => l(
149
+ (r) => {
150
+ const i = o(r);
151
+ return d(i) && D ? i.map((S) => S?.[c]) : i?.[c];
152
+ },
153
+ D,
154
+ p
155
+ ),
156
+ some: () => l(
157
+ (c) => D.length == 0 ? o(c) : j(o(c)),
158
+ [...D, "some"],
159
+ p
160
+ ),
161
+ every: () => l(
162
+ (c) => D.length == 0 ? o(c) : j(o(c)),
163
+ [...D, "every"],
164
+ p
165
+ )
166
+ });
167
+ return l(e);
168
+ };
169
+ class f extends Array {
170
+ constructor(t, n, s = "and", u = /* @__PURE__ */ new Set()) {
171
+ U(d(t)), super(t.length), this._currentLogic = s, this._currentSet = u, this._currentData = t, this._originalData = n ?? t.slice(0, t.length), this._populateInnerStore();
172
+ }
173
+ _currentData;
174
+ _originalData;
175
+ /** ensure that we instantiate intermediate versions of query arrays (e.g.
176
+ * through .filter or .map) as a straight array instance -- we will always
177
+ * explicitly wrap the result in a QueryableArray when appropriate */
178
+ static get [Symbol.species]() {
179
+ return Array;
180
+ }
181
+ get data() {
182
+ return this._currentData;
183
+ }
184
+ /**
185
+ * ensure that we have the elements from _currentData also populated into our
186
+ * numeric indices, so that you can extract data as you would from a regular
187
+ * array
188
+ **/
189
+ _populateInnerStore() {
190
+ this.forEach((t, n) => {
191
+ const s = this._currentData[n];
192
+ v(s) ? this[n] = s : delete this[n];
193
+ }), this.length = this._currentData.length;
194
+ }
195
+ /**
196
+ * get the first element in the query array, based on the current query
197
+ */
198
+ get first() {
199
+ return this._currentData[0];
200
+ }
201
+ /**
202
+ * get the last element in the query array, based on the current query
203
+ */
204
+ get last() {
205
+ return this._currentData[this._currentData.length - 1];
206
+ }
207
+ /**
208
+ * apply a new query to the array, enusring that only elements that match
209
+ * both the current query and the new query are included
210
+ */
211
+ get and() {
212
+ return this._currentLogic = "and", this._currentSet = /* @__PURE__ */ new Set(), this;
213
+ }
214
+ /**
215
+ * apply a new query to the array, ensuring that elements that match either
216
+ * the current query or the new query are included (without duplicates)
217
+ */
218
+ get or() {
219
+ return this._currentLogic = "or", this;
220
+ }
221
+ /**
222
+ * Remove any duplicate members of the array, via deep-equality comparison
223
+ *
224
+ * @returns This QueryableArray sans duplicate members
225
+ */
226
+ unique() {
227
+ return this.uniqueBy((t) => t);
228
+ }
229
+ /**
230
+ * Remove any members of the array that are considered a duplicate via the
231
+ * provided key getter.
232
+ *
233
+ * @param keyGetter
234
+ * Either a property name or a function that can be used to get the
235
+ * value that should be compared for uniqueness
236
+ *
237
+ * @returns This QueryableArray sans duplicate members
238
+ */
239
+ uniqueBy(t) {
240
+ const n = (s) => {
241
+ const u = {}, a = [];
242
+ return s.forEach((_) => {
243
+ const l = E(t) ? t(_) : _[t], o = JSON.stringify(l);
244
+ u[o] || (u[o] = !0, a.push(_));
245
+ }), a;
246
+ };
247
+ return new f(
248
+ n(this._currentData),
249
+ n(this._originalData),
250
+ this._currentLogic,
251
+ this._currentSet
252
+ );
253
+ }
254
+ /**
255
+ * sort the query array by either the value in the specified property or via
256
+ * a function evaluator.
257
+ *
258
+ * @param sortKeyGetter
259
+ * The property or function that can retrieve a sortable value from
260
+ * a given element
261
+ *
262
+ * @param direction
263
+ * Whether we should perform an ascending or descending search
264
+ *
265
+ * @returns A sorted version of this query array
266
+ */
267
+ sortBy(t, n = "asc") {
268
+ const s = (a) => E(t) ? t(a) : a[t], u = (a, _) => {
269
+ const l = s(a), o = s(_);
270
+ return l < o ? n === "asc" ? w.A_BEFORE_B : w.B_BEFORE_A : l > o ? n === "asc" ? w.B_BEFORE_A : w.A_BEFORE_B : w.EQUAL;
271
+ };
272
+ return new f(
273
+ [...this._currentData].sort(u),
274
+ [...this._originalData].sort(u),
275
+ this._currentLogic,
276
+ this._currentSet
277
+ );
278
+ }
279
+ /**
280
+ * turn this query array into a map, with keys based on the provided key
281
+ * getter.
282
+ *
283
+ * This assumes that the key retrieved is unique for the element in
284
+ * question; if multiple elements could return the same key, use `groupBy`
285
+ * instead
286
+ *
287
+ * @param keyGetter
288
+ * A function or name of a property that can extract an appropriate
289
+ * key for any given element.
290
+ *
291
+ * @returns A map of the query array, keyed by the results of the keyGetter
292
+ */
293
+ indexBy(t) {
294
+ const n = {}, s = (u) => E(t) ? t(u) : u?.[t];
295
+ return this._currentData.forEach((u) => {
296
+ n[s(u)] = u;
297
+ }), n;
298
+ }
299
+ /**
300
+ * group all elements in the query array by a given key
301
+ *
302
+ * @param keyGetter
303
+ * The property name or function to retrieve the value
304
+ *
305
+ * @returns A map with keys based on the results of keyGetter and an array
306
+ * of values that returned that key
307
+ */
308
+ groupBy(t) {
309
+ const n = {}, s = (u) => E(t) ? t(u) : u?.[t];
310
+ return this._currentData.forEach((u) => {
311
+ const a = s(u), _ = d(a) ? a : [a];
312
+ for (const l of _)
313
+ n[l] || (n[l] = []), n[l].push(u);
314
+ }), n;
315
+ }
316
+ /**
317
+ * extract inner data from this query array and turn it into a QueryableArray of
318
+ * its own. Will automatically flatten data if the result of the key getter
319
+ * is an array.
320
+ */
321
+ extract(t) {
322
+ return new f(
323
+ this._currentData.flatMap(
324
+ (n) => E(t) ? t(n) : n[t]
325
+ ).filter((n) => !!n),
326
+ this._originalData.flatMap(
327
+ (n) => E(t) ? t(n) : n[t]
328
+ ).filter((n) => !!n)
329
+ );
330
+ }
331
+ joinWith(t) {
332
+ return {
333
+ /**
334
+ * a key on the current array that references a value in the provided
335
+ * array. This can be either a value or an array of values
336
+ *
337
+ * @param myKeyGetter
338
+ * A direct property name or function by which the referenceable
339
+ * id(s) can be extracted via from an element in my array
340
+ *
341
+ * @returns A chainable joiner that can be used to set a new key to the
342
+ * result of the join
343
+ **/
344
+ whereMy: (n) => {
345
+ const s = E(n) ? (u) => n(u) : (u) => u[n];
346
+ return {
347
+ /**
348
+ * a key on the provided array that will have the same value as the
349
+ * result of retrieving the above key
350
+ *
351
+ * @param theirKeyGetter
352
+ * A direct property name or function by which the referenceable
353
+ * id(s) can be extracted via from an element in their array
354
+ *
355
+ * @returns A chainable joiner that can be used to set a new key to the
356
+ * result of the join
357
+ **/
358
+ referencesTheir: (u) => {
359
+ const a = E(u) ? (_) => u(_) : (_) => _[u];
360
+ return {
361
+ /**
362
+ * what key on the original element the joined value / values
363
+ * should be persisted to
364
+ *
365
+ * @param joinKey
366
+ * A new or existing key for type T that the data that
367
+ * was joined will be added to
368
+ *
369
+ * @param options
370
+ * Any additional tweaks to how this join should be
371
+ * performed.
372
+ *
373
+ * @returns A new QueryableArray with objects that contain the joined
374
+ * data
375
+ **/
376
+ storedTo: (_, {
377
+ keepUndefinedReferences: l
378
+ } = {}) => {
379
+ const o = (D) => D.map((p) => {
380
+ const h = s(p);
381
+ if (d(h)) {
382
+ const c = h.map(
383
+ (r) => t.find((i) => a(i) === r)
384
+ );
385
+ return {
386
+ ...p,
387
+ [_]: l ? c : c.filter((r) => !!r)
388
+ };
389
+ } else {
390
+ const c = t.find(
391
+ (r) => a(r) === h
392
+ );
393
+ return {
394
+ ...p,
395
+ [_]: c
396
+ };
397
+ }
398
+ });
399
+ return new f(
400
+ o(this._currentData),
401
+ o(this._originalData)
402
+ );
403
+ }
404
+ };
405
+ }
406
+ };
407
+ }
408
+ };
409
+ }
410
+ /**
411
+ * perform a where query either on a caller-specified getter or via a set of
412
+ * query clauses that have already been queued up for our use by a previous
413
+ * iteration of the query array
414
+ *
415
+ * @param keyOrFn
416
+ * The method through which we should retrieve values for this query
417
+ * array. Ignored if `myQueryClauses` is set.
418
+ *
419
+ * @param myQueryClauses
420
+ * If available, previous iterations of running this query arrays
421
+ * values through a value extraction, then passed on to a query
422
+ * clause. Largely used in cases when we are working with deeply
423
+ * nested arrays or objects, as they have special handling built
424
+ * into the query clause object to resolve deeply with type safety
425
+ *
426
+ * @returns A new QueryClause that can be resolved to filter the query array
427
+ * down based on further chained function calls.
428
+ */
429
+ where(t) {
430
+ return Y(
431
+ E(t) ? (n) => t(n) : (n) => n[t],
432
+ this._currentData,
433
+ this._originalData,
434
+ this._currentLogic,
435
+ this._currentSet,
436
+ (n) => new f(
437
+ n,
438
+ this._originalData,
439
+ this._currentLogic,
440
+ this._currentSet
441
+ )
442
+ );
443
+ }
444
+ // =========================================
445
+ // OVERRIDDEN ARRAY METHODS RETURNING ARRAYS
446
+ // =========================================
447
+ // These are methods that in a native array, will return another array.
448
+ // These will be wrapped in a QueryableArray instead, after being performed on
449
+ // the native _currentData array
450
+ map(t) {
451
+ const n = this._currentData.map(t);
452
+ return new f(n);
453
+ }
454
+ filter(...t) {
455
+ return new f(
456
+ this._currentData.filter(...t),
457
+ this._originalData,
458
+ this._currentLogic,
459
+ this._currentSet
460
+ );
461
+ }
462
+ concat(...t) {
463
+ return new f(this._currentData.concat(...t));
464
+ }
465
+ slice(...t) {
466
+ return new f(this._currentData.slice(...t));
467
+ }
468
+ flat(t) {
469
+ return new f(
470
+ this._currentData.flat(t)
471
+ );
472
+ }
473
+ flatMap(t) {
474
+ return new f(this._currentData.flatMap(t));
475
+ }
476
+ with(...t) {
477
+ return new f(this._currentData.with(...t));
478
+ }
479
+ toSorted(...t) {
480
+ return new f(
481
+ this._currentData.toSorted(...t),
482
+ this._originalData.toSorted(...t),
483
+ this._currentLogic,
484
+ this._currentSet
485
+ );
486
+ }
487
+ toReversed() {
488
+ return new f(
489
+ this._currentData.toReversed(),
490
+ this._originalData.toReversed(),
491
+ this._currentLogic,
492
+ this._currentSet
493
+ );
494
+ }
495
+ toSpliced(t, n, s) {
496
+ return new f(
497
+ v(n) && v(s) ? this._currentData.toSpliced(t, n, s) : this._currentData.toSpliced(t, n),
498
+ this._originalData,
499
+ this._currentLogic,
500
+ this._currentSet
501
+ );
502
+ }
503
+ // ===========================================
504
+ // OVERRIDDEN IN-PLACE MODIFYING ARRAY METHODS
505
+ // ===========================================
506
+ // These are methods that operate directly on the current array reference
507
+ // rather than returning a new copy of the array with the operation performed.
508
+ // We perform these methods on the native _currentData array, then update
509
+ // our inner store to reflect the updates to the _currentData array before
510
+ // returning an appropriate result for the method.
511
+ fill(...t) {
512
+ return this._currentData.fill(...t), this._populateInnerStore(), this;
513
+ }
514
+ sort(...t) {
515
+ return this._currentData.sort(...t), this._populateInnerStore(), this._originalData.sort(...t), this;
516
+ }
517
+ reverse() {
518
+ return this._currentData.reverse(), this._populateInnerStore(), this._originalData.reverse(), this;
519
+ }
520
+ splice(...t) {
521
+ const n = this._currentData.splice(...t);
522
+ return this._populateInnerStore(), new f(n);
523
+ }
524
+ copyWithin(t, n, s) {
525
+ return this._currentData.copyWithin(t, n, s), this._populateInnerStore(), this;
526
+ }
527
+ // =========================================
528
+ // OVERRIDDEN ARRAY METHODS FOR OPTIMIZATION
529
+ // =========================================
530
+ // These are methods that we anticipate being slower when run directly on a
531
+ // QueryableArray, as a result of the optimization that our semi-custom
532
+ // implementation requires. These pass through the function call to our
533
+ // _currentData array to take advantage of the optimized versions
534
+ includes(...t) {
535
+ return this._currentData.includes(...t);
536
+ }
537
+ find(t) {
538
+ return this._currentData.find(t);
539
+ }
540
+ findIndex(t) {
541
+ return this._currentData.findIndex(t);
542
+ }
543
+ findLast(t) {
544
+ return this._currentData.findLast(t);
545
+ }
546
+ findLastIndex(t) {
547
+ return this._currentData.findLastIndex(t);
548
+ }
549
+ indexOf(...t) {
550
+ return this._currentData.indexOf(...t);
551
+ }
552
+ lastIndexOf(...t) {
553
+ return this._currentData.lastIndexOf(...t);
554
+ }
555
+ forEach(...t) {
556
+ return this._currentData.forEach(...t);
557
+ }
558
+ every(t) {
559
+ return this._currentData.every(t);
560
+ }
561
+ some(...t) {
562
+ return this._currentData.some(...t);
563
+ }
564
+ reduce(t, n) {
565
+ const s = this._currentData.reduce(t, n);
566
+ return d(s) ? new f(s) : s;
567
+ }
568
+ reduceRight(t, n) {
569
+ const s = this._currentData.reduceRight(t, n);
570
+ return d(s) ? new f(s) : s;
571
+ }
572
+ // ==========================================
573
+ // OVERRIDDEN ARRAY METHODS FOR FUNCTIONALITY
574
+ // ==========================================
575
+ // These are methods that, if we didn't override them within our instance, we
576
+ // would end up with odd behavior as the interior array and the external
577
+ // representation of it would no longer match.
578
+ pop() {
579
+ const t = this._currentData.pop();
580
+ return this._populateInnerStore(), t;
581
+ }
582
+ shift() {
583
+ const t = this._currentData.shift();
584
+ return this._populateInnerStore(), t;
585
+ }
586
+ push(...t) {
587
+ const n = this._currentData.push(...t);
588
+ return this._populateInnerStore(), n;
589
+ }
590
+ unshift(...t) {
591
+ const n = this._currentData.unshift(...t);
592
+ return this._populateInnerStore(), n;
593
+ }
594
+ at(t) {
595
+ return this._currentData.at(t);
596
+ }
597
+ entries() {
598
+ return this._currentData.entries();
599
+ }
600
+ values() {
601
+ return this._currentData.values();
602
+ }
603
+ keys() {
604
+ return this._currentData.keys();
605
+ }
606
+ join(t) {
607
+ return this._currentData.join(t);
608
+ }
609
+ toString() {
610
+ return this._currentData.toString();
611
+ }
612
+ toLocaleString(t, n) {
613
+ return t ? this._currentData.toLocaleString(t, n) : this._currentData.toLocaleString();
614
+ }
615
+ }
616
+ function Z(e) {
617
+ return d(e) ? new f(e) : new f(
618
+ [e],
619
+ [e]
620
+ );
621
+ }
622
+ const $ = Z;
623
+ export {
624
+ $ as ql,
625
+ Z as queryable
626
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "queryable-array",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "type": "module",
6
+ "main": "./dist/queryable-array.cjs",
7
+ "module": "./dist/queryable-array.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/queryable-array.mjs",
13
+ "require": "./dist/queryable-array.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "vite build",
21
+ "test": "vitest",
22
+ "test:benchmark": "vitest bench",
23
+ "test:benchmark:report": "vitest bench --outputJson benchmark.json --watch false && node scripts/benchmarkFormatter.js && rm benchmark.json",
24
+ "lint": "eslint"
25
+ },
26
+ "keywords": [
27
+ "query",
28
+ "filter"
29
+ ],
30
+ "author": "Kip Price",
31
+ "homepage": "https://practically.cool",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/kipprice/queryable-array.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/kipprice/queryable-array/issues"
39
+ },
40
+ "packageManager": "pnpm@10.17.1",
41
+ "devDependencies": {
42
+ "@eslint/js": "^10.0.1",
43
+ "@types/node": "^25.2.0",
44
+ "@vitest/coverage-v8": "^4.0.18",
45
+ "eslint": "^10.0.2",
46
+ "globals": "^17.4.0",
47
+ "typescript": "^5.9.3",
48
+ "typescript-eslint": "^8.56.1",
49
+ "vite": "^7.3.1",
50
+ "vite-plugin-dts": "^4.5.4",
51
+ "vitest": "^4.0.18"
52
+ }
53
+ }