shelving 1.31.0 → 1.34.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.
Files changed (67) hide show
  1. package/db/Database.d.ts +10 -3
  2. package/db/Database.js +13 -4
  3. package/firestore/client/FirestoreClientProvider.js +5 -6
  4. package/firestore/lite/FirestoreLiteProvider.js +4 -4
  5. package/firestore/server/FirestoreServerProvider.js +5 -5
  6. package/markup/helpers.js +1 -7
  7. package/package.json +7 -7
  8. package/provider/BatchProvider.js +1 -1
  9. package/provider/MemoryProvider.js +8 -8
  10. package/provider/ValidationProvider.js +2 -2
  11. package/query/Filter.d.ts +24 -10
  12. package/query/Filter.js +64 -11
  13. package/query/Filters.d.ts +2 -1
  14. package/query/Filters.js +6 -1
  15. package/query/Query.d.ts +5 -3
  16. package/query/Query.js +8 -2
  17. package/query/Sort.d.ts +7 -6
  18. package/query/Sort.js +15 -5
  19. package/query/Sorts.d.ts +1 -0
  20. package/query/Sorts.js +6 -1
  21. package/query/types.d.ts +8 -3
  22. package/react/index.d.ts +0 -2
  23. package/react/index.js +0 -2
  24. package/react/useDocument.d.ts +6 -0
  25. package/react/useDocument.js +8 -1
  26. package/react/useQuery.d.ts +13 -1
  27. package/react/useQuery.js +15 -1
  28. package/react/useSubscribe.js +1 -1
  29. package/schema/ThroughSchema.d.ts +1 -1
  30. package/schema/ThroughSchema.js +1 -1
  31. package/stream/DataState.js +4 -4
  32. package/stream/State.js +1 -1
  33. package/update/ArrayUpdate.d.ts +17 -0
  34. package/update/ArrayUpdate.js +30 -0
  35. package/update/DataUpdate.d.ts +4 -7
  36. package/update/DataUpdate.js +7 -10
  37. package/update/Increment.d.ts +4 -0
  38. package/update/Increment.js +4 -0
  39. package/update/ObjectUpdate.d.ts +25 -0
  40. package/update/{EntriesUpdate.js → ObjectUpdate.js} +15 -15
  41. package/update/hydrations.d.ts +4 -4
  42. package/update/hydrations.js +4 -4
  43. package/update/index.d.ts +2 -2
  44. package/update/index.js +2 -2
  45. package/util/async.js +1 -1
  46. package/util/data.d.ts +2 -2
  47. package/util/filter.d.ts +15 -19
  48. package/util/filter.js +18 -19
  49. package/util/iterate.d.ts +7 -6
  50. package/util/iterate.js +23 -12
  51. package/util/random.d.ts +8 -11
  52. package/util/random.js +12 -12
  53. package/util/string.d.ts +2 -0
  54. package/util/string.js +11 -4
  55. package/util/transform.d.ts +2 -2
  56. package/util/transform.js +2 -2
  57. package/util/undefined.d.ts +5 -0
  58. package/util/undefined.js +6 -0
  59. package/util/validate.d.ts +6 -1
  60. package/util/validate.js +7 -0
  61. package/react/useDocumentData.d.ts +0 -36
  62. package/react/useDocumentData.js +0 -9
  63. package/react/useQueryFirst.d.ts +0 -36
  64. package/react/useQueryFirst.js +0 -9
  65. package/update/EntriesUpdate.d.ts +0 -29
  66. package/update/ItemsUpdate.d.ts +0 -21
  67. package/update/ItemsUpdate.js +0 -34
package/db/Database.d.ts CHANGED
@@ -55,12 +55,19 @@ export declare class DataQuery<T extends Data = Data> extends Query<T> implement
55
55
  */
56
56
  get count(): number | PromiseLike<number>;
57
57
  /**
58
- * Get an entry for the first document matching this query.
58
+ * Get an entry for the first document matched by this query or `undefined` if this query has no results.
59
59
  *
60
60
  * @return Entry in `[id, data]` format for the first document.
61
61
  * @throws RequiredError if there were no results for this query.
62
62
  */
63
- get first(): Entry<T> | undefined | PromiseLike<Entry<T> | undefined>;
63
+ get result(): Entry<T> | undefined | PromiseLike<Entry<T> | undefined>;
64
+ /**
65
+ * Get an entry for the first document matched by this query.
66
+ *
67
+ * @return Entry in `[id, data]` format for the first document.
68
+ * @throws RequiredError if there were no results for this query.
69
+ */
70
+ get data(): Entry<T> | PromiseLike<Entry<T>>;
64
71
  /**
65
72
  * Subscribe to all matching documents.
66
73
  * - `next()` is called once with the initial results, and again any time the results change.
@@ -103,7 +110,7 @@ export declare class DataQuery<T extends Data = Data> extends Query<T> implement
103
110
  toString(): string;
104
111
  }
105
112
  /** Get the data for a document from a result for that document. */
