shelving 1.64.0 → 1.65.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.
Files changed (154) hide show
  1. package/api/Resource.js +2 -2
  2. package/db/Reference.d.ts +37 -23
  3. package/db/Reference.js +65 -33
  4. package/db/index.d.ts +0 -3
  5. package/db/index.js +0 -3
  6. package/error/ThroughError.d.ts +8 -0
  7. package/error/ThroughError.js +13 -0
  8. package/firestore/client/FirestoreClientProvider.d.ts +11 -10
  9. package/firestore/client/FirestoreClientProvider.js +18 -17
  10. package/firestore/lite/FirestoreLiteProvider.d.ts +10 -10
  11. package/firestore/lite/FirestoreLiteProvider.js +15 -14
  12. package/firestore/server/FirestoreServerProvider.d.ts +11 -10
  13. package/firestore/server/FirestoreServerProvider.js +18 -17
  14. package/index.d.ts +2 -1
  15. package/index.js +2 -1
  16. package/markup/rules.js +4 -4
  17. package/observe/AbstractObserver.d.ts +15 -0
  18. package/observe/AbstractObserver.js +42 -0
  19. package/observe/AsyncObserver.d.ts +5 -0
  20. package/observe/AsyncObserver.js +8 -0
  21. package/{stream/LastStream.d.ts → observe/LastSubject.d.ts} +2 -2
  22. package/observe/LastSubject.js +12 -0
  23. package/observe/MatchObserver.d.ts +9 -0
  24. package/observe/MatchObserver.js +12 -0
  25. package/observe/MatchableObserver.d.ts +7 -0
  26. package/observe/MatchableObserver.js +10 -0
  27. package/observe/Observable.d.ts +20 -0
  28. package/observe/Observable.js +7 -0
  29. package/observe/Observer.d.ts +31 -0
  30. package/observe/Observer.js +48 -0
  31. package/observe/OnceObserver.d.ts +5 -0
  32. package/observe/OnceObserver.js +8 -0
  33. package/observe/Subject.d.ts +46 -0
  34. package/observe/Subject.js +110 -0
  35. package/observe/ThroughObserver.d.ts +5 -0
  36. package/observe/ThroughObserver.js +8 -0
  37. package/observe/TransformObserver.d.ts +9 -0
  38. package/observe/TransformObserver.js +12 -0
  39. package/observe/TransformableObserver.d.ts +7 -0
  40. package/observe/TransformableObserver.js +8 -0
  41. package/observe/index.d.ts +13 -0
  42. package/observe/index.js +13 -0
  43. package/observe/util.d.ts +24 -0
  44. package/observe/util.js +34 -0
  45. package/package.json +1 -1
  46. package/provider/BatchProvider.d.ts +8 -8
  47. package/provider/BatchProvider.js +26 -31
  48. package/provider/CacheProvider.d.ts +12 -21
  49. package/provider/CacheProvider.js +40 -74
  50. package/provider/DebugProvider.d.ts +20 -0
  51. package/provider/DebugProvider.js +170 -0
  52. package/provider/MemoryProvider.d.ts +38 -24
  53. package/provider/MemoryProvider.js +141 -102
  54. package/provider/Provider.d.ts +23 -22
  55. package/provider/ThroughProvider.d.ts +20 -11
  56. package/provider/ThroughProvider.js +12 -12
  57. package/provider/ValidationProvider.d.ts +10 -9
  58. package/provider/ValidationProvider.js +31 -28
  59. package/provider/index.d.ts +1 -1
  60. package/provider/index.js +1 -1
  61. package/query/Filter.d.ts +1 -1
  62. package/query/Filter.js +4 -3
  63. package/query/Filters.d.ts +1 -1
  64. package/react/index.d.ts +0 -6
  65. package/react/index.js +3 -6
  66. package/react/useDocument.d.ts +31 -38
  67. package/react/useDocument.js +71 -76
  68. package/react/useInstance.d.ts +4 -7
  69. package/react/useInstance.js +6 -9
  70. package/react/useLazy.d.ts +5 -9
  71. package/react/useQuery.d.ts +48 -46
  72. package/react/useQuery.js +116 -87
  73. package/react/useReduce.d.ts +8 -1
  74. package/react/useReduce.js +1 -3
  75. package/react/useSubscribe.d.ts +5 -6
  76. package/react/useSubscribe.js +10 -11
  77. package/{stream → state}/ArrayState.d.ts +1 -1
  78. package/{stream → state}/ArrayState.js +8 -10
  79. package/{stream → state}/BooleanState.d.ts +1 -1
  80. package/{stream → state}/BooleanState.js +3 -5
  81. package/{stream → state}/DataState.d.ts +3 -3
  82. package/{stream → state}/DataState.js +6 -6
  83. package/{stream → state}/ObjectState.d.ts +1 -1
  84. package/{stream → state}/ObjectState.js +6 -8
  85. package/state/SelfClosingState.d.ts +18 -0
  86. package/state/SelfClosingState.js +34 -0
  87. package/state/State.d.ts +32 -0
  88. package/state/State.js +69 -0
  89. package/{stream → state}/index.d.ts +1 -4
  90. package/{stream → state}/index.js +1 -4
  91. package/util/array.d.ts +0 -8
  92. package/util/array.js +0 -4
  93. package/util/async.d.ts +4 -5
  94. package/util/async.js +3 -2
  95. package/util/clone.js +3 -2
  96. package/util/data.d.ts +7 -3
  97. package/util/data.js +1 -1
  98. package/util/entry.d.ts +1 -3
  99. package/util/equal.d.ts +4 -5
  100. package/util/filter.d.ts +1 -26
  101. package/util/filter.js +1 -25
  102. package/util/function.d.ts +5 -5
  103. package/util/function.js +3 -3
  104. package/util/hydrate.js +2 -2
  105. package/util/index.d.ts +1 -1
  106. package/util/index.js +1 -1
  107. package/util/iterate.d.ts +3 -9
  108. package/util/iterate.js +7 -11
  109. package/util/match.d.ts +20 -0
  110. package/util/match.js +14 -0
  111. package/util/object.d.ts +0 -19
  112. package/util/object.js +0 -8
  113. package/util/search.d.ts +1 -1
  114. package/util/sort.d.ts +4 -10
  115. package/util/sort.js +0 -11
  116. package/util/timeout.d.ts +2 -0
  117. package/util/timeout.js +8 -1
  118. package/util/transform.d.ts +41 -9
  119. package/util/transform.js +18 -4
  120. package/util/validate.d.ts +6 -10
  121. package/util/validate.js +1 -3
  122. package/api/errors.d.ts +0 -8
  123. package/api/errors.js +0 -9
  124. package/db/PaginationState.d.ts +0 -28
  125. package/db/PaginationState.js +0 -59
  126. package/db/errors.d.ts +0 -13
  127. package/db/errors.js +0 -17
  128. package/db/util.d.ts +0 -11
  129. package/db/util.js +0 -21
  130. package/provider/ErrorProvider.d.ts +0 -31
  131. package/provider/ErrorProvider.js +0 -175
  132. package/react/useCompare.d.ts +0 -3
  133. package/react/useCompare.js +0 -8
  134. package/react/useFetch.d.ts +0 -17
  135. package/react/useFetch.js +0 -41
  136. package/react/usePagination.d.ts +0 -8
  137. package/react/usePagination.js +0 -25
  138. package/react/usePureEffect.d.ts +0 -10
  139. package/react/usePureEffect.js +0 -17
  140. package/react/usePureState.d.ts +0 -11
  141. package/react/usePureState.js +0 -16
  142. package/react/useState.d.ts +0 -39
  143. package/react/useState.js +0 -65
  144. package/stream/LastStream.js +0 -12
  145. package/stream/LazyState.d.ts +0 -11
  146. package/stream/LazyState.js +0 -28
  147. package/stream/LazyStream.d.ts +0 -12
  148. package/stream/LazyStream.js +0 -28
  149. package/stream/State.d.ts +0 -46
  150. package/stream/State.js +0 -66
  151. package/stream/Stream.d.ts +0 -66
  152. package/stream/Stream.js +0 -122
  153. package/util/observe.d.ts +0 -103
  154. package/util/observe.js +0 -147
