shelving 1.71.1 → 1.73.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/{query/Rule.d.ts → constraint/Constraint.d.ts} +2 -2
- package/constraint/Constraint.js +3 -0
- package/constraint/Constraints.d.ts +20 -0
- package/constraint/Constraints.js +35 -0
- package/{query/Filter.d.ts → constraint/FilterConstraint.d.ts} +12 -8
- package/constraint/FilterConstraint.js +96 -0
- package/constraint/FilterConstraints.d.ts +22 -0
- package/constraint/FilterConstraints.js +27 -0
- package/{query/Query.d.ts → constraint/QueryConstraints.d.ts} +17 -22
- package/constraint/QueryConstraints.js +120 -0
- package/{query/Sort.d.ts → constraint/SortConstraint.d.ts} +9 -6
- package/constraint/SortConstraint.js +40 -0
- package/constraint/SortConstraints.d.ts +20 -0
- package/constraint/SortConstraints.js +29 -0
- package/constraint/index.d.ts +7 -0
- package/constraint/index.js +7 -0
- package/db/Changes.d.ts +22 -0
- package/db/Changes.js +40 -0
- package/db/Changes.test.d.ts +1 -0
- package/db/Changes.test.js +11 -0
- package/db/Collection.d.ts +38 -0
- package/db/Collection.js +38 -0
- package/db/Database.d.ts +33 -29
- package/db/Database.js +27 -29
- package/db/Item.d.ts +96 -0
- package/db/Item.js +79 -0
- package/db/Query.d.ts +112 -0
- package/db/Query.js +92 -0
- package/db/index.d.ts +4 -2
- package/db/index.js +4 -2
- package/error/ValidationError.js +1 -1
- package/feedback/ErrorFeedback.d.ts +2 -1
- package/feedback/ErrorFeedback.js +14 -0
- package/feedback/Feedback.d.ts +12 -12
- package/feedback/Feedback.js +31 -15
- package/feedback/InvalidFeedback.d.ts +2 -1
- package/feedback/InvalidFeedback.js +14 -0
- package/feedback/SuccessFeedback.d.ts +2 -1
- package/feedback/SuccessFeedback.js +14 -0
- package/feedback/WarningFeedback.d.ts +2 -1
- package/feedback/WarningFeedback.js +14 -0
- package/firestore/client/FirestoreClientProvider.d.ts +17 -17
- package/firestore/client/FirestoreClientProvider.js +57 -63
- package/firestore/lite/FirestoreLiteProvider.d.ts +15 -15
- package/firestore/lite/FirestoreLiteProvider.js +53 -58
- package/firestore/server/FirestoreServerProvider.d.ts +16 -16
- package/firestore/server/FirestoreServerProvider.js +64 -64
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/markup/regexp.d.ts +9 -1
- package/markup/regexp.js +0 -2
- package/markup/rules.d.ts +14 -1
- package/markup/rules.js +28 -19
- package/package.json +6 -6
- package/provider/BatchProvider.d.ts +11 -14
- package/provider/BatchProvider.js +42 -57
- package/provider/CacheProvider.d.ts +21 -21
- package/provider/CacheProvider.js +73 -59
- package/provider/DebugProvider.d.ts +38 -16
- package/provider/DebugProvider.js +180 -109
- package/provider/MemoryProvider.d.ts +40 -39
- package/provider/MemoryProvider.js +79 -88
- package/provider/Provider.d.ts +40 -50
- package/provider/Provider.js +7 -1
- package/provider/ThroughProvider.d.ts +34 -32
- package/provider/ThroughProvider.js +62 -42
- package/provider/ValidationProvider.d.ts +42 -14
- package/provider/ValidationProvider.js +91 -38
- package/react/index.d.ts +1 -1
- package/react/index.js +1 -1
- package/react/{useDocument.d.ts → useItem.d.ts} +12 -12
- package/react/{useDocument.js → useItem.js} +14 -15
- package/react/useQuery.d.ts +13 -12
- package/react/useQuery.js +9 -10
- package/react/useSubscribe.js +1 -2
- package/schema/ThroughSchema.d.ts +4 -7
- package/schema/ThroughSchema.js +3 -17
- package/state/ObjectState.d.ts +2 -2
- package/state/State.d.ts +7 -4
- package/state/State.js +12 -9
- package/test/basics.d.ts +12 -12
- package/test/index.d.ts +3 -1
- package/test/index.js +1 -1
- package/test/people.d.ts +8 -8
- package/test/util.d.ts +5 -4
- package/test/util.js +1 -1
- package/update/DataUpdate.d.ts +1 -1
- package/update/DataUpdate.js +7 -9
- package/update/ObjectUpdate.d.ts +2 -2
- package/update/ObjectUpdate.js +14 -4
- package/util/array.d.ts +6 -4
- package/util/array.js +21 -16
- package/util/async.d.ts +14 -35
- package/util/async.js +48 -63
- package/util/class.d.ts +11 -0
- package/util/class.js +13 -0
- package/util/data.d.ts +1 -26
- package/util/data.js +0 -11
- package/util/diff.d.ts +2 -1
- package/util/diff.js +2 -1
- package/util/entry.d.ts +14 -26
- package/util/entry.js +7 -6
- package/util/filter.d.ts +2 -2
- package/util/hydrate.d.ts +4 -4
- package/util/hydrate.js +29 -12
- package/util/index.d.ts +0 -1
- package/util/index.js +0 -1
- package/util/iterate.d.ts +11 -33
- package/util/iterate.js +26 -61
- package/util/jsx.d.ts +1 -1
- package/util/object.d.ts +1 -8
- package/util/object.js +8 -8
- package/util/sort.d.ts +2 -4
- package/util/sort.js +7 -8
- package/util/source.d.ts +13 -0
- package/util/source.js +23 -0
- package/util/string.d.ts +15 -7
- package/util/string.js +13 -11
- package/util/transform.d.ts +5 -5
- package/util/transform.js +1 -1
- package/util/validate.d.ts +2 -2
- package/util/validate.js +16 -22
- package/db/Operation.d.ts +0 -79
- package/db/Operation.js +0 -129
- package/db/Reference.d.ts +0 -151
- package/db/Reference.js +0 -216
- package/query/Filter.js +0 -77
- package/query/Filters.d.ts +0 -29
- package/query/Filters.js +0 -27
- package/query/Query.js +0 -114
- package/query/Rule.js +0 -3
- package/query/Rules.d.ts +0 -21
- package/query/Rules.js +0 -39
- package/query/Sort.js +0 -24
- package/query/Sorts.d.ts +0 -20
- package/query/Sorts.js +0 -26
- package/query/index.d.ts +0 -5
- package/query/index.js +0 -5
- package/util/constants.d.ts +0 -12
- package/util/constants.js +0 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Data } from "../util/data.js";
|
|
2
2
|
import type { Transformable } from "../util/transform.js";
|
|
3
|
-
/** Something that can be used to
|
|
4
|
-
export declare abstract class
|
|
3
|
+
/** Something that can be used to constrain a query. */
|
|
4
|
+
export declare abstract class Constraint<T extends Data> implements Transformable<Iterable<T>, Iterable<T>> {
|
|
5
5
|
abstract transform(items: Iterable<T>): Iterable<T>;
|
|
6
6
|
abstract toString(): string;
|
|
7
7
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Data } from "../util/data.js";
|
|
2
|
+
import { ImmutableArray } from "../util/array.js";
|
|
3
|
+
import { Constraint } from "./Constraint.js";
|
|
4
|
+
/** Type of Rule that is powered by several sub-constraints (e.g. `Filters` and `Sorts` and `Query` itself extend this). */
|
|
5
|
+
export declare abstract class Constraints<T extends Data, C extends Constraint<T>> extends Constraint<T> implements Iterable<C> {
|
|
6
|
+
protected readonly _constraints: ImmutableArray<C>;
|
|
7
|
+
/** Get the first constraint. */
|
|
8
|
+
get first(): C | undefined;
|
|
9
|
+
/** Get the last constraint. */
|
|
10
|
+
get last(): C | undefined;
|
|
11
|
+
/** Get the number of constraints. */
|
|
12
|
+
get size(): number;
|
|
13
|
+
constructor(...constraints: C[]);
|
|
14
|
+
/** Clone this set of constraints but add additional constraints. */
|
|
15
|
+
with(...constraints: C[]): this;
|
|
16
|
+
/** Clone this set of constraints but remove specific constraints. */
|
|
17
|
+
without(...constraints: C[]): this;
|
|
18
|
+
/** Iterate over the constraints. */
|
|
19
|
+
[Symbol.iterator](): Iterator<C, void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { withItems, withoutItems } from "../util/array.js";
|
|
2
|
+
import { Constraint } from "./Constraint.js";
|
|
3
|
+
/** Type of Rule that is powered by several sub-constraints (e.g. `Filters` and `Sorts` and `Query` itself extend this). */
|
|
4
|
+
export class Constraints extends Constraint {
|
|
5
|
+
constructor(...constraints) {
|
|
6
|
+
super();
|
|
7
|
+
this._constraints = constraints;
|
|
8
|
+
}
|
|
9
|
+
/** Get the first constraint. */
|
|
10
|
+
get first() {
|
|
11
|
+
return this._constraints[0];
|
|
12
|
+
}
|
|
13
|
+
/** Get the last constraint. */
|
|
14
|
+
get last() {
|
|
15
|
+
return this._constraints[this._constraints.length - 1];
|
|
16
|
+
}
|
|
17
|
+
/** Get the number of constraints. */
|
|
18
|
+
get size() {
|
|
19
|
+
return this._constraints.length;
|
|
20
|
+
}
|
|
21
|
+
/** Clone this set of constraints but add additional constraints. */
|
|
22
|
+
with(...constraints) {
|
|
23
|
+
const _constraints = withItems(this._constraints, constraints);
|
|
24
|
+
return _constraints !== this._constraints ? { __proto__: Object.getPrototypeOf(this), ...this, _constraints: _constraints } : this;
|
|
25
|
+
}
|
|
26
|
+
/** Clone this set of constraints but remove specific constraints. */
|
|
27
|
+
without(...constraints) {
|
|
28
|
+
const _constraints = withoutItems(this._constraints, constraints);
|
|
29
|
+
return _constraints !== this._constraints ? { __proto__: Object.getPrototypeOf(this), ...this, _constraints: _constraints } : this;
|
|
30
|
+
}
|
|
31
|
+
/** Iterate over the constraints. */
|
|
32
|
+
[Symbol.iterator]() {
|
|
33
|
+
return this._constraints.values();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ArrayType, ImmutableArray } from "../util/array.js";
|
|
2
2
|
import { Data, Key } from "../util/data.js";
|
|
3
3
|
import { Matchable } from "../util/match.js";
|
|
4
|
-
import {
|
|
4
|
+
import { NotString } from "../util/string.js";
|
|
5
|
+
import { Constraint } from "./Constraint.js";
|
|
5
6
|
/** Possible operator references. */
|
|
6
7
|
export declare type FilterOperator = "IS" | "NOT" | "IN" | "OUT" | "CONTAINS" | "LT" | "LTE" | "GT" | "GTE";
|
|
7
8
|
/** Format that allows filters to be specified as a string, e.g. `!name` means `name is not` and `age>` means `age is more than` and `tags[]` means `tags array contains` */
|
|
@@ -14,21 +15,24 @@ export declare type FilterProps<T extends Data> = {
|
|
|
14
15
|
} & {
|
|
15
16
|
[K in Key<T> as `${K}<` | `${K}<=` | `${K}>` | `${K}>=`]?: T[K];
|
|
16
17
|
};
|
|
18
|
+
/** List of filters in a flexible format. */
|
|
19
|
+
export declare type FilterList<T extends Data> = FilterProps<T> | FilterConstraint<T> | Iterable<FilterList<T> & NotString>;
|
|
17
20
|
/**
|
|
18
|
-
* Filter: filters a list of
|
|
21
|
+
* Filter: filters a list of data.
|
|
19
22
|
*
|
|
20
|
-
* @param key The name of a property that might exist on
|
|
23
|
+
* @param key The name of a property that might exist on data in the collection.
|
|
21
24
|
* @param operator FilterOperator, e.g. `IS` or `CONTAINS`
|
|
22
25
|
* @param value Value the specified property should be matched against.
|
|
23
26
|
*/
|
|
24
|
-
export declare class
|
|
25
|
-
|
|
26
|
-
static on<X extends Data>(key: FilterKey<X>, value: unknown): Filter<X>;
|
|
27
|
-
readonly key: Key<T>;
|
|
27
|
+
export declare class FilterConstraint<T extends Data = Data> implements Constraint<T>, Matchable<T, void> {
|
|
28
|
+
readonly key: string;
|
|
28
29
|
readonly operator: FilterOperator;
|
|
29
30
|
readonly value: unknown;
|
|
30
|
-
|
|
31
|
+
get filterKey(): string;
|
|
32
|
+
constructor(filterKey: FilterKey<T>, value: unknown);
|
|
31
33
|
match(item: T): boolean;
|
|
32
34
|
transform(items: Iterable<T>): Iterable<T>;
|
|
33
35
|
toString(): string;
|
|
34
36
|
}
|
|
37
|
+
/** Get the separate filters generated from a list of filters. */
|
|
38
|
+
export declare function getFilters<T extends Data>(filters: FilterList<T>): Iterable<FilterConstraint<T>>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { isArray } from "../util/array.js";
|
|
2
|
+
import { isArrayWith, isEqual, isEqualGreater, isEqualLess, isGreater, isInArray, isLess, notEqual, notInArray } from "../util/match.js";
|
|
3
|
+
import { filterItems } from "../util/filter.js";
|
|
4
|
+
import { isIterable } from "../util/iterate.js";
|
|
5
|
+
/** Map `FilterOperator` to its corresponding `Match` function. */
|
|
6
|
+
const MATCHERS = {
|
|
7
|
+
IS: isEqual,
|
|
8
|
+
NOT: notEqual,
|
|
9
|
+
IN: isInArray,
|
|
10
|
+
OUT: notInArray,
|
|
11
|
+
CONTAINS: isArrayWith,
|
|
12
|
+
LT: isLess,
|
|
13
|
+
LTE: isEqualLess,
|
|
14
|
+
GT: isGreater,
|
|
15
|
+
GTE: isEqualGreater,
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Filter: filters a list of data.
|
|
19
|
+
*
|
|
20
|
+
* @param key The name of a property that might exist on data in the collection.
|
|
21
|
+
* @param operator FilterOperator, e.g. `IS` or `CONTAINS`
|
|
22
|
+
* @param value Value the specified property should be matched against.
|
|
23
|
+
*/
|
|
24
|
+
export class FilterConstraint {
|
|
25
|
+
constructor(filterKey, value) {
|
|
26
|
+
if (filterKey.startsWith("!")) {
|
|
27
|
+
this.key = filterKey.slice(1);
|
|
28
|
+
this.operator = isArray(value) ? "OUT" : "NOT";
|
|
29
|
+
}
|
|
30
|
+
else if (filterKey.endsWith(">")) {
|
|
31
|
+
this.key = filterKey.slice(0, -1);
|
|
32
|
+
this.operator = "GT";
|
|
33
|
+
}
|
|
34
|
+
else if (filterKey.endsWith(">=")) {
|
|
35
|
+
this.key = filterKey.slice(0, -2);
|
|
36
|
+
this.operator = "GTE";
|
|
37
|
+
}
|
|
38
|
+
else if (filterKey.endsWith("<")) {
|
|
39
|
+
this.key = filterKey.slice(0, -1);
|
|
40
|
+
this.operator = "LT";
|
|
41
|
+
}
|
|
42
|
+
else if (filterKey.endsWith("<=")) {
|
|
43
|
+
this.key = filterKey.slice(0, -2);
|
|
44
|
+
this.operator = "LTE";
|
|
45
|
+
}
|
|
46
|
+
else if (filterKey.endsWith("[]")) {
|
|
47
|
+
this.key = filterKey.slice(0, -2);
|
|
48
|
+
this.operator = "CONTAINS";
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.key = filterKey;
|
|
52
|
+
this.operator = isArray(value) ? "IN" : "IS";
|
|
53
|
+
}
|
|
54
|
+
this.value = value;
|
|
55
|
+
}
|
|
56
|
+
get filterKey() {
|
|
57
|
+
const { operator, key } = this;
|
|
58
|
+
if (operator === "NOT" || operator === "OUT")
|
|
59
|
+
return `!${key}`;
|
|
60
|
+
else if (operator === "CONTAINS")
|
|
61
|
+
return `${key}[]`;
|
|
62
|
+
else if (operator === "LT")
|
|
63
|
+
return `${key}<`;
|
|
64
|
+
else if (operator === "LTE")
|
|
65
|
+
return `${key}<=`;
|
|
66
|
+
else if (operator === "GT")
|
|
67
|
+
return `${key}>`;
|
|
68
|
+
else if (operator === "GTE")
|
|
69
|
+
return `${key}>=`;
|
|
70
|
+
else
|
|
71
|
+
return key;
|
|
72
|
+
}
|
|
73
|
+
match(item) {
|
|
74
|
+
return MATCHERS[this.operator](item[this.key], this.value);
|
|
75
|
+
}
|
|
76
|
+
transform(items) {
|
|
77
|
+
return filterItems(items, this);
|
|
78
|
+
}
|
|
79
|
+
toString() {
|
|
80
|
+
return `"${this.filterKey}":${JSON.stringify(this.value)}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Get the separate filters generated from a list of filters. */
|
|
84
|
+
export function* getFilters(filters) {
|
|
85
|
+
if (filters instanceof FilterConstraint) {
|
|
86
|
+
yield filters;
|
|
87
|
+
}
|
|
88
|
+
else if (isIterable(filters)) {
|
|
89
|
+
for (const filter of filters)
|
|
90
|
+
yield* getFilters(filter);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
for (const [key, value] of Object.entries(filters))
|
|
94
|
+
yield new FilterConstraint(key, value);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Data } from "../util/data.js";
|
|
2
|
+
import type { Matchable } from "../util/match.js";
|
|
3
|
+
import { FilterList, FilterProps, FilterConstraint } from "./FilterConstraint.js";
|
|
4
|
+
import { Constraints } from "./Constraints.js";
|
|
5
|
+
/**
|
|
6
|
+
* Interface to make sure an object implements all matchers.
|
|
7
|
+
* - Extends `Matchable` so this object itself can be directly be used in `filterItems()` and `filterEntries()`
|
|
8
|
+
*/
|
|
9
|
+
export interface Filterable<T extends Data> extends Matchable<T, void> {
|
|
10
|
+
/** Add a filter to this filterable. */
|
|
11
|
+
filter(props: FilterProps<T>): this;
|
|
12
|
+
/** Match an item against the filters specified for this object. */
|
|
13
|
+
match(item: T): boolean;
|
|
14
|
+
}
|
|
15
|
+
/** A set of filters. */
|
|
16
|
+
export declare class FilterConstraints<T extends Data = Data> extends Constraints<T, FilterConstraint<T>> implements Filterable<T> {
|
|
17
|
+
constructor(...filters: FilterList<T>[]);
|
|
18
|
+
filter(...filters: FilterList<T>[]): this;
|
|
19
|
+
match(item: T): boolean;
|
|
20
|
+
transform(items: Iterable<T>): Iterable<T>;
|
|
21
|
+
toString(): string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { filterItems } from "../util/filter.js";
|
|
2
|
+
import { getFilters } from "./FilterConstraint.js";
|
|
3
|
+
import { Constraints } from "./Constraints.js";
|
|
4
|
+
/** A set of filters. */
|
|
5
|
+
export class FilterConstraints extends Constraints {
|
|
6
|
+
constructor(...filters) {
|
|
7
|
+
super(...getFilters(filters));
|
|
8
|
+
}
|
|
9
|
+
// Implement `Filterable`
|
|
10
|
+
filter(...filters) {
|
|
11
|
+
return this.with(...getFilters(filters));
|
|
12
|
+
}
|
|
13
|
+
match(item) {
|
|
14
|
+
for (const rule of this._constraints)
|
|
15
|
+
if (!rule.match(item))
|
|
16
|
+
return false;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
// Implement `Rule`
|
|
20
|
+
transform(items) {
|
|
21
|
+
return this._constraints.length ? filterItems(items, this) : items;
|
|
22
|
+
}
|
|
23
|
+
// Stringify as object syntax.
|
|
24
|
+
toString() {
|
|
25
|
+
return `{${this._constraints.map(String).join(",")}`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Data
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { SortKey, SortKeys } from "./Sort.js";
|
|
1
|
+
import { Data } from "../util/data.js";
|
|
2
|
+
import { Filterable, FilterConstraints } from "./FilterConstraints.js";
|
|
3
|
+
import { Sortable, SortConstraints } from "./SortConstraints.js";
|
|
4
|
+
import { Constraint } from "./Constraint.js";
|
|
5
|
+
import { FilterList } from "./FilterConstraint.js";
|
|
6
|
+
import { SortKeys, SortList } from "./SortConstraint.js";
|
|
8
7
|
/** Set of props for a query defined as an object. */
|
|
9
|
-
export declare type QueryProps<T extends Data> =
|
|
10
|
-
readonly
|
|
8
|
+
export declare type QueryProps<T extends Data> = {
|
|
9
|
+
readonly filter?: FilterList<T>;
|
|
10
|
+
readonly sort?: SortList<T>;
|
|
11
11
|
readonly limit?: number | null;
|
|
12
12
|
};
|
|
13
13
|
/** Interface that combines Filterable, Sortable, Sliceable. */
|
|
@@ -24,9 +24,9 @@ export interface Queryable<T extends Data> extends Filterable<T>, Sortable<T> {
|
|
|
24
24
|
/** Return a new instance of this class with a before offset defined. */
|
|
25
25
|
before(item: T): this;
|
|
26
26
|
/** Return a new instance of this class with no filters specified. */
|
|
27
|
-
unfilter: this;
|
|
27
|
+
readonly unfilter: this;
|
|
28
28
|
/** Return a new instance of this class with no sorts specified. */
|
|
29
|
-
unsort: this;
|
|
29
|
+
readonly unsort: this;
|
|
30
30
|
/** The maximum number of items allowed by the limit. */
|
|
31
31
|
readonly limit: number | null;
|
|
32
32
|
/** Return a new instance of this class with a limit set. */
|
|
@@ -35,17 +35,12 @@ export interface Queryable<T extends Data> extends Filterable<T>, Sortable<T> {
|
|
|
35
35
|
query(query: QueryProps<T>): this;
|
|
36
36
|
}
|
|
37
37
|
/** Allows filtering, sorting, and limiting on a set of results. */
|
|
38
|
-
export declare class
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
readonly filters: Filters<T>;
|
|
42
|
-
readonly sorts: Sorts<T>;
|
|
38
|
+
export declare class QueryConstraints<T extends Data = Data> extends Constraint<T> implements Queryable<T> {
|
|
39
|
+
readonly filters: FilterConstraints<T>;
|
|
40
|
+
readonly sorts: SortConstraints<T>;
|
|
43
41
|
readonly limit: number | null;
|
|
44
|
-
constructor(
|
|
45
|
-
filter(
|
|
46
|
-
filter<K extends Key<T>>(key: `${K}` | `!${K}` | `${K}>` | `${K}>=` | `${K}<` | `${K}<=`, value: T[K]): this;
|
|
47
|
-
filter<K extends Key<T>>(key: `${K}` | `!${K}`, value: ImmutableArray<T[K]>): this;
|
|
48
|
-
filter<K extends Key<T>>(key: `${K}[]`, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
|
|
42
|
+
constructor({ filter, sort, limit }?: QueryProps<T>);
|
|
43
|
+
filter(...filters: FilterList<T>[]): this;
|
|
49
44
|
get unfilter(): this;
|
|
50
45
|
match(item: T): boolean;
|
|
51
46
|
sort(...keys: SortKeys<T>[]): this;
|
|
@@ -54,7 +49,7 @@ export declare class Query<T extends Data> extends Rule<T> implements Queryable<
|
|
|
54
49
|
after(item: T): this;
|
|
55
50
|
before(item: T): this;
|
|
56
51
|
max(limit: number | null): this;
|
|
57
|
-
query({ sort, limit,
|
|
52
|
+
query({ sort, limit, filter }: QueryProps<T>): this;
|
|
58
53
|
transform(items: Iterable<T>): Iterable<T>;
|
|
59
54
|
toString(): string;
|
|
60
55
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { getProp } from "../util/data.js";
|
|
2
|
+
import { assert } from "../util/assert.js";
|
|
3
|
+
import { limitItems } from "../util/iterate.js";
|
|
4
|
+
import { FilterConstraints } from "./FilterConstraints.js";
|
|
5
|
+
import { SortConstraints } from "./SortConstraints.js";
|
|
6
|
+
import { Constraint } from "./Constraint.js";
|
|
7
|
+
import { FilterConstraint } from "./FilterConstraint.js";
|
|
8
|
+
// Instances to save resources for the default case (empty query).
|
|
9
|
+
const EMPTY_FILTERS = new FilterConstraints(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
10
|
+
const EMPTY_SORTS = new SortConstraints(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
11
|
+
const EMPTY_PROPS = { filter: EMPTY_FILTERS, sort: EMPTY_SORTS, limit: null }; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
12
|
+
/** Allows filtering, sorting, and limiting on a set of results. */
|
|
13
|
+
export class QueryConstraints extends Constraint {
|
|
14
|
+
constructor({ filter = EMPTY_FILTERS, sort = EMPTY_SORTS, limit = null } = EMPTY_PROPS) {
|
|
15
|
+
super();
|
|
16
|
+
this.filters = filter instanceof FilterConstraints ? filter : new FilterConstraints(filter);
|
|
17
|
+
this.sorts = sort instanceof SortConstraints ? sort : new SortConstraints(sort);
|
|
18
|
+
this.limit = limit;
|
|
19
|
+
}
|
|
20
|
+
// Implement `Filterable`
|
|
21
|
+
filter(...filters) {
|
|
22
|
+
return {
|
|
23
|
+
__proto__: Object.getPrototypeOf(this),
|
|
24
|
+
...this,
|
|
25
|
+
filters: this.filters.filter(...filters),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
get unfilter() {
|
|
29
|
+
if (!this.filters.size)
|
|
30
|
+
return this;
|
|
31
|
+
return {
|
|
32
|
+
__proto__: Object.getPrototypeOf(this),
|
|
33
|
+
...this,
|
|
34
|
+
filters: EMPTY_FILTERS,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
match(item) {
|
|
38
|
+
return this.filters.match(item);
|
|
39
|
+
}
|
|
40
|
+
// Implement `Sortable`
|
|
41
|
+
sort(...keys) {
|
|
42
|
+
return {
|
|
43
|
+
__proto__: Object.getPrototypeOf(this),
|
|
44
|
+
...this,
|
|
45
|
+
sorts: this.sorts.sort(...keys),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
get unsort() {
|
|
49
|
+
if (!this.sorts.size)
|
|
50
|
+
return this;
|
|
51
|
+
return {
|
|
52
|
+
__proto__: Object.getPrototypeOf(this),
|
|
53
|
+
...this,
|
|
54
|
+
sorts: EMPTY_SORTS,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
rank(left, right) {
|
|
58
|
+
return this.sorts.rank(left, right);
|
|
59
|
+
}
|
|
60
|
+
// Implement `Queryable`
|
|
61
|
+
after(item) {
|
|
62
|
+
return {
|
|
63
|
+
__proto__: Object.getPrototypeOf(this),
|
|
64
|
+
...this,
|
|
65
|
+
filters: this.filters.with(..._getAfterFilters(this.sorts, item)),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
before(item) {
|
|
69
|
+
return {
|
|
70
|
+
__proto__: Object.getPrototypeOf(this),
|
|
71
|
+
...this,
|
|
72
|
+
filters: this.filters.with(..._getBeforeFilters(this.sorts, item)),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
max(limit) {
|
|
76
|
+
if (this.limit === limit)
|
|
77
|
+
return this;
|
|
78
|
+
return {
|
|
79
|
+
__proto__: Object.getPrototypeOf(this),
|
|
80
|
+
...this,
|
|
81
|
+
limit,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
query({ sort, limit, filter }) {
|
|
85
|
+
return {
|
|
86
|
+
__proto__: Object.getPrototypeOf(this),
|
|
87
|
+
...this,
|
|
88
|
+
filters: filter ? this.filters.filter(filter) : this.filters,
|
|
89
|
+
sorts: sort ? this.sorts.sort(sort) : this.sorts,
|
|
90
|
+
limit: limit !== undefined ? limit : this.limit,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Implement `Rule`
|
|
94
|
+
transform(items) {
|
|
95
|
+
const sorted = this.sorts.transform(this.filters.transform(items));
|
|
96
|
+
return typeof this.limit === "number" ? limitItems(sorted, this.limit) : sorted;
|
|
97
|
+
}
|
|
98
|
+
// Implement toString()
|
|
99
|
+
toString() {
|
|
100
|
+
return `{"filter":${this.filters.toString()}},"sort":${this.sorts.toString()},"limit":${this.limit}}`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function* _getAfterFilters(sorts, item) {
|
|
104
|
+
const lastSort = sorts.last;
|
|
105
|
+
assert(lastSort);
|
|
106
|
+
for (const sort of sorts) {
|
|
107
|
+
const { key, direction } = sort;
|
|
108
|
+
const filterKey = direction === "ASC" ? (sort === lastSort ? `${key}>` : `${key}>=`) : sort === lastSort ? `${key}<` : `${key}<=`;
|
|
109
|
+
yield new FilterConstraint(filterKey, getProp(item, key));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function* _getBeforeFilters(sorts, item) {
|
|
113
|
+
const lastSort = sorts.last;
|
|
114
|
+
assert(lastSort);
|
|
115
|
+
for (const sort of sorts) {
|
|
116
|
+
const { key, direction } = sort;
|
|
117
|
+
const filterKey = direction === "ASC" ? (sort === lastSort ? `${key}<` : `${key}<=`) : sort === lastSort ? `${key}>` : `${key}>=`;
|
|
118
|
+
yield new FilterConstraint(filterKey, getProp(item, key));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import type { ImmutableArray } from "../util/array.js";
|
|
2
2
|
import { Data, Key } from "../util/data.js";
|
|
3
3
|
import { Rankable } from "../util/sort.js";
|
|
4
|
-
import {
|
|
4
|
+
import { Constraint } from "./Constraint.js";
|
|
5
5
|
/** Format that allows sorts to be set as a plain string, e.g. `name` sorts by name in ascending order and `!date` sorts by date in descending order. */
|
|
6
6
|
export declare type SortKey<T extends Data> = Key<T> | `${Key<T>}` | `!${Key<T>}`;
|
|
7
7
|
/** One or more sort keys. */
|
|
8
8
|
export declare type SortKeys<T extends Data> = SortKey<T> | ImmutableArray<SortKey<T>>;
|
|
9
9
|
/** Possible operator references. */
|
|
10
10
|
export declare type SortDirection = "ASC" | "DESC";
|
|
11
|
+
/** List of sorts in a flexible format. */
|
|
12
|
+
export declare type SortList<T extends Data> = SortKeys<T> | SortConstraint<T> | Iterable<SortList<T>>;
|
|
11
13
|
/** Sort a list of values. */
|
|
12
|
-
export declare class
|
|
13
|
-
|
|
14
|
-
static on<X extends Data>(sort: SortKey<X> | Sort<X>): Sort<X>;
|
|
15
|
-
readonly key: Key<T>;
|
|
14
|
+
export declare class SortConstraint<T extends Data = Data> implements Constraint<T>, Rankable<T> {
|
|
15
|
+
readonly key: string;
|
|
16
16
|
readonly direction: SortDirection;
|
|
17
|
-
|
|
17
|
+
get sortKey(): string;
|
|
18
|
+
constructor(sortKey: SortKey<T>);
|
|
18
19
|
rank(left: T, right: T): number;
|
|
19
20
|
transform(items: Iterable<T>): Iterable<T>;
|
|
20
21
|
toString(): string;
|
|
21
22
|
}
|
|
23
|
+
/** Get the separate sorts generated from a list of sorts. */
|
|
24
|
+
export declare function getSorts<T extends Data>(sorts: SortList<T>): Iterable<SortConstraint<T>>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getProp } from "../util/data.js";
|
|
2
|
+
import { rank, rankAsc, rankDesc, sortItems } from "../util/sort.js";
|
|
3
|
+
/** Sort a list of values. */
|
|
4
|
+
export class SortConstraint {
|
|
5
|
+
constructor(sortKey) {
|
|
6
|
+
if (sortKey.startsWith("!")) {
|
|
7
|
+
this.key = sortKey.slice(1);
|
|
8
|
+
this.direction = "DESC";
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
this.key = sortKey;
|
|
12
|
+
this.direction = "ASC";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
get sortKey() {
|
|
16
|
+
return `"${this.direction === "DESC" ? "!" : ""}${this.key}"`;
|
|
17
|
+
}
|
|
18
|
+
rank(left, right) {
|
|
19
|
+
return rank(getProp(left, this.key), this.direction === "ASC" ? rankAsc : rankDesc, getProp(right, this.key));
|
|
20
|
+
}
|
|
21
|
+
transform(items) {
|
|
22
|
+
return sortItems(items, this);
|
|
23
|
+
}
|
|
24
|
+
toString() {
|
|
25
|
+
return this.sortKey;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Get the separate sorts generated from a list of sorts. */
|
|
29
|
+
export function* getSorts(sorts) {
|
|
30
|
+
if (typeof sorts === "string") {
|
|
31
|
+
yield new SortConstraint(sorts);
|
|
32
|
+
}
|
|
33
|
+
else if (sorts instanceof SortConstraint) {
|
|
34
|
+
yield sorts;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
for (const sort of sorts)
|
|
38
|
+
yield* getSorts(sort);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Data } from "../util/data.js";
|
|
2
|
+
import { Rankable } from "../util/sort.js";
|
|
3
|
+
import { Constraints } from "./Constraints.js";
|
|
4
|
+
import { SortConstraint, SortKeys, SortList } from "./SortConstraint.js";
|
|
5
|
+
/**
|
|
6
|
+
* Interface to make sure an object implements all directions.
|
|
7
|
+
* - Extends `Rankable` so this object itself can be directly be used with `filterItems()` and `filterEntries()`
|
|
8
|
+
*/
|
|
9
|
+
export interface Sortable<T extends Data> extends Rankable<T> {
|
|
10
|
+
/** Add one or more sorts to this sortable. */
|
|
11
|
+
sort(...keys: SortKeys<T>[]): this;
|
|
12
|
+
}
|
|
13
|
+
/** A set of sorts. */
|
|
14
|
+
export declare class SortConstraints<T extends Data = Data> extends Constraints<T, SortConstraint<T>> implements Sortable<T> {
|
|
15
|
+
constructor(...sorts: SortList<T>[]);
|
|
16
|
+
sort(...sorts: SortList<T>[]): this;
|
|
17
|
+
rank(left: T, right: T): number;
|
|
18
|
+
transform(items: Iterable<T>): Iterable<T>;
|
|
19
|
+
toString(): string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { sortItems } from "../util/sort.js";
|
|
2
|
+
import { Constraints } from "./Constraints.js";
|
|
3
|
+
import { getSorts } from "./SortConstraint.js";
|
|
4
|
+
/** A set of sorts. */
|
|
5
|
+
export class SortConstraints extends Constraints {
|
|
6
|
+
constructor(...sorts) {
|
|
7
|
+
super(...getSorts(sorts));
|
|
8
|
+
}
|
|
9
|
+
// Implement `Sortable`
|
|
10
|
+
sort(...sorts) {
|
|
11
|
+
return this.with(...getSorts(sorts));
|
|
12
|
+
}
|
|
13
|
+
rank(left, right) {
|
|
14
|
+
for (const rule of this._constraints) {
|
|
15
|
+
const l = rule.rank(left, right);
|
|
16
|
+
if (l !== 0)
|
|
17
|
+
return l;
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
// Implement `Rule`
|
|
22
|
+
transform(items) {
|
|
23
|
+
return this._constraints.length ? sortItems(items, this) : items;
|
|
24
|
+
}
|
|
25
|
+
// Stringify as array syntax.
|
|
26
|
+
toString() {
|
|
27
|
+
return `[${this._constraints.map(String).join(",")}]`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./Constraint.js";
|
|
2
|
+
export * from "./Constraints.js";
|
|
3
|
+
export * from "./FilterConstraint.js";
|
|
4
|
+
export * from "./FilterConstraints.js";
|
|
5
|
+
export * from "./SortConstraint.js";
|
|
6
|
+
export * from "./SortConstraints.js";
|
|
7
|
+
export * from "./QueryConstraints.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./Constraint.js";
|
|
2
|
+
export * from "./Constraints.js";
|
|
3
|
+
export * from "./FilterConstraint.js";
|
|
4
|
+
export * from "./FilterConstraints.js";
|
|
5
|
+
export * from "./SortConstraint.js";
|
|
6
|
+
export * from "./SortConstraints.js";
|
|
7
|
+
export * from "./QueryConstraints.js";
|
package/db/Changes.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Datas, Key } from "../util/data.js";
|
|
2
|
+
import { DataUpdate } from "../update/DataUpdate.js";
|
|
3
|
+
import type { Provider, AsyncProvider } from "../provider/Provider.js";
|
|
4
|
+
import type { Database, AsyncDatabase } from "./Database.js";
|
|
5
|
+
/**
|
|
6
|
+
* Change set of operations to run against a database in `{ "collection/id": data | DataUpdate | null }` format.
|
|
7
|
+
* - If data is an object, sets the item.
|
|
8
|
+
* - If data is a `DataUpdate` instance, updates the item.
|
|
9
|
+
* - If data is null, deletes the item.
|
|
10
|
+
* - If data is undefined, skip the item.
|
|
11
|
+
*/
|
|
12
|
+
export declare type Changes<DB extends Datas> = {
|
|
13
|
+
[K in Key<DB> as `${K}/${string}`]: DB[K] | DataUpdate<DB[K]> | null | undefined;
|
|
14
|
+
};
|
|
15
|
+
/** Apply a set of changes to a synchronous database. */
|
|
16
|
+
export declare function changeDatabase<T extends Datas>({ provider }: Database<T>, changes: Changes<T>): Changes<T>;
|
|
17
|
+
/** Apply a set of changes to an asynchronous database. */
|
|
18
|
+
export declare function changeAsyncDatabase<T extends Datas>({ provider }: AsyncDatabase<T>, changes: Changes<T>): Promise<Changes<T>>;
|
|
19
|
+
/** Apply a set of changes to a synchronous provider. */
|
|
20
|
+
export declare function changeProvider<T extends Datas>(provider: Provider<T>, changes: Changes<T>): Changes<T>;
|
|
21
|
+
/** Apply a set of changes to an asynchronous provider. */
|
|
22
|
+
export declare function changeAsyncProvider<T extends Datas>(provider: AsyncProvider<T>, changes: Changes<T>): Promise<Changes<T>>;
|
package/db/Changes.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { splitString } from "../util/string.js";
|
|
2
|
+
import { DataUpdate } from "../update/DataUpdate.js";
|
|
3
|
+
/** Apply a set of changes to a synchronous database. */
|
|
4
|
+
export function changeDatabase({ provider }, changes) {
|
|
5
|
+
return changeProvider(provider, changes);
|
|
6
|
+
}
|
|
7
|
+
/** Apply a set of changes to an asynchronous database. */
|
|
8
|
+
export function changeAsyncDatabase({ provider }, changes) {
|
|
9
|
+
return changeAsyncProvider(provider, changes);
|
|
10
|
+
}
|
|
11
|
+
/** Apply a set of changes to a synchronous provider. */
|
|
12
|
+
export function changeProvider(provider, changes) {
|
|
13
|
+
for (const [key, change] of Object.entries(changes)) {
|
|
14
|
+
const [collection, id] = splitString(key, "/", 2);
|
|
15
|
+
if (change === undefined)
|
|
16
|
+
continue;
|
|
17
|
+
else if (change === null)
|
|
18
|
+
provider.deleteItem(collection, id);
|
|
19
|
+
else if (change instanceof DataUpdate)
|
|
20
|
+
provider.updateItem(collection, id, change);
|
|
21
|
+
else
|
|
22
|
+
provider.setItem(collection, id, change);
|
|
23
|
+
}
|
|
24
|
+
return changes;
|
|
25
|
+
}
|
|
26
|
+
/** Apply a set of changes to an asynchronous provider. */
|
|
27
|
+
export async function changeAsyncProvider(provider, changes) {
|
|
28
|
+
for (const [key, change] of Object.entries(changes)) {
|
|
29
|
+
const [collection, id] = splitString(key, "/", 2);
|
|
30
|
+
if (change === undefined)
|
|
31
|
+
continue;
|
|
32
|
+
else if (change === null)
|
|
33
|
+
await provider.deleteItem(collection, id);
|
|
34
|
+
else if (change instanceof DataUpdate)
|
|
35
|
+
await provider.updateItem(collection, id, change);
|
|
36
|
+
else
|
|
37
|
+
await provider.setItem(collection, id, change);
|
|
38
|
+
}
|
|
39
|
+
return changes;
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|