shelving 1.10.2 → 1.12.1
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/api/Resource.d.ts +1 -2
- package/db/Database.d.ts +1 -2
- package/db/Document.js +2 -2
- package/db/DocumentState.d.ts +11 -7
- package/db/DocumentState.js +28 -18
- package/db/Documents.js +2 -2
- package/db/DocumentsState.d.ts +12 -6
- package/db/DocumentsState.js +32 -16
- package/db/Pagination.js +11 -2
- package/db/Reference.d.ts +2 -2
- package/firestore-client/FirestoreClientProvider.d.ts +4 -4
- package/firestore-client/FirestoreClientProvider.js +36 -36
- package/firestore-lite/FirestoreLiteProvider.d.ts +23 -0
- package/firestore-lite/FirestoreLiteProvider.js +129 -0
- package/firestore-lite/index.d.ts +1 -0
- package/firestore-lite/index.js +1 -0
- package/firestore-server/FirestoreServerProvider.d.ts +1 -2
- package/firestore-server/FirestoreServerProvider.js +4 -5
- package/package.json +6 -6
- package/query/Filter.js +7 -0
- package/query/Filters.js +7 -0
- package/query/Sort.js +7 -1
- package/query/Sorts.js +7 -1
- package/react/useDocument.js +1 -1
- package/react/useDocuments.js +1 -1
- package/schema/ArraySchema.d.ts +1 -2
- package/schema/MapSchema.d.ts +1 -2
- package/schema/ObjectSchema.d.ts +1 -2
- package/schema/Schema.d.ts +1 -1
- package/schema/Schema.js +2 -1
- package/schema/StringSchema.d.ts +1 -1
- package/schema/index.d.ts +0 -1
- package/schema/index.js +0 -3
- package/stream/State.test.js +6 -5
- package/stream/Stream.js +6 -4
- package/tsconfig.build.tsbuildinfo +1 -1
- package/util/class.d.ts +4 -3
- package/util/class.js +1 -3
- package/util/clone.d.ts +2 -2
- package/util/date.d.ts +10 -0
- package/util/date.js +10 -0
- package/util/index.d.ts +1 -0
- package/util/index.js +1 -0
- package/util/observable.d.ts +4 -0
- package/util/observable.js +3 -0
- package/util/template.d.ts +3 -1
- package/{schema/Validator.d.ts → util/validate.d.ts} +1 -1
- package/{schema/Validator.js → util/validate.js} +1 -1
package/api/Resource.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Arguments, AsyncFetcher } from "../util";
|
|
2
|
-
import type { Validator } from "../schema";
|
|
1
|
+
import type { Arguments, AsyncFetcher, Validator } from "../util";
|
|
3
2
|
/**
|
|
4
3
|
* An abstract API resource definition, used to specify types for e.g. serverless functions..
|
|
5
4
|
*
|
package/db/Database.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Datas } from "../util
|
|
2
|
-
import type { Validators } from "../schema";
|
|
1
|
+
import type { Datas, Validators } from "../util";
|
|
3
2
|
import type { Provider } from "./Provider";
|
|
4
3
|
import { Document } from "./Document";
|
|
5
4
|
import { Documents } from "./Documents";
|
package/db/Document.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { throwAsync, isAsync, } from "../util";
|
|
1
|
+
import { throwAsync, isAsync, createObserver, } from "../util";
|
|
2
2
|
import { DocumentState } from "./DocumentState";
|
|
3
3
|
import { ReferenceRequiredError } from "./errors";
|
|
4
4
|
import { Reference } from "./Reference";
|
|
@@ -94,7 +94,7 @@ export class Document extends Reference {
|
|
|
94
94
|
return DocumentState.get(this);
|
|
95
95
|
}
|
|
96
96
|
subscribe(next, error, complete) {
|
|
97
|
-
return
|
|
97
|
+
return this.db.provider.onDocument(this, createObserver(next, error, complete));
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
100
|
* Set the complete data of this document.
|
package/db/DocumentState.d.ts
CHANGED
|
@@ -26,16 +26,20 @@ export declare class DocumentState<T extends Data = Data> extends State<Result<T
|
|
|
26
26
|
*/
|
|
27
27
|
stop(): void;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Refresh the state.
|
|
30
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
31
|
+
* - If `maxAge` is undefined or falsy, always refresh the state.
|
|
32
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
33
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
30
34
|
*/
|
|
31
|
-
refresh(): void;
|
|
35
|
+
refresh(maxAge?: number | true): void;
|
|
32
36
|
/**
|
|
33
|
-
*
|
|
34
|
-
* @param maxAge
|
|
35
|
-
* - If `maxAge` is a number this specifies how old (in milliseconds the data can be before
|
|
36
|
-
* - If `maxAge` is `true`
|
|
37
|
+
* Get the current value, or refetch the data again if it's outdated.
|
|
38
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
39
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
40
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
37
41
|
*/
|
|
38
|
-
|
|
42
|
+
get(maxAge?: number): Result<T> | Promise<Result<T>>;
|
|
39
43
|
protected dispatchError(reason: Error | unknown): void;
|
|
40
44
|
protected dispatchComplete(): void;
|
|
41
45
|
}
|
package/db/DocumentState.js
CHANGED
|
@@ -67,15 +67,17 @@ export class DocumentState extends State {
|
|
|
67
67
|
* - The realtime subscription will clean itself up when the last observer unsubscribes.
|
|
68
68
|
*/
|
|
69
69
|
start(observer) {
|
|
70
|
+
var _b;
|
|
70
71
|
if (this.closed)
|
|
71
72
|
throw new StreamClosedError(this);
|
|
72
|
-
__classPrivateFieldSet(this, _DocumentState_realtime,
|
|
73
|
+
__classPrivateFieldSet(this, _DocumentState_realtime, (_b = __classPrivateFieldGet(this, _DocumentState_realtime, "f"), _b++, _b), "f");
|
|
73
74
|
if (!__classPrivateFieldGet(this, _DocumentState_stop, "f"))
|
|
74
75
|
__classPrivateFieldSet(this, _DocumentState_stop, __classPrivateFieldGet(this, _DocumentState_ref, "f").subscribe(this), "f");
|
|
75
76
|
if (observer)
|
|
76
77
|
this.on(observer);
|
|
77
78
|
return () => {
|
|
78
|
-
|
|
79
|
+
var _b;
|
|
80
|
+
__classPrivateFieldSet(this, _DocumentState_realtime, (_b = __classPrivateFieldGet(this, _DocumentState_realtime, "f"), _b--, _b), "f");
|
|
79
81
|
if (__classPrivateFieldGet(this, _DocumentState_realtime, "f") <= 0)
|
|
80
82
|
this.stop();
|
|
81
83
|
if (observer)
|
|
@@ -91,20 +93,13 @@ export class DocumentState extends State {
|
|
|
91
93
|
__classPrivateFieldSet(this, _DocumentState_stop, void dispatch(__classPrivateFieldGet(this, _DocumentState_stop, "f")), "f");
|
|
92
94
|
}
|
|
93
95
|
/**
|
|
94
|
-
*
|
|
96
|
+
* Refresh the state.
|
|
97
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
98
|
+
* - If `maxAge` is undefined or falsy, always refresh the state.
|
|
99
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
100
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
95
101
|
*/
|
|
96
|
-
refresh() {
|
|
97
|
-
if (this.closed)
|
|
98
|
-
throw new StreamClosedError(this);
|
|
99
|
-
this.next(__classPrivateFieldGet(this, _DocumentState_ref, "f").get());
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Conditionally refresh a documents state if it's outdated.
|
|
103
|
-
* @param maxAge
|
|
104
|
-
* - If `maxAge` is a number this specifies how old (in milliseconds the data can be before a refresh will be issued).
|
|
105
|
-
* - If `maxAge` is `true` this will start a realtime subscription to the data that lasts for ten seconds.
|
|
106
|
-
*/
|
|
107
|
-
refreshOutdated(maxAge) {
|
|
102
|
+
refresh(maxAge) {
|
|
108
103
|
if (this.closed)
|
|
109
104
|
throw new StreamClosedError(this);
|
|
110
105
|
// No need to refresh if there's an active subscription.
|
|
@@ -114,12 +109,27 @@ export class DocumentState extends State {
|
|
|
114
109
|
setTimeout(this.start(), 10000);
|
|
115
110
|
}
|
|
116
111
|
else {
|
|
117
|
-
//
|
|
118
|
-
if (!this.pending && this.age
|
|
119
|
-
this.
|
|
112
|
+
// Refresh the data if a fetch isn't already pending, and either `maxAge` is falsy or the existing state is older than `maxAge`
|
|
113
|
+
if (!this.pending && (!maxAge || this.age > maxAge))
|
|
114
|
+
this.next(__classPrivateFieldGet(this, _DocumentState_ref, "f").get());
|
|
120
115
|
}
|
|
121
116
|
}
|
|
122
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Get the current value, or refetch the data again if it's outdated.
|
|
120
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
121
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
122
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
123
|
+
*/
|
|
124
|
+
get(maxAge = 0) {
|
|
125
|
+
// Return the current state if its not loading and there's a subscription or the existing state is younger than `maxAge`
|
|
126
|
+
if (!this.loading && (__classPrivateFieldGet(this, _DocumentState_stop, "f") || this.age < maxAge))
|
|
127
|
+
return this.value;
|
|
128
|
+
// Refresh the state and return the next value after the refresh.
|
|
129
|
+
const next = __classPrivateFieldGet(this, _DocumentState_ref, "f").get();
|
|
130
|
+
this.next(next);
|
|
131
|
+
return next;
|
|
132
|
+
}
|
|
123
133
|
// Override to stop any realtime subscription on error or complete.
|
|
124
134
|
dispatchError(reason) {
|
|
125
135
|
this.stop();
|
package/db/Documents.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { throwAsync, isAsync, countEntries, getFirstProp, getLastProp, } from "../util";
|
|
1
|
+
import { throwAsync, isAsync, countEntries, getFirstProp, getLastProp, createObserver, } from "../util";
|
|
2
2
|
import { Query } from "../query";
|
|
3
3
|
import { Document } from "./Document";
|
|
4
4
|
import { DocumentsState } from "./DocumentsState";
|
|
@@ -94,7 +94,7 @@ export class Documents extends Reference {
|
|
|
94
94
|
return isAsync(results) ? results.then(Object.keys) : Object.keys(results);
|
|
95
95
|
}
|
|
96
96
|
subscribe(next, error, complete) {
|
|
97
|
-
return
|
|
97
|
+
return this.db.provider.onDocuments(this, createObserver(next, error, complete));
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
100
|
* Get an entry for the first result in this set of documents (synchronously).
|
package/db/DocumentsState.d.ts
CHANGED
|
@@ -25,15 +25,21 @@ export declare class DocumentsState<T extends Data = Data> extends State<Results
|
|
|
25
25
|
*/
|
|
26
26
|
stop(): void;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* Refresh the state.
|
|
29
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
30
|
+
* - If `maxAge` is undefined or falsy, always refresh the state.
|
|
31
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
32
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
29
33
|
*/
|
|
30
|
-
refresh(): void;
|
|
34
|
+
refresh(maxAge?: number | true): void;
|
|
31
35
|
/**
|
|
32
|
-
*
|
|
33
|
-
* @param maxAge
|
|
34
|
-
* - If `maxAge` is
|
|
36
|
+
* Get the current value, or refetch the data again if it's outdated.
|
|
37
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
38
|
+
* - If `maxAge` is undefined or falsy, always return fresh state.
|
|
39
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
40
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
35
41
|
*/
|
|
36
|
-
|
|
42
|
+
get(maxAge?: number): Results<T> | Promise<Results<T>>;
|
|
37
43
|
protected dispatchNext(results: Results<T>): void;
|
|
38
44
|
protected dispatchError(reason: Error | unknown): void;
|
|
39
45
|
protected dispatchComplete(): void;
|
package/db/DocumentsState.js
CHANGED
|
@@ -11,7 +11,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
11
11
|
};
|
|
12
12
|
var _a, _DocumentsState_states, _DocumentsState_ref, _DocumentsState_realtime, _DocumentsState_stop;
|
|
13
13
|
import { AssertionError } from "../errors";
|
|
14
|
-
import { State } from "../stream";
|
|
14
|
+
import { State, StreamClosedError } from "../stream";
|
|
15
15
|
import { dispatch, LOADING } from "../util";
|
|
16
16
|
import { DocumentState } from "./DocumentState";
|
|
17
17
|
/**
|
|
@@ -63,15 +63,17 @@ export class DocumentsState extends State {
|
|
|
63
63
|
* - The realtime subscription will clean itself up when the last observer unsubscribes.
|
|
64
64
|
*/
|
|
65
65
|
start(observer) {
|
|
66
|
+
var _b;
|
|
66
67
|
if (!this.closed)
|
|
67
68
|
throw new AssertionError("State must not be closed", this.closed);
|
|
68
|
-
__classPrivateFieldSet(this, _DocumentsState_realtime,
|
|
69
|
+
__classPrivateFieldSet(this, _DocumentsState_realtime, (_b = __classPrivateFieldGet(this, _DocumentsState_realtime, "f"), _b++, _b), "f");
|
|
69
70
|
if (!__classPrivateFieldGet(this, _DocumentsState_stop, "f"))
|
|
70
71
|
__classPrivateFieldSet(this, _DocumentsState_stop, __classPrivateFieldGet(this, _DocumentsState_ref, "f").subscribe(this), "f");
|
|
71
72
|
if (observer)
|
|
72
73
|
this.on(observer);
|
|
73
74
|
return () => {
|
|
74
|
-
|
|
75
|
+
var _b;
|
|
76
|
+
__classPrivateFieldSet(this, _DocumentsState_realtime, (_b = __classPrivateFieldGet(this, _DocumentsState_realtime, "f"), _b--, _b), "f");
|
|
75
77
|
if (__classPrivateFieldGet(this, _DocumentsState_realtime, "f") <= 0)
|
|
76
78
|
this.stop();
|
|
77
79
|
if (observer)
|
|
@@ -87,17 +89,15 @@ export class DocumentsState extends State {
|
|
|
87
89
|
__classPrivateFieldSet(this, _DocumentsState_stop, void dispatch(__classPrivateFieldGet(this, _DocumentsState_stop, "f")), "f");
|
|
88
90
|
}
|
|
89
91
|
/**
|
|
90
|
-
*
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Conditionally refresh a documents state if it's outdated.
|
|
97
|
-
* @param maxAge
|
|
98
|
-
* - If `maxAge` is a number this specifies how old (in milliseconds
|
|
92
|
+
* Refresh the state.
|
|
93
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
94
|
+
* - If `maxAge` is undefined or falsy, always refresh the state.
|
|
95
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
96
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
99
97
|
*/
|
|
100
|
-
|
|
98
|
+
refresh(maxAge) {
|
|
99
|
+
if (this.closed)
|
|
100
|
+
throw new StreamClosedError(this);
|
|
101
101
|
// No need to refresh if there's an active subscription.
|
|
102
102
|
if (!__classPrivateFieldGet(this, _DocumentsState_stop, "f")) {
|
|
103
103
|
if (maxAge === true) {
|
|
@@ -105,12 +105,28 @@ export class DocumentsState extends State {
|
|
|
105
105
|
setTimeout(__classPrivateFieldGet(this, _DocumentsState_ref, "f").subscribe({}), 10000);
|
|
106
106
|
}
|
|
107
107
|
else {
|
|
108
|
-
//
|
|
109
|
-
if (!this.pending && this.age
|
|
110
|
-
this.
|
|
108
|
+
// Refresh the data if a fetch isn't already pending, and either `maxAge` is falsy or the existing state is older than `maxAge`
|
|
109
|
+
if (!this.pending && (!maxAge || this.age > maxAge))
|
|
110
|
+
this.next(__classPrivateFieldGet(this, _DocumentsState_ref, "f").get());
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the current value, or refetch the data again if it's outdated.
|
|
116
|
+
* @param maxAge How old the data can be before it's outdated.
|
|
117
|
+
* - If `maxAge` is undefined or falsy, always return fresh state.
|
|
118
|
+
* - If `maxAge` is a number this specifies how old (in milliseconds) the data can be before it's refreshed.
|
|
119
|
+
* - If `maxAge` is `true` a subscription to the data is started for 10 seconds.
|
|
120
|
+
*/
|
|
121
|
+
get(maxAge = 0) {
|
|
122
|
+
// Return the current state if its not loading and there's a subscription or the existing state is younger than `maxAge`
|
|
123
|
+
if (!this.loading && (__classPrivateFieldGet(this, _DocumentsState_stop, "f") || this.age < maxAge))
|
|
124
|
+
return this.value;
|
|
125
|
+
// Refresh the state and return the next value after the refresh.
|
|
126
|
+
const next = __classPrivateFieldGet(this, _DocumentsState_ref, "f").get();
|
|
127
|
+
this.next(next);
|
|
128
|
+
return next;
|
|
129
|
+
}
|
|
114
130
|
// Override dispatchNext to set the state for every individual document too.
|
|
115
131
|
dispatchNext(results) {
|
|
116
132
|
super.dispatchNext(results);
|
package/db/Pagination.js
CHANGED
|
@@ -4,6 +4,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
7
10
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
11
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
12
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
@@ -91,8 +94,14 @@ async function _PaginationState_mergeReference(ref) {
|
|
|
91
94
|
}
|
|
92
95
|
};
|
|
93
96
|
__decorate([
|
|
94
|
-
bindMethod
|
|
97
|
+
bindMethod,
|
|
98
|
+
__metadata("design:type", Function),
|
|
99
|
+
__metadata("design:paramtypes", []),
|
|
100
|
+
__metadata("design:returntype", void 0)
|
|
95
101
|
], PaginationState.prototype, "backward", null);
|
|
96
102
|
__decorate([
|
|
97
|
-
bindMethod
|
|
103
|
+
bindMethod,
|
|
104
|
+
__metadata("design:type", Function),
|
|
105
|
+
__metadata("design:paramtypes", []),
|
|
106
|
+
__metadata("design:returntype", void 0)
|
|
98
107
|
], PaginationState.prototype, "forward", null);
|
package/db/Reference.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import "firebase/firestore";
|
|
2
|
-
import type { FirebaseFirestore as Firestore } from "@firebase/firestore-types";
|
|
1
|
+
import type { Firestore } from "firebase/firestore";
|
|
3
2
|
import { Data, Results, Provider, Document, Documents, Result, Observer, Transforms } from "..";
|
|
4
3
|
/**
|
|
5
4
|
* Firestore client database provider.
|
|
6
|
-
* - Works with the JS
|
|
7
|
-
*
|
|
5
|
+
* - Works with the Firebase JS SDK.
|
|
6
|
+
* - Supports offline mode.
|
|
7
|
+
* - Supports realtime subscriptions.
|
|
8
8
|
*/
|
|
9
9
|
export declare class FirestoreClientProvider implements Provider {
|
|
10
10
|
readonly firestore: Firestore;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "
|
|
3
|
-
import { dispatchNext, dispatchError } from "..";
|
|
4
|
-
import { AddItemsTransform, AddEntriesTransform, IncrementTransform, isTransform, RemoveItemsTransform, RemoveEntriesTransform } from "../util";
|
|
1
|
+
import { getDoc, orderBy, where, limit, addDoc, increment, arrayUnion, arrayRemove, deleteField, collection as getFirestoreCollection, doc as getFirestoreDoc, query as getFirestoreQuery, onSnapshot, setDoc, updateDoc, deleteDoc, getDocs, } from "firebase/firestore";
|
|
2
|
+
import { dispatchNext, dispatchError, AddItemsTransform, AddEntriesTransform, IncrementTransform, isTransform, RemoveItemsTransform, RemoveEntriesTransform, } from "..";
|
|
5
3
|
// Constants.
|
|
6
4
|
// const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
|
|
7
5
|
const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
|
|
@@ -22,24 +20,24 @@ const DIRECTIONS = {
|
|
|
22
20
|
DESC: "desc",
|
|
23
21
|
};
|
|
24
22
|
/** Get a Firestore DocumentReference for a given Shelving `Document` instance. */
|
|
25
|
-
function
|
|
26
|
-
return firestore
|
|
23
|
+
function getDocumentReference(firestore, { path }) {
|
|
24
|
+
return getFirestoreDoc(firestore, path);
|
|
27
25
|
}
|
|
28
26
|
/** Get a Firestore CollectionReference for a given Shelving `Document` instance. */
|
|
29
|
-
function
|
|
30
|
-
return firestore
|
|
27
|
+
function getCollectionReference(firestore, { path }) {
|
|
28
|
+
return getFirestoreCollection(firestore, path);
|
|
31
29
|
}
|
|
32
30
|
/** Create a corresponding `QueryReference` from a Query. */
|
|
33
|
-
function
|
|
31
|
+
function getQueryReference(firestore, ref) {
|
|
34
32
|
const { sorts, filters, slice } = ref.query;
|
|
35
|
-
|
|
33
|
+
const constraints = [];
|
|
36
34
|
for (const { key, direction } of sorts)
|
|
37
|
-
|
|
35
|
+
constraints.push(orderBy(key === "id" ? ID : key, DIRECTIONS[direction]));
|
|
38
36
|
for (const { operator, key, value } of filters)
|
|
39
|
-
|
|
37
|
+
constraints.push(where(key === "id" ? ID : key, OPERATORS[operator], value));
|
|
40
38
|
if (slice.limit !== null)
|
|
41
|
-
|
|
42
|
-
return
|
|
39
|
+
constraints.push(limit(slice.limit));
|
|
40
|
+
return getFirestoreQuery(getCollectionReference(firestore, ref), ...constraints);
|
|
43
41
|
}
|
|
44
42
|
/** Create a set of results from a collection snapshot. */
|
|
45
43
|
function getResults(snapshot) {
|
|
@@ -54,13 +52,13 @@ function convertTransforms(transforms) {
|
|
|
54
52
|
for (const [key, transform] of Object.entries(transforms)) {
|
|
55
53
|
if (isTransform(transform)) {
|
|
56
54
|
if (transform instanceof IncrementTransform) {
|
|
57
|
-
output[key] =
|
|
55
|
+
output[key] = increment(transform.amount);
|
|
58
56
|
}
|
|
59
57
|
else if (transform instanceof AddItemsTransform) {
|
|
60
|
-
output[key] =
|
|
58
|
+
output[key] = arrayUnion(...transform.items);
|
|
61
59
|
}
|
|
62
60
|
else if (transform instanceof RemoveItemsTransform) {
|
|
63
|
-
output[key] =
|
|
61
|
+
output[key] = arrayRemove(...transform.items);
|
|
64
62
|
}
|
|
65
63
|
else if (transform instanceof AddEntriesTransform) {
|
|
66
64
|
for (const [k, v] of Object.entries(transform.props))
|
|
@@ -68,62 +66,64 @@ function convertTransforms(transforms) {
|
|
|
68
66
|
}
|
|
69
67
|
else if (transform instanceof RemoveEntriesTransform) {
|
|
70
68
|
for (const k of transform.props)
|
|
71
|
-
output[`${key}.${k}`] =
|
|
69
|
+
output[`${key}.${k}`] = deleteField();
|
|
72
70
|
}
|
|
73
71
|
else
|
|
74
72
|
throw Error("Unsupported transform");
|
|
75
73
|
}
|
|
76
|
-
else
|
|
74
|
+
else if (transform !== undefined) {
|
|
77
75
|
output[key] = transform;
|
|
76
|
+
}
|
|
78
77
|
}
|
|
79
78
|
return output;
|
|
80
79
|
}
|
|
81
80
|
/**
|
|
82
81
|
* Firestore client database provider.
|
|
83
|
-
* - Works with the JS
|
|
84
|
-
*
|
|
82
|
+
* - Works with the Firebase JS SDK.
|
|
83
|
+
* - Supports offline mode.
|
|
84
|
+
* - Supports realtime subscriptions.
|
|
85
85
|
*/
|
|
86
86
|
export class FirestoreClientProvider {
|
|
87
87
|
constructor(firestore) {
|
|
88
88
|
this.firestore = firestore;
|
|
89
89
|
}
|
|
90
90
|
async getDocument(ref) {
|
|
91
|
-
const snapshot = await
|
|
91
|
+
const snapshot = await getDoc(getDocumentReference(this.firestore, ref));
|
|
92
92
|
return snapshot.data();
|
|
93
93
|
}
|
|
94
94
|
onDocument(ref, observer) {
|
|
95
|
-
return
|
|
95
|
+
return onSnapshot(getDocumentReference(this.firestore, ref), snapshot => dispatchNext(observer, snapshot.data()), error => dispatchError(observer, error));
|
|
96
96
|
}
|
|
97
97
|
async addDocument(ref, data) {
|
|
98
|
-
|
|
98
|
+
const reference = await addDoc(getCollectionReference(this.firestore, ref), data); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
99
|
+
return reference.id;
|
|
99
100
|
}
|
|
100
101
|
async setDocument(ref, data) {
|
|
101
|
-
await
|
|
102
|
+
await setDoc(getDocumentReference(this.firestore, ref), data); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
102
103
|
}
|
|
103
104
|
async updateDocument(ref, transforms) {
|
|
104
|
-
|
|
105
|
-
await getDocument(this.firestore, ref).update(updates);
|
|
105
|
+
await updateDoc(getDocumentReference(this.firestore, ref), convertTransforms(transforms)); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
106
106
|
}
|
|
107
107
|
async deleteDocument(ref) {
|
|
108
|
-
await
|
|
108
|
+
await deleteDoc(getDocumentReference(this.firestore, ref));
|
|
109
109
|
}
|
|
110
110
|
async getDocuments(ref) {
|
|
111
|
-
return getResults(await
|
|
111
|
+
return getResults(await getDocs(getQueryReference(this.firestore, ref)));
|
|
112
112
|
}
|
|
113
113
|
onDocuments(ref, observer) {
|
|
114
|
-
return
|
|
114
|
+
return onSnapshot(getQueryReference(this.firestore, ref), snapshot => dispatchNext(observer, getResults(snapshot)), error => dispatchError(observer, error));
|
|
115
115
|
}
|
|
116
116
|
async setDocuments(ref, data) {
|
|
117
|
-
const snapshot = await
|
|
118
|
-
await Promise.all(snapshot.docs.map(s => s.ref
|
|
117
|
+
const snapshot = await getDocs(getQueryReference(this.firestore, ref));
|
|
118
|
+
await Promise.all(snapshot.docs.map(s => setDoc(s.ref, data))); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
119
119
|
}
|
|
120
120
|
async updateDocuments(ref, transforms) {
|
|
121
|
-
const snapshot = await
|
|
121
|
+
const snapshot = await getDocs(getQueryReference(this.firestore, ref));
|
|
122
122
|
const updates = convertTransforms(transforms);
|
|
123
|
-
await Promise.all(snapshot.docs.map(s => s.ref
|
|
123
|
+
await Promise.all(snapshot.docs.map(s => updateDoc(s.ref, updates))); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
124
124
|
}
|
|
125
125
|
async deleteDocuments(ref) {
|
|
126
|
-
const snapshot = await
|
|
127
|
-
await Promise.all(snapshot.docs.map(s => s.ref
|
|
126
|
+
const snapshot = await getDocs(getQueryReference(this.firestore, ref));
|
|
127
|
+
await Promise.all(snapshot.docs.map(s => deleteDoc(s.ref)));
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Firestore } from "firebase/firestore/lite";
|
|
2
|
+
import { Data, Results, Provider, Document, Documents, Result, Transforms } from "..";
|
|
3
|
+
/**
|
|
4
|
+
* Firestore Lite client database provider.
|
|
5
|
+
* - Works with the Firebase JS SDK.
|
|
6
|
+
* - Does not support offline mode.
|
|
7
|
+
* - Does not support realtime subscriptions.
|
|
8
|
+
*/
|
|
9
|
+
export declare class FirestoreClientProvider implements Provider {
|
|
10
|
+
readonly firestore: Firestore;
|
|
11
|
+
constructor(firestore: Firestore);
|
|
12
|
+
getDocument<X extends Data>(ref: Document<X>): Promise<Result<X>>;
|
|
13
|
+
onDocument(): () => void;
|
|
14
|
+
addDocument<X extends Data>(ref: Documents<X>, data: X): Promise<string>;
|
|
15
|
+
setDocument<X extends Data>(ref: Document<X>, data: X): Promise<void>;
|
|
16
|
+
updateDocument<X extends Data>(ref: Document<X>, transforms: Transforms<X>): Promise<void>;
|
|
17
|
+
deleteDocument<X extends Data>(ref: Document<X>): Promise<void>;
|
|
18
|
+
getDocuments<X extends Data>(ref: Documents<X>): Promise<Results<X>>;
|
|
19
|
+
onDocuments(): () => void;
|
|
20
|
+
setDocuments<X extends Data>(ref: Documents<X>, data: X): Promise<void>;
|
|
21
|
+
updateDocuments<X extends Data>(ref: Documents<X>, transforms: Transforms<X>): Promise<void>;
|
|
22
|
+
deleteDocuments<X extends Data>(ref: Documents<X>): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { getDoc, orderBy, where, limit, addDoc, increment, arrayUnion, arrayRemove, deleteField, collection as getFirestoreCollection, doc as getFirestoreDoc, query as getFirestoreQuery, setDoc, updateDoc, deleteDoc, getDocs, } from "firebase/firestore/lite";
|
|
2
|
+
import { AddItemsTransform, AddEntriesTransform, IncrementTransform, isTransform, RemoveItemsTransform, RemoveEntriesTransform, } from "..";
|
|
3
|
+
// Constants.
|
|
4
|
+
// const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
|
|
5
|
+
const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
|
|
6
|
+
// Map `Filter.types` to `WhereFilterOp`
|
|
7
|
+
const OPERATORS = {
|
|
8
|
+
IS: "==",
|
|
9
|
+
NOT: "!=",
|
|
10
|
+
IN: "in",
|
|
11
|
+
GT: ">",
|
|
12
|
+
GTE: ">=",
|
|
13
|
+
LT: "<",
|
|
14
|
+
LTE: "<=",
|
|
15
|
+
CONTAINS: "array-contains",
|
|
16
|
+
};
|
|
17
|
+
// Map `Filter.types` to `OrderByDirection`
|
|
18
|
+
const DIRECTIONS = {
|
|
19
|
+
ASC: "asc",
|
|
20
|
+
DESC: "desc",
|
|
21
|
+
};
|
|
22
|
+
/** Get a Firestore DocumentReference for a given Shelving `Document` instance. */
|
|
23
|
+
function getDocumentReference(firestore, { path }) {
|
|
24
|
+
return getFirestoreDoc(firestore, path);
|
|
25
|
+
}
|
|
26
|
+
/** Get a Firestore CollectionReference for a given Shelving `Document` instance. */
|
|
27
|
+
function getCollectionReference(firestore, { path }) {
|
|
28
|
+
return getFirestoreCollection(firestore, path);
|
|
29
|
+
}
|
|
30
|
+
/** Create a corresponding `QueryReference` from a Query. */
|
|
31
|
+
function getQueryReference(firestore, ref) {
|
|
32
|
+
const { sorts, filters, slice } = ref.query;
|
|
33
|
+
const constraints = [];
|
|
34
|
+
for (const { key, direction } of sorts)
|
|
35
|
+
constraints.push(orderBy(key === "id" ? ID : key, DIRECTIONS[direction]));
|
|
36
|
+
for (const { operator, key, value } of filters)
|
|
37
|
+
constraints.push(where(key === "id" ? ID : key, OPERATORS[operator], value));
|
|
38
|
+
if (slice.limit !== null)
|
|
39
|
+
constraints.push(limit(slice.limit));
|
|
40
|
+
return getFirestoreQuery(getCollectionReference(firestore, ref), ...constraints);
|
|
41
|
+
}
|
|
42
|
+
/** Create a set of results from a collection snapshot. */
|
|
43
|
+
function getResults(snapshot) {
|
|
44
|
+
const results = {};
|
|
45
|
+
for (const s of snapshot.docs)
|
|
46
|
+
results[s.id] = s.data();
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
/** Convert a set of Shelving `Transform` instances into the corresponding Firestore `FieldValue` instances. */
|
|
50
|
+
function convertTransforms(transforms) {
|
|
51
|
+
const output = {};
|
|
52
|
+
for (const [key, transform] of Object.entries(transforms)) {
|
|
53
|
+
if (isTransform(transform)) {
|
|
54
|
+
if (transform instanceof IncrementTransform) {
|
|
55
|
+
output[key] = increment(transform.amount);
|
|
56
|
+
}
|
|
57
|
+
else if (transform instanceof AddItemsTransform) {
|
|
58
|
+
output[key] = arrayUnion(...transform.items);
|
|
59
|
+
}
|
|
60
|
+
else if (transform instanceof RemoveItemsTransform) {
|
|
61
|
+
output[key] = arrayRemove(...transform.items);
|
|
62
|
+
}
|
|
63
|
+
else if (transform instanceof AddEntriesTransform) {
|
|
64
|
+
for (const [k, v] of Object.entries(transform.props))
|
|
65
|
+
output[`${key}.${k}`] = v;
|
|
66
|
+
}
|
|
67
|
+
else if (transform instanceof RemoveEntriesTransform) {
|
|
68
|
+
for (const k of transform.props)
|
|
69
|
+
output[`${key}.${k}`] = deleteField();
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
throw Error("Unsupported transform");
|
|
73
|
+
}
|
|
74
|
+
else if (transform !== undefined) {
|
|
75
|
+
output[key] = transform;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return output;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Firestore Lite client database provider.
|
|
82
|
+
* - Works with the Firebase JS SDK.
|
|
83
|
+
* - Does not support offline mode.
|
|
84
|
+
* - Does not support realtime subscriptions.
|
|
85
|
+
*/
|
|
86
|
+
export class FirestoreClientProvider {
|
|
87
|
+
constructor(firestore) {
|
|
88
|
+
this.firestore = firestore;
|
|
89
|
+
}
|
|
90
|
+
async getDocument(ref) {
|
|
91
|
+
const snapshot = await getDoc(getDocumentReference(this.firestore, ref));
|
|
92
|
+
return snapshot.data();
|
|
93
|
+
}
|
|
94
|
+
onDocument() {
|
|
95
|
+
throw new Error("FirestoreLiteProvider does not support realtime subscriptions");
|
|
96
|
+
}
|
|
97
|
+
async addDocument(ref, data) {
|
|
98
|
+
const reference = await addDoc(getCollectionReference(this.firestore, ref), data); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
99
|
+
return reference.id;
|
|
100
|
+
}
|
|
101
|
+
async setDocument(ref, data) {
|
|
102
|
+
await setDoc(getDocumentReference(this.firestore, ref), data); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
103
|
+
}
|
|
104
|
+
async updateDocument(ref, transforms) {
|
|
105
|
+
await updateDoc(getDocumentReference(this.firestore, ref), convertTransforms(transforms)); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
106
|
+
}
|
|
107
|
+
async deleteDocument(ref) {
|
|
108
|
+
await deleteDoc(getDocumentReference(this.firestore, ref));
|
|
109
|
+
}
|
|
110
|
+
async getDocuments(ref) {
|
|
111
|
+
return getResults(await getDocs(getQueryReference(this.firestore, ref)));
|
|
112
|
+
}
|
|
113
|
+
onDocuments() {
|
|
114
|
+
throw new Error("FirestoreLiteProvider does not support realtime subscriptions");
|
|
115
|
+
}
|
|
116
|
+
async setDocuments(ref, data) {
|
|
117
|
+
const snapshot = await getDocs(getQueryReference(this.firestore, ref));
|
|
118
|
+
await Promise.all(snapshot.docs.map(s => setDoc(s.ref, data))); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
119
|
+
}
|
|
120
|
+
async updateDocuments(ref, transforms) {
|
|
121
|
+
const snapshot = await getDocs(getQueryReference(this.firestore, ref));
|
|
122
|
+
const updates = convertTransforms(transforms);
|
|
123
|
+
await Promise.all(snapshot.docs.map(s => updateDoc(s.ref, updates))); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
124
|
+
}
|
|
125
|
+
async deleteDocuments(ref) {
|
|
126
|
+
const snapshot = await getDocs(getQueryReference(this.firestore, ref));
|
|
127
|
+
await Promise.all(snapshot.docs.map(s => deleteDoc(s.ref)));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./FirestoreLiteProvider";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./FirestoreLiteProvider";
|
|
@@ -2,8 +2,7 @@ import type { Firestore } from "@google-cloud/firestore";
|
|
|
2
2
|
import { Data, Results, Provider, Document, Documents, Observer, Result, Transforms } from "..";
|
|
3
3
|
/**
|
|
4
4
|
* Firestore server database provider.
|
|
5
|
-
* - Works with the
|
|
6
|
-
* - Equality hash is set on returned values so `deepEqual()` etc can use it to quickly compare results without needing to look deeply.
|
|
5
|
+
* - Works with the Firebase Admin SDK for Node.JS
|
|
7
6
|
*/
|
|
8
7
|
export declare class FirestoreServerProvider implements Provider {
|
|
9
8
|
readonly firestore: Firestore;
|