@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,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firestore Values: values as Firestore stores them, independent of either
|
|
3
|
+
* SDK.
|
|
4
|
+
*
|
|
5
|
+
* {@link toCodecFirestore} derives a codec from any Effect schema to Firestore
|
|
6
|
+
* Values, the way `Schema.toCodecJson` derives one to JSON. That keeps a model
|
|
7
|
+
* a plain Effect schema that needs no service to encode. Each SDK package then
|
|
8
|
+
* maps Firestore Values to and from its own SDK's values.
|
|
9
|
+
*
|
|
10
|
+
* You rarely call this module yourself: {@link Firestore} encodes and decodes
|
|
11
|
+
* documents through it.
|
|
12
|
+
*
|
|
13
|
+
* @stability unstable
|
|
14
|
+
* @since 0.2.0
|
|
15
|
+
*/
|
|
16
|
+
import type { Result, Schema, SchemaAST } from "effect";
|
|
17
|
+
import type * as DocumentPath from "./DocumentPath.ts";
|
|
18
|
+
import type * as FieldValue from "./FieldValue.ts";
|
|
19
|
+
import type { FirestoreError } from "./Firestore.ts";
|
|
20
|
+
import type * as GeoPoint from "./GeoPoint.ts";
|
|
21
|
+
import * as firestoreValue from "./internal/firestoreValue.ts";
|
|
22
|
+
import * as firestoreValueWalk from "./internal/firestoreValueWalk.ts";
|
|
23
|
+
import type * as Timestamp from "./Timestamp.ts";
|
|
24
|
+
|
|
25
|
+
declare module "effect/Schema" {
|
|
26
|
+
namespace Annotations {
|
|
27
|
+
// The augmentation has to repeat Effect's type parameters, `T` included.
|
|
28
|
+
// oxlint-disable-next-line no-unused-vars
|
|
29
|
+
interface Declaration<T, TypeParameters extends ReadonlyArray<Constraint> = readonly []> {
|
|
30
|
+
/**
|
|
31
|
+
* Returns the link used to derive the declaration's Firestore
|
|
32
|
+
* representation, or `undefined` when it already holds a Firestore Value.
|
|
33
|
+
*
|
|
34
|
+
* {@link toCodecFirestore} prefers it to `toCodecJson` and `toCodec`.
|
|
35
|
+
*/
|
|
36
|
+
readonly toCodecFirestore?:
|
|
37
|
+
| ((typeParameters: TypeParameters.Encoded<TypeParameters>) => SchemaAST.Link | undefined)
|
|
38
|
+
| undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A value as Firestore stores it, independent of either SDK.
|
|
45
|
+
*
|
|
46
|
+
* **Details**
|
|
47
|
+
*
|
|
48
|
+
* One of: `null`, a boolean, a number, a string, a {@link Timestamp.Timestamp},
|
|
49
|
+
* a {@link GeoPoint.GeoPoint}, a {@link DocumentPath.DocumentPath}, bytes as a
|
|
50
|
+
* `Uint8Array`, a {@link FieldValue} sentinel, or an array or plain-object map
|
|
51
|
+
* of Firestore Values.
|
|
52
|
+
*
|
|
53
|
+
* A typed {@link Collection.DocumentReference} is not one: Firestore stores
|
|
54
|
+
* only its path, as a {@link DocumentPath.DocumentPath}.
|
|
55
|
+
*
|
|
56
|
+
* @stability unstable
|
|
57
|
+
* @category models
|
|
58
|
+
* @since 0.2.0
|
|
59
|
+
*/
|
|
60
|
+
export type FirestoreValue =
|
|
61
|
+
| null
|
|
62
|
+
| boolean
|
|
63
|
+
| number
|
|
64
|
+
| string
|
|
65
|
+
| Timestamp.Timestamp
|
|
66
|
+
| GeoPoint.GeoPoint
|
|
67
|
+
| DocumentPath.DocumentPath
|
|
68
|
+
| Uint8Array
|
|
69
|
+
| FieldValue.ServerTimestamp
|
|
70
|
+
| FieldValue.Increment
|
|
71
|
+
| FieldValue.DeleteField
|
|
72
|
+
| FieldValue.ArrayUnion<FirestoreValue>
|
|
73
|
+
| FieldValue.ArrayRemove<FirestoreValue>
|
|
74
|
+
| ReadonlyArray<FirestoreValue>
|
|
75
|
+
| { readonly [key: string]: FirestoreValue };
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Checks whether `u` is a {@link FirestoreValue}, all the way down.
|
|
79
|
+
*
|
|
80
|
+
* **Details**
|
|
81
|
+
*
|
|
82
|
+
* Maps must be plain objects. An instance of any other class is not a
|
|
83
|
+
* Firestore Value, since Firestore would store it as a map and read it back as
|
|
84
|
+
* a plain object.
|
|
85
|
+
*
|
|
86
|
+
* @stability unstable
|
|
87
|
+
* @category guards
|
|
88
|
+
* @since 0.2.0
|
|
89
|
+
*/
|
|
90
|
+
export const isFirestoreValue: (u: unknown) => u is FirestoreValue =
|
|
91
|
+
firestoreValue.isFirestoreValue;
|
|
92
|
+
|
|
93
|
+
// The walk the SDK packages map Firestore Values with. Not API: they reach it
|
|
94
|
+
// here because a package can only import another's public modules.
|
|
95
|
+
|
|
96
|
+
/** @internal */
|
|
97
|
+
export type FieldPath = firestoreValueWalk.FieldPath;
|
|
98
|
+
|
|
99
|
+
/** @internal */
|
|
100
|
+
export type Kinds = firestoreValueWalk.Kinds;
|
|
101
|
+
|
|
102
|
+
/** @internal */
|
|
103
|
+
export type Writer = firestoreValueWalk.Writer;
|
|
104
|
+
|
|
105
|
+
/** @internal */
|
|
106
|
+
export type Reader = firestoreValueWalk.Reader;
|
|
107
|
+
|
|
108
|
+
/** @internal */
|
|
109
|
+
export const formatFieldPath: (path: FieldPath) => string = firestoreValueWalk.formatFieldPath;
|
|
110
|
+
|
|
111
|
+
/** @internal */
|
|
112
|
+
export const toSdk: (value: FirestoreValue, writer: Writer) => unknown = firestoreValueWalk.toSdk;
|
|
113
|
+
|
|
114
|
+
/** @internal */
|
|
115
|
+
export const fromSdk: (
|
|
116
|
+
value: unknown,
|
|
117
|
+
reader: Reader,
|
|
118
|
+
) => Result.Result<FirestoreValue, FirestoreError> = firestoreValueWalk.fromSdk;
|
|
119
|
+
|
|
120
|
+
/** @internal */
|
|
121
|
+
export const readDocument: (
|
|
122
|
+
path: string,
|
|
123
|
+
data: unknown,
|
|
124
|
+
reader: Reader,
|
|
125
|
+
) => Result.Result<FirestoreValue | undefined, FirestoreError> = firestoreValueWalk.readDocument;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Type-level representation of the codec {@link toCodecFirestore} returns: the
|
|
129
|
+
* schema's `Type`, encoded as a {@link FirestoreValue}.
|
|
130
|
+
*
|
|
131
|
+
* @stability unstable
|
|
132
|
+
* @category models
|
|
133
|
+
* @since 0.2.0
|
|
134
|
+
*/
|
|
135
|
+
export interface toCodecFirestore<S extends Schema.Constraint> extends Schema.BottomLazy<
|
|
136
|
+
S["ast"],
|
|
137
|
+
toCodecFirestore<S>,
|
|
138
|
+
S["~type.parameters"],
|
|
139
|
+
S["~type.mutability"],
|
|
140
|
+
S["~type.optionality"],
|
|
141
|
+
S["~type.constructor.default"],
|
|
142
|
+
S["~encoded.mutability"],
|
|
143
|
+
S["~encoded.optionality"]
|
|
144
|
+
> {
|
|
145
|
+
readonly Type: S["Type"];
|
|
146
|
+
readonly Encoded: FirestoreValue;
|
|
147
|
+
readonly DecodingServices: S["DecodingServices"];
|
|
148
|
+
readonly EncodingServices: S["EncodingServices"];
|
|
149
|
+
readonly "~type.make.in": S["~type.make.in"];
|
|
150
|
+
readonly "~type.make": S["~type.make"];
|
|
151
|
+
readonly Iso: S["Iso"];
|
|
152
|
+
readonly schema: S;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Derives a codec from a schema to Firestore Values.
|
|
157
|
+
*
|
|
158
|
+
* **When to use**
|
|
159
|
+
*
|
|
160
|
+
* Use to encode or decode a value the way {@link Firestore} stores it outside
|
|
161
|
+
* a read or write, such as in a test or a custom backend. Reads and writes
|
|
162
|
+
* through {@link Firestore} already use it.
|
|
163
|
+
*
|
|
164
|
+
* **Details**
|
|
165
|
+
*
|
|
166
|
+
* It works like `Schema.toCodecJson`: the schema's own transformations run
|
|
167
|
+
* first, then structs, tuples, arrays, unions and `Schema.suspend` are walked.
|
|
168
|
+
* A declaration is resolved in this order:
|
|
169
|
+
*
|
|
170
|
+
* 1. `Schema.Date` and `Schema.DateTimeUtc` become a portable `Timestamp`, and
|
|
171
|
+
* `Schema.Uint8Array` is stored as bytes. Reading a `Date` or `DateTime.Utc`
|
|
172
|
+
* back keeps milliseconds only.
|
|
173
|
+
* 2. A `toCodecFirestore` annotation.
|
|
174
|
+
* 3. A `toCodecJson` annotation, since every JSON value is a Firestore Value.
|
|
175
|
+
* 4. A `toCodec` annotation.
|
|
176
|
+
* 5. Otherwise the value must already be a Firestore Value, and encoding
|
|
177
|
+
* anything else fails with a schema issue at its path.
|
|
178
|
+
*
|
|
179
|
+
* Keywords follow `Schema.toCodecJson`: a `bigint` or `symbol` is written as a
|
|
180
|
+
* string, and `undefined` as `null`. Unlike there, `Schema.Any` is no escape
|
|
181
|
+
* hatch: like `Schema.Unknown` it takes Firestore Values only, since the SDK
|
|
182
|
+
* mappings take nothing else. Derivation never fails and is memoized per
|
|
183
|
+
* schema.
|
|
184
|
+
*
|
|
185
|
+
* @example Encoding a value to Firestore Values
|
|
186
|
+
* ```ts
|
|
187
|
+
* import { DateTime, Effect, Schema } from "effect";
|
|
188
|
+
* import { FirestoreValue } from "@visiolab/effect-firebase/firestore";
|
|
189
|
+
*
|
|
190
|
+
* const Event = Schema.Struct({ name: Schema.String, at: Schema.DateTimeUtc });
|
|
191
|
+
* const codec = FirestoreValue.toCodecFirestore(Event);
|
|
192
|
+
*
|
|
193
|
+
* // `at` is encoded as a portable `Timestamp`.
|
|
194
|
+
* const encoded = Schema.encodeEffect(codec)({
|
|
195
|
+
* name: "launch",
|
|
196
|
+
* at: DateTime.makeUnsafe("2024-01-01T00:00:00Z"),
|
|
197
|
+
* });
|
|
198
|
+
* ```
|
|
199
|
+
*
|
|
200
|
+
* @see {@link FirestoreSchema} for schemas of the values Effect has none for
|
|
201
|
+
*
|
|
202
|
+
* @stability unstable
|
|
203
|
+
* @category converting
|
|
204
|
+
* @since 0.2.0
|
|
205
|
+
*/
|
|
206
|
+
export const toCodecFirestore: <S extends Schema.Constraint>(schema: S) => toCodecFirestore<S> =
|
|
207
|
+
firestoreValue.toCodecFirestore;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firestore geopoints, independent of either SDK.
|
|
3
|
+
*
|
|
4
|
+
* Firestore stores a place on the globe as a latitude and a longitude, and
|
|
5
|
+
* each SDK reads it back as its own `GeoPoint` class. This module owns a
|
|
6
|
+
* portable version of that value, so a document schema works the same under
|
|
7
|
+
* the Admin SDK and the client SDK.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link FirestoreSchema.GeoPoint} for storing a `GeoPoint` in a document
|
|
10
|
+
*
|
|
11
|
+
* @stability unstable
|
|
12
|
+
* @since 0.2.0
|
|
13
|
+
*/
|
|
14
|
+
import type { Equal, Inspectable, Option } from "effect";
|
|
15
|
+
import * as geoPoint from "./internal/geoPoint.ts";
|
|
16
|
+
|
|
17
|
+
const TypeId = geoPoint.TypeId;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A place on the globe, stored the way Firestore stores it.
|
|
21
|
+
*
|
|
22
|
+
* Two geopoints are equal when their latitudes and longitudes are.
|
|
23
|
+
*
|
|
24
|
+
* @see {@link FirestoreSchema.GeoPoint} for the schema of this value
|
|
25
|
+
*
|
|
26
|
+
* @stability unstable
|
|
27
|
+
* @category models
|
|
28
|
+
* @since 0.2.0
|
|
29
|
+
*/
|
|
30
|
+
export interface GeoPoint extends Equal.Equal, Inspectable.Inspectable {
|
|
31
|
+
readonly [TypeId]: typeof TypeId;
|
|
32
|
+
/** Degrees north of the equator, between -90 and 90. */
|
|
33
|
+
readonly latitude: number;
|
|
34
|
+
/** Degrees east of the prime meridian, between -180 and 180. */
|
|
35
|
+
readonly longitude: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks whether `u` is a {@link GeoPoint} built by this module.
|
|
40
|
+
*
|
|
41
|
+
* An SDK's own `GeoPoint` is not one.
|
|
42
|
+
*
|
|
43
|
+
* @stability unstable
|
|
44
|
+
* @category guards
|
|
45
|
+
* @since 0.2.0
|
|
46
|
+
*/
|
|
47
|
+
export const isGeoPoint: (u: unknown) => u is GeoPoint = geoPoint.isGeoPoint;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Builds a geopoint from a latitude and a longitude, throwing when either is
|
|
51
|
+
* out of range.
|
|
52
|
+
*
|
|
53
|
+
* **When to use**
|
|
54
|
+
*
|
|
55
|
+
* Use for coordinates known to be valid, such as constants. Use {@link make}
|
|
56
|
+
* for coordinates you have not checked.
|
|
57
|
+
*
|
|
58
|
+
* **Details**
|
|
59
|
+
*
|
|
60
|
+
* Throws unless `latitude` is a number from -90 to 90 and `longitude` a number
|
|
61
|
+
* from -180 to 180.
|
|
62
|
+
*
|
|
63
|
+
* @example Building a geopoint
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { GeoPoint } from "@visiolab/effect-firebase/firestore";
|
|
66
|
+
*
|
|
67
|
+
* const berlin = GeoPoint.makeUnsafe(52.52, 13.405);
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @see {@link make} for the same check returning an `Option`
|
|
71
|
+
*
|
|
72
|
+
* @stability unstable
|
|
73
|
+
* @category constructors
|
|
74
|
+
* @since 0.2.0
|
|
75
|
+
*/
|
|
76
|
+
export const makeUnsafe: (latitude: number, longitude: number) => GeoPoint = geoPoint.makeUnsafe;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Builds a geopoint from a latitude and a longitude, or `None` when either is
|
|
80
|
+
* out of range.
|
|
81
|
+
*
|
|
82
|
+
* **When to use**
|
|
83
|
+
*
|
|
84
|
+
* Use when the coordinates come from outside the program and may be invalid.
|
|
85
|
+
*
|
|
86
|
+
* **Details**
|
|
87
|
+
*
|
|
88
|
+
* Returns `None` unless `latitude` is a number from -90 to 90 and `longitude`
|
|
89
|
+
* a number from -180 to 180.
|
|
90
|
+
*
|
|
91
|
+
* @example Building a geopoint from untrusted input
|
|
92
|
+
* ```ts
|
|
93
|
+
* import { GeoPoint } from "@visiolab/effect-firebase/firestore";
|
|
94
|
+
*
|
|
95
|
+
* GeoPoint.make(52.52, 13.405); // Option.some(...)
|
|
96
|
+
* GeoPoint.make(91, 0); // Option.none()
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @see {@link makeUnsafe} for the same check throwing instead
|
|
100
|
+
*
|
|
101
|
+
* @stability unstable
|
|
102
|
+
* @category constructors
|
|
103
|
+
* @since 0.2.0
|
|
104
|
+
*/
|
|
105
|
+
export const make: (latitude: number, longitude: number) => Option.Option<GeoPoint> = geoPoint.make;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patches: what an update writes, and how.
|
|
3
|
+
*
|
|
4
|
+
* A patch has the shape of the document it changes. Each part of it says how
|
|
5
|
+
* that part is written:
|
|
6
|
+
*
|
|
7
|
+
* - A plain object handed to a {@link FirestoreModel.Struct} field is a nested
|
|
8
|
+
* patch, written field by field: `{ address: { city } }` writes
|
|
9
|
+
* `"address.city"` and leaves the rest of `address` as it is.
|
|
10
|
+
* - {@link replace} handed to a {@link FirestoreModel.Struct} field writes the
|
|
11
|
+
* struct whole, dropping any field the value leaves out.
|
|
12
|
+
* - Any other value is written whole where it stands. A sentinel from
|
|
13
|
+
* {@link FieldValue} may stand at any depth.
|
|
14
|
+
*
|
|
15
|
+
* Firestore itself tells a merge from a replace by the key: a dotted path
|
|
16
|
+
* merges, a map handed to a field replaces it. A patch tells them apart by the
|
|
17
|
+
* value instead, so the same patch means the same thing wherever it is
|
|
18
|
+
* written, and replacing a map is asked for by name.
|
|
19
|
+
*
|
|
20
|
+
* Which patches a document accepts is up to its model.
|
|
21
|
+
*
|
|
22
|
+
* @example Replacing one struct and deleting a field
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { Schema } from "effect";
|
|
25
|
+
* import { FieldValue, FirestoreModel, Patch } from "@visiolab/effect-firebase/firestore";
|
|
26
|
+
*
|
|
27
|
+
* class User extends FirestoreModel.Class<User>("User")({
|
|
28
|
+
* address: FirestoreModel.Struct({ city: Schema.String, zip: Schema.String }),
|
|
29
|
+
* nickname: FirestoreModel.Optional(Schema.String),
|
|
30
|
+
* }) {}
|
|
31
|
+
*
|
|
32
|
+
* const patch = User.update.make({
|
|
33
|
+
* address: Patch.replace({ city: "Berlin", zip: "10115" }),
|
|
34
|
+
* nickname: FieldValue.deleteField,
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @see {@link Firestore.With.updateDoc} for writing a patch
|
|
39
|
+
* @see {@link FirestoreModel.Update} for which patches a model accepts
|
|
40
|
+
*
|
|
41
|
+
* @stability unstable
|
|
42
|
+
* @since 0.2.0
|
|
43
|
+
*/
|
|
44
|
+
import type { Inspectable } from "effect";
|
|
45
|
+
import * as patch from "./internal/patch.ts";
|
|
46
|
+
|
|
47
|
+
const ReplaceTypeId = patch.ReplaceTypeId;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A struct written whole, as the map `value`. Built by {@link replace}.
|
|
51
|
+
*
|
|
52
|
+
* **Details**
|
|
53
|
+
*
|
|
54
|
+
* Unlike the sentinels in {@link FieldValue}, it never reaches Firestore. It
|
|
55
|
+
* only tells the update how to write the struct, and is the plain map by the
|
|
56
|
+
* time the update is encoded.
|
|
57
|
+
*
|
|
58
|
+
* @stability unstable
|
|
59
|
+
* @category models
|
|
60
|
+
* @since 0.2.0
|
|
61
|
+
*/
|
|
62
|
+
export interface Replace<A> extends Inspectable.Inspectable {
|
|
63
|
+
readonly [ReplaceTypeId]: typeof ReplaceTypeId;
|
|
64
|
+
/** What the struct holds once the update lands. */
|
|
65
|
+
readonly value: A;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Checks whether `u` is a {@link Replace}.
|
|
70
|
+
*
|
|
71
|
+
* @stability unstable
|
|
72
|
+
* @category guards
|
|
73
|
+
* @since 0.2.0
|
|
74
|
+
*/
|
|
75
|
+
export const isReplace: (u: unknown) => u is Replace<unknown> = patch.isReplace;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Marks a {@link FirestoreModel.Struct} field in a patch to be written whole,
|
|
79
|
+
* rather than field by field.
|
|
80
|
+
*
|
|
81
|
+
* **When to use**
|
|
82
|
+
*
|
|
83
|
+
* Use to replace a struct field outright, dropping any keys the new value leaves
|
|
84
|
+
* out. It is also the safe choice when the struct may not be in the document
|
|
85
|
+
* yet: a nested patch writes only some of its fields there, while `replace`
|
|
86
|
+
* writes all of them.
|
|
87
|
+
*
|
|
88
|
+
* **Details**
|
|
89
|
+
*
|
|
90
|
+
* `value` is the whole struct, in the shape `setDoc` writes it in. A plain
|
|
91
|
+
* object in the same place is a nested patch that only touches the fields it
|
|
92
|
+
* names. A field declared with a plain `Schema.Struct` is a value, always
|
|
93
|
+
* written whole, and takes neither.
|
|
94
|
+
*
|
|
95
|
+
* @example Replacing a struct
|
|
96
|
+
* ```ts
|
|
97
|
+
* import { Schema } from "effect";
|
|
98
|
+
* import { FirestoreModel, Patch } from "@visiolab/effect-firebase/firestore";
|
|
99
|
+
*
|
|
100
|
+
* class User extends FirestoreModel.Class<User>("User")({
|
|
101
|
+
* address: FirestoreModel.Struct({ city: Schema.String, zip: Schema.String }),
|
|
102
|
+
* }) {}
|
|
103
|
+
*
|
|
104
|
+
* // Writes `address` as `{ city: "Berlin", zip: "10115" }`, whatever it held.
|
|
105
|
+
* User.update.make({ address: Patch.replace({ city: "Berlin", zip: "10115" }) });
|
|
106
|
+
*
|
|
107
|
+
* // Writes `address.city` only, and leaves `address.zip` as it is.
|
|
108
|
+
* User.update.make({ address: { city: "Berlin" } });
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @see {@link Firestore.With.updateDoc} for writing the patch
|
|
112
|
+
*
|
|
113
|
+
* @stability unstable
|
|
114
|
+
* @category constructors
|
|
115
|
+
* @since 0.2.0
|
|
116
|
+
*/
|
|
117
|
+
export const replace: <const A>(value: A) => Replace<A> = patch.replace;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a read of many documents returns.
|
|
3
|
+
*
|
|
4
|
+
* A read of a collection or a Collection Group finds any number of documents.
|
|
5
|
+
* A query snapshot holds them as one result rather than a bare array, which
|
|
6
|
+
* leaves room for what a backend knows about the read as a whole, such as the
|
|
7
|
+
* client's `metadata`.
|
|
8
|
+
*
|
|
9
|
+
* @example Reading every document in a collection
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { Collection, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
12
|
+
* import { Effect, Schema } from "effect";
|
|
13
|
+
*
|
|
14
|
+
* class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
|
|
15
|
+
*
|
|
16
|
+
* const Users = Collection.make("users", User);
|
|
17
|
+
*
|
|
18
|
+
* const program = Effect.gen(function* () {
|
|
19
|
+
* const firestore = yield* Firestore.Firestore;
|
|
20
|
+
* const snapshot = yield* firestore.getDocs(Users.collection());
|
|
21
|
+
* return yield* Effect.forEach(snapshot.docs, (user) => user.data);
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @see {@link DocumentSnapshot} for what a read of one document returns
|
|
26
|
+
*
|
|
27
|
+
* @stability unstable
|
|
28
|
+
* @since 0.2.0
|
|
29
|
+
*/
|
|
30
|
+
import type { Inspectable } from "effect";
|
|
31
|
+
import type * as Collection from "./Collection.ts";
|
|
32
|
+
import type * as DocumentSnapshot from "./DocumentSnapshot.ts";
|
|
33
|
+
import * as querySnapshot from "./internal/querySnapshot.ts";
|
|
34
|
+
|
|
35
|
+
const TypeId = querySnapshot.TypeId;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A {@link DocumentSnapshot.Existing} snapshot in whichever of the collections
|
|
39
|
+
* `C` it is in.
|
|
40
|
+
*
|
|
41
|
+
* **Details**
|
|
42
|
+
*
|
|
43
|
+
* For a union `C` it is `Existing<A> | Existing<B>` rather than
|
|
44
|
+
* `Existing<A | B>`, so a document's id, ancestors, reference and data all
|
|
45
|
+
* belong to the same collection.
|
|
46
|
+
*
|
|
47
|
+
* @stability unstable
|
|
48
|
+
* @category models
|
|
49
|
+
* @since 0.2.0
|
|
50
|
+
*/
|
|
51
|
+
export type Existing<C extends Collection.Any> = C extends Collection.Any
|
|
52
|
+
? DocumentSnapshot.Existing<C>
|
|
53
|
+
: never;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The documents of `C` that a read of many documents found.
|
|
57
|
+
*
|
|
58
|
+
* **Details**
|
|
59
|
+
*
|
|
60
|
+
* {@link Firestore.With.getDocs} returns one, and
|
|
61
|
+
* {@link Firestore.With.streamDocs} emits one per change. `C` is a union when
|
|
62
|
+
* the read was of a Collection Group with several members, and each document
|
|
63
|
+
* is then in one of them.
|
|
64
|
+
*
|
|
65
|
+
* `DocumentExtra` is what a backend adds to each document beyond the portable
|
|
66
|
+
* snapshot, such as the client's `metadata`. What it adds to the read as a
|
|
67
|
+
* whole is not part of this type: it is intersected where the snapshot is
|
|
68
|
+
* used, as `QuerySnapshot<C, E> & E`.
|
|
69
|
+
*
|
|
70
|
+
* @see {@link DocumentSnapshot.DocumentSnapshot} for the result of a read of one document
|
|
71
|
+
*
|
|
72
|
+
* @stability unstable
|
|
73
|
+
* @category models
|
|
74
|
+
* @since 0.2.0
|
|
75
|
+
*/
|
|
76
|
+
export interface QuerySnapshot<C extends Collection.Any, DocumentExtra extends object = {}>
|
|
77
|
+
extends Inspectable.Inspectable {
|
|
78
|
+
readonly [TypeId]: typeof TypeId;
|
|
79
|
+
/**
|
|
80
|
+
* Every document the read found, in the order Firestore returned them.
|
|
81
|
+
* Empty when there were none.
|
|
82
|
+
*
|
|
83
|
+
* **Details**
|
|
84
|
+
*
|
|
85
|
+
* Each was there when it was read, so each is a
|
|
86
|
+
* {@link DocumentSnapshot.Existing} in the collection of `C` it lives in,
|
|
87
|
+
* with the `DocumentExtra` of the read.
|
|
88
|
+
*
|
|
89
|
+
* @see {@link Collection.Collection.contains} for keeping the documents of one collection
|
|
90
|
+
*/
|
|
91
|
+
readonly docs: ReadonlyArray<Existing<C> & DocumentExtra>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The fields a document snapshot `D` has beyond the portable
|
|
96
|
+
* {@link DocumentSnapshot.Existing}, such as the client's `metadata`.
|
|
97
|
+
*
|
|
98
|
+
* @stability unstable
|
|
99
|
+
* @category models
|
|
100
|
+
* @since 0.3.0
|
|
101
|
+
*/
|
|
102
|
+
export type ExtraOf<D extends DocumentSnapshot.Existing<Collection.Any>> = {
|
|
103
|
+
[K in keyof D as K extends keyof DocumentSnapshot.Existing<Collection.Any> ? never : K]: D[K];
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Checks whether a value is a {@link QuerySnapshot}.
|
|
108
|
+
*
|
|
109
|
+
* @see {@link DocumentSnapshot.isDocumentSnapshot} for a snapshot of one document
|
|
110
|
+
*
|
|
111
|
+
* @stability unstable
|
|
112
|
+
* @category guards
|
|
113
|
+
* @since 0.2.0
|
|
114
|
+
*/
|
|
115
|
+
export const isQuerySnapshot: (u: unknown) => u is QuerySnapshot<Collection.Any> =
|
|
116
|
+
querySnapshot.isQuerySnapshot;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Builds a {@link QuerySnapshot} from the documents a read found.
|
|
120
|
+
*
|
|
121
|
+
* **When to use**
|
|
122
|
+
*
|
|
123
|
+
* Use when implementing a backend or a test double. Application code gets
|
|
124
|
+
* query snapshots from {@link Firestore.With.getDocs} instead.
|
|
125
|
+
*
|
|
126
|
+
* **Details**
|
|
127
|
+
*
|
|
128
|
+
* An SDK that knows more about the read as a whole, such as the client's
|
|
129
|
+
* `metadata`, passes it as `extra`: each of its fields is put on the snapshot
|
|
130
|
+
* and shown when it prints, as {@link DocumentSnapshot.make} does for one
|
|
131
|
+
* document. Its fields must not have the same names as the snapshot's own.
|
|
132
|
+
*
|
|
133
|
+
* The documents are kept as given, so any extra fields on them stay, and stay
|
|
134
|
+
* in the snapshot's type.
|
|
135
|
+
*
|
|
136
|
+
* @see {@link DocumentSnapshot.existing} for building each document
|
|
137
|
+
*
|
|
138
|
+
* @stability unstable
|
|
139
|
+
* @category constructors
|
|
140
|
+
* @since 0.2.0
|
|
141
|
+
*/
|
|
142
|
+
export const make: {
|
|
143
|
+
<C extends Collection.Any, D extends DocumentSnapshot.Existing<C> = DocumentSnapshot.Existing<C>>(
|
|
144
|
+
docs: ReadonlyArray<D> & ReadonlyArray<DocumentSnapshot.Existing<C>>,
|
|
145
|
+
): QuerySnapshot<C, ExtraOf<D>>;
|
|
146
|
+
<C extends Collection.Any, D extends DocumentSnapshot.Existing<C>, E extends object>(
|
|
147
|
+
docs: ReadonlyArray<D> & ReadonlyArray<DocumentSnapshot.Existing<C>>,
|
|
148
|
+
extra: E,
|
|
149
|
+
): QuerySnapshot<C, ExtraOf<D>> & E;
|
|
150
|
+
} = querySnapshot.make;
|