shelving 1.19.0 → 1.20.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 (104) hide show
  1. package/db/Change.d.ts +7 -7
  2. package/db/Database.d.ts +50 -28
  3. package/db/Database.js +31 -24
  4. package/db/Pagination.d.ts +17 -9
  5. package/db/Pagination.js +49 -32
  6. package/db/errors.d.ts +6 -6
  7. package/error/ConditionError.d.ts +7 -0
  8. package/error/ConditionError.js +10 -0
  9. package/error/index.d.ts +1 -0
  10. package/error/index.js +1 -0
  11. package/firestore/client/FirestoreClientProvider.d.ts +7 -7
  12. package/firestore/lite/FirestoreLiteProvider.d.ts +6 -6
  13. package/firestore/server/FirestoreServerProvider.d.ts +8 -8
  14. package/package.json +1 -1
  15. package/provider/BatchProvider.d.ts +5 -5
  16. package/provider/BatchProvider.js +16 -12
  17. package/provider/CacheProvider.d.ts +10 -10
  18. package/provider/CacheProvider.js +11 -20
  19. package/provider/MemoryProvider.d.ts +7 -7
  20. package/provider/Provider.d.ts +18 -18
  21. package/provider/ThroughProvider.d.ts +7 -7
  22. package/provider/ValidationProvider.d.ts +8 -8
  23. package/provider/ValidationProvider.js +5 -19
  24. package/query/Query.d.ts +1 -1
  25. package/query/Query.js +2 -2
  26. package/react/index.d.ts +2 -2
  27. package/react/index.js +2 -2
  28. package/react/{useResult.d.ts → useDocument.d.ts} +4 -4
  29. package/react/{useResult.js → useDocument.js} +4 -6
  30. package/react/{useData.d.ts → useDocumentData.d.ts} +4 -4
  31. package/react/useDocumentData.js +9 -0
  32. package/react/useFetch.d.ts +1 -1
  33. package/react/useFetch.js +2 -3
  34. package/react/usePagination.d.ts +2 -2
  35. package/react/useQuery.d.ts +8 -7
  36. package/react/useQuery.js +10 -11
  37. package/react/useSubscribe.js +2 -2
  38. package/schema/AllowSchema.d.ts +17 -7
  39. package/schema/AllowSchema.js +21 -10
  40. package/schema/ArraySchema.js +1 -1
  41. package/schema/DataSchema.d.ts +1 -1
  42. package/schema/DataSchema.js +1 -1
  43. package/schema/MapSchema.d.ts +0 -1
  44. package/schema/MapSchema.js +6 -6
  45. package/schema/NumberSchema.d.ts +0 -2
  46. package/schema/ObjectSchema.d.ts +1 -2
  47. package/schema/ObjectSchema.js +2 -2
  48. package/schema/StringSchema.d.ts +1 -3
  49. package/schema/StringSchema.js +1 -2
  50. package/stream/ArrayState.d.ts +1 -0
  51. package/stream/ArrayState.js +5 -0
  52. package/stream/State.d.ts +38 -17
  53. package/stream/State.js +66 -20
  54. package/stream/Stream.d.ts +56 -7
  55. package/stream/Stream.js +114 -8
  56. package/stream/index.d.ts +0 -6
  57. package/stream/index.js +0 -6
  58. package/test/basics.js +2 -2
  59. package/util/assert.d.ts +4 -2
  60. package/util/assert.js +7 -2
  61. package/util/async.d.ts +41 -0
  62. package/util/async.js +71 -0
  63. package/util/constants.d.ts +3 -3
  64. package/util/constants.js +3 -3
  65. package/util/data.d.ts +114 -4
  66. package/util/data.js +62 -3
  67. package/util/derive.d.ts +0 -3
  68. package/util/derive.js +4 -12
  69. package/util/diff.d.ts +3 -3
  70. package/util/dispatch.d.ts +3 -3
  71. package/util/dispatch.js +5 -15
  72. package/util/equal.d.ts +2 -2
  73. package/util/equal.js +5 -2
  74. package/util/error.d.ts +0 -4
  75. package/util/hydrate.js +3 -2
  76. package/util/index.d.ts +2 -2
  77. package/util/index.js +2 -2
  78. package/util/{iterable.d.ts → iterate.d.ts} +45 -18
  79. package/util/{iterable.js → iterate.js} +72 -13
  80. package/util/map.d.ts +2 -3
  81. package/util/map.js +2 -2
  82. package/util/merge.d.ts +7 -4
  83. package/util/merge.js +7 -5
  84. package/util/object.d.ts +7 -114
  85. package/util/object.js +3 -60
  86. package/util/observable.d.ts +57 -9
  87. package/util/observable.js +132 -44
  88. package/util/string.js +2 -2
  89. package/util/validate.js +2 -3
  90. package/react/useData.js +0 -9
  91. package/stream/AbstractState.d.ts +0 -35
  92. package/stream/AbstractState.js +0 -84
  93. package/stream/AbstractStream.d.ts +0 -66
  94. package/stream/AbstractStream.js +0 -123
  95. package/stream/DeriveStream.d.ts +0 -12
  96. package/stream/DeriveStream.js +0 -29
  97. package/stream/LimitStream.d.ts +0 -13
  98. package/stream/LimitStream.js +0 -26
  99. package/stream/ValidateStream.d.ts +0 -8
  100. package/stream/ValidateStream.js +0 -18
  101. package/stream/errors.d.ts +0 -4
  102. package/stream/errors.js +0 -6
  103. package/util/promise.d.ts +0 -14
  104. package/util/promise.js +0 -27
