@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.
Files changed (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -2
  3. package/dist/_virtual/_rolldown/runtime.mjs +12 -0
  4. package/dist/app/App.d.mts +27 -0
  5. package/dist/app/App.mjs +27 -0
  6. package/dist/app/index.d.mts +2 -0
  7. package/dist/app/index.mjs +1 -0
  8. package/dist/firestore/Collection.d.mts +423 -0
  9. package/dist/firestore/Collection.mjs +133 -0
  10. package/dist/firestore/CollectionGroup.d.mts +164 -0
  11. package/dist/firestore/CollectionGroup.mjs +84 -0
  12. package/dist/firestore/DocumentPath.d.mts +95 -0
  13. package/dist/firestore/DocumentPath.mjs +71 -0
  14. package/dist/firestore/DocumentSnapshot.d.mts +239 -0
  15. package/dist/firestore/DocumentSnapshot.mjs +134 -0
  16. package/dist/firestore/FieldValue.d.mts +332 -0
  17. package/dist/firestore/FieldValue.mjs +256 -0
  18. package/dist/firestore/Firestore.d.mts +1165 -0
  19. package/dist/firestore/Firestore.mjs +274 -0
  20. package/dist/firestore/FirestoreModel.d.mts +1232 -0
  21. package/dist/firestore/FirestoreModel.mjs +586 -0
  22. package/dist/firestore/FirestoreSchema.d.mts +567 -0
  23. package/dist/firestore/FirestoreSchema.mjs +419 -0
  24. package/dist/firestore/FirestoreValue.d.mts +145 -0
  25. package/dist/firestore/FirestoreValue.mjs +87 -0
  26. package/dist/firestore/GeoPoint.d.mts +90 -0
  27. package/dist/firestore/GeoPoint.mjs +75 -0
  28. package/dist/firestore/Patch.d.mts +73 -0
  29. package/dist/firestore/Patch.mjs +57 -0
  30. package/dist/firestore/QuerySnapshot.d.mts +107 -0
  31. package/dist/firestore/QuerySnapshot.mjs +43 -0
  32. package/dist/firestore/Timestamp.d.mts +273 -0
  33. package/dist/firestore/Timestamp.mjs +255 -0
  34. package/dist/firestore/index.d.mts +14 -0
  35. package/dist/firestore/index.mjs +14 -0
  36. package/dist/firestore/internal/collection.mjs +156 -0
  37. package/dist/firestore/internal/collectionGroup.mjs +64 -0
  38. package/dist/firestore/internal/documentPath.d.mts +4 -0
  39. package/dist/firestore/internal/documentPath.mjs +42 -0
  40. package/dist/firestore/internal/documentSnapshot.mjs +63 -0
  41. package/dist/firestore/internal/fieldValue.d.mts +11 -0
  42. package/dist/firestore/internal/fieldValue.mjs +88 -0
  43. package/dist/firestore/internal/firestore.mjs +145 -0
  44. package/dist/firestore/internal/firestoreError.d.mts +29 -0
  45. package/dist/firestore/internal/firestoreError.mjs +30 -0
  46. package/dist/firestore/internal/firestoreModel.mjs +719 -0
  47. package/dist/firestore/internal/firestoreSchema.mjs +123 -0
  48. package/dist/firestore/internal/firestoreValue.mjs +108 -0
  49. package/dist/firestore/internal/firestoreValueWalk.d.mts +58 -0
  50. package/dist/firestore/internal/firestoreValueWalk.mjs +223 -0
  51. package/dist/firestore/internal/geoPoint.d.mts +4 -0
  52. package/dist/firestore/internal/geoPoint.mjs +39 -0
  53. package/dist/firestore/internal/patch.mjs +19 -0
  54. package/dist/firestore/internal/querySnapshot.mjs +24 -0
  55. package/dist/firestore/internal/schemaAST.mjs +35 -0
  56. package/dist/firestore/internal/snapshotExtra.mjs +35 -0
  57. package/dist/firestore/internal/streamFromListener.mjs +12 -0
  58. package/dist/firestore/internal/timestamp.d.mts +4 -0
  59. package/dist/firestore/internal/timestamp.mjs +66 -0
  60. package/dist/firestore/internal/timestampSchema.mjs +31 -0
  61. package/dist/firestore/internal/transactionFromCallback.mjs +66 -0
  62. package/dist/functions/Callable.d.mts +654 -0
  63. package/dist/functions/Callable.mjs +450 -0
  64. package/dist/functions/CallableError.d.mts +297 -0
  65. package/dist/functions/CallableError.mjs +256 -0
  66. package/dist/functions/CallableMiddleware.d.mts +213 -0
  67. package/dist/functions/CallableMiddleware.mjs +52 -0
  68. package/dist/functions/CallableRequest.d.mts +166 -0
  69. package/dist/functions/CallableRequest.mjs +18 -0
  70. package/dist/functions/Caller.d.mts +103 -0
  71. package/dist/functions/Caller.mjs +115 -0
  72. package/dist/functions/DocumentTrigger.d.mts +795 -0
  73. package/dist/functions/DocumentTrigger.mjs +566 -0
  74. package/dist/functions/FirebaseFunction.d.mts +79 -0
  75. package/dist/functions/FirebaseFunction.mjs +4 -0
  76. package/dist/functions/FunctionGroup.d.mts +175 -0
  77. package/dist/functions/FunctionGroup.mjs +64 -0
  78. package/dist/functions/index.d.mts +9 -0
  79. package/dist/functions/index.mjs +9 -0
  80. package/dist/functions/internal/callable.mjs +167 -0
  81. package/dist/functions/internal/callableMiddleware.d.mts +19 -0
  82. package/dist/functions/internal/callableMiddleware.mjs +15 -0
  83. package/dist/functions/internal/callableRequest.mjs +6 -0
  84. package/dist/functions/internal/documentTrigger.d.mts +10 -0
  85. package/dist/functions/internal/documentTrigger.mjs +73 -0
  86. package/dist/functions/internal/firebaseFunction.d.mts +5 -0
  87. package/dist/functions/internal/firebaseFunction.mjs +24 -0
  88. package/dist/functions/internal/functionGroup.mjs +36 -0
  89. package/package.json +66 -3
  90. package/src/app/App.ts +24 -0
  91. package/src/app/index.ts +7 -0
  92. package/src/firestore/Collection.ts +520 -0
  93. package/src/firestore/CollectionGroup.ts +245 -0
  94. package/src/firestore/DocumentPath.ts +114 -0
  95. package/src/firestore/DocumentSnapshot.ts +308 -0
  96. package/src/firestore/FieldValue.ts +375 -0
  97. package/src/firestore/Firestore.ts +1432 -0
  98. package/src/firestore/FirestoreModel.ts +1884 -0
  99. package/src/firestore/FirestoreSchema.ts +643 -0
  100. package/src/firestore/FirestoreValue.ts +207 -0
  101. package/src/firestore/GeoPoint.ts +105 -0
  102. package/src/firestore/Patch.ts +117 -0
  103. package/src/firestore/QuerySnapshot.ts +150 -0
  104. package/src/firestore/Timestamp.ts +302 -0
  105. package/src/firestore/index.ts +67 -0
  106. package/src/firestore/internal/collection.ts +246 -0
  107. package/src/firestore/internal/collectionGroup.ts +81 -0
  108. package/src/firestore/internal/documentPath.ts +53 -0
  109. package/src/firestore/internal/documentSnapshot.ts +117 -0
  110. package/src/firestore/internal/fieldValue.ts +119 -0
  111. package/src/firestore/internal/firestore.ts +322 -0
  112. package/src/firestore/internal/firestoreError.ts +36 -0
  113. package/src/firestore/internal/firestoreModel.ts +1242 -0
  114. package/src/firestore/internal/firestoreSchema.ts +218 -0
  115. package/src/firestore/internal/firestoreValue.ts +202 -0
  116. package/src/firestore/internal/firestoreValueWalk.ts +349 -0
  117. package/src/firestore/internal/geoPoint.ts +59 -0
  118. package/src/firestore/internal/patch.ts +21 -0
  119. package/src/firestore/internal/querySnapshot.ts +39 -0
  120. package/src/firestore/internal/schemaAST.ts +83 -0
  121. package/src/firestore/internal/snapshotExtra.ts +47 -0
  122. package/src/firestore/internal/streamFromListener.ts +27 -0
  123. package/src/firestore/internal/timestamp.ts +119 -0
  124. package/src/firestore/internal/timestampSchema.ts +42 -0
  125. package/src/firestore/internal/transactionFromCallback.ts +106 -0
  126. package/src/functions/Callable.ts +855 -0
  127. package/src/functions/CallableError.ts +348 -0
  128. package/src/functions/CallableMiddleware.ts +279 -0
  129. package/src/functions/CallableRequest.ts +207 -0
  130. package/src/functions/Caller.ts +141 -0
  131. package/src/functions/DocumentTrigger.ts +927 -0
  132. package/src/functions/FirebaseFunction.ts +96 -0
  133. package/src/functions/FunctionGroup.ts +225 -0
  134. package/src/functions/index.ts +62 -0
  135. package/src/functions/internal/callable.ts +272 -0
  136. package/src/functions/internal/callableMiddleware.ts +54 -0
  137. package/src/functions/internal/callableRequest.ts +8 -0
  138. package/src/functions/internal/documentTrigger.ts +143 -0
  139. package/src/functions/internal/firebaseFunction.ts +58 -0
  140. 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 };