shelving 1.45.1 → 1.47.1

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/db/Database.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Entry, Observable, Observer, Result, Unsubscriber, Results, Validatable, Validator, Key, Data, Entries, Datas, Validators, ValidatorType, Dispatcher } from "../util/index.js";
2
2
  import { PropUpdates, Update } from "../update/index.js";
3
3
  import type { Provider } from "../provider/Provider.js";
4
- import { Filters, Sorts, Query } from "../query/index.js";
4
+ import { Filters, Sorts, Query, FilterProps, SortKeys } from "../query/index.js";
5
5
  /**
6
6
  * Combines a database model and a provider.
7
7
  *
@@ -14,7 +14,7 @@ export declare class Database<V extends Validators<Datas> = Validators<Datas>> {
14
14
  readonly provider: Provider;
15
15
  constructor(validators: V, provider: Provider);
16
16
  /** Create a query on a collection in this model. */
17
- query<K extends Key<V>>(collection: K, filters?: Filters<ValidatorType<V[K]>>, sorts?: Sorts<ValidatorType<V[K]>>, limit?: number | null): DatabaseQuery<ValidatorType<V[K]>>;
17
+ query<K extends Key<V>>(collection: K, filters?: FilterProps<ValidatorType<V[K]>>, sorts?: SortKeys<ValidatorType<V[K]>>, limit?: number | null): DatabaseQuery<ValidatorType<V[K]>>;
18
18
  /** Reference a document in a collection in this model. */
19
19
  doc<K extends Key<V>>(collection: K, id: string): DatabaseDocument<ValidatorType<V[K]>>;
20
20
  /**
@@ -69,14 +69,14 @@ export declare class DatabaseQuery<T extends Data = Data> extends Query<T> imple
69
69
  * @return Entry in `[id, data]` format for the first document.
70
70
  * @throws RequiredError if there were no results for this query.
71
71
  */
72
- get result(): Entry<T> | undefined | PromiseLike<Entry<T> | undefined>;
72
+ get result(): DocumentResult<T> | PromiseLike<DocumentResult<T>>;
73
73
  /**
74
74
  * Get an entry for the first document matched by this query.
75
75
  *
76
76
  * @return Entry in `[id, data]` format for the first document.
77
77
  * @throws RequiredError if there were no results for this query.
78
78
  */
79
- get data(): Entry<T> | PromiseLike<Entry<T>>;
79
+ get data(): DocumentData<T> | PromiseLike<DocumentData<T>>;
80
80
  /**
81
81
  * Subscribe to all matching documents.
82
82
  * - `next()` is called once with the initial results, and again any time the results change.
@@ -111,7 +111,9 @@ export declare class DatabaseQuery<T extends Data = Data> extends Query<T> imple
111
111
  toString(): string;
112
112
  }
113
113
  /** Get the data for a document from a result for that document. */
114
- export declare function getQueryData<T extends Data>(entries: Entries<T>, ref: DatabaseQuery<T>): Entry<T>;
114
+ export declare function getQueryData<T extends Data>(entries: Entries<T>, ref: DatabaseQuery<T>): DocumentData<T>;
115
+ /** Get the data for a document from a result for that document. */
116
+ export declare function getQueryResult<T extends Data>(entries: Entries<T>, ref: DatabaseQuery<T>): DocumentResult<T>;
115
117
  /** A document reference within a specific database. */