@@ -0,0 +1,24 @@
1
+ import type { Transformer } from "../util/transform.js";
2
+ import { Unsubscribe, Subscribable } from "./Observable.js";
3
+ import { ConnectableObserver } from "./Observer.js";
4
+ import { Subject } from "./Subject.js";
5
+ /** Get a promise that resolves to the next value of a source subscribable. */
6
+ export declare function awaitNext<T>(source: Subscribable<T>): Promise<T>;
7
+ /** Get a promise that resolves when a source subscribable is complete. */
8
+ export declare function awaitComplete<T>(source: Subscribable<T>): Promise<void>;
9
+ /** Connect a connectable to a source subscribable but transform the value using a transform. */
10
+ export declare function connectDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, TT>, target: ConnectableObserver<TT>): Unsubscribe;
11
+ /** Connect a connectable to a source subscribable but transform the value using an async transform. */
12
+ export declare function connectAsyncDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, PromiseLike<TT>>, target: ConnectableObserver<TT>): Unsubscribe;
13
+ /** Connect a connectable to a source subscribable, and return the connected connectable. */
14
+ export declare function connected<T, C extends ConnectableObserver<T>>(source: Subscribable<T>, target: C): C;
15
+ export declare function connected<T>(source: Subscribable<T>, target: ConnectableObserver<T>): Subject<T>;
16
+ export declare function connected<T>(source: Subscribable<T>): Subject<T>;
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>;
20
+ export declare function connectedDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, TT>): Subject<TT>;
21
+ /** Connect a connectable to a source subscribable but transform the value using an async transform, and return the connected connectable. */
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>;
24
+ export declare function connectedAsyncDerived<T, TT>(source: Subscribable<T>, transformer: Transformer<T, PromiseLike<TT>>): Subject<TT>;
@@ -0,0 +1,34 @@
1
+ import { subscribe } from "./Observable.js";
2
+ import { TransformObserver } from "./TransformObserver.js";
3
+ import { AsyncObserver } from "./AsyncObserver.js";
4
+ import { OnceObserver } from "./OnceObserver.js";
5
+ import { ThroughObserver } from "./ThroughObserver.js";
6
+ import { Subject } from "./Subject.js";
7
+ /** Get a promise that resolves to the next value of a source subscribable. */
8
+ export function awaitNext(source) {
9
+ return new Promise((next, error) => new OnceObserver({ next, error }).connect(source));
10
+ }
11
+ /** Get a promise that resolves when a source subscribable is complete. */
12
+ export function awaitComplete(source) {
13
+ return new Promise((complete, error) => new ThroughObserver({ complete, error }).connect(source));
14
+ }
15
+ /** Connect a connectable to a source subscribable but transform the value using a transform. */
16
+ export function connectDerived(source, transformer, target) {
17
+ return target.connect(() => subscribe(source, new TransformObserver(transformer, target)));
18
+ }
19
+ /** Connect a connectable to a source subscribable but transform the value using an async transform. */
20
+ export function connectAsyncDerived(source, transformer, target) {
21
+ return target.connect(() => subscribe(source, new TransformObserver(transformer, new AsyncObserver(target))));
22
+ }
23
+ export function connected(source, target = new Subject()) {
24
+ target.connect(source);
25
+ return target;
26
+ }
27
+ export function connectedDerived(source, transformer, target = new Subject()) {
28
+ connectDerived(source, transformer, target);
29
+ return target;
30
+ }
31
+ export function connectedAsyncDerived(source, transformer, target = new Subject()) {
32
+ connectAsyncDerived(source, transformer, target);
33
+ return target;
34
+ }
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.64.0",
14
+ "version": "1.65.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -1,8 +1,8 @@
1
1
  import type { MutableObject } from "../util/object.js";
