shelving 1.65.0 → 1.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db/Database.d.ts +3 -5
- package/db/Database.js +3 -4
- package/db/Reference.d.ts +4 -8
- package/db/Reference.js +5 -6
- package/observe/util.d.ts +3 -3
- package/package.json +1 -1
- package/query/Filter.js +21 -1
- package/query/Query.d.ts +10 -3
- package/query/Query.js +51 -13
- package/query/Rules.js +2 -1
- package/query/Sort.js +1 -1
- package/react/useDocument.d.ts +4 -1
- package/react/useDocument.js +25 -19
- package/react/useQuery.d.ts +6 -2
- package/react/useQuery.js +20 -11
- package/react/useSubscribe.js +1 -1
- package/state/ArrayState.d.ts +1 -1
- package/state/ArrayState.js +1 -1
- package/state/DataState.d.ts +2 -2
- package/state/DataState.js +4 -10
- package/state/ObjectState.d.ts +1 -1
- package/state/ObjectState.js +1 -1
- package/state/State.d.ts +1 -1
- package/state/State.js +2 -2
package/db/Database.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Validators, ValidatorType } from "../util/validate.js";
|
|
2
|
-
import type { Key, Datas } from "../util/data.js";
|
|
2
|
+
import type { Key, Datas, Entity } from "../util/data.js";
|
|
3
3
|
import type { Provider } from "../provider/Provider.js";
|
|
4
|
-
import type {
|
|
5
|
-
import type { FilterProps } from "../query/Filter.js";
|
|
6
|
-
import { Entity } from "../util/data.js";
|
|
4
|
+
import type { QueryProps } from "../query/Query.js";
|
|
7
5
|
import { DocumentReference, QueryReference } from "./Reference.js";
|
|
8
6
|
/**
|
|
9
7
|
* Combines a database model and a provider.
|
|
@@ -17,7 +15,7 @@ export declare class Database<V extends Validators<Datas> = Validators<Datas>> {
|
|
|
17
15
|
readonly provider: Provider;
|
|
18
16
|
constructor(validators: V, provider: Provider);
|
|
19
17
|
/** Create a query on a collection in this model. */
|
|
20
|
-
query<K extends Key<V>>(collection: K,
|
|
18
|
+
query<K extends Key<V>>(collection: K, query?: QueryProps<Entity<ValidatorType<V[K]>>>): QueryReference<ValidatorType<V[K]>>;
|
|
21
19
|
/** Reference a document in a collection in this model. */
|
|
22
20
|
doc<K extends Key<V>>(collection: K, id: string): DocumentReference<ValidatorType<V[K]>>;
|
|
23
21
|
/**
|
package/db/Database.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Filters } from "../query/Filters.js";
|
|
2
|
-
import { Sorts } from "../query/Sorts.js";
|
|
3
1
|
import { DocumentReference, QueryReference } from "./Reference.js";
|
|
4
2
|
/**
|
|
5
3
|
* Combines a database model and a provider.
|
|
@@ -15,8 +13,9 @@ export class Database {
|
|
|
15
13
|
this.provider = provider;
|
|
16
14
|
}
|
|
17
15
|
/** Create a query on a collection in this model. */
|
|
18
|
-
query(collection,
|
|
19
|
-
|
|
16
|
+
query(collection, query) {
|
|
17
|
+
const validator = this.validators[collection];
|
|
18
|
+
return new QueryReference(this, validator, collection, query);
|
|
20
19
|
}
|
|
21
20
|
/** Reference a document in a collection in this model. */
|
|
22
21
|
doc(collection, id) {
|
package/db/Reference.d.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import type { Data, OptionalData, Entity, OptionalEntity, Entities } from "../util/data.js";
|
|
2
2
|
import type { Dispatch } from "../util/function.js";
|
|
3
|
-
import type { SortKeys } from "../query/Sort.js";
|
|
4
3
|
import { ImmutableArray } from "../util/array.js";
|
|
5
4
|
import type { PartialObserver } from "../observe/Observer.js";
|
|
6
5
|
import type { Validator } from "../util/validate.js";
|
|
7
|
-
import { Query } from "../query/Query.js";
|
|
8
|
-
import { Filters } from "../query/Filters.js";
|
|
9
|
-
import { Sorts } from "../query/Sorts.js";
|
|
6
|
+
import { Query, QueryProps } from "../query/Query.js";
|
|
10
7
|
import { DataUpdate, PropUpdates } from "../update/DataUpdate.js";
|
|
11
|
-
import {
|
|
12
|
-
import { Observable, Unsubscribe } from "../observe/Observable.js";
|
|
8
|
+
import type { Observable, Unsubscribe } from "../observe/Observable.js";
|
|
13
9
|
import type { Database } from "./Database.js";
|
|
14
10
|
/** A refence to a location in a database. */
|
|
15
11
|
export interface Reference {
|
|
@@ -21,7 +17,7 @@ export declare class QueryReference<T extends Data = Data> extends Query<Entity<
|
|
|
21
17
|
readonly db: Database;
|
|
22
18
|
readonly validator: Validator<T>;
|
|
23
19
|
readonly collection: string;
|
|
24
|
-
constructor(db: Database, validator: Validator<T>, collection: string, filters
|
|
20
|
+
constructor(db: Database, validator: Validator<T>, collection: string, { sort, limit, ...filters }?: QueryProps<Entity<T>>);
|
|
25
21
|
/** Reference a document in this query's collection. */
|
|
26
22
|
doc(id: string): DocumentReference<T>;
|
|
27
23
|
/**
|
|
@@ -104,7 +100,7 @@ export declare class DocumentReference<T extends Data = Data> implements Observa
|
|
|
104
100
|
readonly id: string;
|
|
105
101
|
constructor(db: Database, validator: Validator<T>, collection: string, id: string);
|
|
106
102
|
/** Create a query on this document's collection. */
|
|
107
|
-
query(
|
|
103
|
+
query(query?: QueryProps<Entity<T>>): QueryReference<T>;
|
|
108
104
|
/** Get an 'optional' reference to this document (uses a `ModelQuery` with an `id` filter). */
|
|
109
105
|
get optional(): QueryReference<T>;
|
|
110
106
|
/**
|
package/db/Reference.js
CHANGED
|
@@ -5,12 +5,11 @@ import { Sorts } from "../query/Sorts.js";
|
|
|
5
5
|
import { callAsync } from "../util/async.js";
|
|
6
6
|
import { countItems, hasItems } from "../util/iterate.js";
|
|
7
7
|
import { DataUpdate } from "../update/DataUpdate.js";
|
|
8
|
-
import { Filter } from "../query/Filter.js";
|
|
9
8
|
import { RequiredError } from "../error/RequiredError.js";
|
|
10
9
|
/** A query reference within a specific database. */
|
|
11
10
|
export class QueryReference extends Query {
|
|
12
|
-
constructor(db, validator, collection,
|
|
13
|
-
super(filters,
|
|
11
|
+
constructor(db, validator, collection, { sort, limit, ...filters } = {}) {
|
|
12
|
+
super(Filters.on(filters), sort && Sorts.on(sort), limit);
|
|
14
13
|
this.db = db;
|
|
15
14
|
this.validator = validator;
|
|
16
15
|
this.collection = collection;
|
|
@@ -127,12 +126,12 @@ export class DocumentReference {
|
|
|
127
126
|
this.id = id;
|
|
128
127
|
}
|
|
129
128
|
/** Create a query on this document's collection. */
|
|
130
|
-
query(
|
|
131
|
-
return new QueryReference(this.db, this.validator, this.collection,
|
|
129
|
+
query(query) {
|
|
130
|
+
return new QueryReference(this.db, this.validator, this.collection, query);
|
|
132
131
|
}
|
|
133
132
|
/** Get an 'optional' reference to this document (uses a `ModelQuery` with an `id` filter). */
|
|
134
133
|
get optional() {
|
|
135
|
-
return new QueryReference(this.db, this.validator, this.collection,
|
|
134
|
+
return new QueryReference(this.db, this.validator, this.collection, { id: this.id, limit: 1 });
|
|
136
135
|
}
|
|
137
136
|
/**
|
|
138
137
|
* Does this document exist?
|
package/observe/util.d.ts
CHANGED
|
@@ -15,10 +15,10 @@ export declare function connected<T, C extends ConnectableObserver<T>>(source: S
|
|
|
15
15
|
export declare function connected<T>(source: Subscribable<T>, target: ConnectableObserver<T>): Subject<T>;
|
|
16
16
|
export declare function connected<T>(source: Subscribable<T>): Subject<T>;
|
|
17
17
|
/** Connect a connectable to a source subscribable but transform the value using a transform, and return the connected connectable. */
|
|
18
|
-
export declare function connectedDerived<T, TT, C extends ConnectableObserver<
|
|
19
|
-
export declare function connectedDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, TT>, target: ConnectableObserver<
|
|
18
|
+
export declare function connectedDerived<T, TT, C extends ConnectableObserver<TT>>(source: Subscribable<T>, transformer: Transformer<T, TT>, target: C): C;
|
|
19
|
+
export declare function connectedDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, TT>, target: ConnectableObserver<TT>): ConnectableObserver<TT>;
|
|
20
20
|
export declare function connectedDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, TT>): Subject<TT>;
|
|
21
21
|
/** Connect a connectable to a source subscribable but transform the value using an async transform, and return the connected connectable. */
|
|
22
22
|
export declare function connectedAsyncDerived<T, TT, C extends ConnectableObserver<TT>>(source: Subscribable<T>, transformer: Transformer<T, PromiseLike<TT>>, target: C): C;
|
|
23
|
-
export declare function connectedAsyncDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, PromiseLike<TT>>, target: ConnectableObserver<
|
|
23
|
+
export declare function connectedAsyncDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, PromiseLike<TT>>, target: ConnectableObserver<TT>): ConnectableObserver<TT>;
|
|
24
24
|
export declare function connectedAsyncDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, PromiseLike<TT>>): Subject<TT>;
|
package/package.json
CHANGED
package/query/Filter.js
CHANGED
|
@@ -51,6 +51,26 @@ export class Filter extends Rule {
|
|
|
51
51
|
return filterItems(items, this);
|
|
52
52
|
}
|
|
53
53
|
toString() {
|
|
54
|
-
return
|
|
54
|
+
return `"${_formatKey(this)}":${JSON.stringify(this.value)}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Convert a `Filter` */
|
|
58
|
+
function _formatKey({ key, operator }) {
|
|
59
|
+
switch (operator) {
|
|
60
|
+
case "NOT":
|
|
61
|
+
case "OUT":
|
|
62
|
+
return `!${key}`;
|
|
63
|
+
case "CONTAINS":
|
|
64
|
+
return `${key}[]`;
|
|
65
|
+
case "LT":
|
|
66
|
+
return `${key}<`;
|
|
67
|
+
case "LTE":
|
|
68
|
+
return `${key}<=`;
|
|
69
|
+
case "GT":
|
|
70
|
+
return `${key}>`;
|
|
71
|
+
case "GTE":
|
|
72
|
+
return `${key}>=`;
|
|
73
|
+
default:
|
|
74
|
+
return key;
|
|
55
75
|
}
|
|
56
76
|
}
|
package/query/Query.d.ts
CHANGED
|
@@ -5,6 +5,11 @@ import { Sortable, Sorts } from "./Sorts.js";
|
|
|
5
5
|
import { Rule } from "./Rule.js";
|
|
6
6
|
import { FilterProps } from "./Filter.js";
|
|
7
7
|
import { SortKey, SortKeys } from "./Sort.js";
|
|
8
|
+
/** Set of props for a query defined as an object. */
|
|
9
|
+
export declare type QueryProps<T extends Data> = FilterProps<T> & {
|
|
10
|
+
readonly sort?: SortKey<T> | ImmutableArray<SortKey<T>>;
|
|
11
|
+
readonly limit?: number | null;
|
|
12
|
+
};
|
|
8
13
|
/** Interface that combines Filterable, Sortable, Sliceable. */
|
|
9
14
|
export interface Queryable<T extends Data> extends Filterable<T>, Sortable<T> {
|
|
10
15
|
/**
|
|
@@ -26,11 +31,13 @@ export interface Queryable<T extends Data> extends Filterable<T>, Sortable<T> {
|
|
|
26
31
|
readonly limit: number | null;
|
|
27
32
|
/** Return a new instance of this class with a limit set. */
|
|
28
33
|
max(max: number | null): this;
|
|
34
|
+
/** Return a new instance of this class with new filters, sorts, limits set. */
|
|
35
|
+
query(query: QueryProps<T>): this;
|
|
29
36
|
}
|
|
30
37
|
/** Allows filtering, sorting, and limiting on a set of results. */
|
|
31
38
|
export declare class Query<T extends Data> extends Rule<T> implements Queryable<T> {
|
|
32
39
|
/** Create a new `Query` object from a set of `QueryProps` */
|
|
33
|
-
static on<X extends Data>(
|
|
40
|
+
static on<X extends Data>({ sort, limit, ...filters }: QueryProps<X>): Query<X>;
|
|
34
41
|
readonly filters: Filters<T>;
|
|
35
42
|
readonly sorts: Sorts<T>;
|
|
36
43
|
readonly limit: number | null;
|
|
@@ -44,10 +51,10 @@ export declare class Query<T extends Data> extends Rule<T> implements Queryable<
|
|
|
44
51
|
sort(...keys: SortKeys<T>[]): this;
|
|
45
52
|
get unsort(): this;
|
|
46
53
|
rank(left: T, right: T): number;
|
|
47
|
-
/** Return a new instance of this class with a limit defined. */
|
|
48
|
-
max(limit: number | null): this;
|
|
49
54
|
after(item: T): this;
|
|
50
55
|
before(item: T): this;
|
|
56
|
+
max(limit: number | null): this;
|
|
57
|
+
query({ sort, limit, ...filters }: QueryProps<T>): this;
|
|
51
58
|
transform(items: Iterable<T>): Iterable<T>;
|
|
52
59
|
toString(): string;
|
|
53
60
|
}
|
package/query/Query.js
CHANGED
|
@@ -17,32 +17,44 @@ export class Query extends Rule {
|
|
|
17
17
|
this.limit = limit;
|
|
18
18
|
}
|
|
19
19
|
/** Create a new `Query` object from a set of `QueryProps` */
|
|
20
|
-
static on(
|
|
21
|
-
return new Query(filters && Filters.on(filters),
|
|
20
|
+
static on({ sort, limit, ...filters }) {
|
|
21
|
+
return new Query(filters && Filters.on(filters), sort && Sorts.on(sort), limit);
|
|
22
22
|
}
|
|
23
23
|
filter(input, value) {
|
|
24
|
-
return {
|
|
24
|
+
return {
|
|
25
|
+
__proto__: Object.getPrototypeOf(this),
|
|
26
|
+
...this,
|
|
27
|
+
filters: this.filters.filter(input, value), // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
28
|
+
};
|
|
25
29
|
}
|
|
26
30
|
get unfilter() {
|
|
27
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
__proto__: Object.getPrototypeOf(this),
|
|
33
|
+
...this,
|
|
34
|
+
filters: EMPTY_FILTERS,
|
|
35
|
+
};
|
|
28
36
|
}
|
|
29
37
|
match(item) {
|
|
30
38
|
return this.filters.match(item);
|
|
31
39
|
}
|
|
32
40
|
// Implement `Sortable`
|
|
33
41
|
sort(...keys) {
|
|
34
|
-
return {
|
|
42
|
+
return {
|
|
43
|
+
__proto__: Object.getPrototypeOf(this),
|
|
44
|
+
...this,
|
|
45
|
+
sorts: this.sorts.sort(...keys),
|
|
46
|
+
};
|
|
35
47
|
}
|
|
36
48
|
get unsort() {
|
|
37
|
-
return {
|
|
49
|
+
return {
|
|
50
|
+
__proto__: Object.getPrototypeOf(this),
|
|
51
|
+
...this,
|
|
52
|
+
sorts: EMPTY_SORTS,
|
|
53
|
+
};
|
|
38
54
|
}
|
|
39
55
|
rank(left, right) {
|
|
40
56
|
return this.sorts.rank(left, right);
|
|
41
57
|
}
|
|
42
|
-
/** Return a new instance of this class with a limit defined. */
|
|
43
|
-
max(limit) {
|
|
44
|
-
return limit === this.limit ? this : { __proto__: Object.getPrototypeOf(this), ...this, limit };
|
|
45
|
-
}
|
|
46
58
|
// Implement `Queryable`
|
|
47
59
|
after(item) {
|
|
48
60
|
const filters = [...this.filters];
|
|
@@ -52,7 +64,11 @@ export class Query extends Rule {
|
|
|
52
64
|
const { key, direction } = sort;
|
|
53
65
|
filters.push(new Filter(key, direction === "ASC" ? (sort === lastSort ? "GT" : "GTE") : sort === lastSort ? "LT" : "LTE", getProp(item, key)));
|
|
54
66
|
}
|
|
55
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
__proto__: Object.getPrototypeOf(this),
|
|
69
|
+
...this,
|
|
70
|
+
filters: new Filters(...filters),
|
|
71
|
+
};
|
|
56
72
|
}
|
|
57
73
|
before(item) {
|
|
58
74
|
const filters = [...this.filters];
|
|
@@ -62,7 +78,27 @@ export class Query extends Rule {
|
|
|
62
78
|
const { key, direction } = sort;
|
|
63
79
|
filters.push(new Filter(key, direction === "ASC" ? (sort === lastSort ? "LT" : "LTE") : sort === lastSort ? "GT" : "GTE", getProp(item, key)));
|
|
64
80
|
}
|
|
65
|
-
return {
|
|
81
|
+
return {
|
|
82
|
+
__proto__: Object.getPrototypeOf(this),
|
|
83
|
+
...this,
|
|
84
|
+
filters: new Filters(...filters),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
max(limit) {
|
|
88
|
+
return {
|
|
89
|
+
__proto__: Object.getPrototypeOf(this),
|
|
90
|
+
...this,
|
|
91
|
+
limit,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
query({ sort, limit, ...filters }) {
|
|
95
|
+
return {
|
|
96
|
+
__proto__: Object.getPrototypeOf(this),
|
|
97
|
+
...this,
|
|
98
|
+
filters: this.filters.filter(filters),
|
|
99
|
+
sorts: sort ? this.sorts.sort(sort) : this.sorts,
|
|
100
|
+
limit: limit !== undefined ? limit : this.limit,
|
|
101
|
+
};
|
|
66
102
|
}
|
|
67
103
|
// Implement `Rule`
|
|
68
104
|
transform(items) {
|
|
@@ -71,6 +107,8 @@ export class Query extends Rule {
|
|
|
71
107
|
}
|
|
72
108
|
// Implement toString()
|
|
73
109
|
toString() {
|
|
74
|
-
|
|
110
|
+
const filters = this.filters.toString();
|
|
111
|
+
const sorts = this.sorts.toString();
|
|
112
|
+
return `{${filters}${sorts ? `${filters ? "," : ""}"sort":[${sorts}]` : ""}${typeof this.limit === "number" ? `${filters || sorts ? "," : ""}"limit":${this.limit}` : ""}}`;
|
|
75
113
|
}
|
|
76
114
|
}
|
package/query/Rules.js
CHANGED
|
@@ -18,8 +18,9 @@ export class Rules extends Rule {
|
|
|
18
18
|
get size() {
|
|
19
19
|
return this._rules.length;
|
|
20
20
|
}
|
|
21
|
+
// Override to join the strings from the rules together with `,` commas.
|
|
21
22
|
toString() {
|
|
22
|
-
return this._rules.map(
|
|
23
|
+
return this._rules.map(String).join(",");
|
|
23
24
|
}
|
|
24
25
|
/** Clone this set of rules but add additional rules. */
|
|
25
26
|
with(...rules) {
|
package/query/Sort.js
CHANGED
package/react/useDocument.d.ts
CHANGED
|
@@ -14,10 +14,13 @@ export declare class DocumentState<T extends Data> extends State<OptionalEntity<
|
|
|
14
14
|
get time(): number | undefined;
|
|
15
15
|
/** How old this state's value is (in milliseconds). */
|
|
16
16
|
get age(): number;
|
|
17
|
+
/** Get the data of the document (throws `RequiredError` if document doesn't exist). */
|
|
17
18
|
get data(): Entity<T>;
|
|
19
|
+
/** Does the document exist (i.e. its value isn't `null`)? */
|
|
20
|
+
get exists(): boolean;
|
|
18
21
|
constructor(ref: DocumentReference<T>);
|
|
19
22
|
/** Refresh this state from the source provider. */
|
|
20
|
-
refresh()
|
|
23
|
+
readonly refresh: () => Promise<void>;
|
|
21
24
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
22
25
|
refreshStale(maxAge: number): void;
|
|
23
26
|
/** Subscribe this state to the source provider. */
|
package/react/useDocument.js
CHANGED
|
@@ -5,7 +5,6 @@ import { State } from "../state/State.js";
|
|
|
5
5
|
import { MatchObserver } from "../observe/MatchObserver.js";
|
|
6
6
|
import { BooleanState } from "../state/BooleanState.js";
|
|
7
7
|
import { ConditionError } from "../error/ConditionError.js";
|
|
8
|
-
import { dispatch } from "../util/function.js";
|
|
9
8
|
import { useReduce } from "./useReduce.js";
|
|
10
9
|
import { useSubscribe } from "./useSubscribe.js";
|
|
11
10
|
/** Hold the current state of a document. */
|
|
@@ -13,14 +12,32 @@ export class DocumentState extends State {
|
|
|
13
12
|
constructor(ref) {
|
|
14
13
|
super();
|
|
15
14
|
this.busy = new BooleanState();
|
|
15
|
+
/** Refresh this state from the source provider. */
|
|
16
|
+
this.refresh = async () => {
|
|
17
|
+
if (this.closed)
|
|
18
|
+
throw new ConditionError("State is closed");
|
|
19
|
+
if (!this.busy.value) {
|
|
20
|
+
this.busy.next(true);
|
|
21
|
+
try {
|
|
22
|
+
const result = await this.ref.value;
|
|
23
|
+
this.next(result);
|
|
24
|
+
}
|
|
25
|
+
catch (thrown) {
|
|
26
|
+
this.error(thrown);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
this.busy.next(false);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
16
33
|
this._table = findSourceProvider(ref.db.provider, CacheProvider).memory.getTable(ref);
|
|
17
34
|
this.ref = ref;
|
|
18
35
|
// If the result is cached use it as the initial value.
|
|
19
|
-
const isCached = this._table.getDocumentTime(ref.id)
|
|
36
|
+
const isCached = typeof this._table.getDocumentTime(ref.id) === "number";
|
|
20
37
|
if (isCached)
|
|
21
38
|
this.next(this._table.getDocument(ref.id)); // Use the existing cached value.
|
|
22
39
|
else
|
|
23
|
-
|
|
40
|
+
void this.refresh(); // Queue a request to refresh the value.
|
|
24
41
|
}
|
|
25
42
|
/** Time this state was last updated with a new value. */
|
|
26
43
|
get time() {
|
|
@@ -31,29 +48,18 @@ export class DocumentState extends State {
|
|
|
31
48
|
const time = this.time;
|
|
32
49
|
return typeof time === "number" ? Date.now() - time : Infinity;
|
|
33
50
|
}
|
|
51
|
+
/** Get the data of the document (throws `RequiredError` if document doesn't exist). */
|
|
34
52
|
get data() {
|
|
35
53
|
return getDocumentData(this.value, this.ref);
|
|
36
54
|
}
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
throw new ConditionError("State is closed");
|
|
41
|
-
if (!this.busy.value) {
|
|
42
|
-
try {
|
|
43
|
-
this.busy.next(true);
|
|
44
|
-
const result = await this.ref.value;
|
|
45
|
-
this.busy.next(false);
|
|
46
|
-
this.next(result);
|
|
47
|
-
}
|
|
48
|
-
catch (thrown) {
|
|
49
|
-
this.error(thrown);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
55
|
+
/** Does the document exist (i.e. its value isn't `null`)? */
|
|
56
|
+
get exists() {
|
|
57
|
+
return !!this.value;
|
|
52
58
|
}
|
|
53
59
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
54
60
|
refreshStale(maxAge) {
|
|
55
61
|
if (this.age > maxAge)
|
|
56
|
-
|
|
62
|
+
void this.refresh();
|
|
57
63
|
}
|
|
58
64
|
/** Subscribe this state to the source provider. */
|
|
59
65
|
connectSource() {
|
package/react/useQuery.d.ts
CHANGED
|
@@ -26,9 +26,13 @@ export declare class QueryState<T extends Data> extends State<Entities<T>> {
|
|
|
26
26
|
get lastValue(): OptionalEntity<T>;
|
|
27
27
|
/** Get the last document matched by this query. */
|
|
28
28
|
get lastData(): Entity<T>;
|
|
29
|
+
/** Does the document have at least one result. */
|
|
30
|
+
get exists(): boolean;
|
|
31
|
+
/** Get the number of items matching this query. */
|
|
32
|
+
get count(): number;
|
|
29
33
|
constructor(ref: QueryReference<T>);
|
|
30
34
|
/** Refresh this state from the source provider. */
|
|
31
|
-
refresh: () => Promise<void>;
|
|
35
|
+
readonly refresh: () => Promise<void>;
|
|
32
36
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
33
37
|
refreshStale(maxAge: number): void;
|
|
34
38
|
/** Subscribe this state to the source provider. */
|
|
@@ -39,7 +43,7 @@ export declare class QueryState<T extends Data> extends State<Entities<T>> {
|
|
|
39
43
|
* Load more items after the last once.
|
|
40
44
|
* - Promise that needs to be handled.
|
|
41
45
|
*/
|
|
42
|
-
loadMore: () => Promise<void>;
|
|
46
|
+
readonly loadMore: () => Promise<void>;
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
49
|
* Use a query in a React component.
|
package/react/useQuery.js
CHANGED
|
@@ -5,7 +5,6 @@ import { State } from "../state/State.js";
|
|
|
5
5
|
import { MatchObserver } from "../observe/MatchObserver.js";
|
|
6
6
|
import { ConditionError } from "../error/ConditionError.js";
|
|
7
7
|
import { BooleanState } from "../state/BooleanState.js";
|
|
8
|
-
import { dispatch } from "../util/function.js";
|
|
9
8
|
import { useReduce } from "./useReduce.js";
|
|
10
9
|
import { useSubscribe } from "./useSubscribe.js";
|
|
11
10
|
/** Hold the current state of a query. */
|
|
@@ -20,16 +19,18 @@ export class QueryState extends State {
|
|
|
20
19
|
if (this.closed)
|
|
21
20
|
throw new ConditionError("State is closed");
|
|
22
21
|
if (!this.busy.value) {
|
|
22
|
+
this.busy.next(true);
|
|
23
23
|
try {
|
|
24
|
-
this.busy.next(true);
|
|
25
24
|
const result = await this.ref.value;
|
|
26
25
|
this._hasMore = result.length < this.limit;
|
|
27
|
-
this.busy.next(false);
|
|
28
26
|
this.next(result);
|
|
29
27
|
}
|
|
30
28
|
catch (thrown) {
|
|
31
29
|
this.error(thrown);
|
|
32
30
|
}
|
|
31
|
+
finally {
|
|
32
|
+
this.busy.next(false);
|
|
33
|
+
}
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
36
|
/**
|
|
@@ -40,27 +41,29 @@ export class QueryState extends State {
|
|
|
40
41
|
if (this.closed)
|
|
41
42
|
throw new ConditionError("State is closed");
|
|
42
43
|
if (!this.busy.value) {
|
|
44
|
+
this.busy.next(true);
|
|
43
45
|
try {
|
|
44
|
-
this.busy.next(true);
|
|
45
46
|
const items = await this.ref.after(this.lastData).value;
|
|
46
47
|
this.next([...this.value, ...items]);
|
|
47
48
|
this._hasMore = items.length < this.limit;
|
|
48
|
-
this.busy.next(false);
|
|
49
49
|
}
|
|
50
50
|
catch (thrown) {
|
|
51
51
|
this.error(thrown);
|
|
52
52
|
}
|
|
53
|
+
finally {
|
|
54
|
+
this.busy.next(false);
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
57
|
};
|
|
55
58
|
this._table = findSourceProvider(ref.db.provider, CacheProvider).memory.getTable(ref);
|
|
56
59
|
this.ref = ref;
|
|
57
60
|
this.limit = (_a = ref.limit) !== null && _a !== void 0 ? _a : Infinity;
|
|
58
61
|
// If the result is cached use it as the initial value.
|
|
59
|
-
const isCached = this._table.getQueryTime(ref)
|
|
62
|
+
const isCached = typeof this._table.getQueryTime(ref) === "number";
|
|
60
63
|
if (isCached)
|
|
61
64
|
this.next(this._table.getQuery(ref)); // Use the existing cached value.
|
|
62
65
|
else
|
|
63
|
-
|
|
66
|
+
void this.refresh(); // Queue a request to refresh the value.
|
|
64
67
|
}
|
|
65
68
|
/** Time this state was last updated with a new value. */
|
|
66
69
|
get time() {
|
|
@@ -91,10 +94,18 @@ export class QueryState extends State {
|
|
|
91
94
|
get lastData() {
|
|
92
95
|
return getQueryFirstData(this.value, this.ref);
|
|
93
96
|
}
|
|
97
|
+
/** Does the document have at least one result. */
|
|
98
|
+
get exists() {
|
|
99
|
+
return !!this.value.length;
|
|
100
|
+
}
|
|
101
|
+
/** Get the number of items matching this query. */
|
|
102
|
+
get count() {
|
|
103
|
+
return this.value.length;
|
|
104
|
+
}
|
|
94
105
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
95
106
|
refreshStale(maxAge) {
|
|
96
|
-
if (
|
|
97
|
-
|
|
107
|
+
if (this.age > maxAge)
|
|
108
|
+
void this.refresh();
|
|
98
109
|
}
|
|
99
110
|
/** Subscribe this state to the source provider. */
|
|
100
111
|
connectSource() {
|
|
@@ -117,7 +128,5 @@ const _getQueryState = (previous, ref) => !ref ? undefined : previous && isSameR
|
|
|
117
128
|
export function useQuery(ref) {
|
|
118
129
|
const state = useReduce(_getQueryState, ref);
|
|
119
130
|
useSubscribe(state);
|
|
120
|
-
if (state && !state.exists)
|
|
121
|
-
dispatch(state.refresh); // Load the query if it isn't cached already.
|
|
122
131
|
return state;
|
|
123
132
|
}
|
package/react/useSubscribe.js
CHANGED
|
@@ -11,6 +11,6 @@ import { BLACKHOLE } from "../util/function.js";
|
|
|
11
11
|
* - Memoise this value to persist the subscription for the lifetime of the component.
|
|
12
12
|
*/
|
|
13
13
|
export function useSubscribe(subscribable) {
|
|
14
|
-
const setState = useState(subscribable instanceof State && subscribable.
|
|
14
|
+
const setState = useState(subscribable instanceof State && !subscribable.loading ? subscribable.value : NOVALUE)[1];
|
|
15
15
|
useEffect(subscribable ? () => subscribe(subscribable, { next: setState, error: setState }) : BLACKHOLE, [subscribable]);
|
|
16
16
|
}
|
package/state/ArrayState.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { State } from "./State.js";
|
|
|
4
4
|
export declare class ArrayState<T> extends State<ImmutableArray<T>> implements Iterable<T> {
|
|
5
5
|
constructor(initial?: ImmutableArray<T>);
|
|
6
6
|
/** Get the length of the current value of this state. */
|
|
7
|
-
get
|
|
7
|
+
get count(): number;
|
|
8
8
|
/** Add an item to this array. */
|
|
9
9
|
add(item: T): void;
|
|
10
10
|
/** Remove an item from this array. */
|
package/state/ArrayState.js
CHANGED
package/state/DataState.d.ts
CHANGED
|
@@ -12,10 +12,10 @@ export declare class DataState<T extends Data> extends State<T> {
|
|
|
12
12
|
}
|
|
13
13
|
/** State that stores an optional data object and has additional methods to help with that. */
|
|
14
14
|
export declare class OptionalDataState<T extends Data> extends State<OptionalData<T>> {
|
|
15
|
-
/** Get the result value of this state. */
|
|
16
|
-
get result(): OptionalData<T>;
|
|
17
15
|
/** Get current data value of this state (or throw `Promise` that resolves to the next required value). */
|
|
18
16
|
get data(): T;
|
|
17
|
+
/** Does the data exist or not? */
|
|
18
|
+
get exists(): boolean;
|
|
19
19
|
/** Set a prop in this object to a new value. */
|
|
20
20
|
set<K extends Key<T>>(key: K, value: T[K]): void;
|
|
21
21
|
/** Update several props in this object. */
|
package/state/DataState.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { getData, withProp } from "../util/data.js";
|
|
2
2
|
import { transformData } from "../util/transform.js";
|
|
3
|
-
import { awaitNext } from "../observe/util.js";
|
|
4
|
-
import { NOERROR } from "../util/constants.js";
|
|
5
3
|
import { State } from "./State.js";
|
|
6
4
|
/** State that stores a data object and has additional methods to help with that. */
|
|
7
5
|
export class DataState extends State {
|
|
@@ -20,18 +18,14 @@ export class DataState extends State {
|
|
|
20
18
|
}
|
|
21
19
|
/** State that stores an optional data object and has additional methods to help with that. */
|
|
22
20
|
export class OptionalDataState extends State {
|
|
23
|
-
/** Get the result value of this state. */
|
|
24
|
-
get result() {
|
|
25
|
-
return this.value;
|
|
26
|
-
}
|
|
27
21
|
/** Get current data value of this state (or throw `Promise` that resolves to the next required value). */
|
|
28
22
|
get data() {
|
|
29
|
-
if (this.reason !== NOERROR)
|
|
30
|
-
throw this.reason;
|
|
31
|
-
if (!this.exists)
|
|
32
|
-
throw awaitNext(this).then(getData);
|
|
33
23
|
return getData(this.value);
|
|
34
24
|
}
|
|
25
|
+
/** Does the data exist or not? */
|
|
26
|
+
get exists() {
|
|
27
|
+
return !!this.value;
|
|
28
|
+
}
|
|
35
29
|
/** Set a prop in this object to a new value. */
|
|
36
30
|
set(key, value) {
|
|
37
31
|
this.next(withProp(this.data, key, value));
|
package/state/ObjectState.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { State } from "./State.js";
|
|
|
5
5
|
export declare class ObjectState<T> extends State<ImmutableObject<T>> implements Iterable<Entry<T>> {
|
|
6
6
|
constructor(initial?: ImmutableObject<T>);
|
|
7
7
|
/** Get the length of the current value of this state. */
|
|
8
|
-
get
|
|
8
|
+
get count(): number;
|
|
9
9
|
/** Remove a named entry from this object. */
|
|
10
10
|
delete(key: string): void;
|
|
11
11
|
/** Set a named entry in this object with a different value. */
|
package/state/ObjectState.js
CHANGED
package/state/State.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare class State<T> extends Subject<T> implements Matchable<T, void> {
|
|
|
23
23
|
/** State is initiated with an initial state. */
|
|
24
24
|
constructor(...args: [] | [T]);
|
|
25
25
|
/** Is there a current value, or is it still loading. */
|
|
26
|
-
get
|
|
26
|
+
get loading(): boolean;
|
|
27
27
|
next(value: T): void;
|
|
28
28
|
error(reason: Error | unknown): void;
|
|
29
29
|
protected _addObserver(observer: Observer<T>): void;
|
package/state/State.js
CHANGED
|
@@ -30,8 +30,8 @@ export class State extends Subject {
|
|
|
30
30
|
return this._value;
|
|
31
31
|
}
|
|
32
32
|
/** Is there a current value, or is it still loading. */
|
|
33
|
-
get
|
|
34
|
-
return this._value
|
|
33
|
+
get loading() {
|
|
34
|
+
return this._value === NOVALUE;
|
|
35
35
|
}
|
|
36
36
|
// Override to only dispatch if the value changes.
|
|
37
37
|
next(value) {
|