116
118
  export declare class DatabaseDocument<T extends Data = Data> implements Observable<Result<T>>, Validatable<T> {
117
119
  readonly db: Database;
@@ -120,7 +122,7 @@ export declare class DatabaseDocument<T extends Data = Data> implements Observab
120
122
  readonly id: string;
121
123
  constructor(db: Database, validator: Validator<T>, collection: string, id: string);
122
124
  /** Create a query on this document's collection. */
123
- query(filters?: Filters<T>, sorts?: Sorts<T>, limit?: number | null): DatabaseQuery<T>;
125
+ query(filters?: FilterProps<T>, sorts?: SortKeys<T>, limit?: number | null): DatabaseQuery<T>;
124
126
  /** Get an 'optional' reference to this document (uses a `ModelQuery` with an `id` filter). */
125
127
  get optional(): DatabaseQuery<T>;
126
128
  /**
@@ -132,7 +134,7 @@ export declare class DatabaseDocument<T extends Data = Data> implements Observab
132
134
  * Get the result of this document.
133
135
  * @return Document's data, or `undefined` if the document doesn't exist (possibly promised).
134
136
  */
135
- get result(): Result<T> | PromiseLike<Result<T>>;
137
+ get result(): DocumentResult<T> | PromiseLike<DocumentResult<T>>;
136
138
  /**
137
139
  * Get the data of this document.
138
140
  * - Useful for destructuring, e.g. `{ name, title } = await documentThatMustExist.asyncData`
@@ -140,7 +142,7 @@ export declare class DatabaseDocument<T extends Data = Data> implements Observab
140
142
  * @return Document's data (possibly promised).
141
143
  * @throws RequiredError if the document's result was undefined.
142
144
  */
143
- get data(): T | PromiseLike<T>;
145
+ get data(): DocumentData<T> | PromiseLike<DocumentData<T>>;
144
146
  /**
145
147
  * Subscribe to the result of this document (indefinitely).
146
148
  * - `next()` is called once with the initial result, and again any time the result changes.
@@ -159,5 +161,14 @@ export declare class DatabaseDocument<T extends Data = Data> implements Observab
159
161
  validate(unsafeData: Data): T;
160
162
  toString(): string;
161
163
  }
164
+ /** Database data embeds the corresponding `Document` instance and string ID into the data. */
165
+ export declare type DocumentData<T extends Data> = T & {
166
+ _id: string;
167
+ _doc: DatabaseDocument<T>;
168
+ };
169
+ /** Get the data for a document from a result for that document. */
170
+ export declare function getDocumentData<T extends Data>(result: Result<T>, ref: DatabaseDocument<T>): DocumentData<T>;
171
+ /** Database result embeds the corresponding `Document` instance and string ID into the result. */
172
+ export declare type DocumentResult<T extends Data> = DocumentData<T> | null;
162
173
  /** Get the data for a document from a result for that document. */
163
- export declare function getDocumentData<T extends Data>(result: Result<T>, ref: DatabaseDocument<T>): T;
174
+ export declare function getDocumentResult<T extends Data>(result: Result<T>, ref: DatabaseDocument<T>): DocumentResult<T>;
package/db/Database.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { callAsync, getFirstItem, throwAsync, validate, getMap, countItems, hasItems, ResultsObserver, } from "../util/index.js";
2
2
  import { DataUpdate, Update } from "../update/index.js";
3
3
  import { Feedback, InvalidFeedback } from "../feedback/index.js";
4
- import { Filters, Query, EqualFilter } from "../query/index.js";
4
+ import { Filters, Sorts, Query, Filter } from "../query/index.js";
5
5
  import { DocumentRequiredError, DocumentValidationError, QueryRequiredError, QueryValidationError } from "./errors.js";
6
6
  /**
7
7
  * Combines a database model and a provider.
@@ -18,7 +18,7 @@ export class Database {
18
18
  }
19
19
  /** Create a query on a collection in this model. */
20
20
  query(collection, filters, sorts, limit) {
21
- return new DatabaseQuery(this, this.validators[collection], collection, filters, sorts, limit);
21
+ return new DatabaseQuery(this, this.validators[collection], collection, filters && Filters.on(filters), sorts && Sorts.on(sorts), limit);
22
22
  }
23
23
  /** Reference a document in a collection in this model. */
24
24
  doc(collection, id) {
@@ -93,7 +93,7 @@ export class DatabaseQuery extends Query {
93
93
  * @throws RequiredError if there were no results for this query.
94
94
  */
95
95
  get result() {
96
- return callAsync(getFirstItem, this.db.provider.getQuery(this.max(1)));
96
+ return callAsync(getQueryResult, this.db.provider.getQuery(this.max(1)), this);
97
97
  }
98
98
  /**
99
99
  * Get an entry for the first document matched by this query.
@@ -170,7 +170,14 @@ export class DatabaseQuery extends Query {
170
170
  export function getQueryData(entries, ref) {
171
171
  const first = getFirstItem(entries);
172
172
  if (first)
173
- return first;
173
+ return getDocumentData(first[1], ref.doc(first[0]));
174
+ throw new QueryRequiredError(ref);
175
+ }
176
+ /** Get the data for a document from a result for that document. */
177
+ export function getQueryResult(entries, ref) {
178
+ const first = getFirstItem(entries);
179
+ if (first)
180
+ return getDocumentData(first[1], ref.doc(first[0]));
174
181
  throw new QueryRequiredError(ref);
175
182
  }
176
183
  /** A document reference within a specific database. */
@@ -183,11 +190,11 @@ export class DatabaseDocument {
183
190
  }
184
191
  /** Create a query on this document's collection. */
185
192
  query(filters, sorts, limit) {
186
- return new DatabaseQuery(this.db, this.validator, this.collection, filters, sorts, limit);
193
+ return new DatabaseQuery(this.db, this.validator, this.collection, filters && Filters.on(filters), sorts && Sorts.on(sorts), limit);
187
194
  }
188
195
  /** Get an 'optional' reference to this document (uses a `ModelQuery` with an `id` filter). */
189
196
  get optional() {
190
- return new DatabaseQuery(this.db, this.validator, this.collection, new Filters(new EqualFilter("id", this.id)));
197
+ return new DatabaseQuery(this.db, this.validator, this.collection, new Filters(new Filter("id", "IS", this.id)));
191
198
  }
192
199
  /**
193
200
  * Does this document exist?
@@ -201,7 +208,7 @@ export class DatabaseDocument {
201
208
  * @return Document's data, or `undefined` if the document doesn't exist (possibly promised).
202
209
  */
203
210
  get result() {
204
- return this.db.provider.get(this);
211
+ return callAsync(getDocumentResult, this.db.provider.get(this), this);
205
212
  }
206
213
  /**
207
214
  * Get the data of this document.
@@ -252,6 +259,10 @@ export class DatabaseDocument {
252
259
  /** Get the data for a document from a result for that document. */
253
260
  export function getDocumentData(result, ref) {
254
261
  if (result)
255
- return result;
262
+ return { ...result, _id: ref.id, _doc: ref };
256
263
  throw new DocumentRequiredError(ref);
257
264
  }
265
+ /** Get the data for a document from a result for that document. */
266
+ export function getDocumentResult(result, ref) {
267
+ return result ? getDocumentData(result, ref) : null;
268
+ }
package/db/Operation.d.ts CHANGED
@@ -8,10 +8,8 @@ export declare abstract class Operation {
8
8
  }
9
9
  /** Represent a list of write operations on a database run in series. */
10
10
  export declare class Operations extends Operation {
11
- /** Return a new write operations list with a set of write operations. */
12
- static with(...operations: Nullish<Operation>[]): Operations;
13
11
  readonly operations: ImmutableArray<Operation>;
14
- constructor(operations: ImmutableArray<Nullish<Operation>>);
12
+ constructor(...operations: Nullish<Operation>[]);
15
13
  run(db: Database): Promise<Operations>;
16
14
  /** Return a new write operations list with an additional write operation added. */
17
15
  with(...operations: Nullish<Operation>[]): {
package/db/Operation.js CHANGED
@@ -1,26 +1,23 @@
1
- import { callAsyncSeries, isNotNullish, isNullish } from "../util/index.js";
1
+ import { callAsyncSeries, notNullish, isNullish } from "../util/index.js";
2
2
  /** Represent a write operation on a database. */
3
3
  export class Operation {
4
4
  }
5
5
  /** Represent a list of write operations on a database run in series. */
6
6
  export class Operations extends Operation {
7
- constructor(operations) {
7
+ constructor(...operations) {
8
8
  super();
9
- this.operations = operations.filter(isNotNullish);
10
- }
11
- /** Return a new write operations list with a set of write operations. */
12
- static with(...operations) {
13
- return new Operations(operations);
9
+ this.operations = operations.filter(notNullish);
14
10
  }
15
11
  async run(db) {
16
- return new Operations(await callAsyncSeries(_write, this.operations, db));
12
+ const ops = await callAsyncSeries(_run, this.operations, db);
13
+ return new Operations(...ops);
17
14
  }
18
15
  /** Return a new write operations list with an additional write operation added. */
19
16
  with(...operations) {
20
17
  return { __proto__: Object.getPrototypeOf(this), ...this, operations: [...this.operations, operations] };
21
18
  }
22
19
  }
23
- const _write = (operation, db) => operation.run(db);
20
+ const _run = (operation, db) => operation.run(db);
24
21
  /** Represent a add operation made to a collection in a database. */
25
22
  export class AddOperation extends Operation {
26
23
  constructor(collection, data) {
@@ -8,6 +8,7 @@ const OPERATORS = {
8
8
  IS: "==",
9
9
  NOT: "!=",
10
10
  IN: "in",
11
+ OUT: "not-in",
11
12
  GT: ">",
12
13
  GTE: ">=",
13
14
  LT: "<",
@@ -8,6 +8,7 @@ const OPERATORS = {
8
8
  IS: "==",
9
9
  NOT: "!=",
10
10
  IN: "in",
11
+ OUT: "not-in",
11
12
  GT: ">",
12
13
  GTE: ">=",
13
14
  LT: "<",
@@ -8,6 +8,7 @@ const OPERATORS = {
8
8
  IS: "==",
9
9
  NOT: "!=",
10
10
  IN: "in",
11
+ OUT: "not-in",
11
12
  GT: ">",
12
13
  GTE: ">=",
13
14
  LT: "<",
package/markup/render.js CHANGED
@@ -40,7 +40,7 @@ function renderString(content, options) {
40
40
  if (matchedRule && matchedResult && matchedResult[0]) {
41
41
  // If index is more than zero, then add a string node before this one.
42
42
  if (matchedIndex) {
43
- const prefix = content.substr(0, matchedIndex);
43
+ const prefix = content.slice(0, matchedIndex);
44
44
  appendNode(nodes, renderString(prefix, options));
45
45
  }
46
46
  // Call the rule's `render()` function to generate the node.
@@ -49,7 +49,7 @@ function renderString(content, options) {
49
49
  element.rule = matchedRule;
50
50
  appendNode(nodes, renderNode(element, childOptions));
51
51
  // Decrement the content.
52
- content = content.substr(matchedIndex + matchedResult[0].length);
52
+ content = content.slice(matchedIndex + matchedResult[0].length);
53
53
  }
54
54
  else {
55
55
  // If nothing else matched add the rest of the string as a node (presumably it doesn't have any punctuation).
package/markup/rules.js CHANGED
@@ -82,9 +82,9 @@ const OL = {
82
82
  const SPLIT_OL_ITEMS = new RegExp(`\\n+(?=${ORDERED})`, "g");
83
83
  const mapOrderedItem = (item, key) => {
84
84
  const firstSpace = item.indexOf(" ");
85
- const value = parseInt(item.substr(0, firstSpace), 10);
85
+ const value = parseInt(item.slice(0, firstSpace), 10);
86
86
  const children = item
87
- .substr(firstSpace + 1)
87
+ .slice(firstSpace + 1)
88
88
  .trimStart()
89
89
  .replace(REPLACE_INDENT, "");
90
90
  return { type: "li", key, props: { value, children } };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.45.1",
14
+ "version": "1.47.1",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
package/query/Filter.d.ts CHANGED
@@ -1,59 +1,21 @@
1
- import { Data, Entry, Matchable, Entries, ImmutableArray } from "../util/index.js";
1
+ import { Data, Entry, Matchable, Entries, Key } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
- import { FilterOperator, QueryKey } from "./types.js";
3
+ import { FilterKey, FilterOperator } from "./types.js";
4
4
  /**
5
5
  * Filter: filters a list of documents.
6
6
  *
7
7
  * @param key The name of a property that might exist on documents in the collection.
8
- * @param type MatchType reference, e.g. `is` or `contains`
8
+ * @param operator FilterOperator, e.g. `IS` or `CONTAINS`
9
9
  * @param value Value the specified property should be matched against.
10
10
  */
11
- export declare abstract class Filter<T extends Data, V extends unknown = unknown> extends Rule<T> implements Matchable<Entry<T>, void> {
12
- abstract readonly operator: FilterOperator;
13
- readonly key: QueryKey<T>;
14
- readonly value: V;
15
- constructor(key: QueryKey<T>, value: V);
16
- abstract match([id, data]: Entry<T>, target: void): boolean;
11
+ export declare class Filter<T extends Data> extends Rule<T> implements Matchable<Entry<T>, void> {
12
+ /** Parse a set of FilterProps and return the corresponding array of `Filter` instances. */
13
+ static on<X extends Data>(key: FilterKey<X>, value: unknown): Filter<X>;
14
+ readonly key: "id" | Key<T>;
15
+ readonly operator: FilterOperator;
16
+ readonly value: unknown;
17
+ constructor(key: "id" | Key<T>, operator: FilterOperator, value: unknown);
18
+ match([id, data]: Entry<T>): boolean;
17
19
  transform(entries: Entries<T>): Entries<T>;
18
20
  toString(): string;
19
21
  }
20
- /** Filter a set of values with an `IS` clause. */
21
- export declare class EqualFilter<T extends Data> extends Filter<T> {
22
- readonly operator = "IS";
23
- match([id, data]: Entry<T>): boolean;
24
- }
25
- /** Filter a set of values with an `NOT` clause. */
26
- export declare class NotEqualFilter<T extends Data> extends Filter<T> {
27
- readonly operator = "NOT";
28
- match([id, data]: Entry<T>): boolean;
29
- }
30
- /** Filter a set of values with an `IS` clause. */
31
- export declare class InArrayFilter<T extends Data> extends Filter<T, ImmutableArray> {
32
- readonly operator = "IN";
33
- match([id, data]: Entry<T>): boolean;
34
- }
35
- /** Filter a set of values with a `CONTAINS` clause. */
36
- export declare class ArrayWithFilter<T extends Data> extends Filter<T> {
37
- readonly operator = "CONTAINS";
38
- match([id, data]: Entry<T>): boolean;
39
- }
40
- /** Filter a set of values with an `LT` clause. */
41
- export declare class LessFilter<T extends Data> extends Filter<T> {
42
- readonly operator = "LT";
43
- match([id, data]: Entry<T>): boolean;
44
- }
45
- /** Filter a set of values with an `LTE` clause. */
46
- export declare class EqualLessFilter<T extends Data> extends Filter<T> {
47
- readonly operator = "LTE";
48
- match([id, data]: Entry<T>): boolean;
49
- }
50
- /** Filter a set of values with an `GT` clause. */
51
- export declare class GreaterFilter<T extends Data> extends Filter<T> {
52
- readonly operator = "GT";
53
- match([id, data]: Entry<T>): boolean;
54
- }
55
- /** Filter a set of values with an `GTE` clause. */
56
- export declare class EqualGreaterFilter<T extends Data> extends Filter<T> {
57
- readonly operator = "GTE";
58
- match([id, data]: Entry<T>): boolean;
59
- }
package/query/Filter.js CHANGED
@@ -1,103 +1,55 @@
1
- import { match, isArrayWith, isEqual, isGreater, isEqualGreater, isInArray, isLess, isEqualLess, isNotEqual, yieldFiltered } from "../util/index.js";
1
+ import { match, isArrayWith, isEqual, isGreater, isEqualGreater, isLess, isEqualLess, notEqual, yieldFiltered, notInArray, isInArray } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
3
  import { getQueryProp } from "./helpers.js";
4
+ /** Map `FilterOperator` to its corresponding `Matcher` function. */
5
+ const MATCHERS = {
6
+ IS: isEqual,
7
+ NOT: notEqual,
8
+ IN: isInArray,
9
+ OUT: notInArray,
10
+ CONTAINS: isArrayWith,
11
+ LT: isLess,
12
+ LTE: isEqualLess,
13
+ GT: isGreater,
14
+ GTE: isEqualGreater,
15
+ };
4
16
  /**
5
17
  * Filter: filters a list of documents.
6
18
  *
7
19
  * @param key The name of a property that might exist on documents in the collection.
8
- * @param type MatchType reference, e.g. `is` or `contains`
20
+ * @param operator FilterOperator, e.g. `IS` or `CONTAINS`
9
21
  * @param value Value the specified property should be matched against.
10
22
  */
11
23
  export class Filter extends Rule {
12
- constructor(key, value) {
24
+ constructor(key, operator, value) {
13
25
  super();
14
26
  this.key = key;
27
+ this.operator = operator;
15
28
  this.value = value;
16
29
  }
30
+ /** Parse a set of FilterProps and return the corresponding array of `Filter` instances. */
31
+ static on(key, value) {
32
+ return key.startsWith("!")
33
+ ? new Filter(key.slice(1), value instanceof Array ? "OUT" : "NOT", value)
34
+ : key.endsWith(">")
35
+ ? new Filter(key.slice(0, -1), "GT", value)
36
+ : key.endsWith(">=")
37
+ ? new Filter(key.slice(0, -2), "GTE", value)
38
+ : key.endsWith("<")
39
+ ? new Filter(key.slice(0, -1), "LT", value)
40
+ : key.endsWith("<=")
41
+ ? new Filter(key.slice(0, -2), "LTE", value)
42
+ : key.endsWith("[]")
43
+ ? new Filter(key.slice(0, -2), "CONTAINS", value)
44
+ : new Filter(key, value instanceof Array ? "IN" : "IS", value);
45
+ }
46
+ match([id, data]) {
47
+ return match(getQueryProp(id, data, this.key), MATCHERS[this.operator], this.value);
48
+ }
17
49
  transform(entries) {
18
50
  return yieldFiltered(entries, this);
19
51
  }
20
52
  toString() {
21
- return `${this.key}:${this.operator}=${JSON.stringify(this.value)}`;
22
- }
23
- }
24
- /** Filter a set of values with an `IS` clause. */
25
- export class EqualFilter extends Filter {
26
- constructor() {
27
- super(...arguments);
28
- this.operator = "IS";
29
- }
30
- match([id, data]) {
31
- return match(getQueryProp(id, data, this.key), isEqual, this.value);
32
- }
33
- }
34
- /** Filter a set of values with an `NOT` clause. */
35
- export class NotEqualFilter extends Filter {
36
- constructor() {
37
- super(...arguments);
38
- this.operator = "NOT";
39
- }
40
- match([id, data]) {
41
- return match(getQueryProp(id, data, this.key), isNotEqual, this.value);
42
- }
43
- }
44
- /** Filter a set of values with an `IS` clause. */
45
- export class InArrayFilter extends Filter {
46
- constructor() {
47
- super(...arguments);
48
- this.operator = "IN";
49
- }
50
- match([id, data]) {
51
- return match(getQueryProp(id, data, this.key), isInArray, this.value);
52
- }
53
- }
54
- /** Filter a set of values with a `CONTAINS` clause. */
55
- export class ArrayWithFilter extends Filter {
56
- constructor() {
57
- super(...arguments);
58
- this.operator = "CONTAINS";
59
- }
60
- match([id, data]) {
61
- return match(getQueryProp(id, data, this.key), isArrayWith, this.value);
62
- }
63
- }
64
- /** Filter a set of values with an `LT` clause. */
65
- export class LessFilter extends Filter {
66
- constructor() {
67
- super(...arguments);
68
- this.operator = "LT";
69
- }
70
- match([id, data]) {
71
- return match(getQueryProp(id, data, this.key), isLess, this.value);
72
- }
73
- }
74
- /** Filter a set of values with an `LTE` clause. */
75
- export class EqualLessFilter extends Filter {
76
- constructor() {
77
- super(...arguments);
78
- this.operator = "LTE";
79
- }
80
- match([id, data]) {
81
- return match(getQueryProp(id, data, this.key), isEqualLess, this.value);
82
- }
83
- }
84
- /** Filter a set of values with an `GT` clause. */
85
- export class GreaterFilter extends Filter {
86
- constructor() {
87
- super(...arguments);
88
- this.operator = "GT";
89
- }
90
- match([id, data]) {
91
- return match(getQueryProp(id, data, this.key), isGreater, this.value);
92
- }
93
- }
94
- /** Filter a set of values with an `GTE` clause. */
95
- export class EqualGreaterFilter extends Filter {
96
- constructor() {
97
- super(...arguments);
98
- this.operator = "GTE";
99
- }
100
- match([id, data]) {
101
- return match(getQueryProp(id, data, this.key), isEqualGreater, this.value);
53
+ return `${this.key}:${this.operator}:${JSON.stringify(this.value)}`;
102
54
  }
103
55
  }
@@ -1,18 +1,17 @@
1
1
  import { Entry, Data, Key, Entries, ImmutableArray, ArrayType } from "../util/index.js";
2
- import type { Filterable, QueryKey } from "./types.js";
2
+ import type { Filterable, FilterProps } from "./types.js";
3
3
  import { Filter } from "./Filter.js";
4
4
  import { Rules } from "./Rules.js";
5
5
  /** A set of filters. */
6
6
  export declare class Filters<T extends Data> extends Rules<T, Filter<T>> implements Filterable<T> {
7
- is<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
8
- not<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
9
- in<K extends QueryKey<T>>(key: K, value: K extends "id" ? readonly string[] : readonly T[K][]): this;
10
- contains<K extends Key<T>>(key: K, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
11
- lt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
12
- lte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
13
- gt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
14
- gte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
7
+ /** Create a `Filters` instance from a set of `FilterProps` */
8
+ static on<X extends Data>(filters: FilterProps<X>): Filters<X>;
9
+ filter(props: FilterProps<T>): this;
10
+ filter(key: "id" | "!id" | "id>" | "id>=" | "id<" | "id<=", value: string): this;
11
+ filter(key: "id" | "!id", value: ImmutableArray<string>): this;
12
+ filter<K extends Key<T>>(key: K | `!${K}` | `${K}>` | `${K}>=` | `${K}<` | `${K}<=`, value: T[K]): this;
13
+ filter<K extends Key<T>>(key: K | `!${K}`, value: ImmutableArray<string>): this;
14
+ filter<K extends Key<T>>(key: `${K}[]`, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
15
15
  match(entry: Entry<T>): boolean;
16
- get unfiltered(): this;
17
16
  transform(iterable: Entries<T>): Entries<T>;
18
17
  }
package/query/Filters.js CHANGED
@@ -1,32 +1,18 @@
1
1
  import { yieldFiltered } from "../util/index.js";
2
- import { ArrayWithFilter, EqualFilter, EqualGreaterFilter, GreaterFilter, InArrayFilter, EqualLessFilter, LessFilter, NotEqualFilter } from "./Filter.js";
2
+ import { Filter } from "./Filter.js";
3
3
  import { Rules } from "./Rules.js";
4
+ function* _yieldFilters(props) {
5
+ for (const [key, value] of Object.entries(props))
6
+ yield Filter.on(key, value);
7
+ }
4
8
  /** A set of filters. */
5
9
  export class Filters extends Rules {
6
- // Implement `Filterable`
7
- is(key, value) {
8
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new EqualFilter(key, value)] };
9
- }
10
- not(key, value) {
11
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new NotEqualFilter(key, value)] };
12
- }
13
- in(key, value) {
14
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new InArrayFilter(key, value)] };
15
- }
16
- contains(key, value) {
17
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new ArrayWithFilter(key, value)] };
10
+ /** Create a `Filters` instance from a set of `FilterProps` */
11
+ static on(filters) {
12
+ return new Filters(..._yieldFilters(filters));
18
13
  }
19
- lt(key, value) {
20
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new LessFilter(key, value)] };
21
- }
22
- lte(key, value) {
23
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new EqualLessFilter(key, value)] };
24
- }
25
- gt(key, value) {
26
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new GreaterFilter(key, value)] };
27
- }
28
- gte(key, value) {
29
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new EqualGreaterFilter(key, value)] };
14
+ filter(input, value) {
15
+ return typeof input === "string" ? this.with(Filter.on(input, value)) : this.with(..._yieldFilters(input));
30
16
  }
31
17
  match(entry) {
32
18
  for (const rule of this._rules)
@@ -34,9 +20,6 @@ export class Filters extends Rules {
34
20
  return false;
35
21
  return true;
36
22
  }
37
- get unfiltered() {
38
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [] };
39
- }
40
23
  // Implement `Rule`
41
24
  transform(iterable) {
42
25
  return this._rules.length ? yieldFiltered(iterable, this) : iterable;
package/query/Query.d.ts CHANGED
@@ -1,27 +1,26 @@
1
1
  import { Data, Entry, Key, Entries, ArrayType, ImmutableArray } from "../util/index.js";
2
- import type { Queryable, QueryKey } from "./types.js";
2
+ import type { Queryable, SortKey, FilterProps, SortKeys } from "./types.js";
3
3
  import { Filters } from "./Filters.js";
4
4
  import { Sorts } from "./Sorts.js";
5
5
  import { Rule } from "./Rule.js";
6
6
  /** Allows filtering, sorting, and limiting on a set of results. */
7
7
  export declare class Query<T extends Data> extends Rule<T> implements Queryable<T> {
8
+ /** Create a new `Query` object from a set of `QueryProps` */
9
+ static on<X extends Data>(filters?: FilterProps<X>, sorts?: SortKey<X> | ImmutableArray<SortKey<X>>, limit?: number | null): Query<X>;
8
10
  readonly filters: Filters<T>;
9
11
  readonly sorts: Sorts<T>;
10
12
  readonly limit: number | null;
11
13
  constructor(filters?: Filters<T>, sorts?: Sorts<T>, limit?: number | null);
12
- is<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
13
- not<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
14
- in<K extends QueryKey<T>>(key: K, value: K extends "id" ? readonly string[] : readonly T[K][]): this;
15
- contains<K extends Key<T>>(key: K, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
16
- lt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
17
- lte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
18
- gt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
19
- gte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
20
- get unfiltered(): this;
14
+ filter(props: FilterProps<T>): this;
15
+ filter(key: "id" | "!id" | "id>" | "id>=" | "id<" | "id<=", value: string): this;
16
+ filter(key: "id" | "!id", value: ImmutableArray<string>): this;
17
+ filter<K extends Key<T>>(key: K | `!${K}` | `${K}>` | `${K}>=` | `${K}<` | `${K}<=`, value: T[K]): this;
18
+ filter<K extends Key<T>>(key: K | `!${K}`, value: ImmutableArray<T[K]>): this;
19
+ filter<K extends Key<T>>(key: `${K}[]`, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
20
+ get unfilter(): this;
21
21
  match(entry: Entry<T>): boolean;
22
- asc(key: QueryKey<T>): this;
23
- desc(key: QueryKey<T>): this;
24
- get unsorted(): this;
22
+ sort(...keys: SortKeys<T>[]): this;
23
+ get unsort(): this;
25
24
  rank(left: Entry<T>, right: Entry<T>): number;
26
25
  /** Return a new instance of this class with a limit defined. */
27
26
  max(limit: number | null): this;