shelving 1.106.0 → 1.107.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/README.md +1 -1
- package/db/{ItemState.d.ts → ItemStore.d.ts} +10 -10
- package/db/{ItemState.js → ItemStore.js} +11 -11
- package/db/{QueryState.d.ts → QueryStore.d.ts} +8 -8
- package/db/{QueryState.js → QueryStore.js} +9 -9
- package/db/index.d.ts +2 -2
- package/db/index.js +2 -2
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +2 -2
- package/react/createDataContext.d.ts +6 -6
- package/react/createDataContext.js +5 -5
- package/react/index.d.ts +1 -1
- package/react/index.js +1 -1
- package/react/useSequence.d.ts +0 -1
- package/react/useSequence.js +0 -1
- package/react/useStore.d.ts +5 -0
- package/react/useStore.js +8 -0
- package/{state/ArrayState.d.ts → store/ArrayStore.d.ts} +4 -4
- package/{state/ArrayState.js → store/ArrayStore.js} +4 -4
- package/{state/BooleanState.d.ts → store/BooleanStore.d.ts} +3 -3
- package/{state/BooleanState.js → store/BooleanStore.js} +3 -3
- package/{state/DataState.d.ts → store/DataStore.d.ts} +9 -9
- package/{state/DataState.js → store/DataStore.js} +9 -9
- package/{state/DictionaryState.d.ts → store/DictionaryStore.d.ts} +4 -4
- package/{state/DictionaryState.js → store/DictionaryStore.js} +4 -4
- package/store/Store.d.ts +42 -0
- package/{state/State.js → store/Store.js} +19 -19
- package/store/index.d.ts +5 -0
- package/store/index.js +5 -0
- package/react/useState.d.ts +0 -4
- package/react/useState.js +0 -8
- package/state/State.d.ts +0 -42
- package/state/index.d.ts +0 -5
- package/state/index.js +0 -5
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- Schemas (validation)
|
|
10
10
|
- Databases (via providers including in-memory, Firestore, IndexedDB)
|
|
11
11
|
- Querying (sorting, filtering, slicing)
|
|
12
|
-
-
|
|
12
|
+
- Store (events and state)
|
|
13
13
|
- React (hooks and state)
|
|
14
14
|
- Helpers (errors, arrays, objects, strings, dates, equality, merging, diffing, cloning, debug)
|
|
15
15
|
|
|
@@ -2,27 +2,27 @@ import type { AbstractProvider } from "../provider/Provider.js";
|
|
|
2
2
|
import type { StopCallback } from "../util/callback.js";
|
|
3
3
|
import type { Data } from "../util/data.js";
|
|
4
4
|
import type { ItemData, ItemValue } from "../util/item.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
/**
|
|
8
|
-
export declare class
|
|
5
|
+
import { BooleanStore } from "../store/BooleanStore.js";
|
|
6
|
+
import { Store } from "../store/Store.js";
|
|
7
|
+
/** Store a single item. */
|
|
8
|
+
export declare class ItemStore<T extends Data = Data> extends Store<ItemValue<T>> {
|
|
9
9
|
readonly provider: AbstractProvider;
|
|
10
10
|
readonly collection: string;
|
|
11
11
|
readonly id: string;
|
|
12
|
-
readonly busy:
|
|
13
|
-
/** Get the data of this
|
|
12
|
+
readonly busy: BooleanStore;
|
|
13
|
+
/** Get the data of this store (throws `RequiredError` if item doesn't exist). */
|
|
14
14
|
get data(): ItemData<T>;
|
|
15
|
-
/** Set the data of this
|
|
15
|
+
/** Set the data of this store. */
|
|
16
16
|
set data(data: T | ItemData<T>);
|
|
17
17
|
/** Does the item exist? */
|
|
18
18
|
get exists(): boolean;
|
|
19
19
|
constructor(provider: AbstractProvider, collection: string, id: string);
|
|
20
|
-
/** Refresh this
|
|
20
|
+
/** Refresh this store from the source provider. */
|
|
21
21
|
refresh(provider?: AbstractProvider): void;
|
|
22
22
|
private _refresh;
|
|
23
|
-
/** Refresh this
|
|
23
|
+
/** Refresh this store if data in the cache is older than `maxAge` (in milliseconds). */
|
|
24
24
|
refreshStale(maxAge: number, provider?: AbstractProvider): void;
|
|
25
|
-
/** Subscribe this
|
|
25
|
+
/** Subscribe this store to a provider. */
|
|
26
26
|
connect(provider?: AbstractProvider): StopCallback;
|
|
27
27
|
[Symbol.asyncIterator](): AsyncGenerator<ItemValue<T>, void, void>;
|
|
28
28
|
private _iterating;
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { CacheProvider } from "../provider/CacheProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BooleanStore } from "../store/BooleanStore.js";
|
|
3
|
+
import { Store } from "../store/Store.js";
|
|
4
4
|
import { call } from "../util/callback.js";
|
|
5
5
|
import { NONE } from "../util/constants.js";
|
|
6
6
|
import { getItemData } from "../util/item.js";
|
|
7
7
|
import { getRequired } from "../util/optional.js";
|
|
8
8
|
import { runSequence } from "../util/sequence.js";
|
|
9
9
|
import { getOptionalSource } from "../util/source.js";
|
|
10
|
-
/**
|
|
11
|
-
export class
|
|
10
|
+
/** Store a single item. */
|
|
11
|
+
export class ItemStore extends Store {
|
|
12
12
|
provider;
|
|
13
13
|
collection;
|
|
14
14
|
id;
|
|
15
|
-
busy = new
|
|
16
|
-
/** Get the data of this
|
|
15
|
+
busy = new BooleanStore();
|
|
16
|
+
/** Get the data of this store (throws `RequiredError` if item doesn't exist). */
|
|
17
17
|
get data() {
|
|
18
18
|
return getRequired(this.value);
|
|
19
19
|
}
|
|
20
|
-
/** Set the data of this
|
|
20
|
+
/** Set the data of this store. */
|
|
21
21
|
set data(data) {
|
|
22
22
|
this.value = getItemData(this.id, data);
|
|
23
23
|
}
|
|
@@ -36,7 +36,7 @@ export class ItemState extends State {
|
|
|
36
36
|
if (this.loading)
|
|
37
37
|
this.refresh();
|
|
38
38
|
}
|
|
39
|
-
/** Refresh this
|
|
39
|
+
/** Refresh this store from the source provider. */
|
|
40
40
|
refresh(provider = this.provider) {
|
|
41
41
|
if (!this.busy.value)
|
|
42
42
|
void this._refresh(provider);
|
|
@@ -54,16 +54,16 @@ export class ItemState extends State {
|
|
|
54
54
|
this.busy.value = false;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
/** Refresh this
|
|
57
|
+
/** Refresh this store if data in the cache is older than `maxAge` (in milliseconds). */
|
|
58
58
|
refreshStale(maxAge, provider) {
|
|
59
59
|
if (this.age > maxAge)
|
|
60
60
|
this.refresh(provider);
|
|
61
61
|
}
|
|
62
|
-
/** Subscribe this
|
|
62
|
+
/** Subscribe this store to a provider. */
|
|
63
63
|
connect(provider = this.provider) {
|
|
64
64
|
return runSequence(this.through(provider.getItemSequence(this.collection, this.id)));
|
|
65
65
|
}
|
|
66
|
-
// Override to subscribe to `MemoryProvider` while things are iterating over this
|
|
66
|
+
// Override to subscribe to `MemoryProvider` while things are iterating over this store.
|
|
67
67
|
async *[Symbol.asyncIterator]() {
|
|
68
68
|
this.start();
|
|
69
69
|
this._iterating++;
|
|
@@ -2,14 +2,14 @@ import type { AbstractProvider } from "../provider/Provider.js";
|
|
|
2
2
|
import type { StopCallback } from "../util/callback.js";
|
|
3
3
|
import type { Data } from "../util/data.js";
|
|
4
4
|
import type { ItemArray, ItemData, ItemQuery, ItemValue } from "../util/item.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
/**
|
|
8
|
-
export declare class
|
|
5
|
+
import { BooleanStore } from "../store/BooleanStore.js";
|
|
6
|
+
import { Store } from "../store/Store.js";
|
|
7
|
+
/** Store a set of multiple items. */
|
|
8
|
+
export declare class QueryStore<T extends Data = Data> extends Store<ItemArray<T>> implements Iterable<ItemData<T>> {
|
|
9
9
|
readonly provider: AbstractProvider;
|
|
10
10
|
readonly collection: string;
|
|
11
11
|
readonly query: ItemQuery<T>;
|
|
12
|
-
readonly busy:
|
|
12
|
+
readonly busy: BooleanStore;
|
|
13
13
|
readonly limit: number;
|
|
14
14
|
/** Can more items be loaded after the current result. */
|
|
15
15
|
get hasMore(): boolean;
|
|
@@ -27,12 +27,12 @@ export declare class QueryState<T extends Data = Data> extends State<ItemArray<T
|
|
|
27
27
|
/** Get the number of items matching this query. */
|
|
28
28
|
get count(): number;
|
|
29
29
|
constructor(provider: AbstractProvider, collection: string, query: ItemQuery<T>);
|
|
30
|
-
/** Refresh this
|
|
30
|
+
/** Refresh this store from the source provider. */
|
|
31
31
|
refresh(provider?: AbstractProvider): void;
|
|
32
32
|
private _refresh;
|
|
33
|
-
/** Refresh this
|
|
33
|
+
/** Refresh this store if data in the cache is older than `maxAge` (in milliseconds). */
|
|
34
34
|
refreshStale(maxAge: number): void;
|
|
35
|
-
/** Subscribe this
|
|
35
|
+
/** Subscribe this store to a provider. */
|
|
36
36
|
connect(provider?: AbstractProvider): StopCallback;
|
|
37
37
|
/**
|
|
38
38
|
* Load more items after the last once.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CacheProvider } from "../provider/CacheProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BooleanStore } from "../store/BooleanStore.js";
|
|
3
|
+
import { Store } from "../store/Store.js";
|
|
4
4
|
import { getOptionalFirstItem, getOptionalLastItem } from "../util/array.js";
|
|
5
5
|
import { call } from "../util/callback.js";
|
|
6
6
|
import { NONE } from "../util/constants.js";
|
|
@@ -8,12 +8,12 @@ import { getRequired } from "../util/optional.js";
|
|
|
8
8
|
import { getAfterQuery, getLimit } from "../util/query.js";
|
|
9
9
|
import { runSequence } from "../util/sequence.js";
|
|
10
10
|
import { getOptionalSource } from "../util/source.js";
|
|
11
|
-
/**
|
|
12
|
-
export class
|
|
11
|
+
/** Store a set of multiple items. */
|
|
12
|
+
export class QueryStore extends Store {
|
|
13
13
|
provider;
|
|
14
14
|
collection;
|
|
15
15
|
query;
|
|
16
|
-
busy = new
|
|
16
|
+
busy = new BooleanStore();
|
|
17
17
|
limit;
|
|
18
18
|
/** Can more items be loaded after the current result. */
|
|
19
19
|
get hasMore() {
|
|
@@ -56,7 +56,7 @@ export class QueryState extends State {
|
|
|
56
56
|
if (this.loading)
|
|
57
57
|
this.refresh();
|
|
58
58
|
}
|
|
59
|
-
/** Refresh this
|
|
59
|
+
/** Refresh this store from the source provider. */
|
|
60
60
|
refresh(provider = this.provider) {
|
|
61
61
|
if (!this.busy.value)
|
|
62
62
|
void this._refresh(provider);
|
|
@@ -76,12 +76,12 @@ export class QueryState extends State {
|
|
|
76
76
|
this.busy.value = false;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
/** Refresh this
|
|
79
|
+
/** Refresh this store if data in the cache is older than `maxAge` (in milliseconds). */
|
|
80
80
|
refreshStale(maxAge) {
|
|
81
81
|
if (this.age > maxAge)
|
|
82
82
|
this.refresh();
|
|
83
83
|
}
|
|
84
|
-
/** Subscribe this
|
|
84
|
+
/** Subscribe this store to a provider. */
|
|
85
85
|
connect(provider = this.provider) {
|
|
86
86
|
return runSequence(this.through(provider.getQuerySequence(this.collection, this.query)));
|
|
87
87
|
}
|
|
@@ -110,7 +110,7 @@ export class QueryState extends State {
|
|
|
110
110
|
this.busy.value = false;
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
-
// Override to subscribe to `MemoryProvider` while things are iterating over this
|
|
113
|
+
// Override to subscribe to `MemoryProvider` while things are iterating over this store.
|
|
114
114
|
async *[Symbol.asyncIterator]() {
|
|
115
115
|
this.start();
|
|
116
116
|
this._iterating++;
|
package/db/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./Database.js";
|
|
2
2
|
export * from "./CollectionReference.js";
|
|
3
3
|
export * from "./ItemReference.js";
|
|
4
|
-
export * from "./
|
|
4
|
+
export * from "./ItemStore.js";
|
|
5
5
|
export * from "./QueryReference.js";
|
|
6
|
-
export * from "./
|
|
6
|
+
export * from "./QueryStore.js";
|
package/db/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./Database.js";
|
|
2
2
|
export * from "./CollectionReference.js";
|
|
3
3
|
export * from "./ItemReference.js";
|
|
4
|
-
export * from "./
|
|
4
|
+
export * from "./ItemStore.js";
|
|
5
5
|
export * from "./QueryReference.js";
|
|
6
|
-
export * from "./
|
|
6
|
+
export * from "./QueryStore.js";
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -17,6 +17,6 @@ export * from "./provider/index.js";
|
|
|
17
17
|
// export * from "./react/index.js"; // Not exported.
|
|
18
18
|
export * from "./schema/index.js";
|
|
19
19
|
export * from "./sequence/index.js";
|
|
20
|
-
export * from "./
|
|
20
|
+
export * from "./store/index.js";
|
|
21
21
|
// export * from "./test/index.js"; // Not exported.
|
|
22
22
|
export * from "./util/index.js";
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"state-management",
|
|
12
12
|
"query-builder"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.107.0",
|
|
15
15
|
"repository": "https://github.com/dhoulb/shelving",
|
|
16
16
|
"author": "Dave Houlbrooke <dave@shax.com>",
|
|
17
17
|
"license": "0BSD",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"./react": "./react/index.js",
|
|
36
36
|
"./schema": "./schema/index.js",
|
|
37
37
|
"./sequence": "./sequence/index.js",
|
|
38
|
-
"./
|
|
38
|
+
"./store": "./store/index.js",
|
|
39
39
|
"./test": "./test/index.js",
|
|
40
40
|
"./util": "./util/index.js"
|
|
41
41
|
},
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import type { AsyncDatabase, Database } from "../db/Database.js";
|
|
3
3
|
import type { DataKey, Datas } from "../util/data.js";
|
|
4
4
|
import type { ItemQuery } from "../util/item.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { ItemStore } from "../db/ItemStore.js";
|
|
6
|
+
import { QueryStore } from "../db/QueryStore.js";
|
|
7
7
|
import { type Optional } from "../util/optional.js";
|
|
8
8
|
/**
|
|
9
9
|
* Create a data context that can be provided to React elements and allows them to call `useItem()` and `useQuery()`
|
|
10
10
|
*/
|
|
11
11
|
export declare function createDataContext<T extends Datas>({ provider, }: Database<T> | AsyncDatabase<T>): {
|
|
12
|
-
readonly useItem: <K extends DataKey<T>>(collection: K, id: string) =>
|
|
13
|
-
readonly useQuery: <K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>) =>
|
|
14
|
-
readonly useOptionalItem: <K extends DataKey<T>>(collection: K, id: Optional<string>) =>
|
|
15
|
-
readonly useOptionalQuery: <K extends DataKey<T>>(collection: K, query: Optional<ItemQuery<T[K]>>) =>
|
|
12
|
+
readonly useItem: <K extends DataKey<T>>(collection: K, id: string) => ItemStore<T[K]>;
|
|
13
|
+
readonly useQuery: <K extends DataKey<T>>(collection: K, query: ItemQuery<T[K]>) => QueryStore<T[K]>;
|
|
14
|
+
readonly useOptionalItem: <K extends DataKey<T>>(collection: K, id: Optional<string>) => ItemStore<T[K]> | undefined;
|
|
15
|
+
readonly useOptionalQuery: <K extends DataKey<T>>(collection: K, query: Optional<ItemQuery<T[K]>>) => QueryStore<T[K]> | undefined;
|
|
16
16
|
readonly DataProvider: ({ children }: {
|
|
17
17
|
children: React.ReactNode;
|
|
18
18
|
}) => React.ReactElement;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ItemStore } from "../db/ItemStore.js";
|
|
2
|
+
import { QueryStore } from "../db/QueryStore.js";
|
|
3
3
|
import { setMapItem } from "../util/map.js";
|
|
4
4
|
import { getRequired } from "../util/optional.js";
|
|
5
5
|
import { createCacheContext } from "./createCacheContext.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useStore } from "./useStore.js";
|
|
7
7
|
/**
|
|
8
8
|
* Create a data context that can be provided to React elements and allows them to call `useItem()` and `useQuery()`
|
|
9
9
|
*/
|
|
@@ -12,12 +12,12 @@ export function createDataContext({ provider, }) {
|
|
|
12
12
|
const useOptionalItem = (collection, id) => {
|
|
13
13
|
const cache = useCache();
|
|
14
14
|
const key = id && `${collection}/${id}`;
|
|
15
|
-
return
|
|
15
|
+
return useStore(id && key ? cache.get(key) || setMapItem(cache, key, new ItemStore(provider, collection, id)) : undefined);
|
|
16
16
|
};
|
|
17
17
|
const useOptionalQuery = (collection, query) => {
|
|
18
18
|
const cache = useCache();
|
|
19
19
|
const key = `${collection}?${JSON.stringify(query)}`;
|
|
20
|
-
return
|
|
20
|
+
return useStore(query && key ? cache.get(key) || setMapItem(cache, key, new QueryStore(provider, collection, query)) : undefined);
|
|
21
21
|
};
|
|
22
22
|
return {
|
|
23
23
|
useOptionalItem,
|
package/react/index.d.ts
CHANGED
package/react/index.js
CHANGED
package/react/useSequence.d.ts
CHANGED
|
@@ -4,6 +4,5 @@
|
|
|
4
4
|
* @param sequence An object implementing the `AsyncIterable` interface.
|
|
5
5
|
* - Subscription is recreated every time this value changes.
|
|
6
6
|
* - Memoise this value to persist the subscription for the lifetime of the component.
|
|
7
|
-
* - If the value is a `State` instance
|
|
8
7
|
*/
|
|
9
8
|
export declare function useSequence<T>(sequence?: AsyncIterable<T>): T | undefined;
|
package/react/useSequence.js
CHANGED
|
@@ -7,7 +7,6 @@ import { runSequence } from "../util/sequence.js";
|
|
|
7
7
|
* @param sequence An object implementing the `AsyncIterable` interface.
|
|
8
8
|
* - Subscription is recreated every time this value changes.
|
|
9
9
|
* - Memoise this value to persist the subscription for the lifetime of the component.
|
|
10
|
-
* - If the value is a `State` instance
|
|
11
10
|
*/
|
|
12
11
|
export function useSequence(sequence) {
|
|
13
12
|
const [value, setValue] = useState(undefined);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AnyStore } from "../store/Store.js";
|
|
2
|
+
import type { Optional } from "../util/optional.js";
|
|
3
|
+
/** Subscribe to a Shelving `Store` instance to refresh this component when its value changes. */
|
|
4
|
+
export declare function useStore<T extends AnyStore>(store: T): T;
|
|
5
|
+
export declare function useStore<T extends AnyStore>(store?: Optional<T>): T | undefined;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
2
|
+
import { NONE } from "../util/constants.js";
|
|
3
|
+
import { BLACKHOLE } from "../util/function.js";
|
|
4
|
+
import { runSequence } from "../util/sequence.js";
|
|
5
|
+
export function useStore(store) {
|
|
6
|
+
useSyncExternalStore(useCallback(onStoreChange => (store ? runSequence(store, onStoreChange, onStoreChange) : BLACKHOLE), [store]), () => (!store ? store : store.loading ? NONE : store.value));
|
|
7
|
+
return store ?? undefined;
|
|
8
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ImmutableArray } from "../util/array.js";
|
|
2
|
-
import {
|
|
3
|
-
/**
|
|
4
|
-
export declare class
|
|
2
|
+
import { Store } from "./Store.js";
|
|
3
|
+
/** Store an array. */
|
|
4
|
+
export declare class ArrayStore<T> extends Store<ImmutableArray<T>> implements Iterable<T> {
|
|
5
5
|
constructor(value?: ImmutableArray<T>, time?: number);
|
|
6
|
-
/** Get the length of the current value of this
|
|
6
|
+
/** Get the length of the current value of this store. */
|
|
7
7
|
get count(): number;
|
|
8
8
|
/** Add items to this array. */
|
|
9
9
|
add(...items: T[]): void;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { omitArrayItems, toggleArrayItems, withArrayItems } from "../util/array.js";
|
|
2
|
-
import {
|
|
3
|
-
/**
|
|
4
|
-
export class
|
|
2
|
+
import { Store } from "./Store.js";
|
|
3
|
+
/** Store an array. */
|
|
4
|
+
export class ArrayStore extends Store {
|
|
5
5
|
constructor(value = [], time) {
|
|
6
6
|
super(value, time);
|
|
7
7
|
}
|
|
8
|
-
/** Get the length of the current value of this
|
|
8
|
+
/** Get the length of the current value of this store. */
|
|
9
9
|
get count() {
|
|
10
10
|
return this.value.length;
|
|
11
11
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NONE } from "../util/constants.js";
|
|
2
|
-
import {
|
|
3
|
-
/**
|
|
4
|
-
export declare class
|
|
2
|
+
import { Store } from "./Store.js";
|
|
3
|
+
/** Store a boolean. */
|
|
4
|
+
export declare class BooleanStore extends Store<boolean> {
|
|
5
5
|
constructor(value?: boolean | typeof NONE, time?: number);
|
|
6
6
|
/** Toggle the current boolean value. */
|
|
7
7
|
toggle(): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
3
|
-
export class
|
|
1
|
+
import { Store } from "./Store.js";
|
|
2
|
+
/** Store a boolean. */
|
|
3
|
+
export class BooleanStore extends Store {
|
|
4
4
|
constructor(value = false, time) {
|
|
5
5
|
super(value, time);
|
|
6
6
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Data, DataKey } from "../util/data.js";
|
|
2
2
|
import type { Updates } from "../util/update.js";
|
|
3
|
-
import {
|
|
4
|
-
/**
|
|
5
|
-
export declare class
|
|
6
|
-
/** Get the data of this
|
|
3
|
+
import { Store } from "./Store.js";
|
|
4
|
+
/** Store a data object. */
|
|
5
|
+
export declare class DataStore<T extends Data> extends Store<T> {
|
|
6
|
+
/** Get the data of this store. */
|
|
7
7
|
get data(): T;
|
|
8
|
-
/** Set the data of this
|
|
8
|
+
/** Set the data of this store. */
|
|
9
9
|
set data(data: T);
|
|
10
10
|
/** Update several props in this data. */
|
|
11
11
|
update(updates: Updates<T>): void;
|
|
@@ -14,11 +14,11 @@ export declare class DataState<T extends Data> extends State<T> {
|
|
|
14
14
|
/** Update a single named prop in this data. */
|
|
15
15
|
setProp<K extends DataKey<T>>(name: K, value: T[K]): void;
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
export declare class
|
|
19
|
-
/** Get current data value of this
|
|
17
|
+
/** Store an optional data object. */
|
|
18
|
+
export declare class OptionalDataStore<T extends Data> extends Store<T | undefined> {
|
|
19
|
+
/** Get current data value of this store (or throw `Promise` that resolves to the next required value). */
|
|
20
20
|
get data(): T;
|
|
21
|
-
/** Set the data of this
|
|
21
|
+
/** Set the data of this store. */
|
|
22
22
|
set data(data: T);
|
|
23
23
|
/** Does the data exist or not? */
|
|
24
24
|
get exists(): boolean;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { withProp } from "../util/object.js";
|
|
2
2
|
import { getRequired } from "../util/optional.js";
|
|
3
3
|
import { updateData } from "../util/update.js";
|
|
4
|
-
import {
|
|
5
|
-
/**
|
|
6
|
-
export class
|
|
7
|
-
/** Get the data of this
|
|
4
|
+
import { Store } from "./Store.js";
|
|
5
|
+
/** Store a data object. */
|
|
6
|
+
export class DataStore extends Store {
|
|
7
|
+
/** Get the data of this store. */
|
|
8
8
|
get data() {
|
|
9
9
|
return this.value;
|
|
10
10
|
}
|
|
11
|
-
/** Set the data of this
|
|
11
|
+
/** Set the data of this store. */
|
|
12
12
|
set data(data) {
|
|
13
13
|
this.value = data;
|
|
14
14
|
}
|
|
@@ -25,13 +25,13 @@ export class DataState extends State {
|
|
|
25
25
|
this.value = withProp(this.data, name, value);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
29
|
-
export class
|
|
30
|
-
/** Get current data value of this
|
|
28
|
+
/** Store an optional data object. */
|
|
29
|
+
export class OptionalDataStore extends Store {
|
|
30
|
+
/** Get current data value of this store (or throw `Promise` that resolves to the next required value). */
|
|
31
31
|
get data() {
|
|
32
32
|
return getRequired(this.value);
|
|
33
33
|
}
|
|
34
|
-
/** Set the data of this
|
|
34
|
+
/** Set the data of this store. */
|
|
35
35
|
set data(data) {
|
|
36
36
|
this.value = data;
|
|
37
37
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { DictionaryItem, ImmutableDictionary } from "../util/dictionary.js";
|
|
2
2
|
import type { Updates } from "../util/update.js";
|
|
3
|
-
import {
|
|
4
|
-
/**
|
|
5
|
-
export declare class
|
|
3
|
+
import { Store } from "./Store.js";
|
|
4
|
+
/** Store a dictionary object. */
|
|
5
|
+
export declare class DictionaryStore<T> extends Store<ImmutableDictionary<T>> implements Iterable<DictionaryItem<T>> {
|
|
6
6
|
constructor(value?: ImmutableDictionary<T>, time?: number);
|
|
7
|
-
/** Get the length of the current value of this
|
|
7
|
+
/** Get the length of the current value of this store. */
|
|
8
8
|
get count(): number;
|
|
9
9
|
/** Set a named entry in this object with a different value. */
|
|
10
10
|
update(updates: Updates<ImmutableDictionary<T>>): void;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { getDictionaryItems, omitDictionaryItems } from "../util/dictionary.js";
|
|
2
2
|
import { withProp } from "../util/object.js";
|
|
3
3
|
import { updateData } from "../util/update.js";
|
|
4
|
-
import {
|
|
5
|
-
/**
|
|
6
|
-
export class
|
|
4
|
+
import { Store } from "./Store.js";
|
|
5
|
+
/** Store a dictionary object. */
|
|
6
|
+
export class DictionaryStore extends Store {
|
|
7
7
|
constructor(value = {}, time) {
|
|
8
8
|
super(value, time);
|
|
9
9
|
}
|
|
10
|
-
/** Get the length of the current value of this
|
|
10
|
+
/** Get the length of the current value of this store. */
|
|
11
11
|
get count() {
|
|
12
12
|
return Object.keys(this.value).length;
|
|
13
13
|
}
|
package/store/Store.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { DeferredSequence } from "../sequence/DeferredSequence.js";
|
|
2
|
+
import { NONE } from "../util/constants.js";
|
|
3
|
+
/** Any `Store` instance. */
|
|
4
|
+
export type AnyStore = Store<any>;
|
|
5
|
+
/**
|
|
6
|
+
* Store that retains its most recent value and is async-iterable to allow values to be observed.
|
|
7
|
+
* - Current value can be read at `store.value` and `store.data`
|
|
8
|
+
* - Stores also send their most-recent value to any new subscribers immediately when a new subscriber is added.
|
|
9
|
+
* - Stores can also be in a loading store where they do not have a current value.
|
|
10
|
+
*
|
|
11
|
+
* @param initial The initial value for the store, a `Promise` that resolves to the initial value, a source `Subscribable` to subscribe to, or another `Store` instance to take the initial value from and subscribe to.
|
|
12
|
+
* - To set the store to be loading, use the `NONE` constant or a `Promise` value.
|
|
13
|
+
* - To set the store to an explicit value, use that value or another `Store` instance with a value.
|
|
14
|
+
* */
|
|
15
|
+
export declare class Store<T> implements AsyncIterable<T> {
|
|
16
|
+
/** Deferred sequence this store uses to issue values as they change. */
|
|
17
|
+
readonly next: DeferredSequence<T>;
|
|
18
|
+
/** Current value of the store (or throw a promise that resolves when this store receives its next value or error). */
|
|
19
|
+
get value(): T;
|
|
20
|
+
set value(value: T);
|
|
21
|
+
private _value;
|
|
22
|
+
/** Is there a current value, or is it still loading. */
|
|
23
|
+
get loading(): boolean;
|
|
24
|
+
/** Time (in milliseconds) this store was last updated with a new value, or `undefined` if this store is currently loading. */
|
|
25
|
+
get time(): number | undefined;
|
|
26
|
+
private _time;
|
|
27
|
+
/** How old this store's value is (in milliseconds). */
|
|
28
|
+
get age(): number;
|
|
29
|
+
/** Current error of this store (or `undefined` if there is no reason). */
|
|
30
|
+
get reason(): unknown;
|
|
31
|
+
set reason(reason: Error | unknown);
|
|
32
|
+
private _reason;
|
|
33
|
+
/** Store is initiated with an initial store. */
|
|
34
|
+
constructor(value: T | typeof NONE, time?: number);
|
|
35
|
+
/** Set the value of the store. */
|
|
36
|
+
set(next: T): void;
|
|
37
|
+
/** Set the value of the store as values are pulled from a sequence. */
|
|
38
|
+
through(sequence: AsyncIterable<T>): AsyncIterable<T>;
|
|
39
|
+
[Symbol.asyncIterator](): AsyncGenerator<T, void, void>;
|
|
40
|
+
}
|
|
41
|
+
/** Is an unknown value a `Store` instance. */
|
|
42
|
+
export declare const isStore: <T extends AnyStore>(value: unknown) => value is T;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { DeferredSequence } from "../sequence/DeferredSequence.js";
|
|
2
2
|
import { NONE } from "../util/constants.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* - Current value can be read at `
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
4
|
+
* Store that retains its most recent value and is async-iterable to allow values to be observed.
|
|
5
|
+
* - Current value can be read at `store.value` and `store.data`
|
|
6
|
+
* - Stores also send their most-recent value to any new subscribers immediately when a new subscriber is added.
|
|
7
|
+
* - Stores can also be in a loading store where they do not have a current value.
|
|
8
8
|
*
|
|
9
|
-
* @param initial The initial value for the
|
|
10
|
-
* - To set the
|
|
11
|
-
* - To set the
|
|
9
|
+
* @param initial The initial value for the store, a `Promise` that resolves to the initial value, a source `Subscribable` to subscribe to, or another `Store` instance to take the initial value from and subscribe to.
|
|
10
|
+
* - To set the store to be loading, use the `NONE` constant or a `Promise` value.
|
|
11
|
+
* - To set the store to an explicit value, use that value or another `Store` instance with a value.
|
|
12
12
|
* */
|
|
13
|
-
export class
|
|
14
|
-
/** Deferred sequence this
|
|
13
|
+
export class Store {
|
|
14
|
+
/** Deferred sequence this store uses to issue values as they change. */
|
|
15
15
|
next = new DeferredSequence();
|
|
16
|
-
/** Current value of the
|
|
16
|
+
/** Current value of the store (or throw a promise that resolves when this store receives its next value or error). */
|
|
17
17
|
get value() {
|
|
18
18
|
if (this._reason !== undefined)
|
|
19
19
|
throw this._reason;
|
|
@@ -34,17 +34,17 @@ export class State {
|
|
|
34
34
|
get loading() {
|
|
35
35
|
return this._value === NONE;
|
|
36
36
|
}
|
|
37
|
-
/** Time (in milliseconds) this
|
|
37
|
+
/** Time (in milliseconds) this store was last updated with a new value, or `undefined` if this store is currently loading. */
|
|
38
38
|
get time() {
|
|
39
39
|
return this._value === NONE ? undefined : this._time;
|
|
40
40
|
}
|
|
41
41
|
_time;
|
|
42
|
-
/** How old this
|
|
42
|
+
/** How old this store's value is (in milliseconds). */
|
|
43
43
|
get age() {
|
|
44
44
|
const time = this.time;
|
|
45
45
|
return typeof time === "number" ? Date.now() - time : Infinity;
|
|
46
46
|
}
|
|
47
|
-
/** Current error of this
|
|
47
|
+
/** Current error of this store (or `undefined` if there is no reason). */
|
|
48
48
|
get reason() {
|
|
49
49
|
return this._reason;
|
|
50
50
|
}
|
|
@@ -55,16 +55,16 @@ export class State {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
_reason = undefined;
|
|
58
|
-
/**
|
|
58
|
+
/** Store is initiated with an initial store. */
|
|
59
59
|
constructor(value, time = Date.now()) {
|
|
60
60
|
this._value = value;
|
|
61
61
|
this._time = time;
|
|
62
62
|
}
|
|
63
|
-
/** Set the value of the
|
|
63
|
+
/** Set the value of the store. */
|
|
64
64
|
set(next) {
|
|
65
65
|
this.value = next;
|
|
66
66
|
}
|
|
67
|
-
/** Set the value of the
|
|
67
|
+
/** Set the value of the store as values are pulled from a sequence. */
|
|
68
68
|
async *through(sequence) {
|
|
69
69
|
for await (const value of sequence) {
|
|
70
70
|
this.value = value;
|
|
@@ -72,7 +72,7 @@ export class State {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
// Implement `AsyncIterable`
|
|
75
|
-
// Issues the current value of the
|
|
75
|
+
// Issues the current value of the store first, then any subsequent values that are issued.
|
|
76
76
|
async *[Symbol.asyncIterator]() {
|
|
77
77
|
await Promise.resolve(); // Introduce a slight delay, i.e. don't immediately yield `this.value` in case it is changed synchronously.
|
|
78
78
|
if (!this.loading)
|
|
@@ -80,5 +80,5 @@ export class State {
|
|
|
80
80
|
yield* this.next;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
/** Is an unknown value a `
|
|
84
|
-
export const
|
|
83
|
+
/** Is an unknown value a `Store` instance. */
|
|
84
|
+
export const isStore = (value) => value instanceof Store;
|
package/store/index.d.ts
ADDED
package/store/index.js
ADDED
package/react/useState.d.ts
DELETED
package/react/useState.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { useCallback, useSyncExternalStore } from "react";
|
|
2
|
-
import { NONE } from "../util/constants.js";
|
|
3
|
-
import { BLACKHOLE } from "../util/function.js";
|
|
4
|
-
import { runSequence } from "../util/sequence.js";
|
|
5
|
-
export function useState(state) {
|
|
6
|
-
useSyncExternalStore(useCallback(onStateChange => (state ? runSequence(state, onStateChange, onStateChange) : BLACKHOLE), [state]), () => (!state ? state : state.loading ? NONE : state.value));
|
|
7
|
-
return state ?? undefined;
|
|
8
|
-
}
|
package/state/State.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { DeferredSequence } from "../sequence/DeferredSequence.js";
|
|
2
|
-
import { NONE } from "../util/constants.js";
|
|
3
|
-
/** Any `State` instance. */
|
|
4
|
-
export type AnyState = State<any>;
|
|
5
|
-
/**
|
|
6
|
-
* Stream that retains its most recent value
|
|
7
|
-
* - Current value can be read at `state.value` and `state.data`
|
|
8
|
-
* - States also send their most-recent value to any new subscribers immediately when a new subscriber is added.
|
|
9
|
-
* - States can also be in a loading state where they do not have a current value.
|
|
10
|
-
*
|
|
11
|
-
* @param initial The initial value for the state, a `Promise` that resolves to the initial value, a source `Subscribable` to subscribe to, or another `State` instance to take the initial value from and subscribe to.
|
|
12
|
-
* - To set the state to be loading, use the `NONE` constant or a `Promise` value.
|
|
13
|
-
* - To set the state to an explicit value, use that value or another `State` instance with a value.
|
|
14
|
-
* */
|
|
15
|
-
export declare class State<T> implements AsyncIterable<T> {
|
|
16
|
-
/** Deferred sequence this state uses to issue values as they change. */
|
|
17
|
-
readonly next: DeferredSequence<T>;
|
|
18
|
-
/** Current value of the state (or throw a promise that resolves when this state receives its next value or error). */
|
|
19
|
-
get value(): T;
|
|
20
|
-
set value(value: T);
|
|
21
|
-
private _value;
|
|
22
|
-
/** Is there a current value, or is it still loading. */
|
|
23
|
-
get loading(): boolean;
|
|
24
|
-
/** Time (in milliseconds) this state was last updated with a new value, or `undefined` if this state is currently loading. */
|
|
25
|
-
get time(): number | undefined;
|
|
26
|
-
private _time;
|
|
27
|
-
/** How old this state's value is (in milliseconds). */
|
|
28
|
-
get age(): number;
|
|
29
|
-
/** Current error of this state (or `undefined` if there is no reason). */
|
|
30
|
-
get reason(): unknown;
|
|
31
|
-
set reason(reason: Error | unknown);
|
|
32
|
-
private _reason;
|
|
33
|
-
/** State is initiated with an initial state. */
|
|
34
|
-
constructor(value: T | typeof NONE, time?: number);
|
|
35
|
-
/** Set the value of the state. */
|
|
36
|
-
set(next: T): void;
|
|
37
|
-
/** Set the value of the state as values are pulled from a sequence. */
|
|
38
|
-
through(sequence: AsyncIterable<T>): AsyncIterable<T>;
|
|
39
|
-
[Symbol.asyncIterator](): AsyncGenerator<T, void, void>;
|
|
40
|
-
}
|
|
41
|
-
/** Is an unknown value a `State` instance. */
|
|
42
|
-
export declare const isState: <T extends AnyState>(value: unknown) => value is T;
|
package/state/index.d.ts
DELETED