package/db/Change.d.ts CHANGED
@@ -4,16 +4,16 @@ import type { Database, DatabaseDocument } from "./Database.js";
4
4
  /** A single change that can be made to a database. */
5
5
  export declare abstract class Change<T extends Datas> {
6
6
  /** Apply this change to a database. */
7
- abstract apply(db: Database<T>): void | Promise<void>;
7
+ abstract apply(db: Database<T>): void | PromiseLike<void>;
8
8
  }
9
9
  /** A change that writes a document in a database. */
10
- export declare class Write<D extends Datas, C extends Key<D>> extends Change<D> {
11
- static on<X extends Datas, Y extends Key<X>>({ collection, id }: DatabaseDocument<X, Y>, value: X[Y] | Transform<X[Y]> | undefined): Write<X, Y>;
10
+ export declare class Write<C extends Key<D>, D extends Datas> extends Change<D> {
11
+ static on<Y extends Key<X>, X extends Datas>({ collection, id }: DatabaseDocument<Y, X>, value: X[Y] | Transform<X[Y]> | undefined): Write<Y, X>;
12
12
  readonly collection: C;
13
13
  readonly id: string;
14
14
  readonly value: D[C] | Transform<D[C]> | undefined;
15
15
  constructor(collection: C, id: string, value: D[C] | Transform<D[C]> | undefined);
16
- apply(db: Database<D>): void | Promise<void>;
16
+ apply(db: Database<D>): void | PromiseLike<void>;
17
17
  }
18
18
  /**
19
19
  * Set of writes that can be applied to documents in a database.
@@ -25,11 +25,11 @@ export declare class Writes<D extends Datas> extends Change<D> {
25
25
  readonly changes: ImmutableArray<Change<D>>;
26
26
  constructor(...changes: ImmutableArray<Change<D>>);
27
27
  /** Return a new `Changes` instance with an additional `Write` instance in its changes list. */
28
- set<C extends Key<D>>({ collection, id }: DatabaseDocument<D, C>, data: D[C]): this;
28
+ set<C extends Key<D>>({ collection, id }: DatabaseDocument<C, D>, data: D[C]): this;
29
29
  /** Return a new `Changes` instance with an additional `Write` instance in its changes list. */
30
- delete<C extends Key<D>>({ collection, id }: DatabaseDocument<D, C>): this;
30
+ delete<C extends Key<D>>({ collection, id }: DatabaseDocument<C, D>): this;
31
31
  /** Return a new `Changes` instance with an additional `Write` instance in its changes list. */
32
- update<C extends Key<D>>({ collection, id }: DatabaseDocument<D, C>, transforms: Transforms<D[C]> | Transform<D[C]>): this;
32
+ update<C extends Key<D>>({ collection, id }: DatabaseDocument<C, D>, transforms: Transforms<D[C]> | Transform<D[C]>): this;
33
33
  apply(db: Database<D>): Promise<void> | undefined;
34
34
  }
35
35
  /** Set of hydrations for all change classes. */
package/db/Database.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Dispatcher, Entry, Observable, Observer, Result, Unsubscriber, Datas, Results, Validatable, Validator, Validators, Key, Data, ImmutableMap } from "../util/index.js";
1
+ import { Dispatcher, Entry, Observable, Observer, Result, Unsubscriber, Datas, ResultsMap, Validatable, Validator, Validators, Key, Data, Results } from "../util/index.js";
2
2
  import { Transform, Transforms } from "../transform/index.js";
3
3
  import type { Provider } from "../provider/Provider.js";
4
4
  import { Filters, Sorts, Query } from "../query/index.js";
@@ -14,18 +14,18 @@ export declare class Database<D extends Datas> {
14
14
  readonly provider: Provider<D>;
15
15
  constructor(validators: Validators<D>, provider: Provider<D>);
16
16
  /** Create a query on a collection in this model. */
17
- query<C extends Key<D>>(collection: C, filters?: Filters<D[C]>, sorts?: Sorts<D[C]>, limit?: number | null): DatabaseQuery<D, C>;
17
+ query<C extends Key<D>>(collection: C, filters?: Filters<D[C]>, sorts?: Sorts<D[C]>, limit?: number | null): DatabaseQuery<C, D>;
18
18
  /** Reference a document in a collection in this model. */
19
- doc<C extends Key<D>>(collection: C, id: string): DatabaseDocument<D, C>;
19
+ doc<C extends Key<D>>(collection: C, id: string): DatabaseDocument<C, D>;
20
20
  }
21
21
  /** A documents reference within a specific database. */