2
2
  import type { DocumentReference, QueryReference } from "../db/Reference.js";
3
- import type { Entity, Data, Result } from "../util/data.js";
4
- import { ImmutableArray } from "../util/array.js";
5
- import { Observable, Observer, Unsubscriber } from "../util/observe.js";
3
+ import type { Data, Entities, OptionalEntity } from "../util/data.js";
4
+ import type { PartialObserver } from "../observe/Observer.js";
5
+ import { Observable, Unsubscribe } from "../observe/Observable.js";
6
6
  import { ThroughProvider } from "./ThroughProvider.js";
7
7
  /**
8
8
  * Provider that batches multiple database reads from a source provider together, for efficiency.
@@ -18,12 +18,12 @@ export declare class BatchProvider extends ThroughProvider {
18
18
  protected readonly _gets: MutableObject<Promise<any>>;
19
19
  /** List of currently ongoing subscription streams. */
20
20
  protected readonly _subs: MutableObject<Observable<any>>;
21
- get<T extends Data>(ref: DocumentReference<T>): Result<Entity<T>> | Promise<Result<Entity<T>>>;
21
+ getDocument<T extends Data>(ref: DocumentReference<T>): OptionalEntity<T> | Promise<OptionalEntity<T>>;
22
22
  /** Await a result and delete it from get requests when done. */
23
23
  private _awaitDocument;
24
- subscribe<T extends Data>(ref: DocumentReference<T>, observer: Observer<Result<Entity<T>>>): Unsubscriber;
25
- getQuery<T extends Data>(ref: QueryReference<T>): Iterable<Entity<T>> | Promise<Iterable<Entity<T>>>;
24
+ subscribeDocument<T extends Data>(ref: DocumentReference<T>, observer: PartialObserver<OptionalEntity<T>>): Unsubscribe;
25
+ getQuery<T extends Data>(ref: QueryReference<T>): Entities<T> | Promise<Entities<T>>;
26
26
  /** Await a set of results and delete from get requests when done. */
27
- private _awaitResults;
28
- subscribeQuery<T extends Data>(ref: QueryReference<T>, observer: Observer<ImmutableArray<Entity<T>>>): Unsubscriber;
27
+ private _awaitEntities;
28
+ subscribeQuery<T extends Data>(ref: QueryReference<T>, observer: PartialObserver<Entities<T>>): Unsubscribe;
29
29
  }
@@ -1,7 +1,6 @@
1
- import { getArray } from "../util/array.js";
2
- import { ArrayObserver, awaitNext } from "../util/observe.js";
3
1
  import { isAsync } from "../util/async.js";
