@visiolab/effect-firebase 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -2
- package/dist/_virtual/_rolldown/runtime.mjs +12 -0
- package/dist/app/App.d.mts +27 -0
- package/dist/app/App.mjs +27 -0
- package/dist/app/index.d.mts +2 -0
- package/dist/app/index.mjs +1 -0
- package/dist/firestore/Collection.d.mts +423 -0
- package/dist/firestore/Collection.mjs +133 -0
- package/dist/firestore/CollectionGroup.d.mts +164 -0
- package/dist/firestore/CollectionGroup.mjs +84 -0
- package/dist/firestore/DocumentPath.d.mts +95 -0
- package/dist/firestore/DocumentPath.mjs +71 -0
- package/dist/firestore/DocumentSnapshot.d.mts +239 -0
- package/dist/firestore/DocumentSnapshot.mjs +134 -0
- package/dist/firestore/FieldValue.d.mts +332 -0
- package/dist/firestore/FieldValue.mjs +256 -0
- package/dist/firestore/Firestore.d.mts +1165 -0
- package/dist/firestore/Firestore.mjs +274 -0
- package/dist/firestore/FirestoreModel.d.mts +1232 -0
- package/dist/firestore/FirestoreModel.mjs +586 -0
- package/dist/firestore/FirestoreSchema.d.mts +567 -0
- package/dist/firestore/FirestoreSchema.mjs +419 -0
- package/dist/firestore/FirestoreValue.d.mts +145 -0
- package/dist/firestore/FirestoreValue.mjs +87 -0
- package/dist/firestore/GeoPoint.d.mts +90 -0
- package/dist/firestore/GeoPoint.mjs +75 -0
- package/dist/firestore/Patch.d.mts +73 -0
- package/dist/firestore/Patch.mjs +57 -0
- package/dist/firestore/QuerySnapshot.d.mts +107 -0
- package/dist/firestore/QuerySnapshot.mjs +43 -0
- package/dist/firestore/Timestamp.d.mts +273 -0
- package/dist/firestore/Timestamp.mjs +255 -0
- package/dist/firestore/index.d.mts +14 -0
- package/dist/firestore/index.mjs +14 -0
- package/dist/firestore/internal/collection.mjs +156 -0
- package/dist/firestore/internal/collectionGroup.mjs +64 -0
- package/dist/firestore/internal/documentPath.d.mts +4 -0
- package/dist/firestore/internal/documentPath.mjs +42 -0
- package/dist/firestore/internal/documentSnapshot.mjs +63 -0
- package/dist/firestore/internal/fieldValue.d.mts +11 -0
- package/dist/firestore/internal/fieldValue.mjs +88 -0
- package/dist/firestore/internal/firestore.mjs +145 -0
- package/dist/firestore/internal/firestoreError.d.mts +29 -0
- package/dist/firestore/internal/firestoreError.mjs +30 -0
- package/dist/firestore/internal/firestoreModel.mjs +719 -0
- package/dist/firestore/internal/firestoreSchema.mjs +123 -0
- package/dist/firestore/internal/firestoreValue.mjs +108 -0
- package/dist/firestore/internal/firestoreValueWalk.d.mts +58 -0
- package/dist/firestore/internal/firestoreValueWalk.mjs +223 -0
- package/dist/firestore/internal/geoPoint.d.mts +4 -0
- package/dist/firestore/internal/geoPoint.mjs +39 -0
- package/dist/firestore/internal/patch.mjs +19 -0
- package/dist/firestore/internal/querySnapshot.mjs +24 -0
- package/dist/firestore/internal/schemaAST.mjs +35 -0
- package/dist/firestore/internal/snapshotExtra.mjs +35 -0
- package/dist/firestore/internal/streamFromListener.mjs +12 -0
- package/dist/firestore/internal/timestamp.d.mts +4 -0
- package/dist/firestore/internal/timestamp.mjs +66 -0
- package/dist/firestore/internal/timestampSchema.mjs +31 -0
- package/dist/firestore/internal/transactionFromCallback.mjs +66 -0
- package/dist/functions/Callable.d.mts +654 -0
- package/dist/functions/Callable.mjs +450 -0
- package/dist/functions/CallableError.d.mts +297 -0
- package/dist/functions/CallableError.mjs +256 -0
- package/dist/functions/CallableMiddleware.d.mts +213 -0
- package/dist/functions/CallableMiddleware.mjs +52 -0
- package/dist/functions/CallableRequest.d.mts +166 -0
- package/dist/functions/CallableRequest.mjs +18 -0
- package/dist/functions/Caller.d.mts +103 -0
- package/dist/functions/Caller.mjs +115 -0
- package/dist/functions/DocumentTrigger.d.mts +795 -0
- package/dist/functions/DocumentTrigger.mjs +566 -0
- package/dist/functions/FirebaseFunction.d.mts +79 -0
- package/dist/functions/FirebaseFunction.mjs +4 -0
- package/dist/functions/FunctionGroup.d.mts +175 -0
- package/dist/functions/FunctionGroup.mjs +64 -0
- package/dist/functions/index.d.mts +9 -0
- package/dist/functions/index.mjs +9 -0
- package/dist/functions/internal/callable.mjs +167 -0
- package/dist/functions/internal/callableMiddleware.d.mts +19 -0
- package/dist/functions/internal/callableMiddleware.mjs +15 -0
- package/dist/functions/internal/callableRequest.mjs +6 -0
- package/dist/functions/internal/documentTrigger.d.mts +10 -0
- package/dist/functions/internal/documentTrigger.mjs +73 -0
- package/dist/functions/internal/firebaseFunction.d.mts +5 -0
- package/dist/functions/internal/firebaseFunction.mjs +24 -0
- package/dist/functions/internal/functionGroup.mjs +36 -0
- package/package.json +66 -3
- package/src/app/App.ts +24 -0
- package/src/app/index.ts +7 -0
- package/src/firestore/Collection.ts +520 -0
- package/src/firestore/CollectionGroup.ts +245 -0
- package/src/firestore/DocumentPath.ts +114 -0
- package/src/firestore/DocumentSnapshot.ts +308 -0
- package/src/firestore/FieldValue.ts +375 -0
- package/src/firestore/Firestore.ts +1432 -0
- package/src/firestore/FirestoreModel.ts +1884 -0
- package/src/firestore/FirestoreSchema.ts +643 -0
- package/src/firestore/FirestoreValue.ts +207 -0
- package/src/firestore/GeoPoint.ts +105 -0
- package/src/firestore/Patch.ts +117 -0
- package/src/firestore/QuerySnapshot.ts +150 -0
- package/src/firestore/Timestamp.ts +302 -0
- package/src/firestore/index.ts +67 -0
- package/src/firestore/internal/collection.ts +246 -0
- package/src/firestore/internal/collectionGroup.ts +81 -0
- package/src/firestore/internal/documentPath.ts +53 -0
- package/src/firestore/internal/documentSnapshot.ts +117 -0
- package/src/firestore/internal/fieldValue.ts +119 -0
- package/src/firestore/internal/firestore.ts +322 -0
- package/src/firestore/internal/firestoreError.ts +36 -0
- package/src/firestore/internal/firestoreModel.ts +1242 -0
- package/src/firestore/internal/firestoreSchema.ts +218 -0
- package/src/firestore/internal/firestoreValue.ts +202 -0
- package/src/firestore/internal/firestoreValueWalk.ts +349 -0
- package/src/firestore/internal/geoPoint.ts +59 -0
- package/src/firestore/internal/patch.ts +21 -0
- package/src/firestore/internal/querySnapshot.ts +39 -0
- package/src/firestore/internal/schemaAST.ts +83 -0
- package/src/firestore/internal/snapshotExtra.ts +47 -0
- package/src/firestore/internal/streamFromListener.ts +27 -0
- package/src/firestore/internal/timestamp.ts +119 -0
- package/src/firestore/internal/timestampSchema.ts +42 -0
- package/src/firestore/internal/transactionFromCallback.ts +106 -0
- package/src/functions/Callable.ts +855 -0
- package/src/functions/CallableError.ts +348 -0
- package/src/functions/CallableMiddleware.ts +279 -0
- package/src/functions/CallableRequest.ts +207 -0
- package/src/functions/Caller.ts +141 -0
- package/src/functions/DocumentTrigger.ts +927 -0
- package/src/functions/FirebaseFunction.ts +96 -0
- package/src/functions/FunctionGroup.ts +225 -0
- package/src/functions/index.ts +62 -0
- package/src/functions/internal/callable.ts +272 -0
- package/src/functions/internal/callableMiddleware.ts +54 -0
- package/src/functions/internal/callableRequest.ts +8 -0
- package/src/functions/internal/documentTrigger.ts +143 -0
- package/src/functions/internal/firebaseFunction.ts +58 -0
- package/src/functions/internal/functionGroup.ts +47 -0
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
import { DocumentSnapshot } from "./DocumentSnapshot.mjs";
|
|
2
|
+
import { UpdateMode } from "./FirestoreModel.mjs";
|
|
3
|
+
import { Any, CollectionReference, DocumentReference, DocumentSchema } from "./Collection.mjs";
|
|
4
|
+
import { FirestoreError, UndefinedCollectionError } from "./internal/firestoreError.mjs";
|
|
5
|
+
import { FirestoreValue } from "./FirestoreValue.mjs";
|
|
6
|
+
import { QuerySnapshot } from "./QuerySnapshot.mjs";
|
|
7
|
+
import { Any as Any$1, Members } from "./CollectionGroup.mjs";
|
|
8
|
+
import { Context, Effect, Schema, Stream } from "effect";
|
|
9
|
+
import * as Result$1 from "effect/Result";
|
|
10
|
+
declare namespace Firestore_d_exports {
|
|
11
|
+
export { BuiltTransaction, CollectionSelector, Create, DocumentData, DocumentRead, Firestore, FirestoreError, MakeOptions, OptionsParameter, QueryDocumentRead, QueryRead, ReadExtra, RunTransaction, StreamFromListenerOptions, Transaction, TransactionBase, TransactionFromCallbackOptions, TransactionHookOptions, TransactionOperations, TransactionOptions, UndefinedCollectionError, Update, UpdateOptions, UpdateSchema, With, make, streamFromListener, transactionFromCallback };
|
|
12
|
+
}
|
|
13
|
+
declare const TypeId = "~@visiolab/effect-firebase/firestore/Firestore";
|
|
14
|
+
declare const TransactionTypeId = "~@visiolab/effect-firebase/firestore/Firestore/Transaction";
|
|
15
|
+
/**
|
|
16
|
+
* The data {@link With.setDoc} and {@link With.addDoc} take for a document in
|
|
17
|
+
* `C`: the `create` shape of the collection's model.
|
|
18
|
+
*
|
|
19
|
+
* @see {@link Update} for the data {@link With.updateDoc} takes
|
|
20
|
+
*
|
|
21
|
+
* @stability unstable
|
|
22
|
+
* @category models
|
|
23
|
+
* @since 0.2.0
|
|
24
|
+
*/
|
|
25
|
+
export type Create<C extends Any> = DocumentSchema<C>["create"]["Type"];
|
|
26
|
+
/**
|
|
27
|
+
* The schema an update to a document in `C` is encoded through in `Mode`: the
|
|
28
|
+
* model's `update` schema, or its `updateUnchecked` schema.
|
|
29
|
+
*
|
|
30
|
+
* @see {@link Update} for the patch type itself
|
|
31
|
+
*
|
|
32
|
+
* @stability unstable
|
|
33
|
+
* @category models
|
|
34
|
+
* @since 0.2.0
|
|
35
|
+
*/
|
|
36
|
+
export type UpdateSchema<C extends Any, Mode extends UpdateMode = "checked"> = Mode extends "unchecked" ? DocumentSchema<C>["updateUnchecked"] : DocumentSchema<C>["update"];
|
|
37
|
+
/**
|
|
38
|
+
* The patch {@link With.updateDoc} takes for a document in `C` in `Mode`.
|
|
39
|
+
*
|
|
40
|
+
* @see {@link Create} for the data {@link With.setDoc} takes
|
|
41
|
+
* @see {@link FirestoreModel.UpdateMode} for which patches each mode accepts
|
|
42
|
+
*
|
|
43
|
+
* @stability unstable
|
|
44
|
+
* @category models
|
|
45
|
+
* @since 0.2.0
|
|
46
|
+
*/
|
|
47
|
+
export type Update<C extends Any, Mode extends UpdateMode = "checked"> = UpdateSchema<C, Mode>["Type"];
|
|
48
|
+
/**
|
|
49
|
+
* Options for {@link With.updateDoc}.
|
|
50
|
+
*
|
|
51
|
+
* @stability unstable
|
|
52
|
+
* @category models
|
|
53
|
+
* @since 0.2.0
|
|
54
|
+
*/
|
|
55
|
+
export interface UpdateOptions<Mode extends UpdateMode = "checked"> {
|
|
56
|
+
/**
|
|
57
|
+
* Which patches the update accepts.
|
|
58
|
+
*
|
|
59
|
+
* **When to use**
|
|
60
|
+
*
|
|
61
|
+
* Leave it at `"checked"`, the default, which only accepts patches that
|
|
62
|
+
* leave every document readable. Use `"unchecked"` to write into an optional
|
|
63
|
+
* struct or a union member's key, when you know the document holds it, for
|
|
64
|
+
* example from an earlier read in the same transaction.
|
|
65
|
+
*
|
|
66
|
+
* @see {@link FirestoreModel.UpdateMode} for exactly which patches each mode accepts
|
|
67
|
+
*/
|
|
68
|
+
readonly mode?: Mode | undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Options for {@link With.runTransaction}.
|
|
72
|
+
*
|
|
73
|
+
* @stability unstable
|
|
74
|
+
* @category models
|
|
75
|
+
* @since 0.2.0
|
|
76
|
+
*/
|
|
77
|
+
export interface TransactionOptions {
|
|
78
|
+
/**
|
|
79
|
+
* How many times the body runs before a transaction that keeps conflicting
|
|
80
|
+
* with other writes gives up. Defaults to the SDK's own default, 5.
|
|
81
|
+
*/
|
|
82
|
+
readonly maxAttempts?: number | undefined;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The transaction a {@link With.runTransaction} body reads and writes
|
|
86
|
+
* through.
|
|
87
|
+
*
|
|
88
|
+
* **When to use**
|
|
89
|
+
*
|
|
90
|
+
* Ask for it in a helper that must run inside a transaction. Requiring it in
|
|
91
|
+
* the type says the helper belongs in a transaction and may run more than
|
|
92
|
+
* once.
|
|
93
|
+
*
|
|
94
|
+
* **Details**
|
|
95
|
+
*
|
|
96
|
+
* It follows Firestore's transaction rules: every read comes before every
|
|
97
|
+
* write, and writes are queued and only sent when the body finishes, so they
|
|
98
|
+
* cannot be read back inside the transaction.
|
|
99
|
+
*
|
|
100
|
+
* It reads one document at a time, never a whole collection, because the
|
|
101
|
+
* client SDK's transactions only read documents.
|
|
102
|
+
*
|
|
103
|
+
* @see {@link AdminFirestore.AdminTransaction} for an Admin SDK transaction that can also read a whole collection
|
|
104
|
+
*
|
|
105
|
+
* @example Moving a counter inside a transaction
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { Effect, Schema } from "effect";
|
|
108
|
+
* import { Collection, DocumentSnapshot, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
109
|
+
*
|
|
110
|
+
* class Counter extends FirestoreModel.Class<Counter>("Counter")({ count: Schema.Finite }) {}
|
|
111
|
+
*
|
|
112
|
+
* const Counters = Collection.make("counters", Counter);
|
|
113
|
+
*
|
|
114
|
+
* const bump = (id: string) =>
|
|
115
|
+
* Effect.gen(function* () {
|
|
116
|
+
* const transaction = yield* Firestore.Transaction;
|
|
117
|
+
* const ref = Counters.doc(Counters.id.make(id));
|
|
118
|
+
* const snapshot = yield* transaction.getDoc(ref);
|
|
119
|
+
* const count = DocumentSnapshot.isExisting(snapshot) ? (yield* snapshot.data).count : 0;
|
|
120
|
+
* yield* transaction.setDoc(ref, { count: count + 1 });
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* const program = Effect.gen(function* () {
|
|
124
|
+
* const firestore = yield* Firestore.Firestore;
|
|
125
|
+
* yield* firestore.runTransaction(bump("visits"));
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @stability unstable
|
|
130
|
+
* @category models
|
|
131
|
+
* @since 0.2.0
|
|
132
|
+
*/
|
|
133
|
+
export interface Transaction extends TransactionBase {
|
|
134
|
+
readonly [TransactionTypeId]: typeof TransactionTypeId;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* What every transaction can do: the reads and writes of {@link Transaction},
|
|
138
|
+
* without the type id that makes a type a {@link Transaction}.
|
|
139
|
+
*
|
|
140
|
+
* **When to use**
|
|
141
|
+
*
|
|
142
|
+
* Extend it to type a backend's richer transaction, as
|
|
143
|
+
* {@link AdminFirestore.AdminTransaction} does, and put {@link Transaction}
|
|
144
|
+
* back in that service's key. Application code asks for {@link Transaction}.
|
|
145
|
+
*
|
|
146
|
+
* **Details**
|
|
147
|
+
*
|
|
148
|
+
* A richer transaction that extended {@link Transaction} would be assignable
|
|
149
|
+
* to it, so every Firestore would drop it from a transaction body's
|
|
150
|
+
* requirements along with {@link Transaction}, and nothing would check that
|
|
151
|
+
* it is provided.
|
|
152
|
+
*
|
|
153
|
+
* @see {@link Transaction} for the portable transaction
|
|
154
|
+
*
|
|
155
|
+
* @stability unstable
|
|
156
|
+
* @category models
|
|
157
|
+
* @since 0.3.0
|
|
158
|
+
*/
|
|
159
|
+
export interface TransactionBase {
|
|
160
|
+
/**
|
|
161
|
+
* Reads one document as part of the transaction. Works like
|
|
162
|
+
* {@link With.getDoc}.
|
|
163
|
+
*
|
|
164
|
+
* Every read must come before the transaction's first write.
|
|
165
|
+
*/
|
|
166
|
+
readonly getDoc: <C extends Any>(ref: DocumentReference<C>) => Effect.Effect<DocumentSnapshot<C>, FirestoreError>;
|
|
167
|
+
/**
|
|
168
|
+
* Queues a write of one whole document. Works like {@link With.setDoc}.
|
|
169
|
+
*/
|
|
170
|
+
readonly setDoc: <C extends Any>(ref: DocumentReference<C>, data: Create<C>) => Effect.Effect<void, FirestoreError | Schema.SchemaError, DocumentSchema<C>["create"]["EncodingServices"]>;
|
|
171
|
+
/**
|
|
172
|
+
* Queues an update of one document. Works like {@link With.updateDoc}.
|
|
173
|
+
*/
|
|
174
|
+
readonly updateDoc: <C extends Any, Mode extends UpdateMode = "checked">(ref: DocumentReference<C>, data: Update<C, Mode>, options?: UpdateOptions<Mode>) => Effect.Effect<void, FirestoreError | Schema.SchemaError, DocumentSchema<C>["update"]["EncodingServices"]>;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Service key for the {@link Transaction} a {@link With.runTransaction} body
|
|
178
|
+
* runs in.
|
|
179
|
+
*
|
|
180
|
+
* @stability unstable
|
|
181
|
+
* @category services
|
|
182
|
+
* @since 0.2.0
|
|
183
|
+
*/
|
|
184
|
+
export declare const Transaction: Context.Service<Transaction, Transaction>;
|
|
185
|
+
/**
|
|
186
|
+
* The trailing parameters a {@link With} read or listen takes: none when
|
|
187
|
+
* `Options` is `never`, as on the portable {@link Firestore}, and an optional
|
|
188
|
+
* `options` otherwise.
|
|
189
|
+
*
|
|
190
|
+
* @stability unstable
|
|
191
|
+
* @category models
|
|
192
|
+
* @since 0.3.0
|
|
193
|
+
*/
|
|
194
|
+
export type OptionsParameter<Options> = [Options] extends [never] ? [] : [options?: Options];
|
|
195
|
+
/**
|
|
196
|
+
* The Firestore service with a backend's additions: the options its reads and
|
|
197
|
+
* listens take, the extra fields its snapshots carry, and further services a
|
|
198
|
+
* transaction body can ask for.
|
|
199
|
+
*
|
|
200
|
+
* **When to use**
|
|
201
|
+
*
|
|
202
|
+
* Use it to type a backend's richer service, as
|
|
203
|
+
* {@link AdminFirestore.AdminFirestore} and
|
|
204
|
+
* {@link ClientFirestore.ClientFirestore} do, or for what {@link make}
|
|
205
|
+
* returns. Application code asks for {@link Firestore}, or for a backend's own
|
|
206
|
+
* service where it needs that backend's features.
|
|
207
|
+
*
|
|
208
|
+
* **Details**
|
|
209
|
+
*
|
|
210
|
+
* - `ReadOptions`: what {@link With.getDoc} and {@link With.getDocs} take
|
|
211
|
+
* besides a reference, such as the client's read `source`. None by default.
|
|
212
|
+
* - `ListenOptions`: what {@link With.streamDoc} and {@link With.streamDocs}
|
|
213
|
+
* take besides a reference. None by default.
|
|
214
|
+
* - `Extra`: what a backend knows about a read beyond the portable snapshot,
|
|
215
|
+
* such as the client's `metadata`. It is on every document snapshot, on
|
|
216
|
+
* each document of a query snapshot, and on the query snapshot itself.
|
|
217
|
+
* - `TransactionServices`: services a {@link With.runTransaction} body can ask
|
|
218
|
+
* for besides {@link Transaction}, such as a backend's own richer
|
|
219
|
+
* transaction. A service's type must not be assignable to
|
|
220
|
+
* {@link Transaction}, so extend {@link TransactionBase}, give it a type id
|
|
221
|
+
* of its own, and put {@link Transaction} back in its key's service type,
|
|
222
|
+
* as {@link AdminFirestore.AdminTransaction} does. A type that is
|
|
223
|
+
* assignable to {@link Transaction}, or to another backend's service, would
|
|
224
|
+
* be dropped from the body's requirements along with it, so nothing would
|
|
225
|
+
* check that it is provided.
|
|
226
|
+
*
|
|
227
|
+
* TypeScript does not compare `TransactionServices` when one `With` is
|
|
228
|
+
* assigned to another, so a backend should test that a transaction body can
|
|
229
|
+
* ask for each service it claims to offer.
|
|
230
|
+
*
|
|
231
|
+
* @see {@link Firestore} for the portable service, which is this with every parameter at its default
|
|
232
|
+
*
|
|
233
|
+
* @stability unstable
|
|
234
|
+
* @category models
|
|
235
|
+
* @since 0.3.0
|
|
236
|
+
*/
|
|
237
|
+
export interface With<ReadOptions = never, ListenOptions = never, Extra extends object = {}, TransactionServices = never> {
|
|
238
|
+
/**
|
|
239
|
+
* Reads one document.
|
|
240
|
+
*
|
|
241
|
+
* **When to use**
|
|
242
|
+
*
|
|
243
|
+
* Use to read a document once. A missing document is not a failure: you get
|
|
244
|
+
* a {@link DocumentSnapshot.Missing} snapshot, and
|
|
245
|
+
* {@link DocumentSnapshot.isExisting} tells the two cases apart.
|
|
246
|
+
*
|
|
247
|
+
* **Details**
|
|
248
|
+
*
|
|
249
|
+
* The snapshot's data is decoded through the collection's model only when
|
|
250
|
+
* you run its `data` effect, so a document that does not match the model
|
|
251
|
+
* fails there, not here.
|
|
252
|
+
*
|
|
253
|
+
* @see {@link With.streamDoc} to keep receiving the document as it changes
|
|
254
|
+
* @see {@link With.getDocs} to read every document in a collection
|
|
255
|
+
*
|
|
256
|
+
* @example Reading a document
|
|
257
|
+
* ```ts
|
|
258
|
+
* import { Effect, Schema } from "effect";
|
|
259
|
+
* import { Collection, DocumentSnapshot, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
260
|
+
*
|
|
261
|
+
* class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
|
|
262
|
+
*
|
|
263
|
+
* const Users = Collection.make("users", User);
|
|
264
|
+
*
|
|
265
|
+
* const program = Effect.gen(function* () {
|
|
266
|
+
* const firestore = yield* Firestore.Firestore;
|
|
267
|
+
* const snapshot = yield* firestore.getDoc(Users.doc(Users.id.make("alice")));
|
|
268
|
+
* if (DocumentSnapshot.isMissing(snapshot)) return undefined;
|
|
269
|
+
* return yield* snapshot.data;
|
|
270
|
+
* });
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
readonly getDoc: <C extends Any>(ref: DocumentReference<C>, ...options: OptionsParameter<ReadOptions>) => Effect.Effect<DocumentSnapshot<C> & Extra, FirestoreError>;
|
|
274
|
+
/**
|
|
275
|
+
* Reads every document in a collection, or in a
|
|
276
|
+
* {@link CollectionGroup.CollectionGroup}.
|
|
277
|
+
*
|
|
278
|
+
* **When to use**
|
|
279
|
+
*
|
|
280
|
+
* Use to read a whole collection once. Pass a collection reference to read
|
|
281
|
+
* one collection under one parent, or a Collection Group to read every
|
|
282
|
+
* collection of the group under every parent.
|
|
283
|
+
*
|
|
284
|
+
* **Details**
|
|
285
|
+
*
|
|
286
|
+
* Returns a {@link QuerySnapshot.QuerySnapshot} whose `docs` are
|
|
287
|
+
* {@link DocumentSnapshot.Existing} snapshots, one per document found. An
|
|
288
|
+
* empty collection reads as a snapshot with no `docs`.
|
|
289
|
+
*
|
|
290
|
+
* Reading a group matches each document by its path to the group's
|
|
291
|
+
* collection it lives in, and addresses and decodes it as that collection's.
|
|
292
|
+
* A document under the group's collection id at a path none of its
|
|
293
|
+
* collections define fails the read with an
|
|
294
|
+
* {@link UndefinedCollectionError}. Reading a collection reference fails
|
|
295
|
+
* with {@link FirestoreError} only.
|
|
296
|
+
*
|
|
297
|
+
* @see {@link With.streamDocs} to keep receiving the documents as they change
|
|
298
|
+
* @see {@link With.getDoc} to read one document
|
|
299
|
+
*
|
|
300
|
+
* @example Reading a collection
|
|
301
|
+
* ```ts
|
|
302
|
+
* import { Effect, Schema } from "effect";
|
|
303
|
+
* import { Collection, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
304
|
+
*
|
|
305
|
+
* class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
|
|
306
|
+
*
|
|
307
|
+
* const Users = Collection.make("users", User);
|
|
308
|
+
*
|
|
309
|
+
* const program = Effect.gen(function* () {
|
|
310
|
+
* const firestore = yield* Firestore.Firestore;
|
|
311
|
+
* const snapshot = yield* firestore.getDocs(Users.collection());
|
|
312
|
+
* return yield* Effect.forEach(snapshot.docs, (doc) => doc.data);
|
|
313
|
+
* });
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
readonly getDocs: {
|
|
317
|
+
<C extends Any>(ref: CollectionReference<C>, ...options: OptionsParameter<ReadOptions>): Effect.Effect<QuerySnapshot<C, Extra> & Extra, FirestoreError>;
|
|
318
|
+
<G extends Any$1>(group: G, ...options: OptionsParameter<ReadOptions>): Effect.Effect<QuerySnapshot<Members<G>, Extra> & Extra, FirestoreError | UndefinedCollectionError>;
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* Listens to one document, emitting a snapshot each time it changes.
|
|
322
|
+
*
|
|
323
|
+
* **When to use**
|
|
324
|
+
*
|
|
325
|
+
* Use to keep a view of a document up to date. For a one-off read, use
|
|
326
|
+
* {@link With.getDoc}.
|
|
327
|
+
*
|
|
328
|
+
* **Details**
|
|
329
|
+
*
|
|
330
|
+
* The first snapshot is the document as it is when the listener attaches. A
|
|
331
|
+
* document that is not there, or is deleted later, comes through as
|
|
332
|
+
* {@link DocumentSnapshot.Missing}.
|
|
333
|
+
*
|
|
334
|
+
* Each snapshot is the whole document, so a reader slower than the changes
|
|
335
|
+
* only sees the latest one: a snapshot not pulled before the next arrives is
|
|
336
|
+
* dropped.
|
|
337
|
+
*
|
|
338
|
+
* The stream never ends on its own. Each run attaches its own listener, which
|
|
339
|
+
* stays until the stream is interrupted. The stream fails with a
|
|
340
|
+
* {@link FirestoreError} when Firestore drops the listener.
|
|
341
|
+
*
|
|
342
|
+
* @see {@link With.getDoc} to read the document once
|
|
343
|
+
* @see {@link With.streamDocs} to listen to a whole collection
|
|
344
|
+
*
|
|
345
|
+
* @example Logging each change to a document
|
|
346
|
+
* ```ts
|
|
347
|
+
* import { Effect, Schema, Stream } from "effect";
|
|
348
|
+
* import { Collection, DocumentSnapshot, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
349
|
+
*
|
|
350
|
+
* class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
|
|
351
|
+
*
|
|
352
|
+
* const Users = Collection.make("users", User);
|
|
353
|
+
*
|
|
354
|
+
* const program = Effect.gen(function* () {
|
|
355
|
+
* const firestore = yield* Firestore.Firestore;
|
|
356
|
+
* yield* firestore.streamDoc(Users.doc(Users.id.make("alice"))).pipe(
|
|
357
|
+
* Stream.filter(DocumentSnapshot.isExisting),
|
|
358
|
+
* Stream.mapEffect((snapshot) => snapshot.data),
|
|
359
|
+
* Stream.runForEach((user) => Effect.log(user.name)),
|
|
360
|
+
* );
|
|
361
|
+
* });
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
readonly streamDoc: <C extends Any>(ref: DocumentReference<C>, ...options: OptionsParameter<ListenOptions>) => Stream.Stream<DocumentSnapshot<C> & Extra, FirestoreError>;
|
|
365
|
+
/**
|
|
366
|
+
* Listens to every document in a collection, or in a
|
|
367
|
+
* {@link CollectionGroup.CollectionGroup}, emitting all of them each time
|
|
368
|
+
* any of them changes.
|
|
369
|
+
*
|
|
370
|
+
* **When to use**
|
|
371
|
+
*
|
|
372
|
+
* Use to keep a view of a whole collection up to date. For a one-off read,
|
|
373
|
+
* use {@link With.getDocs}.
|
|
374
|
+
*
|
|
375
|
+
* **Details**
|
|
376
|
+
*
|
|
377
|
+
* Each element is a {@link QuerySnapshot.QuerySnapshot} of every document
|
|
378
|
+
* there at that moment, as {@link With.getDocs} would return it. As with
|
|
379
|
+
* {@link With.streamDoc}, a snapshot not pulled before the next arrives is
|
|
380
|
+
* dropped, and the stream never ends on its own.
|
|
381
|
+
*
|
|
382
|
+
* Listening to a group matches each document to its collection as
|
|
383
|
+
* {@link With.getDocs} does. The stream fails with an
|
|
384
|
+
* {@link UndefinedCollectionError} at the first snapshot holding a document
|
|
385
|
+
* at a path none of the group's collections define. Listening to a
|
|
386
|
+
* collection reference fails with {@link FirestoreError} only.
|
|
387
|
+
*
|
|
388
|
+
* @see {@link With.getDocs} to read the documents once
|
|
389
|
+
* @see {@link With.streamDoc} to listen to one document
|
|
390
|
+
*/
|
|
391
|
+
readonly streamDocs: {
|
|
392
|
+
<C extends Any>(ref: CollectionReference<C>, ...options: OptionsParameter<ListenOptions>): Stream.Stream<QuerySnapshot<C, Extra> & Extra, FirestoreError>;
|
|
393
|
+
<G extends Any$1>(group: G, ...options: OptionsParameter<ListenOptions>): Stream.Stream<QuerySnapshot<Members<G>, Extra> & Extra, FirestoreError | UndefinedCollectionError>;
|
|
394
|
+
};
|
|
395
|
+
/**
|
|
396
|
+
* Writes a whole document under an id you choose, replacing whatever was
|
|
397
|
+
* there.
|
|
398
|
+
*
|
|
399
|
+
* **When to use**
|
|
400
|
+
*
|
|
401
|
+
* Use to create a document at a known id, or to overwrite one completely.
|
|
402
|
+
*
|
|
403
|
+
* **Details**
|
|
404
|
+
*
|
|
405
|
+
* The data is the model's `create` shape and is written whole: fields left
|
|
406
|
+
* out are removed, not kept. The model asks for every field, so this is
|
|
407
|
+
* rarely a surprise.
|
|
408
|
+
*
|
|
409
|
+
* The data is encoded before anything is sent, so a value that does not
|
|
410
|
+
* encode fails with a `SchemaError` and nothing is written.
|
|
411
|
+
*
|
|
412
|
+
* @see {@link With.addDoc} to write under an id Firestore picks
|
|
413
|
+
* @see {@link With.updateDoc} to change some fields and keep the rest
|
|
414
|
+
*
|
|
415
|
+
* @example Writing a document
|
|
416
|
+
* ```ts
|
|
417
|
+
* import { Effect, Schema } from "effect";
|
|
418
|
+
* import { Collection, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
419
|
+
*
|
|
420
|
+
* class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
|
|
421
|
+
*
|
|
422
|
+
* const Users = Collection.make("users", User);
|
|
423
|
+
*
|
|
424
|
+
* const program = Effect.gen(function* () {
|
|
425
|
+
* const firestore = yield* Firestore.Firestore;
|
|
426
|
+
* yield* firestore.setDoc(Users.doc(Users.id.make("alice")), { name: "Alice" });
|
|
427
|
+
* });
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
readonly setDoc: <C extends Any>(ref: DocumentReference<C>, data: Create<C>) => Effect.Effect<void, FirestoreError | Schema.SchemaError, DocumentSchema<C>["create"]["EncodingServices"]>;
|
|
431
|
+
/**
|
|
432
|
+
* Writes a new document under an id Firestore picks, and returns its
|
|
433
|
+
* reference.
|
|
434
|
+
*
|
|
435
|
+
* **When to use**
|
|
436
|
+
*
|
|
437
|
+
* Use to create a document when its id does not matter.
|
|
438
|
+
*
|
|
439
|
+
* **Details**
|
|
440
|
+
*
|
|
441
|
+
* The data is the model's `create` shape, as for {@link With.setDoc}, and
|
|
442
|
+
* is encoded before anything is sent.
|
|
443
|
+
*
|
|
444
|
+
* @see {@link With.setDoc} to write under an id you choose
|
|
445
|
+
*
|
|
446
|
+
* @example Adding a document
|
|
447
|
+
* ```ts
|
|
448
|
+
* import { Effect, Schema } from "effect";
|
|
449
|
+
* import { Collection, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
450
|
+
*
|
|
451
|
+
* class Post extends FirestoreModel.Class<Post>("Post")({ title: Schema.String }) {}
|
|
452
|
+
*
|
|
453
|
+
* const Posts = Collection.make("posts", Post);
|
|
454
|
+
*
|
|
455
|
+
* const program = Effect.gen(function* () {
|
|
456
|
+
* const firestore = yield* Firestore.Firestore;
|
|
457
|
+
* const ref = yield* firestore.addDoc(Posts.collection(), { title: "Hello" });
|
|
458
|
+
* yield* Effect.log(`Added ${ref.id}`);
|
|
459
|
+
* });
|
|
460
|
+
* ```
|
|
461
|
+
*/
|
|
462
|
+
readonly addDoc: <C extends Any>(ref: CollectionReference<C>, data: Create<C>) => Effect.Effect<DocumentReference<C>, FirestoreError | Schema.SchemaError, DocumentSchema<C>["create"]["EncodingServices"]>;
|
|
463
|
+
/**
|
|
464
|
+
* Changes some fields of an existing document and keeps the rest.
|
|
465
|
+
*
|
|
466
|
+
* **When to use**
|
|
467
|
+
*
|
|
468
|
+
* Use to change part of a document without reading and rewriting it, for
|
|
469
|
+
* example with {@link FieldValue.increment} or
|
|
470
|
+
* {@link FieldValue.serverTimestamp}.
|
|
471
|
+
*
|
|
472
|
+
* **Details**
|
|
473
|
+
*
|
|
474
|
+
* The patch is encoded through the model's `update` schema, or its
|
|
475
|
+
* `updateUnchecked` schema when `mode` is `"unchecked"`. A nested struct in
|
|
476
|
+
* the patch is written field by field at its paths. Wrap it in
|
|
477
|
+
* {@link Patch.replace} to write it whole.
|
|
478
|
+
*
|
|
479
|
+
* The document must exist: Firestore fails the update with a
|
|
480
|
+
* {@link FirestoreError} when it does not, and also rejects an empty patch.
|
|
481
|
+
*
|
|
482
|
+
* Both modes encode the same fields at the same paths, so they require the
|
|
483
|
+
* same services.
|
|
484
|
+
*
|
|
485
|
+
* @see {@link With.setDoc} to write a whole document, existing or not
|
|
486
|
+
* @see {@link UpdateOptions.mode} for when to use `"unchecked"`
|
|
487
|
+
*
|
|
488
|
+
* @example Updating a field
|
|
489
|
+
* ```ts
|
|
490
|
+
* import { Effect, Schema } from "effect";
|
|
491
|
+
* import { Collection, FieldValue, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
492
|
+
*
|
|
493
|
+
* class Post extends FirestoreModel.Class<Post>("Post")({
|
|
494
|
+
* title: Schema.String,
|
|
495
|
+
* views: FirestoreModel.Number(Schema.Finite),
|
|
496
|
+
* }) {}
|
|
497
|
+
*
|
|
498
|
+
* const Posts = Collection.make("posts", Post);
|
|
499
|
+
*
|
|
500
|
+
* const program = Effect.gen(function* () {
|
|
501
|
+
* const firestore = yield* Firestore.Firestore;
|
|
502
|
+
* yield* firestore.updateDoc(Posts.doc(Posts.id.make("hello")), {
|
|
503
|
+
* views: FieldValue.increment(1),
|
|
504
|
+
* });
|
|
505
|
+
* });
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
readonly updateDoc: <C extends Any, Mode extends UpdateMode = "checked">(ref: DocumentReference<C>, data: Update<C, Mode>, options?: UpdateOptions<Mode>) => Effect.Effect<void, FirestoreError | Schema.SchemaError, DocumentSchema<C>["update"]["EncodingServices"]>;
|
|
509
|
+
/**
|
|
510
|
+
* Runs `effect` in a transaction, providing the {@link Transaction} it reads
|
|
511
|
+
* and writes through.
|
|
512
|
+
*
|
|
513
|
+
* **When to use**
|
|
514
|
+
*
|
|
515
|
+
* Use when writes depend on what was read, and must not be based on data
|
|
516
|
+
* another write has changed in the meantime.
|
|
517
|
+
*
|
|
518
|
+
* **Details**
|
|
519
|
+
*
|
|
520
|
+
* When another write conflicts, the SDK discards the queued writes and runs
|
|
521
|
+
* `effect` again from the start. Anything else `effect` does, such as
|
|
522
|
+
* sending an email, writing through the service rather than the
|
|
523
|
+
* transaction, or running another transaction, happens once per attempt, so
|
|
524
|
+
* do it after the transaction instead.
|
|
525
|
+
*
|
|
526
|
+
* A `FirestoreError` is retried where the SDK would retry it: a conflict, or
|
|
527
|
+
* a passing outage such as the database being briefly unavailable. That
|
|
528
|
+
* holds for any `FirestoreError` `effect` fails with, including one from a
|
|
529
|
+
* call outside the transaction. Any other failure, such as an error of your
|
|
530
|
+
* own, a defect, or a write that does not encode, rolls the transaction back
|
|
531
|
+
* and fails with exactly that error.
|
|
532
|
+
*
|
|
533
|
+
* A transaction run inside another is independent: it commits when it is
|
|
534
|
+
* done, whatever happens to the outer one.
|
|
535
|
+
*
|
|
536
|
+
* Interrupting it interrupts `effect`, rolls the transaction back, and
|
|
537
|
+
* returns once the rollback is done.
|
|
538
|
+
*
|
|
539
|
+
* A backend that offers `TransactionServices` provides the same transaction
|
|
540
|
+
* object under each of them, so `effect` does not need them from its caller.
|
|
541
|
+
*
|
|
542
|
+
* @see {@link Transaction} for what the body can read and write
|
|
543
|
+
* @see {@link TransactionOptions} for how many attempts it makes
|
|
544
|
+
*
|
|
545
|
+
* @example Running a transaction
|
|
546
|
+
* ```ts
|
|
547
|
+
* import { Effect, Schema } from "effect";
|
|
548
|
+
* import { Collection, DocumentSnapshot, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
549
|
+
*
|
|
550
|
+
* class Account extends FirestoreModel.Class<Account>("Account")({ balance: Schema.Finite }) {}
|
|
551
|
+
*
|
|
552
|
+
* const Accounts = Collection.make("accounts", Account);
|
|
553
|
+
*
|
|
554
|
+
* const withdraw = (id: string, amount: number) =>
|
|
555
|
+
* Effect.gen(function* () {
|
|
556
|
+
* const firestore = yield* Firestore.Firestore;
|
|
557
|
+
* yield* firestore.runTransaction(
|
|
558
|
+
* Effect.gen(function* () {
|
|
559
|
+
* const transaction = yield* Firestore.Transaction;
|
|
560
|
+
* const ref = Accounts.doc(Accounts.id.make(id));
|
|
561
|
+
* const snapshot = yield* transaction.getDoc(ref);
|
|
562
|
+
* if (DocumentSnapshot.isMissing(snapshot)) return yield* Effect.fail("NoSuchAccount" as const);
|
|
563
|
+
* const { balance } = yield* snapshot.data;
|
|
564
|
+
* if (balance < amount) return yield* Effect.fail("InsufficientFunds" as const);
|
|
565
|
+
* yield* transaction.updateDoc(ref, { balance: balance - amount });
|
|
566
|
+
* }),
|
|
567
|
+
* { maxAttempts: 3 },
|
|
568
|
+
* );
|
|
569
|
+
* });
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
readonly runTransaction: <A, E, R>(effect: Effect.Effect<A, E, R>, options?: TransactionOptions) => Effect.Effect<A, E | FirestoreError, Exclude<R, Transaction | TransactionServices>>;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* The portable Firestore service for a single app, which every backend
|
|
576
|
+
* provides.
|
|
577
|
+
*
|
|
578
|
+
* **When to use**
|
|
579
|
+
*
|
|
580
|
+
* Ask for it in code that should run on any backend, the Admin SDK or the
|
|
581
|
+
* client SDK. Its methods are documented on {@link With}.
|
|
582
|
+
*
|
|
583
|
+
* @see {@link With} for each method, and for what a backend's richer service adds
|
|
584
|
+
* @see {@link AdminFirestore.AdminFirestore} for the Admin SDK's richer service
|
|
585
|
+
* @see {@link ClientFirestore.ClientFirestore} for the client SDK's richer service
|
|
586
|
+
*
|
|
587
|
+
* @stability unstable
|
|
588
|
+
* @category models
|
|
589
|
+
* @since 0.2.0
|
|
590
|
+
*/
|
|
591
|
+
export interface Firestore extends With {
|
|
592
|
+
readonly [TypeId]: typeof TypeId;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Service key for the portable {@link Firestore}.
|
|
596
|
+
*
|
|
597
|
+
* Provide it with {@link AdminFirestore.layer} or
|
|
598
|
+
* {@link ClientFirestore.layer}.
|
|
599
|
+
*
|
|
600
|
+
* @stability unstable
|
|
601
|
+
* @category services
|
|
602
|
+
* @since 0.2.0
|
|
603
|
+
*/
|
|
604
|
+
export declare const Firestore: Context.Service<Firestore, Firestore>;
|
|
605
|
+
/**
|
|
606
|
+
* What a {@link MakeOptions} read or listen hook for many documents reads:
|
|
607
|
+
* one collection, by its path, or every collection with one collection id,
|
|
608
|
+
* wherever it sits.
|
|
609
|
+
*
|
|
610
|
+
* **Details**
|
|
611
|
+
*
|
|
612
|
+
* {@link make} builds it from the collection reference or
|
|
613
|
+
* {@link CollectionGroup.CollectionGroup} the caller passed, and keeps the
|
|
614
|
+
* group itself, so a hook only turns it into its backend's query.
|
|
615
|
+
*
|
|
616
|
+
* @stability unstable
|
|
617
|
+
* @category models
|
|
618
|
+
* @since 0.3.0
|
|
619
|
+
*/
|
|
620
|
+
export type CollectionSelector = {
|
|
621
|
+
readonly _tag: "Collection";
|
|
622
|
+
/** The collection's path, such as `users/alice/posts`. */
|
|
623
|
+
readonly path: string;
|
|
624
|
+
} | {
|
|
625
|
+
readonly _tag: "CollectionGroup";
|
|
626
|
+
/** The collection id every collection read shares, such as `posts`. */
|
|
627
|
+
readonly id: string;
|
|
628
|
+
};
|
|
629
|
+
/**
|
|
630
|
+
* A document's data as a {@link MakeOptions} hook reads and writes it: a map
|
|
631
|
+
* of Firestore Values.
|
|
632
|
+
*
|
|
633
|
+
* **Details**
|
|
634
|
+
*
|
|
635
|
+
* For a write it is either a whole document, for `setDoc` and `addDoc`, or a
|
|
636
|
+
* map from field paths such as `address.city` to the values written there, for
|
|
637
|
+
* `updateDoc`.
|
|
638
|
+
*
|
|
639
|
+
* @stability unstable
|
|
640
|
+
* @category models
|
|
641
|
+
* @since 0.3.0
|
|
642
|
+
*/
|
|
643
|
+
export type DocumentData = {
|
|
644
|
+
readonly [field: string]: FirestoreValue;
|
|
645
|
+
};
|
|
646
|
+
/**
|
|
647
|
+
* The `extra` field of what a {@link MakeOptions} read hook returns: what the
|
|
648
|
+
* backend knows about the read beyond the portable snapshot.
|
|
649
|
+
*
|
|
650
|
+
* **Details**
|
|
651
|
+
*
|
|
652
|
+
* Each field of `extra` is put on the snapshot and shown when it prints, as
|
|
653
|
+
* {@link DocumentSnapshot.make} does. Its fields must not clash with the
|
|
654
|
+
* snapshot's own.
|
|
655
|
+
*
|
|
656
|
+
* - When `Extra` has required fields, `extra` is required, so a snapshot never
|
|
657
|
+
* claims extra data its read did not return.
|
|
658
|
+
* - When every field is optional, `extra` may be left out.
|
|
659
|
+
* - When `Extra` has no fields, `extra` must be left out, so a hook cannot
|
|
660
|
+
* return extra data the snapshots do not carry.
|
|
661
|
+
*
|
|
662
|
+
* @stability unstable
|
|
663
|
+
* @category models
|
|
664
|
+
* @since 0.3.0
|
|
665
|
+
*/
|
|
666
|
+
export type ReadExtra<Extra extends object> = [keyof Extra] extends [never] ? {
|
|
667
|
+
readonly extra?: undefined;
|
|
668
|
+
} : {} extends Extra ? {
|
|
669
|
+
readonly extra?: Extra | undefined;
|
|
670
|
+
} : {
|
|
671
|
+
readonly extra: Extra;
|
|
672
|
+
};
|
|
673
|
+
/**
|
|
674
|
+
* What a {@link MakeOptions} hook returns for one document, with the `Extra`
|
|
675
|
+
* the backend knows about it.
|
|
676
|
+
*
|
|
677
|
+
* @see {@link QueryDocumentRead} for one document of many
|
|
678
|
+
* @see {@link ReadExtra} for its `extra`
|
|
679
|
+
*
|
|
680
|
+
* @stability unstable
|
|
681
|
+
* @category models
|
|
682
|
+
* @since 0.3.0
|
|
683
|
+
*/
|
|
684
|
+
export type DocumentRead<Extra extends object = {}> = {
|
|
685
|
+
/**
|
|
686
|
+
* The document's data, or `undefined` when it is not there. Data that is not
|
|
687
|
+
* made of Firestore Values fails the read with a {@link FirestoreError}.
|
|
688
|
+
*/
|
|
689
|
+
readonly data: DocumentData | undefined;
|
|
690
|
+
} & ReadExtra<Extra>;
|
|
691
|
+
/**
|
|
692
|
+
* What a {@link MakeOptions} hook returns for one document of many: always one
|
|
693
|
+
* that exists, with the `Extra` the backend knows about it.
|
|
694
|
+
*
|
|
695
|
+
* @see {@link DocumentRead} for a single document, which may be missing
|
|
696
|
+
* @see {@link ReadExtra} for its `extra`
|
|
697
|
+
*
|
|
698
|
+
* @stability unstable
|
|
699
|
+
* @category models
|
|
700
|
+
* @since 0.3.0
|
|
701
|
+
*/
|
|
702
|
+
export type QueryDocumentRead<Extra extends object = {}> = {
|
|
703
|
+
/**
|
|
704
|
+
* The document's full path, such as `users/alice/posts/hello`. It matches a
|
|
705
|
+
* document of a Collection Group to the collection it lives in.
|
|
706
|
+
*/
|
|
707
|
+
readonly path: string;
|
|
708
|
+
/**
|
|
709
|
+
* The document's data. Data that is not made of Firestore Values fails the
|
|
710
|
+
* read with a {@link FirestoreError}.
|
|
711
|
+
*/
|
|
712
|
+
readonly data: DocumentData;
|
|
713
|
+
} & ReadExtra<Extra>;
|
|
714
|
+
/**
|
|
715
|
+
* What a {@link MakeOptions} hook returns for many documents, with the
|
|
716
|
+
* `Extra` the backend knows about each of them and about the read as a whole.
|
|
717
|
+
*
|
|
718
|
+
* **Details**
|
|
719
|
+
*
|
|
720
|
+
* The read's own `extra` is put on the {@link QuerySnapshot.QuerySnapshot}, as
|
|
721
|
+
* {@link QuerySnapshot.make} does.
|
|
722
|
+
*
|
|
723
|
+
* @see {@link ReadExtra} for its `extra`
|
|
724
|
+
*
|
|
725
|
+
* @stability unstable
|
|
726
|
+
* @category models
|
|
727
|
+
* @since 0.3.0
|
|
728
|
+
*/
|
|
729
|
+
export type QueryRead<Extra extends object = {}> = {
|
|
730
|
+
/** Every document read. */
|
|
731
|
+
readonly docs: ReadonlyArray<QueryDocumentRead<Extra>>;
|
|
732
|
+
} & ReadExtra<Extra>;
|
|
733
|
+
/**
|
|
734
|
+
* The reads and writes of one transaction attempt, which a
|
|
735
|
+
* {@link RunTransaction.run} hook hands to the body. {@link make} builds the
|
|
736
|
+
* {@link Transaction} the body uses from them.
|
|
737
|
+
*
|
|
738
|
+
* **Details**
|
|
739
|
+
*
|
|
740
|
+
* Writes in a transaction are queued until it commits, so a backend may
|
|
741
|
+
* complete `setDoc` and `updateDoc` straight away.
|
|
742
|
+
*
|
|
743
|
+
* @stability unstable
|
|
744
|
+
* @category models
|
|
745
|
+
* @since 0.3.0
|
|
746
|
+
*/
|
|
747
|
+
export interface TransactionOperations {
|
|
748
|
+
/** Reads the document at `path` as part of the transaction. */
|
|
749
|
+
readonly getDoc: (path: string) => Effect.Effect<DocumentRead, FirestoreError>;
|
|
750
|
+
/** Queues a write of the whole document at `path`. */
|
|
751
|
+
readonly setDoc: (path: string, data: DocumentData) => Effect.Effect<void, FirestoreError>;
|
|
752
|
+
/** Queues an update of the document at `path`, with `data` keyed by field path. */
|
|
753
|
+
readonly updateDoc: (path: string, data: DocumentData) => Effect.Effect<void, FirestoreError>;
|
|
754
|
+
/**
|
|
755
|
+
* Reads many documents as part of the transaction, for a backend that can.
|
|
756
|
+
*
|
|
757
|
+
* When it is given, the transaction {@link make} builds has a `getDocs` that
|
|
758
|
+
* works like {@link With.getDocs}, which {@link RunTransaction.context} can
|
|
759
|
+
* offer under a key of its own.
|
|
760
|
+
*/
|
|
761
|
+
readonly getDocs?: ((selector: CollectionSelector) => Effect.Effect<QueryRead, FirestoreError>) | undefined;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* The transaction {@link make} builds from `Operations` and hands to
|
|
765
|
+
* {@link RunTransaction.context}: a {@link Transaction}, plus a `getDocs` that
|
|
766
|
+
* works like {@link With.getDocs} when `Operations` has `getDocs`.
|
|
767
|
+
*
|
|
768
|
+
* @stability unstable
|
|
769
|
+
* @category models
|
|
770
|
+
* @since 0.3.0
|
|
771
|
+
*/
|
|
772
|
+
export type BuiltTransaction<Operations extends TransactionOperations> = Transaction & (Operations extends {
|
|
773
|
+
readonly getDocs: NonNullable<TransactionOperations["getDocs"]>;
|
|
774
|
+
} ? {
|
|
775
|
+
readonly getDocs: Firestore["getDocs"];
|
|
776
|
+
} : {});
|
|
777
|
+
/**
|
|
778
|
+
* The {@link MakeOptions.runTransaction} hook: how a backend runs a
|
|
779
|
+
* transaction, and which further services a body can find the transaction
|
|
780
|
+
* under.
|
|
781
|
+
*
|
|
782
|
+
* **Details**
|
|
783
|
+
*
|
|
784
|
+
* `Operations` is what the backend can do in a transaction. It decides
|
|
785
|
+
* whether the transaction {@link make} builds can read many documents.
|
|
786
|
+
*
|
|
787
|
+
* @see {@link transactionFromCallback} to build one for an SDK that runs transactions as a callback
|
|
788
|
+
*
|
|
789
|
+
* @stability unstable
|
|
790
|
+
* @category models
|
|
791
|
+
* @since 0.3.0
|
|
792
|
+
*/
|
|
793
|
+
export interface RunTransaction<Operations extends TransactionOperations = TransactionOperations, TransactionServices = never> {
|
|
794
|
+
/**
|
|
795
|
+
* Runs `body` in a transaction, once per attempt, with that attempt's
|
|
796
|
+
* operations. Fails with what the body failed with, or with a
|
|
797
|
+
* {@link FirestoreError} of its own. Backs {@link With.runTransaction}.
|
|
798
|
+
*
|
|
799
|
+
* The body needs nothing from context: everything the caller's effect
|
|
800
|
+
* needs, including the transaction's span, is already provided, so it may
|
|
801
|
+
* run on a runtime of its own, such as inside an SDK callback.
|
|
802
|
+
*/
|
|
803
|
+
readonly run: <A, E>(body: (operations: Operations) => Effect.Effect<A, E>, options: TransactionHookOptions) => Effect.Effect<A, E | FirestoreError>;
|
|
804
|
+
/**
|
|
805
|
+
* Further services a transaction body can find the transaction under,
|
|
806
|
+
* besides {@link Transaction}, such as a backend's own richer transaction.
|
|
807
|
+
*
|
|
808
|
+
* It is handed the transaction {@link make} built for the attempt, and each
|
|
809
|
+
* service holds that same object, so the body reads and writes through one
|
|
810
|
+
* transaction whichever key it asks for.
|
|
811
|
+
*/
|
|
812
|
+
readonly context?: ((transaction: BuiltTransaction<Operations>) => Context.Context<TransactionServices>) | undefined;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* The options a {@link RunTransaction.run} hook is handed.
|
|
816
|
+
*
|
|
817
|
+
* @stability unstable
|
|
818
|
+
* @category options
|
|
819
|
+
* @since 0.3.0
|
|
820
|
+
*/
|
|
821
|
+
export interface TransactionHookOptions {
|
|
822
|
+
/**
|
|
823
|
+
* Only present when the caller set it, so a backend that passes these
|
|
824
|
+
* options on unchanged keeps its SDK's default otherwise. The client SDK,
|
|
825
|
+
* for one, turns retrying off when `maxAttempts` is present but
|
|
826
|
+
* `undefined`.
|
|
827
|
+
*/
|
|
828
|
+
readonly maxAttempts?: number;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* The hooks {@link make} builds a Firestore service from.
|
|
832
|
+
*
|
|
833
|
+
* **Details**
|
|
834
|
+
*
|
|
835
|
+
* Each hook works in Firestore's own terms: document paths, a
|
|
836
|
+
* {@link CollectionSelector}, and {@link DocumentData}. It converts between
|
|
837
|
+
* its backend's values and Firestore Values. Each returns an `Effect`, or a
|
|
838
|
+
* `Stream` for listening, that fails with a {@link FirestoreError} built from
|
|
839
|
+
* the backend's error, with its `code` and `cause`. {@link make} passes that
|
|
840
|
+
* error on unchanged.
|
|
841
|
+
*
|
|
842
|
+
* - `ReadOptions` and `ListenOptions`: what a backend's reads and listens
|
|
843
|
+
* take on top of the portable ones, such as the client's read source. The
|
|
844
|
+
* hook receives whatever the caller passed, or `undefined`.
|
|
845
|
+
* - `Extra`: what the read and listen hooks know about a read beyond the
|
|
846
|
+
* portable snapshot, such as the client's `metadata`. It is inferred from
|
|
847
|
+
* `getDoc`, and `getDocs`, `streamDoc` and `streamDocs` must return the
|
|
848
|
+
* same, or the hooks do not compile.
|
|
849
|
+
* - `Operations` and `TransactionServices`: those of the
|
|
850
|
+
* {@link RunTransaction} hook.
|
|
851
|
+
*
|
|
852
|
+
* @stability unstable
|
|
853
|
+
* @category options
|
|
854
|
+
* @since 0.3.0
|
|
855
|
+
*/
|
|
856
|
+
export interface MakeOptions<ReadOptions = never, ListenOptions = never, Extra extends object = {}, Operations extends TransactionOperations = TransactionOperations, TransactionServices = never> {
|
|
857
|
+
/** Reads the document at `path`. Backs {@link With.getDoc}. */
|
|
858
|
+
readonly getDoc: (path: string, options: ReadOptions | undefined) => Effect.Effect<DocumentRead<Extra>, FirestoreError>;
|
|
859
|
+
/**
|
|
860
|
+
* Reads every document `selector` names. Backs {@link With.getDocs}, which
|
|
861
|
+
* matches each document of a Collection Group to its collection.
|
|
862
|
+
*/
|
|
863
|
+
readonly getDocs: (selector: CollectionSelector, options: ReadOptions | undefined) => Effect.Effect<QueryRead<NoInfer<Extra>>, FirestoreError>;
|
|
864
|
+
/**
|
|
865
|
+
* Writes the whole document at `path`, replacing whatever was there. Backs
|
|
866
|
+
* {@link With.setDoc}.
|
|
867
|
+
*/
|
|
868
|
+
readonly setDoc: (path: string, data: DocumentData) => Effect.Effect<void, FirestoreError>;
|
|
869
|
+
/**
|
|
870
|
+
* Updates the document at `path`, with `data` keyed by field path. Backs
|
|
871
|
+
* {@link With.updateDoc}.
|
|
872
|
+
*/
|
|
873
|
+
readonly updateDoc: (path: string, data: DocumentData) => Effect.Effect<void, FirestoreError>;
|
|
874
|
+
/**
|
|
875
|
+
* Writes a document under a new id in the collection at `path`, and returns
|
|
876
|
+
* that id. Backs {@link With.addDoc}.
|
|
877
|
+
*/
|
|
878
|
+
readonly addDoc: (path: string, data: DocumentData) => Effect.Effect<string, FirestoreError>;
|
|
879
|
+
/**
|
|
880
|
+
* Listens to the document at `path`, emitting a read each time it changes.
|
|
881
|
+
* Backs {@link With.streamDoc}.
|
|
882
|
+
*
|
|
883
|
+
* Each run of the stream should attach its own listener and detach it when
|
|
884
|
+
* the run stops, keeping only the latest read for a reader that falls
|
|
885
|
+
* behind. {@link streamFromListener} does this for an SDK listener.
|
|
886
|
+
*/
|
|
887
|
+
readonly streamDoc: (path: string, options: ListenOptions | undefined) => Stream.Stream<DocumentRead<NoInfer<Extra>>, FirestoreError>;
|
|
888
|
+
/**
|
|
889
|
+
* Listens to every document `selector` names, as
|
|
890
|
+
* {@link MakeOptions.streamDoc} listens to one. Backs
|
|
891
|
+
* {@link With.streamDocs}.
|
|
892
|
+
*/
|
|
893
|
+
readonly streamDocs: (selector: CollectionSelector, options: ListenOptions | undefined) => Stream.Stream<QueryRead<NoInfer<Extra>>, FirestoreError>;
|
|
894
|
+
/**
|
|
895
|
+
* Runs a transaction, and offers it under further services. Backs
|
|
896
|
+
* {@link With.runTransaction}.
|
|
897
|
+
*
|
|
898
|
+
* @see {@link RunTransaction} for what it has to do
|
|
899
|
+
* @see {@link transactionFromCallback} to build it for an SDK that runs transactions as a callback
|
|
900
|
+
*/
|
|
901
|
+
readonly runTransaction: RunTransaction<Operations, TransactionServices>;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Builds a Firestore service from one backend's hooks.
|
|
905
|
+
*
|
|
906
|
+
* **When to use**
|
|
907
|
+
*
|
|
908
|
+
* Use to implement a backend, such as a new SDK or an in-memory fake for
|
|
909
|
+
* tests. Applications use a backend's layer instead, such as
|
|
910
|
+
* {@link AdminFirestore.layer} or {@link ClientFirestore.layer}.
|
|
911
|
+
*
|
|
912
|
+
* **Details**
|
|
913
|
+
*
|
|
914
|
+
* The hooks only move Firestore Values to and from the backend. `make`
|
|
915
|
+
* handles everything above them, the same way for every backend:
|
|
916
|
+
*
|
|
917
|
+
* - encoding writes through the collection's model, in its `create` shape or
|
|
918
|
+
* the update shape for the requested mode
|
|
919
|
+
* - decoding reads into snapshots, with any extra the hooks return
|
|
920
|
+
* - matching each document of a Collection Group to its collection
|
|
921
|
+
* - tracing spans
|
|
922
|
+
* - providing the {@link Transaction} a transaction body runs in
|
|
923
|
+
*
|
|
924
|
+
* The return type is inferred from the hooks: a {@link With} whose reads and
|
|
925
|
+
* listens take the options the hooks take, whose snapshots carry the extra
|
|
926
|
+
* they return, and whose transaction bodies get the services the transaction
|
|
927
|
+
* hook's `context` offers. It is always the portable {@link Firestore} as
|
|
928
|
+
* well, so a backend can provide it under {@link Firestore}.
|
|
929
|
+
*
|
|
930
|
+
* A backend's richer service extends {@link With}, not {@link Firestore}, has a
|
|
931
|
+
* type id of its own, and its key's service type adds {@link Firestore} back,
|
|
932
|
+
* as {@link AdminFirestore.AdminFirestore} does. Its type then stays apart from
|
|
933
|
+
* {@link Firestore}, so providing a {@link Firestore} alone does not satisfy
|
|
934
|
+
* code that asks for the richer one.
|
|
935
|
+
*
|
|
936
|
+
* @see {@link transactionFromCallback} to build the `runTransaction` hook from an SDK callback
|
|
937
|
+
* @see {@link streamFromListener} to build the `streamDoc` and `streamDocs` hooks from an SDK listener
|
|
938
|
+
*
|
|
939
|
+
* @example An in-memory backend
|
|
940
|
+
* ```ts
|
|
941
|
+
* import { Effect, Layer, Stream } from "effect";
|
|
942
|
+
* import { Firestore } from "@visiolab/effect-firebase/firestore";
|
|
943
|
+
*
|
|
944
|
+
* const documents = new Map<string, Firestore.DocumentData>();
|
|
945
|
+
*
|
|
946
|
+
* const getDoc = (path: string) => Effect.sync(() => ({ data: documents.get(path) }));
|
|
947
|
+
* const setDoc = (path: string, data: Firestore.DocumentData) =>
|
|
948
|
+
* Effect.sync(() => void documents.set(path, data));
|
|
949
|
+
* const updateDoc = (path: string, data: Firestore.DocumentData) =>
|
|
950
|
+
* Effect.sync(() => void documents.set(path, { ...documents.get(path), ...data }));
|
|
951
|
+
* const getDocs = (selector: Firestore.CollectionSelector) =>
|
|
952
|
+
* Effect.sync(() => ({
|
|
953
|
+
* docs: [...documents]
|
|
954
|
+
* .filter(([path]) => selector._tag === "Collection" && path.startsWith(`${selector.path}/`))
|
|
955
|
+
* .map(([path, data]) => ({ path, data })),
|
|
956
|
+
* }));
|
|
957
|
+
*
|
|
958
|
+
* const firestore = Firestore.make({
|
|
959
|
+
* getDoc,
|
|
960
|
+
* getDocs,
|
|
961
|
+
* setDoc,
|
|
962
|
+
* updateDoc,
|
|
963
|
+
* addDoc: (path, data) => {
|
|
964
|
+
* const id = crypto.randomUUID();
|
|
965
|
+
* return Effect.as(setDoc(`${path}/${id}`, data), id);
|
|
966
|
+
* },
|
|
967
|
+
* streamDoc: (path) => Stream.fromEffect(getDoc(path)),
|
|
968
|
+
* streamDocs: (selector) => Stream.fromEffect(getDocs(selector)),
|
|
969
|
+
* runTransaction: { run: (body) => body({ getDoc, setDoc, updateDoc }) },
|
|
970
|
+
* });
|
|
971
|
+
*
|
|
972
|
+
* const FirestoreInMemory = Layer.succeed(Firestore.Firestore, firestore);
|
|
973
|
+
* ```
|
|
974
|
+
*
|
|
975
|
+
* @stability unstable
|
|
976
|
+
* @category constructors
|
|
977
|
+
* @since 0.3.0
|
|
978
|
+
*/
|
|
979
|
+
export declare const make: <ReadOptions = never, ListenOptions = never, Extra extends object = {}, Operations extends TransactionOperations = TransactionOperations, TransactionServices = never>(options: MakeOptions<ReadOptions, ListenOptions, Extra, Operations, TransactionServices>) => With<ReadOptions, ListenOptions, Extra, TransactionServices> & Firestore;
|
|
980
|
+
/**
|
|
981
|
+
* Options for {@link transactionFromCallback}: how to drive an SDK that runs
|
|
982
|
+
* a transaction as a callback and retries it.
|
|
983
|
+
*
|
|
984
|
+
* **Details**
|
|
985
|
+
*
|
|
986
|
+
* `T` is the SDK's own transaction object, handed to each attempt. It is
|
|
987
|
+
* inferred from `operations` alone, so annotate that parameter, as in
|
|
988
|
+
* `operations: (tx: Admin.Transaction) => ...`. `Operations` and
|
|
989
|
+
* `TransactionServices` are those of the {@link RunTransaction} hook it
|
|
990
|
+
* builds.
|
|
991
|
+
*
|
|
992
|
+
* The fields may come in any order, with one exception: put `context` after
|
|
993
|
+
* an `operations` that builds its object inline with unannotated functions,
|
|
994
|
+
* such as `getDoc: (path) => ...`. TypeScript types `context` as soon as it
|
|
995
|
+
* reaches it, and only learns `Operations` from such an `operations` once it
|
|
996
|
+
* reaches that.
|
|
997
|
+
*
|
|
998
|
+
* @stability unstable
|
|
999
|
+
* @category options
|
|
1000
|
+
* @since 0.3.0
|
|
1001
|
+
*/
|
|
1002
|
+
export interface TransactionFromCallbackOptions<T, Operations extends TransactionOperations = TransactionOperations, TransactionServices = never> {
|
|
1003
|
+
/**
|
|
1004
|
+
* Runs `callback` in a transaction and retries it by the SDK's own rules,
|
|
1005
|
+
* such as the Admin SDK's `firestore.runTransaction(callback, options)`.
|
|
1006
|
+
* `options` are the hook's own, passed on unchanged, so `maxAttempts` is
|
|
1007
|
+
* only present when the caller set it.
|
|
1008
|
+
*
|
|
1009
|
+
* The callback takes whatever the SDK passes it and hands it to
|
|
1010
|
+
* `operations` as the SDK's transaction. It is typed `unknown` rather than
|
|
1011
|
+
* `T` because TypeScript would settle `T` from `run` before seeing
|
|
1012
|
+
* `operations` whenever `run` comes first.
|
|
1013
|
+
*/
|
|
1014
|
+
readonly run: <A>(callback: (transaction: unknown) => Promise<A>, options: TransactionHookOptions) => Promise<A>;
|
|
1015
|
+
/** The reads and writes of one attempt, over the SDK's transaction. */
|
|
1016
|
+
readonly operations: (transaction: T) => Operations;
|
|
1017
|
+
/**
|
|
1018
|
+
* Builds the error for a rejection of `run` that does not come from the
|
|
1019
|
+
* body, such as a failed commit, with the SDK's `code` and `cause`.
|
|
1020
|
+
*/
|
|
1021
|
+
readonly onError: (cause: unknown) => FirestoreError;
|
|
1022
|
+
/**
|
|
1023
|
+
* Further services a body can find the transaction under.
|
|
1024
|
+
*
|
|
1025
|
+
* @see {@link RunTransaction.context}
|
|
1026
|
+
*/
|
|
1027
|
+
readonly context?: RunTransaction<Operations, TransactionServices>["context"];
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Builds the {@link MakeOptions.runTransaction} hook for an SDK that runs a
|
|
1031
|
+
* transaction as a callback and retries it. It is the counterpart of Effect's
|
|
1032
|
+
* `SqlClient.makeWithTransaction`.
|
|
1033
|
+
*
|
|
1034
|
+
* **When to use**
|
|
1035
|
+
*
|
|
1036
|
+
* Use when implementing a backend over an SDK with a
|
|
1037
|
+
* `runTransaction(callback)` API, such as the Admin or client SDK.
|
|
1038
|
+
*
|
|
1039
|
+
* **Details**
|
|
1040
|
+
*
|
|
1041
|
+
* Each attempt runs the body over that attempt's operations. What the
|
|
1042
|
+
* callback throws for a failed body decides whether the SDK retries it:
|
|
1043
|
+
*
|
|
1044
|
+
* - A {@link FirestoreError} with a `cause` throws that cause, so the SDK
|
|
1045
|
+
* retries by its own rules, as for a read answered with `ABORTED`.
|
|
1046
|
+
* - Any other failure throws a marker without a `code`, which the SDK never
|
|
1047
|
+
* retries, even when the body's own error carries a `code`.
|
|
1048
|
+
*
|
|
1049
|
+
* A failure the SDK returns without retrying comes back as the body's own
|
|
1050
|
+
* error, unwrapped. Anything else the SDK rejects with goes through
|
|
1051
|
+
* `onError`. Interrupting the transaction interrupts the body and waits for
|
|
1052
|
+
* the SDK to roll back.
|
|
1053
|
+
*
|
|
1054
|
+
* `Operations` is inferred from `operations`, so `context` receives a
|
|
1055
|
+
* transaction with `getDocs` exactly when `operations` has one.
|
|
1056
|
+
*
|
|
1057
|
+
* @see {@link make} for the service this hook is part of
|
|
1058
|
+
* @see {@link streamFromListener} for the matching helper for listeners
|
|
1059
|
+
*
|
|
1060
|
+
* @example Wrapping an SDK's runTransaction
|
|
1061
|
+
* ```ts
|
|
1062
|
+
* import { Effect } from "effect";
|
|
1063
|
+
* import { Firestore } from "@visiolab/effect-firebase/firestore";
|
|
1064
|
+
*
|
|
1065
|
+
* interface SdkTransaction {
|
|
1066
|
+
* get(path: string): Promise<Firestore.DocumentData | undefined>;
|
|
1067
|
+
* set(path: string, data: Firestore.DocumentData): void;
|
|
1068
|
+
* update(path: string, data: Firestore.DocumentData): void;
|
|
1069
|
+
* }
|
|
1070
|
+
*
|
|
1071
|
+
* declare const db: {
|
|
1072
|
+
* runTransaction<A>(callback: (tx: SdkTransaction) => Promise<A>, options?: object): Promise<A>;
|
|
1073
|
+
* };
|
|
1074
|
+
*
|
|
1075
|
+
* const fail = (cause: unknown) => new Firestore.FirestoreError({ message: "Transaction failed", cause });
|
|
1076
|
+
*
|
|
1077
|
+
* const runTransaction = Firestore.transactionFromCallback({
|
|
1078
|
+
* run: (callback, options) => db.runTransaction(callback, options),
|
|
1079
|
+
* operations: (tx: SdkTransaction) => ({
|
|
1080
|
+
* getDoc: (path: string) =>
|
|
1081
|
+
* Effect.tryPromise({ try: () => tx.get(path), catch: fail }).pipe(
|
|
1082
|
+
* Effect.map((data) => ({ data })),
|
|
1083
|
+
* ),
|
|
1084
|
+
* setDoc: (path: string, data: Firestore.DocumentData) => Effect.sync(() => tx.set(path, data)),
|
|
1085
|
+
* updateDoc: (path: string, data: Firestore.DocumentData) =>
|
|
1086
|
+
* Effect.sync(() => tx.update(path, data)),
|
|
1087
|
+
* }),
|
|
1088
|
+
* onError: fail,
|
|
1089
|
+
* });
|
|
1090
|
+
* ```
|
|
1091
|
+
*
|
|
1092
|
+
* @stability unstable
|
|
1093
|
+
* @category constructors
|
|
1094
|
+
* @since 0.3.0
|
|
1095
|
+
*/
|
|
1096
|
+
export declare const transactionFromCallback: <T, Operations extends TransactionOperations, TransactionServices = never>(options: TransactionFromCallbackOptions<T, Operations, TransactionServices>) => RunTransaction<Operations, TransactionServices>;
|
|
1097
|
+
/**
|
|
1098
|
+
* Options for {@link streamFromListener}: how to listen through an SDK
|
|
1099
|
+
* listener.
|
|
1100
|
+
*
|
|
1101
|
+
* `S` is the snapshot the SDK's listener receives, and `A` what the stream
|
|
1102
|
+
* emits for it.
|
|
1103
|
+
*
|
|
1104
|
+
* @stability unstable
|
|
1105
|
+
* @category options
|
|
1106
|
+
* @since 0.3.0
|
|
1107
|
+
*/
|
|
1108
|
+
export interface StreamFromListenerOptions<S, A> {
|
|
1109
|
+
/** Attaches a listener, and returns the function that detaches it. */
|
|
1110
|
+
readonly subscribe: (onNext: (snapshot: S) => void, onError: (error: unknown) => void) => () => void;
|
|
1111
|
+
/** Builds the error the stream fails with from what the listener received. */
|
|
1112
|
+
readonly onError: (error: unknown) => FirestoreError;
|
|
1113
|
+
/** Maps a snapshot the listener received to what the stream emits. */
|
|
1114
|
+
readonly map: (snapshot: S) => Result$1.Result<A, FirestoreError>;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Builds a listen hook's stream, such as {@link MakeOptions.streamDoc}, from
|
|
1118
|
+
* an SDK listener that returns a function to stop listening.
|
|
1119
|
+
*
|
|
1120
|
+
* **When to use**
|
|
1121
|
+
*
|
|
1122
|
+
* Use when implementing a backend over an SDK with an `onSnapshot`-style
|
|
1123
|
+
* listener.
|
|
1124
|
+
*
|
|
1125
|
+
* **Details**
|
|
1126
|
+
*
|
|
1127
|
+
* Each run of the stream attaches its own listener through `subscribe`, and
|
|
1128
|
+
* detaches it when the run stops. A reader that falls behind only sees the
|
|
1129
|
+
* latest snapshot.
|
|
1130
|
+
*
|
|
1131
|
+
* `map` turns each snapshot into what the stream emits. A snapshot it fails on
|
|
1132
|
+
* fails the stream instead of throwing into the SDK. An error the listener
|
|
1133
|
+
* receives fails the stream with what `onError` builds from it.
|
|
1134
|
+
*
|
|
1135
|
+
* @see {@link make} for the service this hook is part of
|
|
1136
|
+
* @see {@link transactionFromCallback} for the matching helper for transactions
|
|
1137
|
+
*
|
|
1138
|
+
* @example Wrapping an SDK listener
|
|
1139
|
+
* ```ts
|
|
1140
|
+
* import { Result } from "effect";
|
|
1141
|
+
* import { Firestore } from "@visiolab/effect-firebase/firestore";
|
|
1142
|
+
*
|
|
1143
|
+
* declare const sdk: {
|
|
1144
|
+
* onSnapshot(
|
|
1145
|
+
* path: string,
|
|
1146
|
+
* onNext: (data: Firestore.DocumentData | undefined) => void,
|
|
1147
|
+
* onError: (error: unknown) => void,
|
|
1148
|
+
* ): () => void;
|
|
1149
|
+
* };
|
|
1150
|
+
*
|
|
1151
|
+
* const streamDoc = (path: string) =>
|
|
1152
|
+
* Firestore.streamFromListener({
|
|
1153
|
+
* subscribe: (onNext, onError) => sdk.onSnapshot(path, onNext, onError),
|
|
1154
|
+
* onError: (cause) =>
|
|
1155
|
+
* new Firestore.FirestoreError({ message: `Failed to listen to "${path}"`, cause }),
|
|
1156
|
+
* map: (data) => Result.succeed({ data }),
|
|
1157
|
+
* });
|
|
1158
|
+
* ```
|
|
1159
|
+
*
|
|
1160
|
+
* @stability unstable
|
|
1161
|
+
* @category constructors
|
|
1162
|
+
* @since 0.3.0
|
|
1163
|
+
*/
|
|
1164
|
+
export declare const streamFromListener: <S, A>(options: StreamFromListenerOptions<S, A>) => Stream.Stream<A, FirestoreError>;
|
|
1165
|
+
export { FirestoreError, Firestore_d_exports, UndefinedCollectionError };
|