106
- export declare function getQueryFirst<T extends Data>(results: Results<T>, ref: DataQuery<T>): Entry<T>;
113
+ export declare function getQueryData<T extends Data>(results: Results<T>, ref: DataQuery<T>): Entry<T>;
107
114
  /** A document reference within a specific database. */
108
115
  export declare class DataDocument<T extends Data = Data> implements Observable<Result<T>>, Validatable<T> {
109
116
  readonly provider: Provider;
package/db/Database.js CHANGED
@@ -80,13 +80,22 @@ export class DataQuery extends Query {
80
80
  return callAsync(countItems, this.results);
81
81
  }
82
82
  /**
83
- * Get an entry for the first document matching this query.
83
+ * Get an entry for the first document matched by this query or `undefined` if this query has no results.
84
84
  *
85
85
  * @return Entry in `[id, data]` format for the first document.
86
86
  * @throws RequiredError if there were no results for this query.
87
87
  */
88
- get first() {
89
- return callAsync(getQueryFirst, this.max(1).results, this);
88
+ get result() {
89
+ return callAsync(getFirstItem, this.max(1).results);
90
+ }
91
+ /**
92
+ * Get an entry for the first document matched by this query.
93
+ *
94
+ * @return Entry in `[id, data]` format for the first document.
95
+ * @throws RequiredError if there were no results for this query.
96
+ */
97
+ get data() {
98
+ return callAsync(getQueryData, this.max(1).results, this);
90
99
  }
91
100
  /**
92
101
  * Subscribe to all matching documents.
@@ -161,7 +170,7 @@ export class DataQuery extends Query {
161
170
  }
162
171
  }
163
172
  /** Get the data for a document from a result for that document. */
164
- export function getQueryFirst(results, ref) {
173
+ export function getQueryData(results, ref) {
165
174
  const first = getFirstItem(results);
166
175
  if (first)
167
176
  return first;
@@ -1,5 +1,5 @@
1
1
  import { orderBy as firestoreOrderBy, where as firestoreWhere, limit as firestoreLimit, increment as firestoreIncrement, arrayUnion as firestoreArrayUnion, arrayRemove as firestoreArrayRemove, deleteField as firestoreDeleteField, collection as firestoreCollection, doc as firestoreDocument, query as firestoreQuery, onSnapshot, addDoc, setDoc, updateDoc, deleteDoc, getDoc, getDocs, } from "firebase/firestore";
2
- import { Provider, dispatchNext, dispatchError, Update, EntriesUpdate, Increment, DataUpdate, AssertionError, ItemsUpdate, UnsupportedError, } from "../../index.js";
2
+ import { Provider, dispatchNext, dispatchError, Update, ObjectUpdate, Increment, DataUpdate, AssertionError, ArrayUpdate, UnsupportedError, } from "../../index.js";
3
3
  // Constants.
4
4
  // const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
5
5
  const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
@@ -56,9 +56,9 @@ function* yieldFieldValues(updates, prefix = "") {
56
56
  yield [`${prefix}${key}`, update !== undefined ? update : firestoreDeleteField()];
57
57
  else if (update instanceof Increment)
58
58
  yield [`${prefix}${key}`, firestoreIncrement(update.amount)];
59
- else if (update instanceof DataUpdate || update instanceof EntriesUpdate)
59
+ else if (update instanceof DataUpdate || update instanceof ObjectUpdate)
60
60
  yield* yieldFieldValues(update, `${prefix}${key}.`);
61
- else if (update instanceof ItemsUpdate) {
61
+ else if (update instanceof ArrayUpdate) {
62
62
  if (update.adds.length && update.deletes.length)
63
63
  throw new UnsupportedError("Cannot add/delete array items in one update");
64
64
  if (update.adds.length)
@@ -82,11 +82,10 @@ export class FirestoreClientProvider extends Provider {
82
82
  this.firestore = firestore;
83
83
  }
84
84
  async get(ref) {
85
- const snapshot = await getDoc(getDocument(this.firestore, ref));
86
- return snapshot.data();
85
+ return (await getDoc(getDocument(this.firestore, ref))).data() || null;
87
86
  }
88
87
  subscribe(ref, observer) {
89
- return onSnapshot(getDocument(this.firestore, ref), snapshot => dispatchNext(observer, snapshot.data()), thrown => dispatchError(observer, thrown));
88
+ return onSnapshot(getDocument(this.firestore, ref), snapshot => dispatchNext(observer, snapshot.data() || null), thrown => dispatchError(observer, thrown));
90
89
  }
91
90
  async add(ref, data) {
92
91
  const reference = await addDoc(getCollection(this.firestore, ref), data);
@@ -1,5 +1,5 @@
1
1
  import { orderBy as firestoreOrderBy, where as firestoreWhere, limit as firestoreLimit, increment as firestoreIncrement, arrayUnion as firestoreArrayUnion, arrayRemove as firestoreArrayRemove, deleteField as firestoreDeleteField, collection as firestoreCollection, doc as firestoreDocument, query as firestoreQuery, setDoc, addDoc, updateDoc, deleteDoc, getDoc, getDocs, } from "firebase/firestore/lite";
2
- import { Provider, Update, EntriesUpdate, Increment, AssertionError, DataUpdate, ItemsUpdate, UnsupportedError, } from "../../index.js";
2
+ import { Provider, Update, ObjectUpdate, Increment, AssertionError, DataUpdate, ArrayUpdate, UnsupportedError, } from "../../index.js";
3
3
  // Constants.
4
4
  // const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
5
5
  const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
@@ -56,9 +56,9 @@ function* yieldFieldValues(updates, prefix = "") {
56
56
  yield [`${prefix}${key}`, update !== undefined ? update : firestoreDeleteField()];
57
57
  else if (update instanceof Increment)
58
58
  yield [`${prefix}${key}`, firestoreIncrement(update.amount)];
59
- else if (update instanceof DataUpdate || update instanceof EntriesUpdate)
59
+ else if (update instanceof DataUpdate || update instanceof ObjectUpdate)
60
60
  yield* yieldFieldValues(update, `${prefix}${key}.`);
61
- else if (update instanceof ItemsUpdate) {
61
+ else if (update instanceof ArrayUpdate) {
62
62
  if (update.adds.length && update.deletes.length)
63
63
  throw new UnsupportedError("Cannot add/delete array items in one update");
64
64
  if (update.adds.length)
@@ -83,7 +83,7 @@ export class FirestoreClientProvider extends Provider {
83
83
  }
84
84
  async get(ref) {
85
85
  const snapshot = await getDoc(getDocument(this.firestore, ref));
86
- return snapshot.data();
86
+ return snapshot.data() || null;
87
87
  }
88
88
  subscribe() {
89
89
  throw new Error("FirestoreLiteProvider does not support realtime subscriptions");
@@ -1,5 +1,5 @@
1
1
  import { Firestore, FieldValue } from "@google-cloud/firestore";
2
- import { Provider, dispatchNext, dispatchError, Update, Increment, DataUpdate, AssertionError, ItemsUpdate, UnsupportedError, EntriesUpdate, } from "../../index.js";
2
+ import { Provider, dispatchNext, dispatchError, Update, Increment, DataUpdate, AssertionError, ArrayUpdate, UnsupportedError, ObjectUpdate, } from "../../index.js";
3
3
  // Constants.
4
4
  // const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
5
5
  const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
@@ -56,9 +56,9 @@ function* yieldFieldValues(updates, prefix = "") {
56
56
  yield [`${prefix}${key}`, update !== undefined ? update : FieldValue.delete()];
57
57
  if (update instanceof Increment)
58
58
  yield [`${prefix}${key}`, FieldValue.increment(update.amount)];
59
- else if (update instanceof DataUpdate || update instanceof EntriesUpdate)
59
+ else if (update instanceof DataUpdate || update instanceof ObjectUpdate)
60
60
  yield* yieldFieldValues(update, `${prefix}${key}.`);
61
- else if (update instanceof ItemsUpdate) {
61
+ else if (update instanceof ArrayUpdate) {
62
62
  if (update.adds.length && update.deletes.length)
63
63
  throw new UnsupportedError("Cannot add/delete array items in one update");
64
64
  if (update.adds.length)
@@ -80,10 +80,10 @@ export class FirestoreServerProvider extends Provider {
80
80
  this.firestore = firestore;
81
81
  }
82
82
  async get(ref) {
83
- return (await getDocument(this.firestore, ref).get()).data();
83
+ return (await getDocument(this.firestore, ref).get()).data() || null;
84
84
  }
85
85
  subscribe(ref, observer) {
86
- return getDocument(this.firestore, ref).onSnapshot(snapshot => dispatchNext(observer, snapshot.data()), thrown => dispatchError(observer, thrown));
86
+ return getDocument(this.firestore, ref).onSnapshot(snapshot => dispatchNext(observer, snapshot.data() || null), thrown => dispatchError(observer, thrown));
87
87
  }
88
88
  async add(ref, data) {
89
89
  return (await getCollection(this.firestore, ref).add(data)).id;
package/markup/helpers.js CHANGED
@@ -37,13 +37,7 @@ export function nodeToHtml(node) {
37
37
  }
38
38
  return "";
39
39
  }
40
- const propToString = ([key, value]) => value === true
41
- ? key
42
- : typeof value === "number" && Number.isFinite(value)
43
- ? `${key}="${value.toString()}"`
44
- : typeof value === "string"
45
- ? `${key}=${serialise(value)}`
46
- : "";
40
+ const propToString = ([key, value]) => (value === true ? key : typeof value === "number" && Number.isFinite(value) ? `${key}="${value.toString()}"` : typeof value === "string" ? `${key}=${serialise(value)}` : "");
47
41
  /**
48
42
  * Iterate through all elements in a node.
49
43
  * - This is useful if you, e.g. want to apply a `className` to all `<h1>` elements, or make a list of all URLs found in a Node.
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.31.0",
14
+ "version": "1.34.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -63,20 +63,20 @@
63
63
  "@types/jest": "^27.0.3",
64
64
  "@types/react": "^17.0.37",
65
65
  "@types/react-dom": "^17.0.11",
66
- "@typescript-eslint/eslint-plugin": "^5.6.0",
67
- "@typescript-eslint/parser": "^5.6.0",
68
- "eslint": "^8.4.1",
66
+ "@typescript-eslint/eslint-plugin": "^5.7.0",
67
+ "@typescript-eslint/parser": "^5.7.0",
68
+ "eslint": "^8.5.0",
69
69
  "eslint-config-prettier": "^8.3.0",
70
70
  "eslint-plugin-import": "^2.25.3",
71
71
  "eslint-plugin-prettier": "^4.0.0",
72
72
  "firebase": "^9.6.1",
73
- "jest": "^27.4.4",
73
+ "jest": "^27.4.5",
74
74
  "jest-ts-webcompat-resolver": "^1.0.0",
75
75
  "prettier": "^2.5.1",
76
76
  "react": "^17.0.2",
77
77
  "react-dom": "^17.0.2",
78
- "ts-jest": "^27.1.1",
79
- "typescript": "^4.5.3"
78
+ "ts-jest": "^27.1.2",
79
+ "typescript": "^4.5.4"
80
80
  },
81
81
  "peerDependencies": {
82
82
  "@google-cloud/firestore": ">=4.0.0",
@@ -1,4 +1,4 @@
1
- import { isAsync, toMap, TransformObserver, awaitNext, } from "../util/index.js";
1
+ import { isAsync, toMap, TransformObserver, awaitNext } from "../util/index.js";
2
2
  import { LazyState } from "../stream/index.js";
3
3
  import { ThroughProvider } from "./ThroughProvider.js";
4
4
  /** How long to wait after all subscriptions have ended to close the source subscription. */
@@ -1,4 +1,4 @@
1
- import { randomId, dispatchNext, isMapEqual, } from "../util/index.js";
1
+ import { getRandomKey, dispatchNext, isMapEqual } from "../util/index.js";
2
2
  import { DocumentRequiredError } from "../db/index.js";
3
3
  import { Provider } from "./Provider.js";
4
4
  /**
@@ -18,23 +18,23 @@ export class MemoryProvider extends Provider {
18
18
  return ((_a = this._tables)[collection] || (_a[collection] = new Table()));
19
19
  }
20
20
  get(ref) {
21
- return this._table(ref).data.get(ref.id);
21
+ return this._table(ref).data.get(ref.id) || null;
22
22
  }
23
23
  subscribe(ref, observer) {
24
24
  const table = this._table(ref);
25
25
  const id = ref.id;
26
26
  // Call next() immediately with initial results.
27
- dispatchNext(observer, table.data.get(id));
27
+ dispatchNext(observer, table.data.get(id) || null);
28
28
  // Call next() every time the collection changes.
29
29
  return table.on(changes => {
30
- changes.has(id) && dispatchNext(observer, changes.get(id));
30
+ changes.has(id) && dispatchNext(observer, changes.get(id) || null);
31
31
  });
32
32
  }
33
33
  add(ref, data) {
34
34
  const table = this._table(ref);
35
- let id = randomId();
35
+ let id = getRandomKey();
36
36
  while (table.data.get(id))
37
- id = randomId(); // Regenerate ID until unique.
37
+ id = getRandomKey(); // Regenerate ID until unique.
38
38
  table.write(id, data);
39
39
  return id;
40
40
  }
@@ -54,7 +54,7 @@ export class MemoryProvider extends Provider {
54
54
  delete(ref) {
55
55
  const table = this._table(ref);
56
56
  const id = ref.id;
57
- table.write(id, undefined);
57
+ table.write(id, null);
58
58
  }
59
59
  getQuery(ref) {
60
60
  return ref.transform(this._table(ref).data);
@@ -110,7 +110,7 @@ export class MemoryProvider extends Provider {
110
110
  // If there's no limit set: only need to run the filtering (more efficient because sort order doesn't matter).
111
111
  let count = 0;
112
112
  for (const [id] of ref.limit ? ref.transform(table.data) : ref.filters.transform(table.data)) {
113
- table.write(id, undefined);
113
+ table.write(id, null);
114
114
  count++;
115
115
  }
116
116
  return count;
@@ -1,10 +1,10 @@
1
- import { validate, ValidateObserver, callAsync } from "../util/index.js";
1
+ import { validate, ValidateObserver, callAsync, validateResult } from "../util/index.js";
2
2
  import { validateUpdate } from "../update/index.js";
3
3
  import { ThroughProvider } from "./ThroughProvider.js";
4
4
  /** Validates any values that are read from or written to a source provider. */
5
5
  export class ValidationProvider extends ThroughProvider {
6
6
  get(ref) {
7
- return callAsync(validate, super.get(ref), ref);
7
+ return callAsync(validateResult, super.get(ref), ref);
8
8
  }
9
9
  subscribe(ref, observer) {
10
10
  return super.subscribe(ref, new ValidateObserver(ref, observer));
package/query/Filter.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Data, Entry, Matchable, Matcher, Results } from "../util/index.js";
1
+ import { Data, Entry, Matchable, Results, ImmutableArray } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
3
  import { FilterOperator, QueryKey } from "./types.js";
4
4
  /**
@@ -8,39 +8,53 @@ import { FilterOperator, QueryKey } from "./types.js";
8
8
  * @param type MatchType reference, e.g. `is` or `contains`
9
9
  * @param value Value the specified property should be matched against.
10
10
  */
11
- export interface Filter<T extends Data> {
12
- readonly operator: FilterOperator;
13
- readonly matcher: Matcher<unknown, unknown>;
14
- }
15
- export declare abstract class Filter<T extends Data> extends Rule<T> implements Matchable<Entry<T>, void> {
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;
16
13
  readonly key: QueryKey<T>;
17
- readonly value: unknown;
18
- constructor(key: QueryKey<T>, value: unknown);
19
- match([id, data]: Entry<T>): boolean;
14
+ readonly value: V;
15
+ constructor(key: QueryKey<T>, value: V);
16
+ abstract match([id, data]: Entry<T>, target: void): boolean;
20
17
  transform(results: Results<T>): Results<T>;
21
18
  toString(): string;
22
19
  }
23
20
  /** Filter a set of values with an `IS` clause. */
24
21
  export declare class EqualFilter<T extends Data> extends Filter<T> {
22
+ readonly operator = "IS";
23
+ readonly matcher: (item: unknown, target: unknown) => boolean;
24
+ match([id, data]: Entry<T>): boolean;
25
25
  }
26
26
  /** Filter a set of values with an `NOT` clause. */
27
27
  export declare class NotEqualFilter<T extends Data> extends Filter<T> {
28
+ readonly operator = "NOT";
29
+ match([id, data]: Entry<T>): boolean;
28
30
  }
29
31
  /** Filter a set of values with an `IS` clause. */
30
- export declare class InArrayFilter<T extends Data> extends Filter<T> {
32
+ export declare class InArrayFilter<T extends Data> extends Filter<T, ImmutableArray> {
33
+ readonly operator = "IN";
34
+ match([id, data]: Entry<T>): boolean;
31
35
  }
32
36
  /** Filter a set of values with an `CONTAINS` clause. */
33
37
  export declare class ArrayContainsFilter<T extends Data> extends Filter<T> {
38
+ readonly operator = "CONTAINS";
39
+ match([id, data]: Entry<T>): boolean;
34
40
  }
35
41
  /** Filter a set of values with an `LT` clause. */
36
42
  export declare class LessThanFilter<T extends Data> extends Filter<T> {
43
+ readonly operator = "LT";
44
+ match([id, data]: Entry<T>): boolean;
37
45
  }
38
46
  /** Filter a set of values with an `LTE` clause. */
39
47
  export declare class LessThanEqualFilter<T extends Data> extends Filter<T> {
48
+ readonly operator = "LTE";
49
+ match([id, data]: Entry<T>): boolean;
40
50
  }
41
51
  /** Filter a set of values with an `GT` clause. */
42
52
  export declare class GreaterThanFilter<T extends Data> extends Filter<T> {
53
+ readonly operator = "GT";
54
+ match([id, data]: Entry<T>): boolean;
43
55
  }
44
56
  /** Filter a set of values with an `GTE` clause. */
45
57
  export declare class GreaterThanEqualFilter<T extends Data> extends Filter<T> {
58
+ readonly operator = "GTE";
59
+ match([id, data]: Entry<T>): boolean;
46
60
  }
package/query/Filter.js CHANGED
@@ -1,15 +1,19 @@
1
1
  import { match, CONTAINS, IS, GT, GTE, IN, LT, LTE, NOT, yieldFiltered } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
3
  import { getQueryProp } from "./helpers.js";
4
+ /**
5
+ * Filter: filters a list of documents.
6
+ *
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`
9
+ * @param value Value the specified property should be matched against.
10
+ */
4
11
  export class Filter extends Rule {
5
12
  constructor(key, value) {
6
13
  super();
7
14
  this.key = key;
8
15
  this.value = value;
9
16
  }
10
- match([id, data]) {
11
- return match(getQueryProp(id, data, this.key), this.matcher, this.value);
12
- }
13
17
  transform(results) {
14
18
  return yieldFiltered(results, this);
15
19
  }
@@ -19,33 +23,82 @@ export class Filter extends Rule {
19
23
  }
20
24
  /** Filter a set of values with an `IS` clause. */
21
25
  export class EqualFilter extends Filter {
26
+ constructor() {
27
+ super(...arguments);
28
+ this.operator = "IS";
29
+ this.matcher = IS;
30
+ }
31
+ match([id, data]) {
32
+ return match(getQueryProp(id, data, this.key), IS, this.value);
33
+ }
22
34
  }
23
- Object.assign(EqualFilter.prototype, { direction: "IS", matcher: IS });
24
35
  /** Filter a set of values with an `NOT` clause. */
25
36
  export class NotEqualFilter extends Filter {
37
+ constructor() {
38
+ super(...arguments);
39
+ this.operator = "NOT";
40
+ }
41
+ match([id, data]) {
42
+ return match(getQueryProp(id, data, this.key), NOT, this.value);
43
+ }
26
44
  }
27
- Object.assign(NotEqualFilter.prototype, { direction: "NOT", matcher: NOT });
28
45
  /** Filter a set of values with an `IS` clause. */
29
46
  export class InArrayFilter extends Filter {
47
+ constructor() {
48
+ super(...arguments);
49
+ this.operator = "IN";
50
+ }
51
+ match([id, data]) {
52
+ return match(getQueryProp(id, data, this.key), IN, this.value);
53
+ }
30
54
  }
31
- Object.assign(InArrayFilter.prototype, { direction: "IN", matcher: IN });
32
55
  /** Filter a set of values with an `CONTAINS` clause. */
33
56
  export class ArrayContainsFilter extends Filter {
57
+ constructor() {
58
+ super(...arguments);
59
+ this.operator = "CONTAINS";
60
+ }
61
+ match([id, data]) {
62
+ return match(getQueryProp(id, data, this.key), CONTAINS, this.value);
63
+ }
34
64
  }
35
- Object.assign(ArrayContainsFilter.prototype, { direction: "CONTAINS", matcher: CONTAINS });
36
65
  /** Filter a set of values with an `LT` clause. */
37
66
  export class LessThanFilter extends Filter {
67
+ constructor() {
68
+ super(...arguments);
69
+ this.operator = "LT";
70
+ }
71
+ match([id, data]) {
72
+ return match(getQueryProp(id, data, this.key), LT, this.value);
73
+ }
38
74
  }
39
- Object.assign(LessThanFilter.prototype, { direction: "LT", matcher: LT });
40
75
  /** Filter a set of values with an `LTE` clause. */
41
76
  export class LessThanEqualFilter extends Filter {
77
+ constructor() {
78
+ super(...arguments);
79
+ this.operator = "LTE";
80
+ }
81
+ match([id, data]) {
82
+ return match(getQueryProp(id, data, this.key), LTE, this.value);
83
+ }
42
84
  }
43
- Object.assign(LessThanEqualFilter.prototype, { direction: "LTE", matcher: LTE });
44
85
  /** Filter a set of values with an `GT` clause. */
45
86
  export class GreaterThanFilter extends Filter {
87
+ constructor() {
88
+ super(...arguments);
89
+ this.operator = "GT";
90
+ }
91
+ match([id, data]) {
92
+ return match(getQueryProp(id, data, this.key), GT, this.value);
93
+ }
46
94
  }
47
- Object.assign(GreaterThanFilter.prototype, { direction: "GT", matcher: GT });
48
95
  /** Filter a set of values with an `GTE` clause. */
49
96
  export class GreaterThanEqualFilter extends Filter {
97
+ constructor() {
98
+ super(...arguments);
99
+ this.operator = "GTE";
100
+ }
101
+ match([id, data]) {
102
+ return match(getQueryProp(id, data, this.key), GTE, this.value);
103
+ }
50
104
  }
51
- Object.assign(GreaterThanEqualFilter.prototype, { direction: "GTE", matcher: GTE });
@@ -1,4 +1,4 @@
1
- import { Entry, ArrayType, ImmutableArray, Data, Key, Results } from "../util/index.js";
1
+ import { Entry, Data, Key, Results, ImmutableArray, ArrayType } from "../util/index.js";
2
2
  import type { Filterable, QueryKey } from "./types.js";
3
3
  import { Filter } from "./Filter.js";
4
4
  import { Rules } from "./Rules.js";
@@ -13,5 +13,6 @@ export declare class Filters<T extends Data> extends Rules<T, Filter<T>> impleme
13
13
  gt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
14
14
  gte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
15
15
  match(entry: Entry<T>): boolean;
16
+ get unfiltered(): this;
16
17
  transform(iterable: Results<T>): Results<T>;
17
18
  }
package/query/Filters.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { yieldFiltered } from "../util/index.js";
2
- import { ArrayContainsFilter, EqualFilter, GreaterThanEqualFilter, GreaterThanFilter, InArrayFilter, LessThanEqualFilter, LessThanFilter, NotEqualFilter, } from "./Filter.js";
2
+ import { ArrayContainsFilter, EqualFilter, GreaterThanEqualFilter, GreaterThanFilter, InArrayFilter, LessThanEqualFilter, LessThanFilter, NotEqualFilter } from "./Filter.js";
3
3
  import { Rules } from "./Rules.js";
4
4
  /** A set of filters. */
5
5
  export class Filters extends Rules {
6
+ // Implement `Filterable`
6
7
  is(key, value) {
7
8
  return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new EqualFilter(key, value)] };
8
9
  }
@@ -33,6 +34,10 @@ export class Filters extends Rules {
33
34
  return false;
34
35
  return true;
35
36
  }
37
+ get unfiltered() {
38
+ return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [] };
39
+ }
40
+ // Implement `Rule`
36
41
  transform(iterable) {
37
42
  return this._rules.length ? yieldFiltered(iterable, this) : iterable;
38
43
  }
package/query/Query.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Data, ArrayType, ImmutableArray, Entry, Key, Results } from "../util/index.js";
1
+ import { Data, Entry, Key, Results, ArrayType, ImmutableArray } from "../util/index.js";
2
2
  import type { Queryable, QueryKey } from "./types.js";
3
3
  import { Filters } from "./Filters.js";
4
4
  import { Sorts } from "./Sorts.js";
@@ -17,9 +17,11 @@ export declare class Query<T extends Data> extends Rule<T> implements Queryable<
17
17
  lte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
18
18
  gt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
19
19
  gte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
20
+ get unfiltered(): this;
20
21
  match(entry: Entry<T>): boolean;
21
- asc(key?: QueryKey<T>): this;
22
- desc(key?: QueryKey<T>): this;
22
+ asc(key: QueryKey<T>): this;
23
+ desc(key: QueryKey<T>): this;
24
+ get unsorted(): this;
23
25
  rank(left: Entry<T>, right: Entry<T>): number;
24
26
  /** Return a new instance of this class with a limit defined. */
25
27
  max(limit: number | null): this;
package/query/Query.js CHANGED
@@ -40,16 +40,22 @@ export class Query extends Rule {
40
40
  gte(key, value) {
41
41
  return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.gte(key, value) };
42
42
  }
43
+ get unfiltered() {
44
+ return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.unfiltered };
45
+ }
43
46
  match(entry) {
44
47
  return this.filters.match(entry);
45
48
  }
46
49
  // Implement `Sortable`
47
- asc(key = "id") {
50
+ asc(key) {
48
51
  return { __proto__: Object.getPrototypeOf(this), ...this, sorts: this.sorts.asc(key) };
49
52
  }
50
- desc(key = "id") {
53
+ desc(key) {
51
54
  return { __proto__: Object.getPrototypeOf(this), ...this, sorts: this.sorts.desc(key) };
52
55
  }
56
+ get unsorted() {
57
+ return { __proto__: Object.getPrototypeOf(this), ...this, sorts: this.sorts.unsorted };
58
+ }
53
59
  rank(left, right) {
54
60
  return this.sorts.rank(left, right);
55
61
  }
package/query/Sort.d.ts CHANGED
@@ -1,21 +1,22 @@
1
- import { Data, Entry, Rankable, Ranker, Results } from "../util/index.js";
1
+ import { Data, Entry, Rankable, Results } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
3
  import { QueryKey, SortDirection } from "./types.js";
4
4
  /** Sort a list of values. */
5
- export interface Sort<T extends Data> {
6
- readonly direction: SortDirection;
7
- readonly ranker: Ranker<unknown>;
8
- }
9
5
  export declare abstract class Sort<T extends Data> extends Rule<T> implements Rankable<Entry<T>> {
6
+ abstract readonly direction: SortDirection;
10
7
  readonly key: QueryKey<T>;
11
8
  constructor(key: QueryKey<T>);
12
- rank([leftId, leftData]: Entry<T>, [rightId, rightData]: Entry<T>): number;
9
+ abstract rank(left: Entry<T>, right: Entry<T>): number;
13
10
  transform(iterable: Results<T>): Results<T>;
14
11
  toString(): string;
15
12
  }
16
13
  /** Sort a list of values in ascending order. */
17
14
  export declare class AscendingSort<T extends Data> extends Sort<T> {
15
+ readonly direction = "ASC";
16
+ rank([leftId, leftData]: Entry<T>, [rightId, rightData]: Entry<T>): number;
18
17
  }
19
18
  /** Sort a list of values in descending order. */
20
19
  export declare class DescendingSort<T extends Data> extends Sort<T> {
20
+ readonly direction = "DESC";
21
+ rank([leftId, leftData]: Entry<T>, [rightId, rightData]: Entry<T>): number;
21
22
  }
package/query/Sort.js CHANGED
@@ -1,14 +1,12 @@
1
1
  import { ASC, rank, DESC, sortItems } from "../util/index.js";
2
2
  import { getQueryProp } from "./helpers.js";
3
3
  import { Rule } from "./Rule.js";
4
+ /** Sort a list of values. */
4
5
  export class Sort extends Rule {
5
6
  constructor(key) {
6
7
  super();
7
8
  this.key = key;
8
9
  }
9
- rank([leftId, leftData], [rightId, rightData]) {
10
- return rank(getQueryProp(leftId, leftData, this.key), this.ranker, getQueryProp(rightId, rightData, this.key));
11
- }
12
10
  transform(iterable) {
13
11
  return sortItems(iterable, this);
14
12
  }
@@ -18,9 +16,21 @@ export class Sort extends Rule {
18
16
  }
19
17
  /** Sort a list of values in ascending order. */
20
18
  export class AscendingSort extends Sort {
19
+ constructor() {
20
+ super(...arguments);
21
+ this.direction = "ASC";
22
+ }
23
+ rank([leftId, leftData], [rightId, rightData]) {
24
+ return rank(getQueryProp(leftId, leftData, this.key), ASC, getQueryProp(rightId, rightData, this.key));
25
+ }
21
26
  }
22
- Object.assign(AscendingSort.prototype, { direction: "ASC", ranker: ASC });
23
27
  /** Sort a list of values in descending order. */
24
28
  export class DescendingSort extends Sort {
29
+ constructor() {
30
+ super(...arguments);
31
+ this.direction = "DESC";
32
+ }
33
+ rank([leftId, leftData], [rightId, rightData]) {
34
+ return rank(getQueryProp(leftId, leftData, this.key), DESC, getQueryProp(rightId, rightData, this.key));
35
+ }
25
36
  }
26
- Object.assign(DescendingSort.prototype, { direction: "DESC", ranker: DESC });
package/query/Sorts.d.ts CHANGED
@@ -6,6 +6,7 @@ import { Rules } from "./Rules.js";
6
6
  export declare class Sorts<T extends Data> extends Rules<T, Sort<T>> implements Sortable<T> {
7
7
  asc(key: QueryKey<T>): this;
8
8
  desc(key: QueryKey<T>): this;
9
+ get unsorted(): this;
9
10
  rank(left: Entry<T>, right: Entry<T>): number;
10
11
  transform(iterable: Results<T>): Results<T>;
11
12
  }