22
- export declare class DatabaseQuery<D extends Datas, C extends Key<D>> extends Query<D[C]> implements Observable<Results<D[C]>>, Validatable<Results<D[C]>>, Iterable<Entry<D[C]>> {
22
+ export declare class DatabaseQuery<C extends Key<D>, D extends Datas> extends Query<D[C]> implements Observable<Results<D[C]>>, Validatable<Results<D[C]>>, Iterable<Entry<D[C]>> {
23
23
  readonly db: Database<D>;
24
24
  readonly validator: Validator<D[C]>;
25
25
  readonly collection: C;
26
26
  constructor(db: Database<D>, collection: C, filters?: Filters<D[C]>, sorts?: Sorts<D[C]>, limit?: number | null);
27
27
  /** Reference a document in this query's collection. */
28
- doc(id: string): DatabaseDocument<D, C>;
28
+ doc(id: string): DatabaseDocument<C, D>;
29
29
  /**
30
30
  * Create a new document with a random ID.
31
31
  * - Created document is guaranteed to have a unique ID.
@@ -33,22 +33,27 @@ export declare class DatabaseQuery<D extends Datas, C extends Key<D>> extends Qu
33
33
  * @param data Complete data to set the document to.
34
34
  * @return String ID for the created document (possibly promised).
35
35
  */
36
- add(data: D[C]): string | Promise<string>;
36
+ add(data: D[C]): string | PromiseLike<string>;
37
37
  /**
38
38
  * Get an iterable that yields the results of this entry.
39
39
  * @return Map containing the results.
40
40
  */
41
- get results(): Results<D[C]> | Promise<Results<D[C]>>;
41
+ get results(): Results<D[C]> | PromiseLike<Results<D[C]>>;
42
42
  /**
43
- * Read the results of this query into a map.
44
- * @return Set of results in `id: data` format (possibly promised).
43
+ * Get an iterable that yields the results of this entry.
44
+ * @return Map containing the results.
45
45
  */
46
- get map(): ImmutableMap<D[C]> | Promise<ImmutableMap<D[C]>>;
46
+ get resultsMap(): ResultsMap<D[C]> | PromiseLike<ResultsMap<D[C]>>;
47
47
  /**
48
48
  * Count the number of results of this set of documents.
49
49
  * @return Number of documents in the collection (possibly promised).
50
50
  */
51
- get count(): number | Promise<number>;
51
+ get count(): number | PromiseLike<number>;
52
+ /**
53
+ * Get an entry for the first document matching this query.
54
+ * @return Entry in `[id, data]` format for the first document, or `undefined` if there are no matching documents (possibly promised).
55
+ */
56
+ get first(): Entry<D[C]> | undefined | PromiseLike<Entry<D[C]> | undefined>;
52
57
  /**
53
58
  * Subscribe to all matching documents.
54
59
  * - `next()` is called once with the initial results, and again any time the results change.
@@ -62,17 +67,24 @@ export declare class DatabaseQuery<D extends Datas, C extends Key<D>> extends Qu
62
67
  */
63
68
  subscribe(next: Observer<Results<D[C]>> | Dispatcher<Results<D[C]>>, error?: Dispatcher<Error | unknown>, complete?: Dispatcher<void>): Unsubscriber;
64
69
  /**
65
- * Get an entry for the first document matching this query.
66
- * @return Entry in `[id, data]` format for the first document, or `undefined` if there are no matching documents (possibly promised).
70
+ * Subscribe to all matching documents.
71
+ * - `next()` is called once with the initial results, and again any time the results change.
72
+ *
73
+ * @param observer Observer with `next`, `error`, or `complete` methods that the document results are reported back to.
74
+ * @param next Callback that is called once initially and again whenever the results change.
75
+ * @param error Callback that is called if an error occurs.
76
+ * @param complete Callback that is called when the subscription is done.
77
+ *
78
+ * @return Function that ends the subscription.
67
79
  */
68
- get first(): Entry<D[C]> | undefined | Promise<Entry<D[C]> | undefined>;
80
+ subscribeMap(next: Observer<ResultsMap<D[C]>> | Dispatcher<ResultsMap<D[C]>>, error?: Dispatcher<Error | unknown>, complete?: Dispatcher<void>): Unsubscriber;
69
81
  /**
70
82
  * Set all matching documents to the same exact value.
71
83
  *
72
84
  * @param data Complete data to set the document to.
73
85
  * @return Nothing (possibly promised).
74
86
  */
75
- set(data: D[C]): void | Promise<void>;
87
+ set(data: D[C]): void | PromiseLike<void>;
76
88
  /**
77
89
  * Update all matching documents with the same partial value.
78
90
  *
@@ -81,17 +93,17 @@ export declare class DatabaseQuery<D extends Datas, C extends Key<D>> extends Qu
81
93
  *
82
94
  * @return Nothing (possibly promised).
83
95
  */
84
- update(transform: Transform<D[C]> | Transforms<D[C]>): void | Promise<void>;
96
+ update(transform: Transform<D[C]> | Transforms<D[C]>): void | PromiseLike<void>;
85
97
  /**
86
98
  * Delete all matching documents.
87
99
  * @return Nothing (possibly promised).
88
100
  */
89
- delete(): void | Promise<void>;
101
+ delete(): void | PromiseLike<void>;
90
102
  /**
91
103
  * Combine `set()`, `update()`, `delete()` into a single method.
92
104
  * @return Nothing (possibly promised).
93
105
  */
94
- write(value: Result<D[C]> | Transform<D[C]>): void | Promise<void>;
106
+ write(value: Result<D[C]> | Transform<D[C]>): void | PromiseLike<void>;
95
107
  /** Iterate over the resuls (will throw `Promise` if the results are asynchronous). */
96
108
  [Symbol.iterator](): Iterator<Entry<D[C]>, void>;
97
109
  /** Validate a set of results for this query reference. */
@@ -99,28 +111,28 @@ export declare class DatabaseQuery<D extends Datas, C extends Key<D>> extends Qu
99
111
  toString(): string;
100
112
  }
101
113
  /** A document reference within a specific database. */
102
- export declare class DatabaseDocument<D extends Datas, C extends Key<D>> implements Observable<Result<D[C]>>, Validatable<D[C]> {
114
+ export declare class DatabaseDocument<C extends Key<D>, D extends Datas> implements Observable<Result<D[C]>>, Validatable<D[C]> {
103
115
  readonly db: Database<D>;
104
116
  readonly validator: Validator<D[C]>;
105
117
  readonly collection: C;
106
118
  readonly id: string;
107
119
  constructor(db: Database<D>, collection: C, id: string);
108
120
  /** Create a query on this document's collection. */
109
- query(filters?: Filters<D[C]>, sorts?: Sorts<D[C]>, limit?: number | null): DatabaseQuery<D, C>;
121
+ query(filters?: Filters<D[C]>, sorts?: Sorts<D[C]>, limit?: number | null): DatabaseQuery<C, D>;
110
122
  /** Get an 'optional' reference to this document (uses a `ModelQuery` with an `id` filter). */
111
- get optional(): DatabaseQuery<D, C>;
123
+ get optional(): DatabaseQuery<C, D>;
112
124
  /**
113
125
  * Does this document exist?
114
126
  *
115
127
  * @return Document's data, or `undefined` if the document doesn't exist (possibly promised).
116
128
  */
117
- get exists(): boolean | Promise<boolean>;
129
+ get exists(): boolean | PromiseLike<boolean>;
118
130
  /**
119
131
  * Get the result of this document.
120
132
  *
121
133
  * @return Document's data, or `undefined` if the document doesn't exist (possibly promised).
122
134
  */
123
- get result(): Result<D[C]> | Promise<Result<D[C]>>;
135
+ get result(): Result<D[C]> | PromiseLike<Result<D[C]>>;
124
136
  /**
125
137
  * Get the data of this document.
126
138
  * - Useful for destructuring, e.g. `{ name, title } = await documentThatMustExist.asyncData`
@@ -128,7 +140,7 @@ export declare class DatabaseDocument<D extends Datas, C extends Key<D>> impleme
128
140
  * @return Document's data (possibly promised).
129
141
  * @throws RequiredError if the document's result was undefined.
130
142
  */
131
- get data(): D[C] | Promise<D[C]>;
143
+ get data(): D[C] | PromiseLike<D[C]>;
132
144
  /**
133
145
  * Subscribe to the result of this document (indefinitely).
134
146
  * - `next()` is called once with the initial result, and again any time the result changes.
@@ -148,7 +160,7 @@ export declare class DatabaseDocument<D extends Datas, C extends Key<D>> impleme
148
160
  *
149
161
  * @return Nothing (possibly promised).
150
162
  */
151
- set(data: D[C]): void | Promise<void>;
163
+ set(data: D[C]): void | PromiseLike<void>;
152
164
  /**
153
165
  * Update this document with partial data.
154
166
  * - If the document exists, merge the partial data into it.
@@ -160,19 +172,29 @@ export declare class DatabaseDocument<D extends Datas, C extends Key<D>> impleme
160
172
  * @return Nothing (possibly promised).
161
173
  * @throws Error If the document does not exist (ideally a `RequiredError` but may be provider-specific).
162
174
  */
163
- update(transforms: Transform<D[C]> | Transforms<D[C]>): void | Promise<void>;
175
+ update(transforms: Transform<D[C]> | Transforms<D[C]>): void | PromiseLike<void>;
164
176
  /**
165
177
  * Delete this document.
166
178
  * - Will not throw an error if the document doesn't exist.
167
179
  *
168
180
  * @return Nothing (possibly promised).
169
181
  */
170
- delete(): void | Promise<void>;
182
+ delete(): void | PromiseLike<void>;
171
183
  /**
172
184
  * Combine `set()`, `update()`, `delete()` into a single method.
173
185
  */
174
- write(value: Result<D[C]> | Transform<D[C]>): void | Promise<void>;
186
+ write(value: Result<D[C]> | Transform<D[C]>): void | PromiseLike<void>;
175
187
  /** Validate data for this query reference. */
176
188
  validate(unsafeData: Data): D[C];
177
189
  toString(): string;
178
190
  }
191
+ /** Get the data for a document from a result for that document. */
192
+ export declare function getDocumentData<C extends Key<D>, D extends Datas>(result: Result<D[C]>, ref: DatabaseDocument<C, D>): D[C];
193
+ /** Database query with a collection name. */
194
+ export declare type CollectionQuery<C extends string, T extends Data> = DatabaseQuery<C, {
195
+ [K in C]: T;
196
+ }>;
197
+ /** Database document with a collection name. */
198
+ export declare type CollectionDocument<C extends string, T extends Data> = DatabaseDocument<C, {
199
+ [K in C]: T;
200
+ }>;
package/db/Database.js CHANGED
@@ -1,4 +1,4 @@
1
- import { deriveAsync, createObserver, getFirstItem, isAsync, throwAsync, validate, toMap, countItems, } from "../util/index.js";
1
+ import { callAsync, createObserver, getFirstItem, throwAsync, validate, toMap, countItems, DeriveObserver, } from "../util/index.js";
2
2
  import { DataTransform, Transform } from "../transform/index.js";
3
3
  import { Feedback, InvalidFeedback } from "../feedback/index.js";
4
4
  import { Filters, Query, EqualFilter } from "../query/index.js";
@@ -54,18 +54,25 @@ export class DatabaseQuery extends Query {
54
54
  return this.db.provider.getQuery(this);
55
55
  }
56
56
  /**
57
- * Read the results of this query into a map.
58
- * @return Set of results in `id: data` format (possibly promised).
57
+ * Get an iterable that yields the results of this entry.
58
+ * @return Map containing the results.
59
59
  */
60
- get map() {
61
- return deriveAsync(this.db.provider.getQuery(this), toMap);
60
+ get resultsMap() {
61
+ return callAsync(toMap, this.db.provider.getQuery(this));
62
62
  }
63
63
  /**
64
64
  * Count the number of results of this set of documents.
65
65
  * @return Number of documents in the collection (possibly promised).
66
66
  */
67
67
  get count() {
68
- return deriveAsync(this.db.provider.getQuery(this), countItems);
68
+ return callAsync(countItems, this.results);
69
+ }
70
+ /**
71
+ * Get an entry for the first document matching this query.
72
+ * @return Entry in `[id, data]` format for the first document, or `undefined` if there are no matching documents (possibly promised).
73
+ */
74
+ get first() {
75
+ return callAsync(getFirstItem, this.max(1).results);
69
76
  }
70
77
  /**
71
78
  * Subscribe to all matching documents.
@@ -82,11 +89,18 @@ export class DatabaseQuery extends Query {
82
89
  return this.db.provider.subscribeQuery(this, createObserver(next, error, complete));
83
90
  }
84
91
  /**
85
- * Get an entry for the first document matching this query.
86
- * @return Entry in `[id, data]` format for the first document, or `undefined` if there are no matching documents (possibly promised).
92
+ * Subscribe to all matching documents.
93
+ * - `next()` is called once with the initial results, and again any time the results change.
94
+ *
95
+ * @param observer Observer with `next`, `error`, or `complete` methods that the document results are reported back to.
96
+ * @param next Callback that is called once initially and again whenever the results change.
97
+ * @param error Callback that is called if an error occurs.
98
+ * @param complete Callback that is called when the subscription is done.
99
+ *
100
+ * @return Function that ends the subscription.
87
101
  */
88
- get first() {
89
- return deriveAsync(this.max(1).results, getFirstItem);
102
+ subscribeMap(next, error, complete) {
103
+ return this.db.provider.subscribeQuery(this, new DeriveObserver(toMap, createObserver(next, error, complete)));
90
104
  }
91
105
  /**
92
106
  * Set all matching documents to the same exact value.
@@ -171,8 +185,7 @@ export class DatabaseDocument {
171
185
  * @return Document's data, or `undefined` if the document doesn't exist (possibly promised).
172
186
  */
173
187
  get exists() {
174
- const result = this.db.provider.get(this);
175
- return isAsync(result) ? result.then(Boolean) : !!result;
188
+ return callAsync(Boolean, this.db.provider.get(this));
176
189
  }
177
190
  /**
178
191
  * Get the result of this document.
@@ -190,12 +203,7 @@ export class DatabaseDocument {
190
203
  * @throws RequiredError if the document's result was undefined.
191
204
  */
192
205
  get data() {
193
- const result = this.db.provider.get(this);
194
- if (isAsync(result))
195
- return _awaitRequired(this, result);
196
- if (!result)
197
- throw new DocumentRequiredError(this);
198
- return result;
206
+ return callAsync(getDocumentData, this.db.provider.get(this), this);
199
207
  }
200
208
  /**
201
209
  * Subscribe to the result of this document (indefinitely).
@@ -264,10 +272,9 @@ export class DatabaseDocument {
264
272
  return `${this.collection}/${this.id}`;
265
273
  }
266
274
  }
267
- /** Wait for a result and throw a `DocumentRequiredError` if the document doesn't exist. */
268
- async function _awaitRequired(ref, asyncResult) {
269
- const result = await asyncResult;
270
- if (!result)
271
- throw new DocumentRequiredError(ref);
272
- return result;
275
+ /** Get the data for a document from a result for that document. */
276
+ export function getDocumentData(result, ref) {
277
+ if (result)
278
+ return result;
279
+ throw new DocumentRequiredError(ref);
273
280
  }
@@ -1,26 +1,34 @@
1
- import { Results, Datas, Key, ImmutableMap, Entry } from "../util/index.js";
2
- import { AbstractState } from "../stream/index.js";
1
+ import { Datas, Key, ResultsMap, Entry, Results } from "../util/index.js";
2
+ import { State } from "../stream/index.js";
3
3
  import { DatabaseQuery } from "./Database.js";
4
4
  /**
5
5
  * State that wraps a `Documents` reference to enable pagination.
6
6
  * - If you pass in initial values, it will use that as the first page.
7
7
  * - If you don't pass in initial values, it will autoload the first page.
8
8
  */
9
- export declare class Pagination<D extends Datas, C extends Key<D>> extends AbstractState<Results<D[C]>, ImmutableMap<D[C]>> implements Iterable<Entry<D[C]>> {
10
- protected _pending: boolean;
11
- readonly ref: DatabaseQuery<D, C>;
12
- constructor(ref: DatabaseQuery<D, C>);
9
+ export declare class Pagination<C extends Key<D>, D extends Datas> extends State<ResultsMap<D[C]>> implements Iterable<Entry<D[C]>> {
10
+ readonly ref: DatabaseQuery<C, D>;
11
+ readonly limit: number;
12
+ readonly startLoading: boolean;
13
+ readonly startDone: boolean;
14
+ readonly endLoading: boolean;
15
+ readonly endDone: boolean;
16
+ constructor(ref: DatabaseQuery<C, D>);
13
17
  /**
14
18
  * Load more results before the start.
15
19
  * - Promise that needs to be handled.
16
20
  */
17
- backward: () => Promise<void>;
21
+ loadStart: () => Promise<void>;
18
22
  /**
19
23
  * Load more results after the end.
20
24
  * - Promise that needs to be handled.
21
25
  */
22
- forward: () => Promise<void>;
23
- _derive(results: Results<D[C]>): void;
26
+ loadEnd: () => Promise<void>;
27
+ /**
28
+ * Merge more results into this pagination.
29
+ * @return The change in the number of results.
30
+ */
31
+ merge(more: Results<D[C]>): void;
24
32
  /** Iterate over the entries of the values currently in the pagination. */
25
33
  [Symbol.iterator](): Iterator<Entry<D[C]>>;
26
34
  }
package/db/Pagination.js CHANGED
@@ -1,60 +1,77 @@
1
- import { getFirstItem, getLastItem, assertLength, assertNumber, yieldMerged } from "../util/index.js";
2
- import { AbstractState, StreamClosedError } from "../stream/index.js";
1
+ import { getFirstItem, getLastItem, assertLength, assertNumber, yieldMerged, toMap } from "../util/index.js";
2
+ import { State } from "../stream/index.js";
3
+ import { ConditionError } from "../index.js";
3
4
  /**
4
5
  * State that wraps a `Documents` reference to enable pagination.
5
6
  * - If you pass in initial values, it will use that as the first page.
6
7
  * - If you don't pass in initial values, it will autoload the first page.
7
8
  */
8
- export class Pagination extends AbstractState {
9
+ export class Pagination extends State {
9
10
  constructor(ref) {
10
11
  super();
11
- this._pending = false;
12
+ this.startLoading = false;
13
+ this.startDone = false;
14
+ this.endLoading = false;
15
+ this.endDone = false;
12
16
  /**
13
17
  * Load more results before the start.
14
18
  * - Promise that needs to be handled.
15
19
  */
16
- this.backward = async () => {
20
+ this.loadStart = async () => {
17
21
  if (this.closed)
18
- throw new StreamClosedError();
19
- if (!this._pending) {
20
- const entry = getFirstItem(this.value);
21
- this._pending = true;
22
- this.next(await (entry ? this.ref.before(entry[0], entry[1]).results : this.ref.results));
22
+ throw new ConditionError();
23
+ if (!this.startLoading) {
24
+ this.startLoading = true;
25
+ if (!this.loading) {
26
+ const firstItem = getFirstItem(this.value);
27
+ if (firstItem) {
28
+ const next = await this.ref.after(firstItem[0], firstItem[1]).resultsMap;
29
+ this.startDone = next.size < this.limit;
30
+ this.startLoading = false;
31
+ return this.merge(next);
32
+ }
33
+ }
34
+ const next = await this.ref.resultsMap;
35
+ this.startDone = next.size < this.limit;
36
+ this.startLoading = false;
37
+ return this.next(next);
23
38
  }
24
39
  };
25
40
  /**
26
41
  * Load more results after the end.
27
42
  * - Promise that needs to be handled.
28
43
  */
29
- this.forward = async () => {
44
+ this.loadEnd = async () => {
30
45
  if (this.closed)
31
- throw new StreamClosedError();
32
- if (!this._pending) {
33
- const entry = getLastItem(this.value);
34
- this._pending = true;
35
- this.next(await (entry ? this.ref.after(entry[0], entry[1]).results : this.ref.results));
46
+ throw new ConditionError();
47
+ if (!this.endLoading) {
48
+ this.endLoading = true;
49
+ if (!this.loading) {
50
+ const lastItem = getLastItem(this.value);
51
+ if (lastItem) {
52
+ const next = await this.ref.after(lastItem[0], lastItem[1]).resultsMap;
53
+ this.endDone = next.size < this.limit;
54
+ this.endLoading = false;
55
+ return this.merge(next);
56
+ }
57
+ }
58
+ const next = await this.ref.resultsMap;
59
+ this.endDone = next.size < this.limit;
60
+ this.endLoading = false;
61
+ return this.next(next);
36
62
  }
37
63
  };
38
64
  this.ref = ref;
39
65
  assertNumber(ref.limit); // Collection must have a numeric limit to paginate (otherwise you'd be retrieving the entire set of documents).
40
66
  assertLength(ref.sorts, 1, Infinity); // Collection must have at least one sort order to paginate.
67
+ this.limit = ref.limit;
41
68
  }
42
- // Dispatch doesn't just dispatch the next value, it merges it into the current results and dispatches the combined results.
43
- _derive(results) {
44
- this._pending = false;
45
- if (this.loading) {
46
- // Merge the results into the existing results.
47
- const current = this.value;
48
- const next = new Map(this.ref.sorts.derive(yieldMerged(this.value, results)));
49
- const change = next.size - current.size;
50
- super._dispatch(next);
51
- // Automatically complete the pagination if there are fewer entries than the slice limit.
52
- if (typeof this.ref.limit === "number" && change < this.ref.limit)
53
- this.complete();
54
- }
55
- else {
56
- super._dispatch(new Map(results));
57
- }
69
+ /**
70
+ * Merge more results into this pagination.
71
+ * @return The change in the number of results.
72
+ */
73
+ merge(more) {
74
+ this.next(toMap(this.ref.sorts.derive(yieldMerged(more, this.value))));
58
75
  }
59
76
  /** Iterate over the entries of the values currently in the pagination. */
60
77
  [Symbol.iterator]() {
package/db/errors.d.ts CHANGED
@@ -4,16 +4,16 @@ import { Feedback } from "../feedback/index.js";
4
4
  import type { DatabaseDocument, DatabaseQuery } from "./Database.js";
5
5
  /** Thrown if a document doesn't exist. */
6
6
  export declare class DocumentRequiredError<D extends Datas, C extends Key<D>> extends RequiredError {
7
- ref: DatabaseDocument<D, C>;
8
- constructor(ref: DatabaseDocument<D, C>);
7
+ ref: DatabaseDocument<C, D>;
8
+ constructor(ref: DatabaseDocument<C, D>);
9
9
  }
10
10
  /** Thrown if a document can't validate. */
11
11
  export declare class DocumentValidationError<D extends Datas, C extends Key<D>> extends ValidationError {
12
- ref: DatabaseDocument<D, C>;
13
- constructor(ref: DatabaseDocument<D, C>, feedback: Feedback);
12
+ ref: DatabaseDocument<C, D>;
13
+ constructor(ref: DatabaseDocument<C, D>, feedback: Feedback);
14
14
  }
15
15
  /** Thrown if a query can't validate a set of results. */
16
16
  export declare class QueryValidationError<D extends Datas, C extends Key<D>> extends ValidationError {
17
- ref: DatabaseQuery<D, C>;
18
- constructor(ref: DatabaseQuery<D, C>, feedback: Feedback);
17
+ ref: DatabaseQuery<C, D>;
18
+ constructor(ref: DatabaseQuery<C, D>, feedback: Feedback);
19
19
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Thrown if the program is in a condition it shouldn't have reached.
3
+ * e.g. writing to a stream that's already closed
4
+ */
5
+ export declare class ConditionError extends Error {
6
+ constructor(message?: string);
7
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Thrown if the program is in a condition it shouldn't have reached.
3
+ * e.g. writing to a stream that's already closed
4
+ */
5
+ export class ConditionError extends Error {
6
+ constructor(message = "Invalid condition") {
7
+ super(message);
8
+ }
9
+ }
10
+ ConditionError.prototype.name = "ConditionError";
package/error/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export * from "./PermissionError.js";
3
3
  export * from "./RequiredError.js";
4
4
  export * from "./UnsupportedError.js";
5
5
  export * from "./ValidationError.js";
6
+ export * from "./ConditionError.js";
package/error/index.js CHANGED
@@ -3,3 +3,4 @@ export * from "./PermissionError.js";
3
3
  export * from "./RequiredError.js";
4
4
  export * from "./UnsupportedError.js";
5
5
  export * from "./ValidationError.js";
6
+ export * from "./ConditionError.js";
@@ -9,11 +9,11 @@ import { Results, Provider, DatabaseDocument, DatabaseQuery, Result, Observer, T
9
9
  export declare class FirestoreClientProvider<D extends Datas> extends Provider<D> implements AsynchronousProvider<D> {
10
10
  readonly firestore: Firestore;
11
11
  constructor(firestore: Firestore);
12
- get<C extends Key<D>>(ref: DatabaseDocument<D, C>): Promise<Result<D[C]>>;
13
- subscribe<C extends Key<D>>(ref: DatabaseDocument<D, C>, observer: Observer<Result<D[C]>>): () => void;
14
- add<C extends Key<D>>(ref: DatabaseQuery<D, C>, data: D[C]): Promise<string>;
15
- write<C extends Key<D>>(ref: DatabaseDocument<D, C>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
16
- getQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>): Promise<Results<D[C]>>;
17
- subscribeQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>, observer: Observer<Results<D[C]>>): () => void;
18
- writeQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
12
+ get<C extends Key<D>>(ref: DatabaseDocument<C, D>): Promise<Result<D[C]>>;
13
+ subscribe<C extends Key<D>>(ref: DatabaseDocument<C, D>, observer: Observer<Result<D[C]>>): () => void;
14
+ add<C extends Key<D>>(ref: DatabaseQuery<C, D>, data: D[C]): Promise<string>;
15
+ write<C extends Key<D>>(ref: DatabaseDocument<C, D>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
16
+ getQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>): Promise<Results<D[C]>>;
17
+ subscribeQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>, observer: Observer<Results<D[C]>>): () => void;
18
+ writeQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
19
19
  }
@@ -1,5 +1,5 @@
1
1
  import type { Firestore } from "firebase/firestore/lite";
2
- import { Results, Provider, DatabaseDocument, DatabaseQuery, Result, Transform, AsynchronousProvider, Datas, Key } from "../../index.js";
2
+ import { Provider, DatabaseDocument, DatabaseQuery, Result, Transform, AsynchronousProvider, Datas, Key, Results } from "../../index.js";
3
3
  /**
4
4
  * Firestore Lite client database provider.
5
5
  * - Works with the Firebase JS SDK.
@@ -9,11 +9,11 @@ import { Results, Provider, DatabaseDocument, DatabaseQuery, Result, Transform,
9
9
  export declare class FirestoreClientProvider<D extends Datas> extends Provider<D> implements AsynchronousProvider<D> {
10
10
  readonly firestore: Firestore;
11
11
  constructor(firestore: Firestore);
12
- get<C extends Key<D>>(ref: DatabaseDocument<D, C>): Promise<Result<D[C]>>;
12
+ get<C extends Key<D>>(ref: DatabaseDocument<C, D>): Promise<Result<D[C]>>;
13
13
  subscribe(): () => void;
14
- add<C extends Key<D>>(ref: DatabaseQuery<D, C>, data: D[C]): Promise<string>;
15
- write<C extends Key<D>>(ref: DatabaseDocument<D, C>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
16
- getQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>): Promise<Results<D[C]>>;
14
+ add<C extends Key<D>>(ref: DatabaseQuery<C, D>, data: D[C]): Promise<string>;
15
+ write<C extends Key<D>>(ref: DatabaseDocument<C, D>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
16
+ getQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>): Promise<Results<D[C]>>;
17
17
  subscribeQuery(): () => void;
18
- writeQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
18
+ writeQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
19
19
  }
@@ -1,5 +1,5 @@
1
1
  import { Firestore } from "@google-cloud/firestore";
2
- import { Results, Provider, DatabaseDocument, DatabaseQuery, Observer, Result, Transform, AsynchronousProvider, Datas, Key, Entry } from "../../index.js";
2
+ import { Provider, DatabaseDocument, DatabaseQuery, Observer, Result, Transform, AsynchronousProvider, Datas, Key, Entry, Results } from "../../index.js";
3
3
  /**
4
4
  * Firestore server database provider.
5
5
  * - Works with the Firebase Admin SDK for Node.JS
@@ -7,11 +7,11 @@ import { Results, Provider, DatabaseDocument, DatabaseQuery, Observer, Result, T
7
7
  export declare class FirestoreServerProvider<D extends Datas> extends Provider<D> implements AsynchronousProvider<D> {
8
8
  readonly firestore: Firestore;
9
9
  constructor(firestore?: Firestore);
10
- get<C extends Key<D>>(ref: DatabaseDocument<D, C>): Promise<Result<D[C]>>;
11
- subscribe<C extends Key<D>>(ref: DatabaseDocument<D, C>, observer: Observer<Result<D[C]>>): () => void;
12
- add<C extends Key<D>>(ref: DatabaseQuery<D, C>, data: D[C]): Promise<string>;
13
- write<C extends Key<D>>(ref: DatabaseDocument<D, C>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
14
- getQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>): Promise<Iterable<Entry<D[C]>>>;
15
- subscribeQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>, observer: Observer<Results<D[C]>>): () => void;
16
- writeQuery<C extends Key<D>>(ref: DatabaseQuery<D, C>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
10
+ get<C extends Key<D>>(ref: DatabaseDocument<C, D>): Promise<Result<D[C]>>;
11
+ subscribe<C extends Key<D>>(ref: DatabaseDocument<C, D>, observer: Observer<Result<D[C]>>): () => void;
12
+ add<C extends Key<D>>(ref: DatabaseQuery<C, D>, data: D[C]): Promise<string>;
13
+ write<C extends Key<D>>(ref: DatabaseDocument<C, D>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
14
+ getQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>): Promise<Iterable<Entry<D[C]>>>;
15
+ subscribeQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>, observer: Observer<Results<D[C]>>): () => void;
16
+ writeQuery<C extends Key<D>>(ref: DatabaseQuery<C, D>, value: D[C] | Transform<D[C]> | undefined): Promise<void>;
17
17
  }
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.19.0",
14
+ "version": "1.20.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",