shelving 1.68.0 → 1.68.3
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/feedback/Feedback.js +2 -2
- package/firestore/client/FirestoreClientProvider.d.ts +1 -1
- package/firestore/client/FirestoreClientProvider.js +7 -3
- package/firestore/lite/FirestoreLiteProvider.js +6 -2
- package/firestore/server/FirestoreServerProvider.d.ts +1 -1
- package/firestore/server/FirestoreServerProvider.js +7 -3
- package/index.js +3 -0
- package/package.json +3 -2
- package/provider/ThroughProvider.d.ts +4 -2
- package/provider/ThroughProvider.js +11 -3
- package/provider/ValidationProvider.js +2 -3
- package/react/index.d.ts +1 -0
- package/react/index.js +1 -0
- package/react/useCache.d.ts +23 -3
- package/react/useCache.js +23 -3
- package/react/useDocument.d.ts +6 -2
- package/react/useDocument.js +23 -21
- package/react/useQuery.d.ts +8 -3
- package/react/useQuery.js +41 -37
- package/schema/AllowSchema.js +4 -4
- package/schema/ArraySchema.js +3 -3
- package/schema/DateSchema.js +4 -4
- package/schema/NumberSchema.js +2 -2
- package/schema/SlugSchema.js +2 -2
- package/schema/ThroughSchema.d.ts +4 -2
- package/schema/ThroughSchema.js +10 -4
- package/update/ArrayUpdate.d.ts +2 -1
- package/update/ArrayUpdate.js +22 -3
- package/update/DataUpdate.d.ts +3 -2
- package/update/DataUpdate.js +42 -2
- package/update/Delete.d.ts +11 -0
- package/update/Delete.js +15 -0
- package/update/Increment.d.ts +5 -5
- package/update/Increment.js +5 -5
- package/update/ObjectUpdate.d.ts +8 -8
- package/update/ObjectUpdate.js +38 -18
- package/update/Update.d.ts +4 -1
- package/update/Update.js +6 -0
- package/update/hydrations.js +3 -1
- package/update/index.d.ts +2 -2
- package/update/index.js +2 -2
- package/util/color.d.ts +6 -2
- package/util/color.js +17 -11
- package/util/constants.d.ts +0 -2
- package/util/constants.js +0 -2
- package/util/data.d.ts +6 -6
- package/util/data.js +3 -3
- package/util/date.d.ts +5 -5
- package/util/date.js +19 -19
- package/util/number.d.ts +3 -3
- package/util/number.js +5 -5
- package/util/search.js +2 -2
- package/util/string.d.ts +4 -4
- package/util/string.js +7 -7
- package/util/transform.js +2 -2
- package/util/validate.d.ts +9 -0
- package/util/validate.js +13 -2
- package/update/util.d.ts +0 -7
- package/update/util.js +0 -44
package/feedback/Feedback.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { debug } from "../util/debug.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getTitle } from "../util/string.js";
|
|
3
3
|
/**
|
|
4
4
|
* The `Feedback` class represents a feedback message that should be shown to the user.
|
|
5
5
|
* - Basic `Feedback` is neither good nor bad, `SuccessFeedback` indicates good news, and `ErrorFeedback` indicates bad news.
|
|
@@ -27,7 +27,7 @@ export class Feedback {
|
|
|
27
27
|
if (v instanceof Feedback)
|
|
28
28
|
messages[k] = v.feedback;
|
|
29
29
|
else
|
|
30
|
-
messages[k] =
|
|
30
|
+
messages[k] = getTitle(v);
|
|
31
31
|
}
|
|
32
32
|
return messages;
|
|
33
33
|
}
|
|
@@ -2,9 +2,9 @@ import type { Firestore } from "firebase/firestore";
|
|
|
2
2
|
import type { DocumentReference, QueryReference } from "../../db/Reference.js";
|
|
3
3
|
import type { Data, Entities, OptionalEntity } from "../../util/data.js";
|
|
4
4
|
import type { Unsubscribe } from "../../observe/Observable.js";
|
|
5
|
+
import { Observer } from "../../observe/Observer.js";
|
|
5
6
|
import { AsynchronousProvider, Provider } from "../../provider/Provider.js";
|
|
6
7
|
import { DataUpdate } from "../../update/DataUpdate.js";
|
|
7
|
-
import { Observer } from "../../observe/Observer.js";
|
|
8
8
|
/**
|
|
9
9
|
* Firestore client database provider.
|
|
10
10
|
* - Works with the Firebase JS SDK.
|
|
@@ -1,11 +1,13 @@
|
|
|
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 { dispatchError, dispatchNext } from "../../observe/Observer.js";
|
|
2
3
|
import { UnsupportedError } from "../../error/UnsupportedError.js";
|
|
3
4
|
import { Provider } from "../../provider/Provider.js";
|
|
4
5
|
import { ArrayUpdate } from "../../update/ArrayUpdate.js";
|
|
5
6
|
import { DataUpdate } from "../../update/DataUpdate.js";
|
|
6
7
|
import { Increment } from "../../update/Increment.js";
|
|
7
8
|
import { ObjectUpdate } from "../../update/ObjectUpdate.js";
|
|
8
|
-
import {
|
|
9
|
+
import { Delete } from "../../update/Delete.js";
|
|
10
|
+
import { Update } from "../../update/Update.js";
|
|
9
11
|
// Constants.
|
|
10
12
|
// const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
|
|
11
13
|
const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
|
|
@@ -60,7 +62,9 @@ function getOptionalEntity(snapshot) {
|
|
|
60
62
|
/** Convert `Update` instances into corresponding Firestore `FieldValue` instances. */
|
|
61
63
|
function* yieldFieldValues(updates, prefix = "") {
|
|
62
64
|
for (const [key, update] of updates) {
|
|
63
|
-
if (update
|
|
65
|
+
if (!(update instanceof Update))
|
|
66
|
+
yield [`${prefix}${key}`, update];
|
|
67
|
+
else if (update instanceof Delete)
|
|
64
68
|
yield [`${prefix}${key}`, firestoreDeleteField()];
|
|
65
69
|
else if (update instanceof Increment)
|
|
66
70
|
yield [`${prefix}${key}`, firestoreIncrement(update.amount)];
|
|
@@ -75,7 +79,7 @@ function* yieldFieldValues(updates, prefix = "") {
|
|
|
75
79
|
yield [`${prefix}${key}`, firestoreArrayRemove(...update.deletes)];
|
|
76
80
|
}
|
|
77
81
|
else
|
|
78
|
-
yield [`${prefix}${key}`, update];
|
|
82
|
+
yield [`${prefix}${key}`, update.transform()];
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
/**
|
|
@@ -5,6 +5,8 @@ import { ArrayUpdate } from "../../update/ArrayUpdate.js";
|
|
|
5
5
|
import { DataUpdate } from "../../update/DataUpdate.js";
|
|
6
6
|
import { Increment } from "../../update/Increment.js";
|
|
7
7
|
import { ObjectUpdate } from "../../update/ObjectUpdate.js";
|
|
8
|
+
import { Update } from "../../update/Update.js";
|
|
9
|
+
import { Delete } from "../../update/Delete.js";
|
|
8
10
|
// Constants.
|
|
9
11
|
// const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
|
|
10
12
|
const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
|
|
@@ -59,7 +61,9 @@ function getOptionalData(snapshot) {
|
|
|
59
61
|
/** Convert `Update` instances into corresponding Firestore `FieldValue` instances. */
|
|
60
62
|
function* yieldFieldValues(updates, prefix = "") {
|
|
61
63
|
for (const [key, update] of updates) {
|
|
62
|
-
if (update
|
|
64
|
+
if (!(update instanceof Update))
|
|
65
|
+
yield [`${prefix}${key}`, update];
|
|
66
|
+
else if (update instanceof Delete)
|
|
63
67
|
yield [`${prefix}${key}`, firestoreDeleteField()];
|
|
64
68
|
else if (update instanceof Increment)
|
|
65
69
|
yield [`${prefix}${key}`, firestoreIncrement(update.amount)];
|
|
@@ -74,7 +78,7 @@ function* yieldFieldValues(updates, prefix = "") {
|
|
|
74
78
|
yield [`${prefix}${key}`, firestoreArrayRemove(...update.deletes)];
|
|
75
79
|
}
|
|
76
80
|
else
|
|
77
|
-
yield [`${prefix}${key}`, update];
|
|
81
|
+
yield [`${prefix}${key}`, update.transform()];
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
/**
|
|
@@ -2,9 +2,9 @@ import { Firestore } from "@google-cloud/firestore";
|
|
|
2
2
|
import type { DocumentReference, QueryReference } from "../../db/Reference.js";
|
|
3
3
|
import type { Data, Entities, OptionalEntity } from "../../util/data.js";
|
|
4
4
|
import type { Unsubscribe } from "../../observe/Observable.js";
|
|
5
|
+
import { Observer } from "../../observe/Observer.js";
|
|
5
6
|
import { AsynchronousProvider, Provider } from "../../provider/Provider.js";
|
|
6
7
|
import { DataUpdate } from "../../update/DataUpdate.js";
|
|
7
|
-
import { Observer } from "../../observe/Observer.js";
|
|
8
8
|
/**
|
|
9
9
|
* Firestore server database provider.
|
|
10
10
|
* - Works with the Firebase Admin SDK for Node.JS
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Firestore, FieldValue } from "@google-cloud/firestore";
|
|
2
|
+
import { dispatchError, dispatchNext } from "../../observe/Observer.js";
|
|
2
3
|
import { UnsupportedError } from "../../error/UnsupportedError.js";
|
|
3
4
|
import { Provider } from "../../provider/Provider.js";
|
|
4
5
|
import { ArrayUpdate } from "../../update/ArrayUpdate.js";
|
|
5
6
|
import { DataUpdate } from "../../update/DataUpdate.js";
|
|
6
7
|
import { Increment } from "../../update/Increment.js";
|
|
7
8
|
import { ObjectUpdate } from "../../update/ObjectUpdate.js";
|
|
8
|
-
import {
|
|
9
|
+
import { Delete } from "../../update/Delete.js";
|
|
10
|
+
import { Update } from "../../update/Update.js";
|
|
9
11
|
// Constants.
|
|
10
12
|
// const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
|
|
11
13
|
const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
|
|
@@ -60,7 +62,9 @@ function getOptionalEntity(snapshot) {
|
|
|
60
62
|
/** Convert `Update` instances into corresponding Firestore `FieldValue` instances. */
|
|
61
63
|
function* yieldFieldValues(updates, prefix = "") {
|
|
62
64
|
for (const [key, update] of updates) {
|
|
63
|
-
if (update
|
|
65
|
+
if (!(update instanceof Update))
|
|
66
|
+
yield [`${prefix}${key}`, update];
|
|
67
|
+
else if (update instanceof Delete)
|
|
64
68
|
yield [`${prefix}${key}`, FieldValue.delete()];
|
|
65
69
|
else if (update instanceof Increment)
|
|
66
70
|
yield [`${prefix}${key}`, FieldValue.increment(update.amount)];
|
|
@@ -75,7 +79,7 @@ function* yieldFieldValues(updates, prefix = "") {
|
|
|
75
79
|
yield [`${prefix}${key}`, FieldValue.arrayRemove(...update.deletes)];
|
|
76
80
|
}
|
|
77
81
|
else
|
|
78
|
-
yield [`${prefix}${key}`, update];
|
|
82
|
+
yield [`${prefix}${key}`, update.transform()];
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
/**
|
package/index.js
CHANGED
|
@@ -19,5 +19,8 @@ export * from "./update/index.js";
|
|
|
19
19
|
export * from "./util/index.js";
|
|
20
20
|
// Integrations.
|
|
21
21
|
// export * from "./react/index.js"; // Not exported.
|
|
22
|
+
// export * from "./firestore/client/index.js"; // Not exported.
|
|
23
|
+
// export * from "./firestore/lite/index.js"; // Not exported.
|
|
24
|
+
// export * from "./firestore/server/index.js"; // Not exported.
|
|
22
25
|
// Testing.
|
|
23
26
|
// export * from "./test/index.js"; // Not exported.
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"state-management",
|
|
12
12
|
"query-builder"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.68.
|
|
14
|
+
"version": "1.68.3",
|
|
15
15
|
"repository": "https://github.com/dhoulb/shelving",
|
|
16
16
|
"author": "Dave Houlbrooke <dave@shax.com>",
|
|
17
17
|
"license": "0BSD",
|
|
@@ -29,11 +29,12 @@
|
|
|
29
29
|
"./firestore/lite": "./firestore/lite/index.js",
|
|
30
30
|
"./firestore/server": "./firestore/server/index.js",
|
|
31
31
|
"./markup": "./markup/index.js",
|
|
32
|
+
"./observe": "./observe/index.js",
|
|
32
33
|
"./provider": "./provider/index.js",
|
|
33
34
|
"./query": "./query/index.js",
|
|
34
35
|
"./react": "./react/index.js",
|
|
35
36
|
"./schema": "./schema/index.js",
|
|
36
|
-
"./
|
|
37
|
+
"./state": "./state/index.js",
|
|
37
38
|
"./test": "./test/index.js",
|
|
38
39
|
"./update": "./update/index.js",
|
|
39
40
|
"./util": "./util/index.js"
|
|
@@ -31,5 +31,7 @@ export interface SynchronousThroughProvider extends SynchronousProvider {
|
|
|
31
31
|
export interface AsynchronousThroughProvider extends AsynchronousProvider {
|
|
32
32
|
new (source: AsynchronousProvider): AsynchronousProvider;
|
|
33
33
|
}
|
|
34
|
-
/** Find a
|
|
35
|
-
export declare function
|
|
34
|
+
/** Find a possible source provider in a database's provider stack (if it exists). */
|
|
35
|
+
export declare function getOptionalSourceProvider<P extends Provider>(provider: Provider, type: Class<P>): P | undefined;
|
|
36
|
+
/** Find a source provider in a database's provider stack. */
|
|
37
|
+
export declare function getSourceProvider<P extends Provider>(provider: Provider, type: Class<P>): P;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AssertionError } from "../error/AssertionError.js";
|
|
1
2
|
import { Provider } from "./Provider.js";
|
|
2
3
|
/**
|
|
3
4
|
* Pass all reads and writes through to a source provider.
|
|
@@ -41,10 +42,17 @@ export class ThroughProvider extends Provider {
|
|
|
41
42
|
return this.source.deleteQuery(ref);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
/** Find a
|
|
45
|
-
export function
|
|
45
|
+
/** Find a possible source provider in a database's provider stack (if it exists). */
|
|
46
|
+
export function getOptionalSourceProvider(provider, type) {
|
|
46
47
|
if (provider instanceof type)
|
|
47
48
|
return provider;
|
|
48
49
|
if (provider instanceof ThroughProvider)
|
|
49
|
-
return
|
|
50
|
+
return getSourceProvider(provider.source, type);
|
|
51
|
+
}
|
|
52
|
+
/** Find a source provider in a database's provider stack. */
|
|
53
|
+
export function getSourceProvider(provider, type) {
|
|
54
|
+
const source = getOptionalSourceProvider(provider, type);
|
|
55
|
+
if (!source)
|
|
56
|
+
throw new AssertionError(`Source provider ${type.name} not found`, provider);
|
|
57
|
+
return source;
|
|
50
58
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { callAsync } from "../util/async.js";
|
|
2
2
|
import { validate } from "../util/validate.js";
|
|
3
|
-
import { validateUpdate } from "../update/util.js";
|
|
4
3
|
import { Feedback } from "../feedback/Feedback.js";
|
|
5
4
|
import { ValidationError } from "../error/ValidationError.js";
|
|
6
5
|
import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
|
|
@@ -21,7 +20,7 @@ export class ValidationProvider extends ThroughProvider {
|
|
|
21
20
|
return super.setDocument(ref, validate(value, ref.validator));
|
|
22
21
|
}
|
|
23
22
|
updateDocument(ref, update) {
|
|
24
|
-
return super.updateDocument(ref,
|
|
23
|
+
return super.updateDocument(ref, update.validate(ref.validator));
|
|
25
24
|
}
|
|
26
25
|
getQuery(ref) {
|
|
27
26
|
return callAsync(_validateEntities, super.getQuery(ref), ref);
|
|
@@ -33,7 +32,7 @@ export class ValidationProvider extends ThroughProvider {
|
|
|
33
32
|
return super.setQuery(ref, validate(value, ref.validator));
|
|
34
33
|
}
|
|
35
34
|
updateQuery(ref, update) {
|
|
36
|
-
return super.updateQuery(ref,
|
|
35
|
+
return super.updateQuery(ref, update.validate(ref.validator));
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
/** Validate an entity for a document reference. */
|
package/react/index.d.ts
CHANGED
package/react/index.js
CHANGED
package/react/useCache.d.ts
CHANGED
|
@@ -15,12 +15,32 @@ export declare class CacheController<T> {
|
|
|
15
15
|
/**
|
|
16
16
|
* Default cache
|
|
17
17
|
* - This is a flexible generic cache intended to be the default.
|
|
18
|
-
* - Use this cache unless
|
|
18
|
+
* - Use this cache unless you want to cache a completely independent set of items without interference.
|
|
19
19
|
*/
|
|
20
20
|
export declare const CACHE: CacheController<any>;
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Use a global cache in a component.
|
|
23
|
+
* - Throws an error if used outside of `<Cache>`
|
|
24
|
+
*/
|
|
22
25
|
export declare const useCache: () => Map<string, any>;
|
|
23
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Component that provides a global cache to its children.
|
|
28
|
+
*
|
|
29
|
+
* Note: If mounted globally this cache will bloat over time, so you need a strategy to clear or reset the cache occasionally.
|
|
30
|
+
*
|
|
31
|
+
* A good strategy is to wrap a separate `<Cache>` around each page of your app.
|
|
32
|
+
* This means the cache can only grow to the size of each page and the memory is released when the user navigates to a new page.
|
|
33
|
+
* You might need to use `<Cache key="something unique to the page">` to ensure the cache component is destroyed and remounted for each page.
|
|
34
|
+
*
|
|
35
|
+
* Put a `<Suspense>` boundary _inside_ `<Cache>`
|
|
36
|
+
* - This prevents promises being thrown up through the cache causing it to be destroyed.
|
|
37
|
+
* - When the promise resolves and the render is tried again the data would not exist (because the cache was destroyed).
|
|
38
|
+
* - This will cause an infinite loading loop.
|
|
39
|
+
*
|
|
40
|
+
* Put your error boundary _outside_ your `<Cache>`
|
|
41
|
+
* - The error being thrown up through the cache causes it to be destroyed.
|
|
42
|
+
* - This means when the uses tells the error boundary to try again (if supported) all data on the page will be retried.
|
|
43
|
+
*/
|
|
24
44
|
export declare const Cache: ({ children }: {
|
|
25
45
|
children: React.ReactNode;
|
|
26
46
|
}) => React.ReactElement | null;
|
package/react/useCache.js
CHANGED
|
@@ -27,10 +27,30 @@ const _reduceMap = (previous) => previous || new Map();
|
|
|
27
27
|
/**
|
|
28
28
|
* Default cache
|
|
29
29
|
* - This is a flexible generic cache intended to be the default.
|
|
30
|
-
* - Use this cache unless
|
|
30
|
+
* - Use this cache unless you want to cache a completely independent set of items without interference.
|
|
31
31
|
*/
|
|
32
32
|
export const CACHE = new CacheController(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Use a global cache in a component.
|
|
35
|
+
* - Throws an error if used outside of `<Cache>`
|
|
36
|
+
*/
|
|
34
37
|
export const useCache = CACHE.useCache;
|
|
35
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Component that provides a global cache to its children.
|
|
40
|
+
*
|
|
41
|
+
* Note: If mounted globally this cache will bloat over time, so you need a strategy to clear or reset the cache occasionally.
|
|
42
|
+
*
|
|
43
|
+
* A good strategy is to wrap a separate `<Cache>` around each page of your app.
|
|
44
|
+
* This means the cache can only grow to the size of each page and the memory is released when the user navigates to a new page.
|
|
45
|
+
* You might need to use `<Cache key="something unique to the page">` to ensure the cache component is destroyed and remounted for each page.
|
|
46
|
+
*
|
|
47
|
+
* Put a `<Suspense>` boundary _inside_ `<Cache>`
|
|
48
|
+
* - This prevents promises being thrown up through the cache causing it to be destroyed.
|
|
49
|
+
* - When the promise resolves and the render is tried again the data would not exist (because the cache was destroyed).
|
|
50
|
+
* - This will cause an infinite loading loop.
|
|
51
|
+
*
|
|
52
|
+
* Put your error boundary _outside_ your `<Cache>`
|
|
53
|
+
* - The error being thrown up through the cache causes it to be destroyed.
|
|
54
|
+
* - This means when the uses tells the error boundary to try again (if supported) all data on the page will be retried.
|
|
55
|
+
*/
|
|
36
56
|
export const Cache = CACHE.Cache;
|
package/react/useDocument.d.ts
CHANGED
|
@@ -13,7 +13,8 @@ export declare class DocumentState<T extends Data> extends State<OptionalEntity<
|
|
|
13
13
|
get exists(): boolean;
|
|
14
14
|
constructor(ref: DocumentReference<T>);
|
|
15
15
|
/** Refresh this state from the source provider. */
|
|
16
|
-
readonly refresh: () =>
|
|
16
|
+
readonly refresh: () => void;
|
|
17
|
+
_refresh(): Promise<void>;
|
|
17
18
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
18
19
|
refreshStale(maxAge: number): void;
|
|
19
20
|
/** Subscribe this state to the source provider. */
|
|
@@ -23,6 +24,9 @@ export declare class DocumentState<T extends Data> extends State<OptionalEntity<
|
|
|
23
24
|
protected _addFirstObserver(): void;
|
|
24
25
|
protected _removeLastObserver(): void;
|
|
25
26
|
}
|
|
26
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Use a document in a React component.
|
|
29
|
+
* - Uses the default cache, so will error if not used inside `<Cache>`
|
|
30
|
+
*/
|
|
27
31
|
export declare function useDocument<T extends Data>(ref: DocumentReference<T>): DocumentState<T>;
|
|
28
32
|
export declare function useDocument<T extends Data>(ref?: DocumentReference<T>): DocumentState<T> | undefined;
|
package/react/useDocument.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { reduceMapItem } from "../util/map.js";
|
|
2
|
-
import { getDocumentData
|
|
2
|
+
import { getDocumentData } from "../db/Reference.js";
|
|
3
3
|
import { CacheProvider } from "../provider/CacheProvider.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getOptionalSourceProvider } from "../provider/ThroughProvider.js";
|
|
5
5
|
import { State } from "../state/State.js";
|
|
6
6
|
import { BooleanState } from "../state/BooleanState.js";
|
|
7
7
|
import { ConditionError } from "../error/ConditionError.js";
|
|
@@ -12,34 +12,23 @@ import { useCache } from "./useCache.js";
|
|
|
12
12
|
export class DocumentState extends State {
|
|
13
13
|
constructor(ref) {
|
|
14
14
|
var _a;
|
|
15
|
-
const table = (_a =
|
|
15
|
+
const table = (_a = getOptionalSourceProvider(ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(ref);
|
|
16
16
|
const time = table ? table.getDocumentTime(ref.id) : null;
|
|
17
17
|
const isCached = typeof time === "number";
|
|
18
18
|
super(table && isCached ? table.getDocument(ref.id) : NOVALUE);
|
|
19
19
|
this.busy = new BooleanState();
|
|
20
20
|
/** Refresh this state from the source provider. */
|
|
21
|
-
this.refresh =
|
|
21
|
+
this.refresh = () => {
|
|
22
22
|
if (this.closed)
|
|
23
23
|
throw new ConditionError("State is closed");
|
|
24
|
-
if (!this.busy.value)
|
|
25
|
-
this.
|
|
26
|
-
try {
|
|
27
|
-
const result = await this.ref.value;
|
|
28
|
-
this.next(result);
|
|
29
|
-
}
|
|
30
|
-
catch (thrown) {
|
|
31
|
-
this.error(thrown);
|
|
32
|
-
}
|
|
33
|
-
finally {
|
|
34
|
-
this.busy.next(false);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
24
|
+
if (!this.busy.value)
|
|
25
|
+
void this._refresh();
|
|
37
26
|
};
|
|
38
27
|
this._time = time;
|
|
39
28
|
this.ref = ref;
|
|
40
29
|
// Queue a request to refresh the value if it doesn't exist.
|
|
41
30
|
if (this.loading)
|
|
42
|
-
|
|
31
|
+
this.refresh();
|
|
43
32
|
}
|
|
44
33
|
/** Get the data of the document (throws `RequiredError` if document doesn't exist). */
|
|
45
34
|
get data() {
|
|
@@ -49,10 +38,23 @@ export class DocumentState extends State {
|
|
|
49
38
|
get exists() {
|
|
50
39
|
return !!this.value;
|
|
51
40
|
}
|
|
41
|
+
async _refresh() {
|
|
42
|
+
this.busy.next(true);
|
|
43
|
+
try {
|
|
44
|
+
const result = await this.ref.value;
|
|
45
|
+
this.next(result);
|
|
46
|
+
}
|
|
47
|
+
catch (thrown) {
|
|
48
|
+
this.error(thrown);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
this.busy.next(false);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
52
54
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
53
55
|
refreshStale(maxAge) {
|
|
54
56
|
if (this.age > maxAge)
|
|
55
|
-
|
|
57
|
+
this.refresh();
|
|
56
58
|
}
|
|
57
59
|
/** Subscribe this state to the source provider. */
|
|
58
60
|
connectSource() {
|
|
@@ -61,7 +63,7 @@ export class DocumentState extends State {
|
|
|
61
63
|
/** Subscribe this state to any `CacheProvider` that exists in the provider chain. */
|
|
62
64
|
connectCache() {
|
|
63
65
|
var _a;
|
|
64
|
-
const table = (_a =
|
|
66
|
+
const table = (_a = getOptionalSourceProvider(this.ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(this.ref);
|
|
65
67
|
table && this.connect(() => table.subscribeCachedDocument(this.ref.id, this));
|
|
66
68
|
}
|
|
67
69
|
// Override to subscribe to the cache when an observer is added.
|
|
@@ -75,7 +77,7 @@ export class DocumentState extends State {
|
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
/** Reuse the previous `DocumentState` or create a new one. */
|
|
78
|
-
const _reduceDocumentState = (existing, ref) =>
|
|
80
|
+
const _reduceDocumentState = (existing, ref) => existing || new DocumentState(ref);
|
|
79
81
|
export function useDocument(ref) {
|
|
80
82
|
const cache = useCache();
|
|
81
83
|
const state = ref ? reduceMapItem(cache, ref.toString(), _reduceDocumentState, ref) : undefined;
|
package/react/useQuery.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ export declare class QueryState<T extends Data> extends State<Entities<T>> {
|
|
|
25
25
|
get count(): number;
|
|
26
26
|
constructor(ref: QueryReference<T>);
|
|
27
27
|
/** Refresh this state from the source provider. */
|
|
28
|
-
readonly refresh: () =>
|
|
28
|
+
readonly refresh: () => void;
|
|
29
|
+
_refresh(): Promise<void>;
|
|
29
30
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
30
31
|
refreshStale(maxAge: number): void;
|
|
31
32
|
/** Subscribe this state to the source provider. */
|
|
@@ -38,8 +39,12 @@ export declare class QueryState<T extends Data> extends State<Entities<T>> {
|
|
|
38
39
|
* Load more items after the last once.
|
|
39
40
|
* - Promise that needs to be handled.
|
|
40
41
|
*/
|
|
41
|
-
readonly loadMore: () =>
|
|
42
|
+
readonly loadMore: () => void;
|
|
43
|
+
_loadMore(): Promise<void>;
|
|
42
44
|
}
|
|
43
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Use a query in a React component.
|
|
47
|
+
* - Uses the default cache, so will error if not used inside `<Cache>`
|
|
48
|
+
*/
|
|
44
49
|
export declare function useQuery<T extends Data>(ref: QueryReference<T>): QueryState<T>;
|
|
45
50
|
export declare function useQuery<T extends Data>(ref?: QueryReference<T>): QueryState<T> | undefined;
|
package/react/useQuery.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { reduceMapItem } from "../util/map.js";
|
|
2
|
-
import { getQueryFirstData, getQueryFirstValue
|
|
2
|
+
import { getQueryFirstData, getQueryFirstValue } from "../db/Reference.js";
|
|
3
3
|
import { CacheProvider } from "../provider/CacheProvider.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getOptionalSourceProvider } from "../provider/ThroughProvider.js";
|
|
5
5
|
import { State } from "../state/State.js";
|
|
6
6
|
import { ConditionError } from "../error/ConditionError.js";
|
|
7
7
|
import { BooleanState } from "../state/BooleanState.js";
|
|
@@ -12,59 +12,35 @@ import { useCache } from "./useCache.js";
|
|
|
12
12
|
export class QueryState extends State {
|
|
13
13
|
constructor(ref) {
|
|
14
14
|
var _a, _b;
|
|
15
|
-
const table = (_a =
|
|
15
|
+
const table = (_a = getOptionalSourceProvider(ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(ref);
|
|
16
16
|
const time = table ? table.getQueryTime(ref) : null;
|
|
17
17
|
const isCached = typeof time === "number";
|
|
18
18
|
super(table && isCached ? table.getQuery(ref) : NOVALUE);
|
|
19
19
|
this.busy = new BooleanState();
|
|
20
20
|
this._hasMore = false;
|
|
21
21
|
/** Refresh this state from the source provider. */
|
|
22
|
-
this.refresh =
|
|
22
|
+
this.refresh = () => {
|
|
23
23
|
if (this.closed)
|
|
24
24
|
throw new ConditionError("State is closed");
|
|
25
|
-
if (!this.busy.value)
|
|
26
|
-
this.
|
|
27
|
-
try {
|
|
28
|
-
const result = await this.ref.value;
|
|
29
|
-
this._hasMore = result.length < this.limit;
|
|
30
|
-
this.next(result);
|
|
31
|
-
}
|
|
32
|
-
catch (thrown) {
|
|
33
|
-
this.error(thrown);
|
|
34
|
-
}
|
|
35
|
-
finally {
|
|
36
|
-
this.busy.next(false);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
25
|
+
if (!this.busy.value)
|
|
26
|
+
void this._refresh();
|
|
39
27
|
};
|
|
40
28
|
/**
|
|
41
29
|
* Load more items after the last once.
|
|
42
30
|
* - Promise that needs to be handled.
|
|
43
31
|
*/
|
|
44
|
-
this.loadMore =
|
|
32
|
+
this.loadMore = () => {
|
|
45
33
|
if (this.closed)
|
|
46
34
|
throw new ConditionError("State is closed");
|
|
47
|
-
if (!this.busy.value)
|
|
48
|
-
this.
|
|
49
|
-
try {
|
|
50
|
-
const items = await this.ref.after(this.lastData).value;
|
|
51
|
-
this.next([...this.value, ...items]);
|
|
52
|
-
this._hasMore = items.length < this.limit;
|
|
53
|
-
}
|
|
54
|
-
catch (thrown) {
|
|
55
|
-
this.error(thrown);
|
|
56
|
-
}
|
|
57
|
-
finally {
|
|
58
|
-
this.busy.next(false);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
35
|
+
if (!this.busy.value)
|
|
36
|
+
void this._loadMore();
|
|
61
37
|
};
|
|
62
38
|
this._time = time;
|
|
63
39
|
this.ref = ref;
|
|
64
40
|
this.limit = (_b = ref.limit) !== null && _b !== void 0 ? _b : Infinity;
|
|
65
41
|
// Queue a request to refresh the value if it doesn't exist.
|
|
66
42
|
if (this.loading)
|
|
67
|
-
|
|
43
|
+
this.refresh();
|
|
68
44
|
}
|
|
69
45
|
/** Can more items be loaded after the current result. */
|
|
70
46
|
get hasMore() {
|
|
@@ -94,10 +70,24 @@ export class QueryState extends State {
|
|
|
94
70
|
get count() {
|
|
95
71
|
return this.value.length;
|
|
96
72
|
}
|
|
73
|
+
async _refresh() {
|
|
74
|
+
this.busy.next(true);
|
|
75
|
+
try {
|
|
76
|
+
const result = await this.ref.value;
|
|
77
|
+
this._hasMore = result.length < this.limit;
|
|
78
|
+
this.next(result);
|
|
79
|
+
}
|
|
80
|
+
catch (thrown) {
|
|
81
|
+
this.error(thrown);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
this.busy.next(false);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
97
87
|
/** Refresh this state if data in the cache is older than `maxAge` (in milliseconds). */
|
|
98
88
|
refreshStale(maxAge) {
|
|
99
89
|
if (this.age > maxAge)
|
|
100
|
-
|
|
90
|
+
this.refresh();
|
|
101
91
|
}
|
|
102
92
|
/** Subscribe this state to the source provider. */
|
|
103
93
|
connectSource() {
|
|
@@ -106,7 +96,7 @@ export class QueryState extends State {
|
|
|
106
96
|
/** Subscribe this state to any `CacheProvider` that exists in the provider chain. */
|
|
107
97
|
connectCache() {
|
|
108
98
|
var _a;
|
|
109
|
-
const table = (_a =
|
|
99
|
+
const table = (_a = getOptionalSourceProvider(this.ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(this.ref);
|
|
110
100
|
return table && this.connect(() => table.subscribeCachedQuery(this.ref, this));
|
|
111
101
|
}
|
|
112
102
|
// Override to subscribe to the cache when an observer is added.
|
|
@@ -118,9 +108,23 @@ export class QueryState extends State {
|
|
|
118
108
|
// Disconnect all sources.
|
|
119
109
|
this.disconnect();
|
|
120
110
|
}
|
|
111
|
+
async _loadMore() {
|
|
112
|
+
this.busy.next(true);
|
|
113
|
+
try {
|
|
114
|
+
const items = await this.ref.after(this.lastData).value;
|
|
115
|
+
this.next([...this.value, ...items]);
|
|
116
|
+
this._hasMore = items.length < this.limit;
|
|
117
|
+
}
|
|
118
|
+
catch (thrown) {
|
|
119
|
+
this.error(thrown);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
this.busy.next(false);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
121
125
|
}
|
|
122
126
|
/** Reuse the previous `QueryState` or create a new one. */
|
|
123
|
-
const _reduceQueryState = (existing, ref) =>
|
|
127
|
+
const _reduceQueryState = (existing, ref) => existing || new QueryState(ref);
|
|
124
128
|
export function useQuery(ref) {
|
|
125
129
|
const cache = useCache();
|
|
126
130
|
const state = ref ? reduceMapItem(cache, ref.toString(), _reduceQueryState, ref) : undefined;
|
package/schema/AllowSchema.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getString } from "../util/string.js";
|
|
3
3
|
import { isItem } from "../util/array.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getOptionalNumber } from "../util/number.js";
|
|
5
5
|
import { isKey } from "../util/object.js";
|
|
6
6
|
import { Schema } from "./Schema.js";
|
|
7
7
|
/** Validate a value against a specific set of allowed values. */
|
|
@@ -27,13 +27,13 @@ export class AllowSchema extends Schema {
|
|
|
27
27
|
/** Define a valid string from an allowed set of strings. */
|
|
28
28
|
export class AllowStringSchema extends AllowSchema {
|
|
29
29
|
validate(unsafeValue = this.value) {
|
|
30
|
-
return validateAllowed(
|
|
30
|
+
return validateAllowed(getString(unsafeValue), this.allow);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
/** Define a valid number from an allowed set of numbers. */
|
|
34
34
|
export class AllowNumberSchema extends AllowSchema {
|
|
35
35
|
validate(unsafeValue = this.value) {
|
|
36
|
-
return validateAllowed(
|
|
36
|
+
return validateAllowed(getOptionalNumber(unsafeValue), this.allow);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
/** Valid string from an allowed set of strings. */
|
package/schema/ArraySchema.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { uniqueArray } from "../util/array.js";
|
|
3
|
+
import { validateArray } from "../util/validate.js";
|
|
4
4
|
import { Schema } from "./Schema.js";
|
|
5
5
|
/**
|
|
6
6
|
* Define a valid array.
|
|
@@ -41,7 +41,7 @@ export class ArraySchema extends Schema {
|
|
|
41
41
|
validate(unsafeValue = this.value) {
|
|
42
42
|
if (!(unsafeValue instanceof Array))
|
|
43
43
|
throw new InvalidFeedback("Must be array", { value: unsafeValue });
|
|
44
|
-
const safeArray =
|
|
44
|
+
const safeArray = validateArray(unsafeValue, this.items);
|
|
45
45
|
const dedupedArray = this.unique ? uniqueArray(safeArray) : safeArray;
|
|
46
46
|
if (typeof this.min === "number" && dedupedArray.length < this.min)
|
|
47
47
|
throw new InvalidFeedback(dedupedArray.length ? `Minimum ${this.min} items` : "Required", { value: dedupedArray });
|