@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
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@visiolab/effect-firebase",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.3.0",
4
+ "description": "Effect bindings for Firebase.",
5
+ "homepage": "https://github.com/VisioLab/effect-firebase",
6
+ "bugs": {
7
+ "url": "https://github.com/VisioLab/effect-firebase/issues"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/VisioLab/effect-firebase.git",
13
+ "directory": "packages/effect-firebase"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "type": "module",
20
+ "sideEffects": [],
21
+ "exports": {
22
+ "./app": "./dist/app/index.mjs",
23
+ "./app/App": "./dist/app/App.mjs",
24
+ "./firestore": "./dist/firestore/index.mjs",
25
+ "./firestore/Collection": "./dist/firestore/Collection.mjs",
26
+ "./firestore/CollectionGroup": "./dist/firestore/CollectionGroup.mjs",
27
+ "./firestore/DocumentPath": "./dist/firestore/DocumentPath.mjs",
28
+ "./firestore/DocumentSnapshot": "./dist/firestore/DocumentSnapshot.mjs",
29
+ "./firestore/FieldValue": "./dist/firestore/FieldValue.mjs",
30
+ "./firestore/Firestore": "./dist/firestore/Firestore.mjs",
31
+ "./firestore/FirestoreModel": "./dist/firestore/FirestoreModel.mjs",
32
+ "./firestore/FirestoreSchema": "./dist/firestore/FirestoreSchema.mjs",
33
+ "./firestore/FirestoreValue": "./dist/firestore/FirestoreValue.mjs",
34
+ "./firestore/GeoPoint": "./dist/firestore/GeoPoint.mjs",
35
+ "./firestore/Patch": "./dist/firestore/Patch.mjs",
36
+ "./firestore/QuerySnapshot": "./dist/firestore/QuerySnapshot.mjs",
37
+ "./firestore/Timestamp": "./dist/firestore/Timestamp.mjs",
38
+ "./functions": "./dist/functions/index.mjs",
39
+ "./functions/Callable": "./dist/functions/Callable.mjs",
40
+ "./functions/CallableError": "./dist/functions/CallableError.mjs",
41
+ "./functions/CallableMiddleware": "./dist/functions/CallableMiddleware.mjs",
42
+ "./functions/CallableRequest": "./dist/functions/CallableRequest.mjs",
43
+ "./functions/Caller": "./dist/functions/Caller.mjs",
44
+ "./functions/DocumentTrigger": "./dist/functions/DocumentTrigger.mjs",
45
+ "./functions/FirebaseFunction": "./dist/functions/FirebaseFunction.mjs",
46
+ "./functions/FunctionGroup": "./dist/functions/FunctionGroup.mjs",
47
+ "./package.json": "./package.json"
48
+ },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "devDependencies": {
53
+ "@effect/vitest": "^4.1.0",
54
+ "effect": "4.1.0",
55
+ "typescript": "^7.0.2",
56
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
57
+ "vite-plus": "1.4.0",
58
+ "vitest": "5.0.5"
59
+ },
60
+ "peerDependencies": {
61
+ "effect": "~4.1.0"
62
+ },
63
+ "scripts": {
64
+ "build": "vp pack",
65
+ "dev": "vp pack --watch",
66
+ "test": "vp test",
67
+ "check": "vp check"
68
+ }
6
69
  }
