shelving 1.65.1 → 1.66.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/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<T>>(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<T>): ConnectableObserver<TT>;
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<T>): ConnectableObserver<TT>;
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
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.65.1",
14
+ "version": "1.66.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -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(): Promise<void>;
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. */
@@ -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,6 +12,24 @@ 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.
@@ -20,7 +37,7 @@ export class DocumentState extends State {
20
37
  if (isCached)
21
38
  this.next(this._table.getDocument(ref.id)); // Use the existing cached value.
22
39
  else
23
- dispatch(this.refresh); // Queue a request to refresh the value.
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
- /** Refresh this state from the source provider. */
38
- async refresh() {
39
- if (this.closed)
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
- dispatch(this.refresh);
62
+ void this.refresh();
57
63
  }
58
64
  /** Subscribe this state to the source provider. */
59
65
  connectSource() {
@@ -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,16 +41,18 @@ 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);
@@ -60,7 +63,7 @@ export class QueryState extends State {
60
63
  if (isCached)
61
64
  this.next(this._table.getQuery(ref)); // Use the existing cached value.
62
65
  else
63
- dispatch(this.refresh); // Queue a request to refresh the value.
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 (!this.busy.value && this.age > maxAge)
97
- dispatch(this.refresh);
107
+ if (this.age > maxAge)
108
+ void this.refresh();
98
109
  }
99
110
  /** Subscribe this state to the source provider. */
100
111
  connectSource() {
@@ -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 length(): number;
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. */
@@ -6,7 +6,7 @@ export class ArrayState extends State {
6
6
  super(initial);
7
7
  }
8
8
  /** Get the length of the current value of this state. */
9
- get length() {
9
+ get count() {
10
10
  return this.value.length;
11
11
  }
12
12
  /** Add an item to this array. */
@@ -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. */
@@ -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.loading)
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));
@@ -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 length(): number;
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. */
@@ -6,7 +6,7 @@ export class ObjectState extends State {
6
6
  super(initial);
7
7
  }
8
8
  /** Get the length of the current value of this state. */
9
- get length() {
9
+ get count() {
10
10
  return Object.keys(this.value).length;
11
11
  }
12
12
  /** Remove a named entry from this object. */