4
- import { LazyState } from "../stream/LazyState.js";
2
+ import { DelayedSelfClosingState } from "../state/SelfClosingState.js";
3
+ import { awaitNext, connected } from "../observe/util.js";
5
4
  import { ThroughProvider } from "./ThroughProvider.js";
6
5
  /** How long to wait after all subscriptions have ended to close the source subscription. */
7
6
  const STOP_DELAY = 2000;
@@ -23,38 +22,37 @@ export class BatchProvider extends ThroughProvider {
23
22
  this._subs = {}; // eslint-disable-line @typescript-eslint/no-explicit-any
24
23
  }
25
24
  // Override to combine multiple requests into one.
26
- get(ref) {
25
+ getDocument(ref) {
27
26
  const key = ref.toString();
28
27
  const get = this._gets[key];
29
28
  if (get)
30
29
  return get;
31
- const result = super.get(ref);
30
+ const result = super.getDocument(ref);
32
31
  return isAsync(result) ? (this._gets[key] = this._awaitDocument(ref, result)) : result;
33
32
  }
34
33
  /** Await a result and delete it from get requests when done. */
35
- async _awaitDocument(ref, asyncResult) {
36
- const result = await asyncResult;
34
+ async _awaitDocument(ref, asyncEntity) {
35
+ const result = await asyncEntity;
37
36
  const key = ref.toString();
38
37
  delete this._gets[key];
39
38
  return result;
40
39
  }
41
40
  // Override to combine multiple subscriptions into one.
42
- subscribe(ref, observer) {
41
+ subscribeDocument(ref, observer) {
43
42
  var _a;
44
43
  const key = ref.toString();
45
- // TidyState completes itself `STOP_DELAY` milliseconds after its last observer unsubscribes.
46
- // States also send their most recently received value to any new observers.
47
- const sub = ((_a = this._subs)[key] || (_a[key] = new LazyState(STOP_DELAY).from(s => {
44
+ // Subscribe a new `DelayedSelfClosingState` to the source.
45
+ // Self-closing state closes itself when it's not being used for `STOP_DELAY` milliseconds, which ends the source subscription too.
46
+ return ((_a = this._subs)[key] || (_a[key] = connected(s => {
48
47
  var _a;
49
- const stop = super.subscribe(ref, s);
50
- // The first value from the new subscription can be reused for any concurrent get requests.
51
- (_a = this._gets)[key] || (_a[key] = this._awaitDocument(ref, awaitNext(sub)));
48
+ const stop = super.subscribeDocument(ref, s);
49
+ if (s instanceof DelayedSelfClosingState)
50
+ (_a = this._gets)[key] || (_a[key] = this._awaitDocument(ref, awaitNext(s))); // The first value from the new subscription can also power any concurrent get requests (which saves that separate request).
52
51
  return () => {
53
- delete this._subs[key];
52
+ delete this._subs[key]; // Delete this subscription from subs to allow a new subscription to be made in future.
54
53
  stop();
55
54
  };
56
- })));
57
- return sub.subscribe(observer);
55
+ }, new DelayedSelfClosingState(STOP_DELAY)))).subscribe(observer);
58
56
  }
59
57
  // Override to combine multiple requests into one.
60
58
  getQuery(ref) {
@@ -63,12 +61,11 @@ export class BatchProvider extends ThroughProvider {
63
61
  if (get)
64
62
  return get;
65
63
  const result = super.getQuery(ref);
66
- return isAsync(result) ? (this._gets[key] = this._awaitResults(ref, result)) : result;
64
+ return isAsync(result) ? (this._gets[key] = this._awaitEntities(ref, result)) : result;
67
65
  }
68
66
  /** Await a set of results and delete from get requests when done. */
69
- async _awaitResults(ref, asyncResults) {
70
- // Convert the iterable to a map because it might be read multiple times.
71
- const results = getArray(await asyncResults);
67
+ async _awaitEntities(ref, asyncEntities) {
68
+ const results = await asyncEntities;
72
69
  const key = ref.toString();
73
70
  delete this._gets[key];
74
71
  return results;
@@ -77,19 +74,17 @@ export class BatchProvider extends ThroughProvider {
77
74
  subscribeQuery(ref, observer) {
78
75
  var _a;
79
76
  const key = ref.toString();
80
- // TidyState completes itself `STOP_DELAY` milliseconds after its last observer unsubscribes.
81
- // States also send their most recently received value to any new observers.
82
- const sub = ((_a = this._subs)[key] || (_a[key] = new LazyState(STOP_DELAY).from(o => {
77
+ // Subscribe a new `DelayedSelfClosingState` to the source.
78
+ // Self-closing state closes itself when it's not being used for `STOP_DELAY` milliseconds, which ends the source subscription too.
79
+ return ((_a = this._subs)[key] || (_a[key] = connected(s => {
83
80
  var _a;
84
- // Convert the iterable to an array because it might be read multiple times.
85
- const stop = super.subscribeQuery(ref, new ArrayObserver(o));
86
- // The first value from the subscription can be reused for any concurrent get requests.
87
- (_a = this._gets)[key] || (_a[key] = this._awaitResults(ref, awaitNext(sub)));
81
+ const stop = super.subscribeQuery(ref, s);
82
+ if (s instanceof DelayedSelfClosingState)
83
+ (_a = this._gets)[key] || (_a[key] = this._awaitEntities(ref, awaitNext(s))); // The first value from the subscription can also power any concurrent get requests (which saves that separate request).
88
84
  return () => {
89
- delete this._subs[key];
85
+ delete this._subs[key]; // Delete this subscription from subs to allow a new subscription to be made in future.
90
86
  stop();
91
87
  };
92
- })));
93
- return sub.subscribe(observer);
88
+ }, new DelayedSelfClosingState(STOP_DELAY)))).subscribe(observer);
94
89
  }
95
90
  }
@@ -1,6 +1,7 @@
1
1
  import type { DocumentReference, QueryReference } from "../db/Reference.js";
2
- import type { Data, Result, Entity } from "../util/data.js";
3
- import { Observer, Unsubscriber } from "../util/observe.js";
2
+ import type { Data, Entities, OptionalEntity } from "../util/data.js";
3
+ import type { PartialObserver } from "../observe/Observer.js";
4
+ import type { Unsubscribe } from "../observe/Observable.js";
4
5
  import { DataUpdate } from "../update/DataUpdate.js";
5
6
  import type { Provider, AsynchronousProvider } from "./Provider.js";
6
7
  import { MemoryProvider } from "./MemoryProvider.js";
@@ -8,26 +9,16 @@ import { ThroughProvider } from "./ThroughProvider.js";
8
9
  /** Keep a copy of received data in a local cache. */
9
10
  export declare class CacheProvider extends ThroughProvider implements AsynchronousProvider {
10
11
  /** The local cache provider. */
11
- readonly cache: MemoryProvider;
12
- /** Last-known-correct time for data, indexed by key to power `getCachedAge()` etc. */
13
- private _times;
12
+ readonly memory: MemoryProvider;
14
13
  constructor(source: Provider, cache?: MemoryProvider);
15
- /** Is a given document or query in the cache? */
16
- isCached<T extends Data>(ref: DocumentReference<T> | QueryReference<T>): boolean;
17
- /** Get the cache age for a given document or query reference. */
18
- getCachedAge<T extends Data>(ref: DocumentReference<T> | QueryReference<T>): number;
19
- /** Cache an individual document result. */
20
- private _cacheResult;
21
- get<T extends Data>(ref: DocumentReference<T>): Promise<Result<Entity<T>>>;
22
- subscribe<T extends Data>(ref: DocumentReference<T>, observer: Observer<Result<Entity<T>>>): Unsubscriber;
23
- add<T extends Data>(ref: QueryReference<T>, data: T): Promise<string>;
24
- set<T extends Data>(ref: DocumentReference<T>, data: T): Promise<void>;
25
- update<T extends Data>(ref: DocumentReference<T>, update: DataUpdate<T>): Promise<void>;
26
- delete<T extends Data>(ref: DocumentReference<T>): Promise<void>;
27
- /** Cache a set of document entries. */
28
- private _cacheEntities;
29
- getQuery<T extends Data>(ref: QueryReference<T>): Promise<Iterable<Entity<T>>>;
30
- subscribeQuery<T extends Data>(ref: QueryReference<T>, observer: Observer<Iterable<Entity<T>>>): Unsubscriber;
14
+ getDocument<T extends Data>(ref: DocumentReference<T>): Promise<OptionalEntity<T>>;
15
+ subscribeDocument<T extends Data>(ref: DocumentReference<T>, observer: PartialObserver<OptionalEntity<T>>): Unsubscribe;
16
+ addDocument<T extends Data>(ref: QueryReference<T>, data: T): Promise<string>;
17
+ setDocument<T extends Data>(ref: DocumentReference<T>, data: T): Promise<void>;
18
+ updateDocument<T extends Data>(ref: DocumentReference<T>, update: DataUpdate<T>): Promise<void>;
19
+ deleteDocument<T extends Data>(ref: DocumentReference<T>): Promise<void>;
20
+ getQuery<T extends Data>(ref: QueryReference<T>): Promise<Entities<T>>;
21
+ subscribeQuery<T extends Data>(ref: QueryReference<T>, observer: PartialObserver<Entities<T>>): Unsubscribe;
31
22
  setQuery<T extends Data>(ref: QueryReference<T>, data: T): Promise<number>;
32
23
  updateQuery<T extends Data>(ref: QueryReference<T>, update: DataUpdate<T>): Promise<number>;
33
24
  deleteQuery<T extends Data>(ref: QueryReference<T>): Promise<number>;
@@ -1,108 +1,74 @@
1
- import { TransformObserver } from "../util/observe.js";
1
+ import { TransformObserver } from "../observe/TransformObserver.js";
2
2
  import { MemoryProvider } from "./MemoryProvider.js";
3
3
  import { ThroughProvider } from "./ThroughProvider.js";
4
4
  /** Keep a copy of received data in a local cache. */
5
5
  export class CacheProvider extends ThroughProvider {
6
6
  constructor(source, cache = new MemoryProvider()) {
7
7
  super(source);
8
- /** Last-known-correct time for data, indexed by key to power `getCachedAge()` etc. */
9
- this._times = {};
10
- this.cache = cache;
11
- }
12
- /** Is a given document or query in the cache? */
13
- isCached(ref) {
14
- const key = ref.toString();
15
- return typeof this._times[key] === "number";
16
- }
17
- /** Get the cache age for a given document or query reference. */
18
- getCachedAge(ref) {
19
- const key = ref.toString();
20
- const time = this._times[key];
21
- return typeof time !== "number" ? Infinity : Date.now() - time;
22
- }
23
- /** Cache an individual document result. */
24
- _cacheResult(ref, result) {
25
- result ? this.cache.table(ref).set(result) : this.cache.table(ref).delete(ref.id);
26
- this._times[`${ref.collection}.${ref.id}`] = Date.now();
27
- return result;
8
+ this.memory = cache;
28
9
  }
29
10
  // Override to cache any got result.
30
- async get(ref) {
31
- return this._cacheResult(ref, await super.get(ref));
11
+ async getDocument(ref) {
12
+ const table = this.memory.getTable(ref);
13
+ const result = await super.getDocument(ref);
14
+ result ? table.setEntity(result) : table.deleteDocument(ref.id);
15
+ return result;
32
16
  }
33
17
  // Override to cache any got results.
34
- subscribe(ref, observer) {
35
- return super.subscribe(ref, new TransformObserver(result => this._cacheResult(ref, result), observer));
36
- }
37
- async add(ref, data) {
38
- const id = await super.add(ref, data);
39
- const { collection } = ref;
40
- this.cache.table(ref).set({ id, ...data });
41
- this._times[`${collection}.${id}`] = Date.now();
18
+ subscribeDocument(ref, observer) {
19
+ const table = this.memory.getTable(ref);
20
+ return super.subscribeDocument(ref, new TransformObserver((result) => {
21
+ result ? table.setEntity(result) : table.deleteDocument(ref.id);
22
+ return result;
23
+ }, observer));
24
+ }
25
+ async addDocument(ref, data) {
26
+ const id = await super.addDocument(ref, data);
27
+ this.memory.getTable(ref).setDocument(id, data);
42
28
  return id;
43
29
  }
44
- async set(ref, data) {
45
- await super.set(ref, data);
46
- const { collection, id } = ref;
47
- this.cache.table(ref).set({ id, ...data });
48
- this._times[`${collection}.${id}`] = Date.now();
49
- }
50
- async update(ref, update) {
51
- await super.update(ref, update);
52
- // Update the document in the cache if it exists using `updateDocuments()` and an `id` query.
53
- // Using `updateDocument()` would throw `RequiredError` if the document didn't exist.
54
- this.cache.updateQuery(ref.optional, update);
55
- // Don't update `_times` because we're not refreshing all the data.
30
+ async setDocument(ref, data) {
31
+ await super.setDocument(ref, data);
32
+ this.memory.getTable(ref).setDocument(ref.id, data);
56
33
  }
57
- async delete(ref) {
58
- await super.delete(ref);
59
- const { collection, id } = ref;
60
- this.cache.delete(ref);
61
- this._times[`${collection}.${id}`] = Date.now();
34
+ async updateDocument(ref, update) {
35
+ await super.updateDocument(ref, update);
36
+ // Update the cache but only if the document exists.
37
+ const table = this.memory.getTable(ref);
38
+ if (table.getDocument(ref.id))
39
+ table.updateDocument(ref.id, update);
62
40
  }
63
- /** Cache a set of document entries. */
64
- *_cacheEntities(ref, entities) {
65
- // We know the received set of results is the 'complete' set of results for this query.
66
- // So for correctness any documents matching this query that aren't in the new set of results should be deleted.
67
- // None of this applies if there's a query limit, because the document could have been moved to a different page so shouldn't be deleted.
68
- if (!ref.limit)
69
- for (const id of Object.keys(this.cache.getQuery(ref)))
70
- if (!(id in entities))
71
- this.cache.delete(ref.doc(id));
72
- // Save new results to the cache.
73
- const { collection } = ref;
74
- const now = Date.now();
75
- const table = this.cache.table(ref);
76
- for (const entity of entities) {
77
- const { id } = entity;
78
- table.set(entity);
79
- this._times[`${collection}.${id}`] = now;
80
- yield entity;
81
- }
82
- // Save the last-cached time.
83
- this._times[ref.toString()] = now;
41
+ async deleteDocument(ref) {
42
+ await super.deleteDocument(ref);
43
+ this.memory.deleteDocument(ref);
84
44
  }
85
45
  // Override to cache any got results.
86
46
  async getQuery(ref) {
87
- return this._cacheEntities(ref, await super.getQuery(ref));
47
+ const entities = await super.getQuery(ref);
48
+ this.memory.getTable(ref).setEntities(ref, entities);
49
+ return entities;
88
50
  }
89
51
  // Override to cache any got results.
90
52
  subscribeQuery(ref, observer) {
91
- return super.subscribeQuery(ref, new TransformObserver(entries => this._cacheEntities(ref, entries), observer));
53
+ const table = this.memory.getTable(ref);
54
+ return super.subscribeQuery(ref, new TransformObserver((entities) => {
55
+ table.setEntities(ref, entities);
56
+ return entities;
57
+ }, observer));
92
58
  }
93
59
  async setQuery(ref, data) {
94
60
  const count = await super.setQuery(ref, data);
95
- this.cache.setQuery(ref, data);
61
+ this.memory.setQuery(ref, data);
96
62
  return count;
97
63
  }
98
64
  async updateQuery(ref, update) {
99
65
  const count = await super.updateQuery(ref, update);
100
- this.cache.updateQuery(ref, update);
66
+ this.memory.updateQuery(ref, update);
101
67
  return count;
102
68
  }
103
69
  async deleteQuery(ref) {
104
70
  const count = await super.deleteQuery(ref);
105
- this.cache.deleteQuery(ref);
71
+ this.memory.deleteQuery(ref);
106
72
  return count;
107
73
  }
108
74
  }
@@ -0,0 +1,20 @@
1
+ import type { Data, Entities, OptionalEntity } from "../util/data.js";
2
+ import type { DocumentReference, QueryReference } from "../db/Reference.js";
3
+ import type { DataUpdate } from "../update/DataUpdate.js";
4
+ import type { PartialObserver } from "../observe/Observer.js";
5
+ import type { Unsubscribe } from "../observe/Observable.js";
6
+ import { ThroughProvider } from "./ThroughProvider.js";
7
+ /** Provider that logs its operations to the console for debugging purposes. */
8
+ export declare class DebugProvider extends ThroughProvider {
9
+ getDocument<T extends Data>(ref: DocumentReference<T>): OptionalEntity<T> | PromiseLike<OptionalEntity<T>>;
10
+ subscribeDocument<T extends Data>(ref: DocumentReference<T>, observer: PartialObserver<OptionalEntity<T>>): Unsubscribe;
11
+ addDocument<T extends Data>(ref: QueryReference<T>, data: T): string | PromiseLike<string>;
12
+ setDocument<T extends Data>(ref: DocumentReference<T>, data: T): void | PromiseLike<void>;
13
+ updateDocument<T extends Data>(ref: DocumentReference<T>, update: DataUpdate<T>): void | PromiseLike<void>;
14
+ deleteDocument<T extends Data>(ref: DocumentReference<T>): void | PromiseLike<void>;
15
+ getQuery<T extends Data>(ref: QueryReference<T>): Entities<T> | PromiseLike<Entities<T>>;
16
+ subscribeQuery<T extends Data>(ref: QueryReference<T>, observer: PartialObserver<Entities<T>>): Unsubscribe;
17
+ setQuery<T extends Data>(ref: QueryReference<T>, data: T): number | PromiseLike<number>;
18
+ updateQuery<T extends Data>(ref: QueryReference<T>, update: DataUpdate<T>): number | PromiseLike<number>;
19
+ deleteQuery<T extends Data>(ref: QueryReference<T>): number | PromiseLike<number>;
20
+ }
@@ -0,0 +1,170 @@
1
+ /* eslint-disable no-console */
2
+ import { isAsync } from "../util/async.js";
3
+ import { ThroughObserver } from "../observe/ThroughObserver.js";
4
+ import { ThroughProvider } from "./ThroughProvider.js";
5
+ /** Provider that logs its operations to the console for debugging purposes. */
6
+ export class DebugProvider extends ThroughProvider {
7
+ getDocument(ref) {
8
+ console.log(`Get "${ref}"`);
9
+ try {
10
+ const result = super.getDocument(ref);
11
+ return isAsync(result)
12
+ ? result.then(undefined, reason => {
13
+ console.error(`Error getting "${ref}"`, reason);
14
+ throw reason;
15
+ })
16
+ : result;
17
+ }
18
+ catch (reason) {
19
+ console.error(`Error getting "${ref}"`, reason);
20
+ throw reason;
21
+ }
22
+ }
23
+ subscribeDocument(ref, observer) {
24
+ console.log(`Subscribe "${ref}"`);
25
+ return super.subscribeDocument(ref, new DatabaseDebugObserver(ref, observer));
26
+ }
27
+ addDocument(ref, data) {
28
+ console.log(`Add to "${ref}"`, data);
29
+ try {
30
+ const result = super.addDocument(ref, data);
31
+ return isAsync(result)
32
+ ? result.then(undefined, reason => {
33
+ console.error(`Error adding to "${ref}"`, reason);
34
+ throw reason;
35
+ })
36
+ : result;
37
+ }
38
+ catch (reason) {
39
+ console.error(`Error adding to "${ref}"`, reason);
40
+ throw reason;
41
+ }
42
+ }
43
+ setDocument(ref, data) {
44
+ console.log(`Set "${ref}"`, data);
45
+ try {
46
+ const result = super.setDocument(ref, data);
47
+ return isAsync(result)
48
+ ? result.then(undefined, reason => {
49
+ console.error(`Error setting "${ref}"`, reason);
50
+ throw reason;
51
+ })
52
+ : result;
53
+ }
54
+ catch (reason) {
55
+ console.error(`Error setting "${ref}"`, reason);
56
+ throw reason;
57
+ }
58
+ }
59
+ updateDocument(ref, update) {
60
+ console.log(`Update "${ref}"`, update.updates);
61
+ try {
62
+ const result = super.updateDocument(ref, update);
63
+ return isAsync(result)
64
+ ? result.then(undefined, reason => {
65
+ console.error(`Error updating "${ref}"`, reason);
66
+ throw reason;
67
+ })
68
+ : result;
69
+ }
70
+ catch (reason) {
71
+ console.error(`Error updating "${ref}"`, reason);
72
+ throw reason;
73
+ }
74
+ }
75
+ deleteDocument(ref) {
76
+ console.log(`Delete "${ref}"`);
77
+ try {
78
+ const result = super.deleteDocument(ref);
79
+ return isAsync(result)
80
+ ? result.then(undefined, reason => {
81
+ console.error(`Error deleting "${ref}"`, reason);
82
+ throw reason;
83
+ })
84
+ : result;
85
+ }
86
+ catch (reason) {
87
+ console.error(`Error deleting "${ref}"`, reason);
88
+ throw reason;
89
+ }
90
+ }
91
+ getQuery(ref) {
92
+ console.log(`Get "${ref}"`);
93
+ try {
94
+ const results = super.getQuery(ref);
95
+ return isAsync(results)
96
+ ? results.then(undefined, reason => {
97
+ console.error(`Error getting "${ref}"`, reason);
98
+ throw reason;
99
+ })
100
+ : results;
101
+ }
102
+ catch (reason) {
103
+ console.error(`Error getting "${ref}"`, reason);
104
+ throw reason;
105
+ }
106
+ }
107
+ subscribeQuery(ref, observer) {
108
+ console.log(`Subscribe "${ref}"`);
109
+ return super.subscribeQuery(ref, new DatabaseDebugObserver(ref, observer));
110
+ }
111
+ setQuery(ref, data) {
112
+ console.log(`Set "${ref}"`, data);
113
+ try {
114
+ const result = super.setQuery(ref, data);
115
+ return isAsync(result)
116
+ ? result.then(undefined, reason => {
117
+ console.error(`Error setting "${ref}"`, reason);
118
+ throw reason;
119
+ })
120
+ : result;
121
+ }
122
+ catch (reason) {
123
+ console.error(`Error setting "${ref}"`, reason);
124
+ throw reason;
125
+ }
126
+ }
127
+ updateQuery(ref, update) {
128
+ console.log(`Update "${ref}"`, update.updates);
129
+ try {
130
+ const result = super.updateQuery(ref, update);
131
+ return isAsync(result)
132
+ ? result.then(undefined, reason => {
133
+ console.error(`Error updating "${ref}"`, reason);
134
+ throw reason;
135
+ })
136
+ : result;
137
+ }
138
+ catch (reason) {
139
+ console.error(`Error updating "${ref}"`, reason);
140
+ throw reason;
141
+ }
142
+ }
143
+ deleteQuery(ref) {
144
+ console.log(`Delete "${ref}"`);
145
+ try {
146
+ const result = super.deleteQuery(ref);
147
+ return isAsync(result)
148
+ ? result.then(undefined, reason => {
149
+ console.error(`Error writing "${ref}"`, reason);
150
+ throw reason;
151
+ })
152
+ : result;
153
+ }
154
+ catch (reason) {
155
+ console.error(`Error writing "${ref}"`, reason);
156
+ throw reason;
157
+ }
158
+ }
159
+ }
160
+ /** Observer that wraps errors in subscriptions in `ReferenceReadError` */
161
+ class DatabaseDebugObserver extends ThroughObserver {
162
+ constructor(ref, target) {
163
+ super(target);
164
+ this.ref = ref;
165
+ }
166
+ error(reason) {
167
+ console.log(`Error in subscription to ${this.ref}`, reason);
168
+ super.error(reason);
169
+ }
170
+ }