package/src/app/App.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * What the SDK packages' `App` modules share.
3
+ *
4
+ * The app service itself belongs to each SDK package: see the `App` modules
5
+ * of [`@visiolab/effect-firebase-admin`](/api/admin/app/App/) and
6
+ * [`@visiolab/effect-firebase-client`](/api/client/app/App/).
7
+ *
8
+ * @stability unstable
9
+ * @since 0.1.0
10
+ */
11
+
12
+ /**
13
+ * The name Firebase gives an app that was initialized without one.
14
+ *
15
+ * **When to use**
16
+ *
17
+ * Use to compare against an app's `name`, or to name the default app
18
+ * explicitly.
19
+ *
20
+ * @stability unstable
21
+ * @category constants
22
+ * @since 0.1.0
23
+ */
24
+ export const defaultAppName = "[DEFAULT]";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * What the SDK packages' `App` modules share.
3
+ *
4
+ * @stability unstable
5
+ * @since 0.1.0
6
+ */
7
+ export * as App from "./App.ts";
@@ -0,0 +1,520 @@
1
+ /**
2
+ * Typed Firestore collections.
3
+ *
4
+ * Define each collection once: where it sits, how its documents are addressed,
5
+ * and the {@link FirestoreModel.Class} its documents hold. Every collection and
6
+ * document reference is built from that definition, so a path can only be
7
+ * built from the ids that locate it, and every read and write through
8
+ * {@link Firestore.With} is typed by the collection's model.
9
+ *
10
+ * @example Defining collections and referencing documents
11
+ * ```ts
12
+ * import { Collection, FirestoreModel } from "@visiolab/effect-firebase/firestore";
13
+ * import { Schema } from "effect";
14
+ *
15
+ * class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
16
+ * class Post extends FirestoreModel.Class<Post>("Post")({ title: Schema.String }) {}
17
+ *
18
+ * const Users = Collection.make("users", User);
19
+ * const UserPosts = Users.subcollection("posts", Post);
20
+ *
21
+ * const alice = Users.id.make("alice");
22
+ * const hello = UserPosts.id.make("hello");
23
+ *
24
+ * UserPosts.collection(alice).path; // "users/alice/posts"
25
+ * UserPosts.doc(alice, hello).path; // "users/alice/posts/hello"
26
+ * ```
27
+ *
28
+ * @see {@link CollectionGroup} for reading every collection with one collection id at once
29
+ * @see {@link DocumentPath} for a reference to a document as Firestore stores it in a field
30
+ *
31
+ * @stability unstable
32
+ * @since 0.1.0
33
+ */
34
+ import type { Brand, Equal, Inspectable, Option, Schema } from "effect";
35
+ import type * as DocumentSnapshot from "./DocumentSnapshot.ts";
36
+ import type * as FirestoreModel from "./FirestoreModel.ts";
37
+ import * as collection from "./internal/collection.ts";
38
+
39
+ const CollectionTypeId = collection.CollectionTypeId;
40
+ const CollectionReferenceTypeId = collection.CollectionReferenceTypeId;
41
+ const DocumentReferenceTypeId = collection.DocumentReferenceTypeId;
42
+
43
+ /**
44
+ * One link of a collection's path: a segment and the model of the documents
45
+ * stored under it.
46
+ *
47
+ * @stability unstable
48
+ * @category models
49
+ * @since 0.1.0
50
+ */
51
+ export type Level = readonly [segment: string, schema: FirestoreModel.Any];
52
+
53
+ /**
54
+ * The {@link Level}s from a root collection down to a collection itself.
55
+ *
56
+ * **Details**
57
+ *
58
+ * A {@link Collection} carries the whole chain rather than only its own
59
+ * segment, so the id of every ancestor stays typed.
60
+ *
61
+ * @stability unstable
62
+ * @category models
63
+ * @since 0.1.0
64
+ */
65
+ export type Levels = readonly [Level, ...ReadonlyArray<Level>];
66
+
67
+ type Join<S extends ReadonlyArray<string>> = S extends readonly [
68
+ infer Head extends string,
69
+ ...infer Tail extends ReadonlyArray<string>,
70
+ ]
71
+ ? Tail extends readonly []
72
+ ? Head
73
+ : `${Head}/${Join<Tail>}`
74
+ : never;
75
+
76
+ /** The last link of the chain. */
77
+ type Leaf<L extends Levels> = L extends readonly [...ReadonlyArray<Level>, infer T extends Level]
78
+ ? T
79
+ : never;
80
+
81
+ /** The segments of a chain, as a tuple. */
82
+ type SegmentsOf<L extends Levels> = { readonly [K in keyof L]: L[K][0] };
83
+
84
+ /**
85
+ * Document ids are branded with the collection's path, so an id only ever fits
86
+ * the collection it came from. Two collections that share a segment - `posts`
87
+ * under `users` and `posts` under `orgs` - still get distinct ids, because the
88
+ * brand is the whole path rather than the last segment.
89
+ */
90
+ type IdOf<L extends Levels> = string & Brand.Brand<Join<SegmentsOf<L>>>;
91
+
92
+ /**
93
+ * One id per ancestor, ordered from the root down. A root collection needs
94
+ * none, which is why the recursion bottoms out at the empty tuple.
95
+ *
96
+ * The parent chain is matched inline rather than looked up through an alias, so
97
+ * that the recursion stays deferred until `L` is a concrete chain. Going
98
+ * through an alias makes it eager, and the compiler gives up on the depth.
99
+ */
100
+ type AncestorsOf<L extends Levels> = L extends readonly [...infer I, Level]
101
+ ? I extends Levels
102
+ ? [...AncestorsOf<I>, IdOf<I>]
103
+ : []
104
+ : [];
105
+
106
+ /**
107
+ * What {@link Collection.contains} narrows each member of `V` to: the members
108
+ * already in `C` are kept, the ones in another collection are dropped, and one
109
+ * whose collection is left open is narrowed to `C`.
110
+ *
111
+ * Every branch is `V` or an intersection with it, which is what lets the
112
+ * compiler see the result is assignable to `V`, as a type predicate must be.
113
+ */
114
+ type Contained<V, C extends Any> =
115
+ V extends DocumentReference<Any>
116
+ ? NarrowTo<V, DocumentReference<C>>
117
+ : V extends DocumentSnapshot.Existing<Any>
118
+ ? NarrowTo<V, DocumentSnapshot.Existing<C>>
119
+ : V extends DocumentSnapshot.Missing<Any>
120
+ ? NarrowTo<V, DocumentSnapshot.Missing<C>>
121
+ : never;
122
+
123
+ /**
124
+ * `V` when it is `T`, and `never` when it is not. A `V` whose collection is
125
+ * `any` passes the check without saying anything about it, so it is narrowed
126
+ * by intersecting it with `T` instead.
127
+ */
128
+ type NarrowTo<V, T> = V extends T ? (0 extends 1 & LevelsOf<V> ? V & T : V) : never;
129
+
130
+ /** The chain of the collection a reference or snapshot is in. */
131
+ type LevelsOf<V> =
132
+ V extends DocumentReference<Collection<infer L>>
133
+ ? L
134
+ : V extends { readonly ref: DocumentReference<Collection<infer L>> }
135
+ ? L
136
+ : never;
137
+
138
+ /**
139
+ * The schema of a document id in one collection. It decodes a string and
140
+ * brands it with the collection's full path, so the id only fits that
141
+ * collection.
142
+ *
143
+ * **When to use**
144
+ *
145
+ * Use `collection.id.make(raw)` to turn a string you trust into an id, and
146
+ * `Schema.decodeUnknownEffect(collection.id)` to check one from outside.
147
+ *
148
+ * **Details**
149
+ *
150
+ * The brand is the whole path, so `posts` under `users` and `posts` under
151
+ * `orgs` have different id types. Its constructor takes a `string`, which
152
+ * makes passing anything else a type error rather than a runtime throw.
153
+ *
154
+ * @stability unstable
155
+ * @category models
156
+ * @since 0.1.0
157
+ */
158
+ export interface IdSchema<T extends string> extends Schema.Codec<T, string> {
159
+ readonly "~type.make.in": string;
160
+ }
161
+
162
+ /**
163
+ * The definition of a collection: where it sits, how its documents are
164
+ * addressed, and the model they hold.
165
+ *
166
+ * **Details**
167
+ *
168
+ * A definition does not point at any data. It describes a collection, and
169
+ * {@link Collection.collection} and {@link Collection.doc} turn it into a
170
+ * reference once you have the ids that address it. Build one with
171
+ * {@link make}, and nest one with {@link Collection.subcollection}.
172
+ *
173
+ * @see {@link CollectionReference} for a reference to one collection, with its ancestor ids filled in
174
+ * @see {@link CollectionGroup.CollectionGroup} for every collection with one collection id, read together
175
+ *
176
+ * @stability unstable
177
+ * @category models
178
+ * @since 0.1.0
179
+ */
180
+ export interface Collection<L extends Levels> extends Inspectable.Inspectable {
181
+ readonly [CollectionTypeId]: typeof CollectionTypeId;
182
+ // `L` appears nowhere else in an inferrable position, so without it
183
+ // `C extends Collection<infer L>` has nothing to infer from.
184
+ /** The chain this collection was built from. Only read at the type level. */
185
+ readonly "~levels": L;
186
+ /** The path segments, from the root collection down, such as `["users", "posts"]`. */
187
+ readonly path: SegmentsOf<L>;
188
+ /** The last segment: the collection id Firestore knows the collection by. */
189
+ readonly segment: Leaf<L>[0];
190
+ /**
191
+ * The schema of the ids of documents in this collection.
192
+ *
193
+ * @see {@link IdSchema} for how to make and decode one
194
+ */
195
+ readonly id: IdSchema<IdOf<L>>;
196
+ /**
197
+ * The model of the documents in this collection. Reads decode through it,
198
+ * and writes encode through its {@link FirestoreModel.Class.create} and
199
+ * {@link FirestoreModel.Class.update} variants.
200
+ */
201
+ readonly schema: Leaf<L>[1];
202
+
203
+ /**
204
+ * Checks whether a Document Reference or Document Snapshot belongs to this
205
+ * collection, and narrows it to this collection's types when it does.
206
+ *
207
+ * **When to use**
208
+ *
209
+ * Use to pick out the documents of one collection from a mixed result, such
210
+ * as a read of a {@link CollectionGroup.CollectionGroup} with several
211
+ * members: `snapshot.docs.filter(UserPosts.contains)`.
212
+ *
213
+ * **Details**
214
+ *
215
+ * Two definitions are the same collection only when they are the same
216
+ * object, so a definition built separately at the same path does not
217
+ * contain this one's documents. It does not use `this`, so it can be passed
218
+ * on unbound.
219
+ *
220
+ * @see {@link isDocumentReference} for checking that a value is a reference to any collection
221
+ */
222
+ readonly contains: <V extends DocumentReference<Any> | DocumentSnapshot.DocumentSnapshot<Any>>(
223
+ value: V,
224
+ ) => value is Contained<V, Collection<L>>;
225
+
226
+ /**
227
+ * Defines a subcollection under the documents of this collection.
228
+ *
229
+ * **When to use**
230
+ *
231
+ * Use for a nested collection, such as `posts` under each user. Use
232
+ * {@link make} for a root collection.
233
+ *
234
+ * **Details**
235
+ *
236
+ * Throws when `segment` is not a collection id Firestore accepts: empty,
237
+ * containing `/`, `.` or `..`, matching `__.*__`, or longer than 1500
238
+ * bytes. An invalid definition is a programming error, not a failure to
239
+ * recover from.
240
+ */
241
+ subcollection<const Segment extends string, S extends FirestoreModel.Any>(
242
+ segment: Segment,
243
+ schema: S,
244
+ ): Collection<readonly [...L, readonly [Segment, S]]>;
245
+
246
+ /**
247
+ * References this collection, given one id per ancestor, from the root down.
248
+ * A root collection takes none.
249
+ *
250
+ * **When to use**
251
+ *
252
+ * Use to address the collection itself, as {@link Firestore.With.getDocs},
253
+ * {@link Firestore.With.streamDocs} and {@link Firestore.With.addDoc} take
254
+ * it.
255
+ *
256
+ * @see {@link Collection.doc} for referencing one document in it
257
+ */
258
+ collection(...ids: AncestorsOf<L>): CollectionReference<Collection<L>>;
259
+
260
+ /**
261
+ * References a document in this collection, given one id per ancestor
262
+ * followed by the document's own.
263
+ *
264
+ * **When to use**
265
+ *
266
+ * Use to address one document, as {@link Firestore.With.getDoc},
267
+ * {@link Firestore.With.setDoc} and {@link Firestore.With.updateDoc} take it.
268
+ *
269
+ * @see {@link Collection.collection} for referencing the collection itself
270
+ * @see {@link fromPath} for a reference from a path string
271
+ */
272
+ doc(...ids: [...AncestorsOf<L>, IdOf<L>]): DocumentReference<Collection<L>>;
273
+ }
274
+
275
+ /**
276
+ * Any collection, whatever it is nested under and whatever it holds. Use it as
277
+ * a type bound.
278
+ *
279
+ * **Details**
280
+ *
281
+ * It is `Collection<any>` rather than `Collection<Levels>` because `L` sits in
282
+ * an argument position in {@link Collection.doc}, which makes it invariant.
283
+ *
284
+ * @stability unstable
285
+ * @category models
286
+ * @since 0.1.0
287
+ */
288
+ export type Any = Collection<any>;
289
+
290
+ /**
291
+ * The id of a document in `C`, branded with `C`'s path.
292
+ *
293
+ * @see {@link IdSchema} for how to make one
294
+ *
295
+ * @stability unstable
296
+ * @category models
297
+ * @since 0.1.0
298
+ */
299
+ export type Id<C extends Any> = IdOf<C["~levels"]>;
300
+
301
+ /**
302
+ * The ids that address `C`: one per ancestor, from the root down. Empty for a
303
+ * root collection.
304
+ *
305
+ * @stability unstable
306
+ * @category models
307
+ * @since 0.1.0
308
+ */
309
+ export type Ancestors<C extends Any> = AncestorsOf<C["~levels"]>;
310
+
311
+ /**
312
+ * The model of the documents in `C`, as {@link make} or
313
+ * {@link Collection.subcollection} was given it.
314
+ *
315
+ * @stability unstable
316
+ * @category models
317
+ * @since 0.1.0
318
+ */
319
+ export type DocumentSchema<C extends Any> = Leaf<C["~levels"]>[1];
320
+
321
+ /**
322
+ * A reference to one collection, with the ids of its ancestors filled in.
323
+ *
324
+ * **Details**
325
+ *
326
+ * Build one with {@link Collection.collection}. Two references are
327
+ * `Equal` when they have the same path and come from the same definition.
328
+ *
329
+ * @see {@link Collection} for the definition it is built from
330
+ * @see {@link DocumentReference} for a reference to one document
331
+ *
332
+ * @stability unstable
333
+ * @category models
334
+ * @since 0.1.0
335
+ */
336
+ export interface CollectionReference<C extends Any> extends Inspectable.Inspectable, Equal.Equal {
337
+ readonly [CollectionReferenceTypeId]: typeof CollectionReferenceTypeId;
338
+ /** The definition this reference was built from. */
339
+ readonly collection: C;
340
+ /** The ids that address it, from the root down. */
341
+ readonly ancestors: Readonly<Ancestors<C>>;
342
+ /** The collection id Firestore knows it by: the last segment of its path. */
343
+ readonly id: Leaf<C["~levels"]>[0];
344
+ /** The full path, such as `users/alice/posts`. */
345
+ readonly path: string;
346
+
347
+ /**
348
+ * References a document in this collection, by its own id.
349
+ *
350
+ * @see {@link Collection.doc} for referencing it from the definition with every id
351
+ */
352
+ doc(id: Id<C>): DocumentReference<C>;
353
+ }
354
+
355
+ /**
356
+ * A typed reference to one document in a collection.
357
+ *
358
+ * **Details**
359
+ *
360
+ * It knows the {@link Collection} it was built from, so reads through it
361
+ * decode with that collection's model and writes encode with it. Build one
362
+ * with {@link Collection.doc}, {@link CollectionReference.doc} or
363
+ * {@link fromPath}. Two references are `Equal` when they have the same path and
364
+ * come from the same definition.
365
+ *
366
+ * @see {@link CollectionGroup.DocumentReference} for a reference into any collection of a group
367
+ * @see {@link DocumentPath.DocumentPath} for the untyped path a reference is stored as in a field
368
+ *
369
+ * @stability unstable
370
+ * @category models
371
+ * @since 0.1.0
372
+ */
373
+ export interface DocumentReference<C extends Any> extends Inspectable.Inspectable, Equal.Equal {
374
+ readonly [DocumentReferenceTypeId]: typeof DocumentReferenceTypeId;
375
+ /** The definition of the collection holding it. */
376
+ readonly collection: C;
377
+ /** The ids that address its collection, from the root down. */
378
+ readonly ancestors: Readonly<Ancestors<C>>;
379
+ /** The document's own id. */
380
+ readonly id: Id<C>;
381
+ /** The full path, such as `users/alice/posts/hello`. */
382
+ readonly path: string;
383
+ /** A reference to the collection holding it. */
384
+ readonly parent: CollectionReference<C>;
385
+ }
386
+
387
+ /**
388
+ * Checks whether a value is a collection definition.
389
+ *
390
+ * @see {@link isCollectionReference} for a reference to a collection
391
+ * @see {@link CollectionGroup.isCollectionGroup} for a Collection Group
392
+ *
393
+ * @stability unstable
394
+ * @category guards
395
+ * @since 0.1.0
396
+ */
397
+ export const isCollection: (u: unknown) => u is Any = collection.isCollection;
398
+
399
+ /**
400
+ * Checks whether a value is a {@link CollectionReference}.
401
+ *
402
+ * @see {@link isCollection} for a collection definition
403
+ * @see {@link isDocumentReference} for a reference to a document
404
+ *
405
+ * @stability unstable
406
+ * @category guards
407
+ * @since 0.1.0
408
+ */
409
+ export const isCollectionReference: (u: unknown) => u is CollectionReference<Any> =
410
+ collection.isCollectionReference;
411
+
412
+ /**
413
+ * Checks whether a value is a {@link DocumentReference} into any collection.
414
+ *
415
+ * @see {@link Collection.contains} for checking that a reference belongs to one collection, with narrowing
416
+ * @see {@link isCollectionReference} for a reference to a collection
417
+ * @see {@link DocumentPath.isDocumentPath} for the untyped path stored in a field
418
+ *
419
+ * @stability unstable
420
+ * @category guards
421
+ * @since 0.1.0
422
+ */
423
+ export const isDocumentReference: (u: unknown) => u is DocumentReference<Any> =
424
+ collection.isDocumentReference;
425
+
426
+ /**
427
+ * Defines a root collection and the model of its documents.
428
+ *
429
+ * **When to use**
430
+ *
431
+ * Use once per root collection, at module level, and build every reference to
432
+ * it from the result. Use {@link Collection.subcollection} on the result for a
433
+ * collection nested under its documents.
434
+ *
435
+ * **Details**
436
+ *
437
+ * The documents are described by a {@link FirestoreModel.Class} rather than a
438
+ * plain schema, since a document is written in other shapes than it is read
439
+ * in, and the model carries each of them. Pass a {@link FirestoreModel.Union}
440
+ * of classes for a collection that holds several document shapes.
441
+ *
442
+ * Throws when `segment` is not a collection id Firestore accepts: empty,
443
+ * containing `/`, `.` or `..`, matching `__.*__`, or longer than 1500 bytes. An
444
+ * invalid definition is a programming error, not a failure to recover from.
445
+ *
446
+ * @example Defining a collection with a subcollection
447
+ * ```ts
448
+ * import { Collection, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
449
+ * import { Effect, Schema } from "effect";
450
+ *
451
+ * class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
452
+ * class Post extends FirestoreModel.Class<Post>("Post")({ title: Schema.String }) {}
453
+ *
454
+ * const Users = Collection.make("users", User);
455
+ * const UserPosts = Users.subcollection("posts", Post);
456
+ *
457
+ * const program = Effect.gen(function* () {
458
+ * const firestore = yield* Firestore.Firestore;
459
+ * const alice = Users.id.make("alice");
460
+ *
461
+ * yield* firestore.setDoc(Users.doc(alice), { name: "Alice" });
462
+ * const post = yield* firestore.addDoc(UserPosts.collection(alice), { title: "Hello" });
463
+ * return post.path; // "users/alice/posts/<generated id>"
464
+ * });
465
+ * ```
466
+ *
467
+ * @see {@link fromPath} for a reference to a document of an existing collection from a path string
468
+ * @see {@link CollectionGroup.make} for reading several collections with one collection id together
469
+ *
470
+ * @stability unstable
471
+ * @category constructors
472
+ * @since 0.1.0
473
+ */
474
+ export const make: <const Segment extends string, S extends FirestoreModel.Any>(
475
+ segment: Segment,
476
+ schema: S,
477
+ ) => Collection<readonly [readonly [Segment, S]]> = collection.make;
478
+
479
+ /**
480
+ * References the document of `collection` at a path string, such as
481
+ * `users/alice/posts/hello`, or returns `None` when the path is not in that
482
+ * collection.
483
+ *
484
+ * **When to use**
485
+ *
486
+ * Use when a path arrives as a string, such as from a URL, a message or a
487
+ * stored {@link DocumentPath.DocumentPath}, and you know which collection it
488
+ * should be in.
489
+ *
490
+ * **Details**
491
+ *
492
+ * The path is checked against every segment the collection was defined with,
493
+ * so a path into another collection returns `None`, even one with the same
494
+ * last segment. Every id in the path must be non-empty.
495
+ *
496
+ * @example Parsing a path
497
+ * ```ts
498
+ * import { Collection, FirestoreModel } from "@visiolab/effect-firebase/firestore";
499
+ * import { Option, Schema } from "effect";
500
+ *
501
+ * class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
502
+ * class Post extends FirestoreModel.Class<Post>("Post")({ title: Schema.String }) {}
503
+ *
504
+ * const Users = Collection.make("users", User);
505
+ * const UserPosts = Users.subcollection("posts", Post);
506
+ *
507
+ * Option.isSome(Collection.fromPath(UserPosts, "users/alice/posts/hello")); // true
508
+ * Option.isSome(Collection.fromPath(UserPosts, "orgs/acme/posts/hello")); // false
509
+ * ```
510
+ *
511
+ * @see {@link Collection.doc} for a reference built from typed ids
512
+ *
513
+ * @stability unstable
514
+ * @category constructors
515
+ * @since 0.2.0
516
+ */
517
+ export const fromPath: <C extends Any>(
518
+ collection: C,
519
+ path: string,
520
+ ) => Option.Option<DocumentReference<C>> = collection.fromPath;