@visiolab/effect-firebase 0.0.0-stage → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +72 -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
package/package.json
CHANGED
|
@@ -1,6 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visiolab/effect-firebase",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Effect bindings for Firebase.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"effect",
|
|
7
|
+
"firebase",
|
|
8
|
+
"firestore",
|
|
9
|
+
"typescript"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://visiolab.github.io/effect-firebase/",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/VisioLab/effect-firebase/issues"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/VisioLab/effect-firebase.git",
|
|
19
|
+
"directory": "packages/effect-firebase"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": [],
|
|
27
|
+
"exports": {
|
|
28
|
+
"./app": "./dist/app/index.mjs",
|
|
29
|
+
"./app/App": "./dist/app/App.mjs",
|
|
30
|
+
"./firestore": "./dist/firestore/index.mjs",
|
|
31
|
+
"./firestore/Collection": "./dist/firestore/Collection.mjs",
|
|
32
|
+
"./firestore/CollectionGroup": "./dist/firestore/CollectionGroup.mjs",
|
|
33
|
+
"./firestore/DocumentPath": "./dist/firestore/DocumentPath.mjs",
|
|
34
|
+
"./firestore/DocumentSnapshot": "./dist/firestore/DocumentSnapshot.mjs",
|
|
35
|
+
"./firestore/FieldValue": "./dist/firestore/FieldValue.mjs",
|
|
36
|
+
"./firestore/Firestore": "./dist/firestore/Firestore.mjs",
|
|
37
|
+
"./firestore/FirestoreModel": "./dist/firestore/FirestoreModel.mjs",
|
|
38
|
+
"./firestore/FirestoreSchema": "./dist/firestore/FirestoreSchema.mjs",
|
|
39
|
+
"./firestore/FirestoreValue": "./dist/firestore/FirestoreValue.mjs",
|
|
40
|
+
"./firestore/GeoPoint": "./dist/firestore/GeoPoint.mjs",
|
|
41
|
+
"./firestore/Patch": "./dist/firestore/Patch.mjs",
|
|
42
|
+
"./firestore/QuerySnapshot": "./dist/firestore/QuerySnapshot.mjs",
|
|
43
|
+
"./firestore/Timestamp": "./dist/firestore/Timestamp.mjs",
|
|
44
|
+
"./functions": "./dist/functions/index.mjs",
|
|
45
|
+
"./functions/Callable": "./dist/functions/Callable.mjs",
|
|
46
|
+
"./functions/CallableError": "./dist/functions/CallableError.mjs",
|
|
47
|
+
"./functions/CallableMiddleware": "./dist/functions/CallableMiddleware.mjs",
|
|
48
|
+
"./functions/CallableRequest": "./dist/functions/CallableRequest.mjs",
|
|
49
|
+
"./functions/Caller": "./dist/functions/Caller.mjs",
|
|
50
|
+
"./functions/DocumentTrigger": "./dist/functions/DocumentTrigger.mjs",
|
|
51
|
+
"./functions/FirebaseFunction": "./dist/functions/FirebaseFunction.mjs",
|
|
52
|
+
"./functions/FunctionGroup": "./dist/functions/FunctionGroup.mjs",
|
|
53
|
+
"./package.json": "./package.json"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@effect/vitest": "^4.1.0",
|
|
60
|
+
"effect": "4.1.0",
|
|
61
|
+
"typescript": "^7.0.2",
|
|
62
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
|
|
63
|
+
"vite-plus": "1.4.0",
|
|
64
|
+
"vitest": "5.0.5"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"effect": "~4.1.0"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "vp pack",
|
|
71
|
+
"dev": "vp pack --watch",
|
|
72
|
+
"test": "vp test",
|
|
73
|
+
"check": "vp check"
|
|
74
|
+
}
|
|
6
75
|
}
|
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`](https://visiolab.github.io/effect-firebase/api/admin/app/App/) and
|
|
6
|
+
* [`@visiolab/effect-firebase-client`](https://visiolab.github.io/effect-firebase/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]";
|
package/src/app/index.ts
ADDED
|
@@ -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;
|