@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VisioLab GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,82 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @visiolab/effect-firebase
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Typed Firestore collections and Firebase services for [Effect](https://effect.website), independent of any Firebase SDK.
|
|
4
|
+
|
|
5
|
+
This package defines the services and the document models. Two packages implement them:
|
|
6
|
+
|
|
7
|
+
| Package | Runs on | SDK |
|
|
8
|
+
| ----------------------------------------------------------------------------------------------------------- | --------------------- | ---------------- |
|
|
9
|
+
| [`@visiolab/effect-firebase-admin`](https://github.com/VisioLab/effect-firebase/tree/main/packages/admin) | servers and functions | `firebase-admin` |
|
|
10
|
+
| [`@visiolab/effect-firebase-client`](https://github.com/VisioLab/effect-firebase/tree/main/packages/client) | browsers and apps | `firebase` |
|
|
11
|
+
|
|
12
|
+
Code written against this package, such as models, collections and the portable `Firestore` service, runs unchanged on either.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add effect @visiolab/effect-firebase @visiolab/effect-firebase-admin firebase-admin
|
|
18
|
+
# or, in a browser or app
|
|
19
|
+
pnpm add effect @visiolab/effect-firebase @visiolab/effect-firebase-client firebase
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Example
|
|
23
|
+
|
|
24
|
+
Define a document with `FirestoreModel.Class`, where it lives with `Collection.make`, and read and write it through the `Firestore` service:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { Effect, Layer, Schema } from "effect";
|
|
28
|
+
import {
|
|
29
|
+
Collection,
|
|
30
|
+
DocumentSnapshot,
|
|
31
|
+
Firestore,
|
|
32
|
+
FirestoreModel,
|
|
33
|
+
} from "@visiolab/effect-firebase/firestore";
|
|
34
|
+
import { App } from "@visiolab/effect-firebase-admin/app";
|
|
35
|
+
import { AdminFirestore } from "@visiolab/effect-firebase-admin/firestore";
|
|
36
|
+
|
|
37
|
+
class User extends FirestoreModel.Class<User>("User")({
|
|
38
|
+
name: Schema.String,
|
|
39
|
+
visits: FirestoreModel.Number(Schema.Int),
|
|
40
|
+
}) {}
|
|
41
|
+
|
|
42
|
+
const Users = Collection.make("users", User);
|
|
43
|
+
|
|
44
|
+
const program = Effect.gen(function* () {
|
|
45
|
+
const firestore = yield* Firestore.Firestore;
|
|
46
|
+
const alice = Users.doc(Users.id.make("alice"));
|
|
47
|
+
|
|
48
|
+
yield* firestore.setDoc(alice, { name: "Alice", visits: 0 });
|
|
49
|
+
|
|
50
|
+
const snapshot = yield* firestore.getDoc(alice);
|
|
51
|
+
if (DocumentSnapshot.isExisting(snapshot)) {
|
|
52
|
+
const user = yield* snapshot.data;
|
|
53
|
+
yield* Effect.log(`${user.name} has visited ${user.visits} times`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const FirestoreLive = AdminFirestore.layer().pipe(Layer.provide(App.layer()));
|
|
58
|
+
|
|
59
|
+
Effect.runPromise(program.pipe(Effect.provide(FirestoreLive)));
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`program` only asks for the portable `Firestore`, so providing `ClientFirestore.layer` from `@visiolab/effect-firebase-client` runs it on the client SDK instead.
|
|
63
|
+
|
|
64
|
+
## Modules
|
|
65
|
+
|
|
66
|
+
Everything is exported from `@visiolab/effect-firebase/firestore`, and each module from its own path, such as `@visiolab/effect-firebase/firestore/Collection`.
|
|
67
|
+
|
|
68
|
+
| Module | What it is for |
|
|
69
|
+
| ------------------ | --------------------------------------------------------------------------------- |
|
|
70
|
+
| `Firestore` | The service: read, listen to, write and update documents, and run transactions. |
|
|
71
|
+
| `FirestoreModel` | Document models, with the shapes a document is created, updated and read in. |
|
|
72
|
+
| `FirestoreSchema` | Schemas for Firestore's own values, such as timestamps and server-side sentinels. |
|
|
73
|
+
| `Collection` | Where documents live, and typed references to them. |
|
|
74
|
+
| `CollectionGroup` | Every collection with the same id, read together. |
|
|
75
|
+
| `DocumentSnapshot` | A document as a read found it: there or missing. |
|
|
76
|
+
| `QuerySnapshot` | The documents a read of a collection or group found. |
|
|
77
|
+
| `FieldValue` | Server-side writes: `serverTimestamp`, `increment`, `arrayUnion` and the others. |
|
|
78
|
+
| `Patch` | `Patch.replace`, to write a nested struct whole in an update. |
|
|
79
|
+
| `Timestamp` | Firestore's timestamp, to the nanosecond. |
|
|
80
|
+
| `GeoPoint` | Firestore's geographic point. |
|
|
81
|
+
| `DocumentPath` | An untyped document reference, as Firestore stores it in a field. |
|
|
82
|
+
| `FirestoreValue` | Values as Firestore stores them, and the codec models encode through. |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
export { __exportAll };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
declare namespace App_d_exports {
|
|
2
|
+
export { defaultAppName };
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* What the SDK packages' `App` modules share.
|
|
6
|
+
*
|
|
7
|
+
* The app service itself belongs to each SDK package: see the `App` modules
|
|
8
|
+
* of [`@visiolab/effect-firebase-admin`](/api/admin/app/App/) and
|
|
9
|
+
* [`@visiolab/effect-firebase-client`](/api/client/app/App/).
|
|
10
|
+
*
|
|
11
|
+
* @stability unstable
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The name Firebase gives an app that was initialized without one.
|
|
16
|
+
*
|
|
17
|
+
* **When to use**
|
|
18
|
+
*
|
|
19
|
+
* Use to compare against an app's `name`, or to name the default app
|
|
20
|
+
* explicitly.
|
|
21
|
+
*
|
|
22
|
+
* @stability unstable
|
|
23
|
+
* @category constants
|
|
24
|
+
* @since 0.1.0
|
|
25
|
+
*/
|
|
26
|
+
export declare const defaultAppName = "[DEFAULT]";
|
|
27
|
+
export { App_d_exports };
|
package/dist/app/App.mjs
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
// src/app/App.ts
|
|
3
|
+
var App_exports = /* @__PURE__ */ __exportAll({ defaultAppName: () => defaultAppName });
|
|
4
|
+
/**
|
|
5
|
+
* What the SDK packages' `App` modules share.
|
|
6
|
+
*
|
|
7
|
+
* The app service itself belongs to each SDK package: see the `App` modules
|
|
8
|
+
* of [`@visiolab/effect-firebase-admin`](/api/admin/app/App/) and
|
|
9
|
+
* [`@visiolab/effect-firebase-client`](/api/client/app/App/).
|
|
10
|
+
*
|
|
11
|
+
* @stability unstable
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The name Firebase gives an app that was initialized without one.
|
|
16
|
+
*
|
|
17
|
+
* **When to use**
|
|
18
|
+
*
|
|
19
|
+
* Use to compare against an app's `name`, or to name the default app
|
|
20
|
+
* explicitly.
|
|
21
|
+
*
|
|
22
|
+
* @stability unstable
|
|
23
|
+
* @category constants
|
|
24
|
+
* @since 0.1.0
|
|
25
|
+
*/
|
|
26
|
+
const defaultAppName = "[DEFAULT]";
|
|
27
|
+
export { App_exports, defaultAppName };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { App_exports as App } from "./App.mjs";
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { DocumentSnapshot, Existing, Missing } from "./DocumentSnapshot.mjs";
|
|
2
|
+
import { Any as Any$1 } from "./FirestoreModel.mjs";
|
|
3
|
+
import { Brand, Equal, Inspectable, Option, Schema } from "effect";
|
|
4
|
+
declare namespace Collection_d_exports {
|
|
5
|
+
export { Ancestors, Any, Collection, CollectionReference, DocumentReference, DocumentSchema, Id, IdSchema, Level, Levels, fromPath, isCollection, isCollectionReference, isDocumentReference, make };
|
|
6
|
+
}
|
|
7
|
+
declare const CollectionTypeId = "~@visiolab/effect-firebase/firestore/Collection";
|
|
8
|
+
declare const CollectionReferenceTypeId = "~@visiolab/effect-firebase/firestore/CollectionReference";
|
|
9
|
+
declare const DocumentReferenceTypeId = "~@visiolab/effect-firebase/firestore/DocumentReference";
|
|
10
|
+
/**
|
|
11
|
+
* One link of a collection's path: a segment and the model of the documents
|
|
12
|
+
* stored under it.
|
|
13
|
+
*
|
|
14
|
+
* @stability unstable
|
|
15
|
+
* @category models
|
|
16
|
+
* @since 0.1.0
|
|
17
|
+
*/
|
|
18
|
+
export type Level = readonly [segment: string, schema: Any$1];
|
|
19
|
+
/**
|
|
20
|
+
* The {@link Level}s from a root collection down to a collection itself.
|
|
21
|
+
*
|
|
22
|
+
* **Details**
|
|
23
|
+
*
|
|
24
|
+
* A {@link Collection} carries the whole chain rather than only its own
|
|
25
|
+
* segment, so the id of every ancestor stays typed.
|
|
26
|
+
*
|
|
27
|
+
* @stability unstable
|
|
28
|
+
* @category models
|
|
29
|
+
* @since 0.1.0
|
|
30
|
+
*/
|
|
31
|
+
export type Levels = readonly [Level, ...ReadonlyArray<Level>];
|
|
32
|
+
type Join<S extends ReadonlyArray<string>> = S extends readonly [infer Head extends string, ...infer Tail extends ReadonlyArray<string>] ? Tail extends readonly [] ? Head : `${Head}/${Join<Tail>}` : never;
|
|
33
|
+
/** The last link of the chain. */
|
|
34
|
+
type Leaf<L extends Levels> = L extends readonly [...ReadonlyArray<Level>, infer T extends Level] ? T : never;
|
|
35
|
+
/** The segments of a chain, as a tuple. */
|
|
36
|
+
type SegmentsOf<L extends Levels> = { readonly [K in keyof L]: L[K][0]; };
|
|
37
|
+
/**
|
|
38
|
+
* Document ids are branded with the collection's path, so an id only ever fits
|
|
39
|
+
* the collection it came from. Two collections that share a segment - `posts`
|
|
40
|
+
* under `users` and `posts` under `orgs` - still get distinct ids, because the
|
|
41
|
+
* brand is the whole path rather than the last segment.
|
|
42
|
+
*/
|
|
43
|
+
type IdOf<L extends Levels> = string & Brand.Brand<Join<SegmentsOf<L>>>;
|
|
44
|
+
/**
|
|
45
|
+
* One id per ancestor, ordered from the root down. A root collection needs
|
|
46
|
+
* none, which is why the recursion bottoms out at the empty tuple.
|
|
47
|
+
*
|
|
48
|
+
* The parent chain is matched inline rather than looked up through an alias, so
|
|
49
|
+
* that the recursion stays deferred until `L` is a concrete chain. Going
|
|
50
|
+
* through an alias makes it eager, and the compiler gives up on the depth.
|
|
51
|
+
*/
|
|
52
|
+
type AncestorsOf<L extends Levels> = L extends readonly [...infer I, Level] ? I extends Levels ? [...AncestorsOf<I>, IdOf<I>] : [] : [];
|
|
53
|
+
/**
|
|
54
|
+
* What {@link Collection.contains} narrows each member of `V` to: the members
|
|
55
|
+
* already in `C` are kept, the ones in another collection are dropped, and one
|
|
56
|
+
* whose collection is left open is narrowed to `C`.
|
|
57
|
+
*
|
|
58
|
+
* Every branch is `V` or an intersection with it, which is what lets the
|
|
59
|
+
* compiler see the result is assignable to `V`, as a type predicate must be.
|
|
60
|
+
*/
|
|
61
|
+
type Contained<V, C extends Any> = V extends DocumentReference<Any> ? NarrowTo<V, DocumentReference<C>> : V extends Existing<Any> ? NarrowTo<V, Existing<C>> : V extends Missing<Any> ? NarrowTo<V, Missing<C>> : never;
|
|
62
|
+
/**
|
|
63
|
+
* `V` when it is `T`, and `never` when it is not. A `V` whose collection is
|
|
64
|
+
* `any` passes the check without saying anything about it, so it is narrowed
|
|
65
|
+
* by intersecting it with `T` instead.
|
|
66
|
+
*/
|
|
67
|
+
type NarrowTo<V, T> = V extends T ? (0 extends 1 & LevelsOf<V> ? V & T : V) : never;
|
|
68
|
+
/** The chain of the collection a reference or snapshot is in. */
|
|
69
|
+
type LevelsOf<V> = V extends DocumentReference<Collection<infer L>> ? L : V extends {
|
|
70
|
+
readonly ref: DocumentReference<Collection<infer L>>;
|
|
71
|
+
} ? L : never;
|
|
72
|
+
/**
|
|
73
|
+
* The schema of a document id in one collection. It decodes a string and
|
|
74
|
+
* brands it with the collection's full path, so the id only fits that
|
|
75
|
+
* collection.
|
|
76
|
+
*
|
|
77
|
+
* **When to use**
|
|
78
|
+
*
|
|
79
|
+
* Use `collection.id.make(raw)` to turn a string you trust into an id, and
|
|
80
|
+
* `Schema.decodeUnknownEffect(collection.id)` to check one from outside.
|
|
81
|
+
*
|
|
82
|
+
* **Details**
|
|
83
|
+
*
|
|
84
|
+
* The brand is the whole path, so `posts` under `users` and `posts` under
|
|
85
|
+
* `orgs` have different id types. Its constructor takes a `string`, which
|
|
86
|
+
* makes passing anything else a type error rather than a runtime throw.
|
|
87
|
+
*
|
|
88
|
+
* @stability unstable
|
|
89
|
+
* @category models
|
|
90
|
+
* @since 0.1.0
|
|
91
|
+
*/
|
|
92
|
+
export interface IdSchema<T extends string> extends Schema.Codec<T, string> {
|
|
93
|
+
readonly "~type.make.in": string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The definition of a collection: where it sits, how its documents are
|
|
97
|
+
* addressed, and the model they hold.
|
|
98
|
+
*
|
|
99
|
+
* **Details**
|
|
100
|
+
*
|
|
101
|
+
* A definition does not point at any data. It describes a collection, and
|
|
102
|
+
* {@link Collection.collection} and {@link Collection.doc} turn it into a
|
|
103
|
+
* reference once you have the ids that address it. Build one with
|
|
104
|
+
* {@link make}, and nest one with {@link Collection.subcollection}.
|
|
105
|
+
*
|
|
106
|
+
* @see {@link CollectionReference} for a reference to one collection, with its ancestor ids filled in
|
|
107
|
+
* @see {@link CollectionGroup.CollectionGroup} for every collection with one collection id, read together
|
|
108
|
+
*
|
|
109
|
+
* @stability unstable
|
|
110
|
+
* @category models
|
|
111
|
+
* @since 0.1.0
|
|
112
|
+
*/
|
|
113
|
+
export interface Collection<L extends Levels> extends Inspectable.Inspectable {
|
|
114
|
+
readonly [CollectionTypeId]: typeof CollectionTypeId;
|
|
115
|
+
/** The chain this collection was built from. Only read at the type level. */
|
|
116
|
+
readonly "~levels": L;
|
|
117
|
+
/** The path segments, from the root collection down, such as `["users", "posts"]`. */
|
|
118
|
+
readonly path: SegmentsOf<L>;
|
|
119
|
+
/** The last segment: the collection id Firestore knows the collection by. */
|
|
120
|
+
readonly segment: Leaf<L>[0];
|
|
121
|
+
/**
|
|
122
|
+
* The schema of the ids of documents in this collection.
|
|
123
|
+
*
|
|
124
|
+
* @see {@link IdSchema} for how to make and decode one
|
|
125
|
+
*/
|
|
126
|
+
readonly id: IdSchema<IdOf<L>>;
|
|
127
|
+
/**
|
|
128
|
+
* The model of the documents in this collection. Reads decode through it,
|
|
129
|
+
* and writes encode through its {@link FirestoreModel.Class.create} and
|
|
130
|
+
* {@link FirestoreModel.Class.update} variants.
|
|
131
|
+
*/
|
|
132
|
+
readonly schema: Leaf<L>[1];
|
|
133
|
+
/**
|
|
134
|
+
* Checks whether a Document Reference or Document Snapshot belongs to this
|
|
135
|
+
* collection, and narrows it to this collection's types when it does.
|
|
136
|
+
*
|
|
137
|
+
* **When to use**
|
|
138
|
+
*
|
|
139
|
+
* Use to pick out the documents of one collection from a mixed result, such
|
|
140
|
+
* as a read of a {@link CollectionGroup.CollectionGroup} with several
|
|
141
|
+
* members: `snapshot.docs.filter(UserPosts.contains)`.
|
|
142
|
+
*
|
|
143
|
+
* **Details**
|
|
144
|
+
*
|
|
145
|
+
* Two definitions are the same collection only when they are the same
|
|
146
|
+
* object, so a definition built separately at the same path does not
|
|
147
|
+
* contain this one's documents. It does not use `this`, so it can be passed
|
|
148
|
+
* on unbound.
|
|
149
|
+
*
|
|
150
|
+
* @see {@link isDocumentReference} for checking that a value is a reference to any collection
|
|
151
|
+
*/
|
|
152
|
+
readonly contains: <V extends DocumentReference<Any> | DocumentSnapshot<Any>>(value: V) => value is Contained<V, Collection<L>>;
|
|
153
|
+
/**
|
|
154
|
+
* Defines a subcollection under the documents of this collection.
|
|
155
|
+
*
|
|
156
|
+
* **When to use**
|
|
157
|
+
*
|
|
158
|
+
* Use for a nested collection, such as `posts` under each user. Use
|
|
159
|
+
* {@link make} for a root collection.
|
|
160
|
+
*
|
|
161
|
+
* **Details**
|
|
162
|
+
*
|
|
163
|
+
* Throws when `segment` is not a collection id Firestore accepts: empty,
|
|
164
|
+
* containing `/`, `.` or `..`, matching `__.*__`, or longer than 1500
|
|
165
|
+
* bytes. An invalid definition is a programming error, not a failure to
|
|
166
|
+
* recover from.
|
|
167
|
+
*/
|
|
168
|
+
subcollection<const Segment extends string, S extends Any$1>(segment: Segment, schema: S): Collection<readonly [...L, readonly [Segment, S]]>;
|
|
169
|
+
/**
|
|
170
|
+
* References this collection, given one id per ancestor, from the root down.
|
|
171
|
+
* A root collection takes none.
|
|
172
|
+
*
|
|
173
|
+
* **When to use**
|
|
174
|
+
*
|
|
175
|
+
* Use to address the collection itself, as {@link Firestore.With.getDocs},
|
|
176
|
+
* {@link Firestore.With.streamDocs} and {@link Firestore.With.addDoc} take
|
|
177
|
+
* it.
|
|
178
|
+
*
|
|
179
|
+
* @see {@link Collection.doc} for referencing one document in it
|
|
180
|
+
*/
|
|
181
|
+
collection(...ids: AncestorsOf<L>): CollectionReference<Collection<L>>;
|
|
182
|
+
/**
|
|
183
|
+
* References a document in this collection, given one id per ancestor
|
|
184
|
+
* followed by the document's own.
|
|
185
|
+
*
|
|
186
|
+
* **When to use**
|
|
187
|
+
*
|
|
188
|
+
* Use to address one document, as {@link Firestore.With.getDoc},
|
|
189
|
+
* {@link Firestore.With.setDoc} and {@link Firestore.With.updateDoc} take it.
|
|
190
|
+
*
|
|
191
|
+
* @see {@link Collection.collection} for referencing the collection itself
|
|
192
|
+
* @see {@link fromPath} for a reference from a path string
|
|
193
|
+
*/
|
|
194
|
+
doc(...ids: [...AncestorsOf<L>, IdOf<L>]): DocumentReference<Collection<L>>;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Any collection, whatever it is nested under and whatever it holds. Use it as
|
|
198
|
+
* a type bound.
|
|
199
|
+
*
|
|
200
|
+
* **Details**
|
|
201
|
+
*
|
|
202
|
+
* It is `Collection<any>` rather than `Collection<Levels>` because `L` sits in
|
|
203
|
+
* an argument position in {@link Collection.doc}, which makes it invariant.
|
|
204
|
+
*
|
|
205
|
+
* @stability unstable
|
|
206
|
+
* @category models
|
|
207
|
+
* @since 0.1.0
|
|
208
|
+
*/
|
|
209
|
+
export type Any = Collection<any>;
|
|
210
|
+
/**
|
|
211
|
+
* The id of a document in `C`, branded with `C`'s path.
|
|
212
|
+
*
|
|
213
|
+
* @see {@link IdSchema} for how to make one
|
|
214
|
+
*
|
|
215
|
+
* @stability unstable
|
|
216
|
+
* @category models
|
|
217
|
+
* @since 0.1.0
|
|
218
|
+
*/
|
|
219
|
+
export type Id<C extends Any> = IdOf<C["~levels"]>;
|
|
220
|
+
/**
|
|
221
|
+
* The ids that address `C`: one per ancestor, from the root down. Empty for a
|
|
222
|
+
* root collection.
|
|
223
|
+
*
|
|
224
|
+
* @stability unstable
|
|
225
|
+
* @category models
|
|
226
|
+
* @since 0.1.0
|
|
227
|
+
*/
|
|
228
|
+
export type Ancestors<C extends Any> = AncestorsOf<C["~levels"]>;
|
|
229
|
+
/**
|
|
230
|
+
* The model of the documents in `C`, as {@link make} or
|
|
231
|
+
* {@link Collection.subcollection} was given it.
|
|
232
|
+
*
|
|
233
|
+
* @stability unstable
|
|
234
|
+
* @category models
|
|
235
|
+
* @since 0.1.0
|
|
236
|
+
*/
|
|
237
|
+
export type DocumentSchema<C extends Any> = Leaf<C["~levels"]>[1];
|
|
238
|
+
/**
|
|
239
|
+
* A reference to one collection, with the ids of its ancestors filled in.
|
|
240
|
+
*
|
|
241
|
+
* **Details**
|
|
242
|
+
*
|
|
243
|
+
* Build one with {@link Collection.collection}. Two references are
|
|
244
|
+
* `Equal` when they have the same path and come from the same definition.
|
|
245
|
+
*
|
|
246
|
+
* @see {@link Collection} for the definition it is built from
|
|
247
|
+
* @see {@link DocumentReference} for a reference to one document
|
|
248
|
+
*
|
|
249
|
+
* @stability unstable
|
|
250
|
+
* @category models
|
|
251
|
+
* @since 0.1.0
|
|
252
|
+
*/
|
|
253
|
+
export interface CollectionReference<C extends Any> extends Inspectable.Inspectable, Equal.Equal {
|
|
254
|
+
readonly [CollectionReferenceTypeId]: typeof CollectionReferenceTypeId;
|
|
255
|
+
/** The definition this reference was built from. */
|
|
256
|
+
readonly collection: C;
|
|
257
|
+
/** The ids that address it, from the root down. */
|
|
258
|
+
readonly ancestors: Readonly<Ancestors<C>>;
|
|
259
|
+
/** The collection id Firestore knows it by: the last segment of its path. */
|
|
260
|
+
readonly id: Leaf<C["~levels"]>[0];
|
|
261
|
+
/** The full path, such as `users/alice/posts`. */
|
|
262
|
+
readonly path: string;
|
|
263
|
+
/**
|
|
264
|
+
* References a document in this collection, by its own id.
|
|
265
|
+
*
|
|
266
|
+
* @see {@link Collection.doc} for referencing it from the definition with every id
|
|
267
|
+
*/
|
|
268
|
+
doc(id: Id<C>): DocumentReference<C>;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* A typed reference to one document in a collection.
|
|
272
|
+
*
|
|
273
|
+
* **Details**
|
|
274
|
+
*
|
|
275
|
+
* It knows the {@link Collection} it was built from, so reads through it
|
|
276
|
+
* decode with that collection's model and writes encode with it. Build one
|
|
277
|
+
* with {@link Collection.doc}, {@link CollectionReference.doc} or
|
|
278
|
+
* {@link fromPath}. Two references are `Equal` when they have the same path and
|
|
279
|
+
* come from the same definition.
|
|
280
|
+
*
|
|
281
|
+
* @see {@link CollectionGroup.DocumentReference} for a reference into any collection of a group
|
|
282
|
+
* @see {@link DocumentPath.DocumentPath} for the untyped path a reference is stored as in a field
|
|
283
|
+
*
|
|
284
|
+
* @stability unstable
|
|
285
|
+
* @category models
|
|
286
|
+
* @since 0.1.0
|
|
287
|
+
*/
|
|
288
|
+
export interface DocumentReference<C extends Any> extends Inspectable.Inspectable, Equal.Equal {
|
|
289
|
+
readonly [DocumentReferenceTypeId]: typeof DocumentReferenceTypeId;
|
|
290
|
+
/** The definition of the collection holding it. */
|
|
291
|
+
readonly collection: C;
|
|
292
|
+
/** The ids that address its collection, from the root down. */
|
|
293
|
+
readonly ancestors: Readonly<Ancestors<C>>;
|
|
294
|
+
/** The document's own id. */
|
|
295
|
+
readonly id: Id<C>;
|
|
296
|
+
/** The full path, such as `users/alice/posts/hello`. */
|
|
297
|
+
readonly path: string;
|
|
298
|
+
/** A reference to the collection holding it. */
|
|
299
|
+
readonly parent: CollectionReference<C>;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Checks whether a value is a collection definition.
|
|
303
|
+
*
|
|
304
|
+
* @see {@link isCollectionReference} for a reference to a collection
|
|
305
|
+
* @see {@link CollectionGroup.isCollectionGroup} for a Collection Group
|
|
306
|
+
*
|
|
307
|
+
* @stability unstable
|
|
308
|
+
* @category guards
|
|
309
|
+
* @since 0.1.0
|
|
310
|
+
*/
|
|
311
|
+
export declare const isCollection: (u: unknown) => u is Any;
|
|
312
|
+
/**
|
|
313
|
+
* Checks whether a value is a {@link CollectionReference}.
|
|
314
|
+
*
|
|
315
|
+
* @see {@link isCollection} for a collection definition
|
|
316
|
+
* @see {@link isDocumentReference} for a reference to a document
|
|
317
|
+
*
|
|
318
|
+
* @stability unstable
|
|
319
|
+
* @category guards
|
|
320
|
+
* @since 0.1.0
|
|
321
|
+
*/
|
|
322
|
+
export declare const isCollectionReference: (u: unknown) => u is CollectionReference<Any>;
|
|
323
|
+
/**
|
|
324
|
+
* Checks whether a value is a {@link DocumentReference} into any collection.
|
|
325
|
+
*
|
|
326
|
+
* @see {@link Collection.contains} for checking that a reference belongs to one collection, with narrowing
|
|
327
|
+
* @see {@link isCollectionReference} for a reference to a collection
|
|
328
|
+
* @see {@link DocumentPath.isDocumentPath} for the untyped path stored in a field
|
|
329
|
+
*
|
|
330
|
+
* @stability unstable
|
|
331
|
+
* @category guards
|
|
332
|
+
* @since 0.1.0
|
|
333
|
+
*/
|
|
334
|
+
export declare const isDocumentReference: (u: unknown) => u is DocumentReference<Any>;
|
|
335
|
+
/**
|
|
336
|
+
* Defines a root collection and the model of its documents.
|
|
337
|
+
*
|
|
338
|
+
* **When to use**
|
|
339
|
+
*
|
|
340
|
+
* Use once per root collection, at module level, and build every reference to
|
|
341
|
+
* it from the result. Use {@link Collection.subcollection} on the result for a
|
|
342
|
+
* collection nested under its documents.
|
|
343
|
+
*
|
|
344
|
+
* **Details**
|
|
345
|
+
*
|
|
346
|
+
* The documents are described by a {@link FirestoreModel.Class} rather than a
|
|
347
|
+
* plain schema, since a document is written in other shapes than it is read
|
|
348
|
+
* in, and the model carries each of them. Pass a {@link FirestoreModel.Union}
|
|
349
|
+
* of classes for a collection that holds several document shapes.
|
|
350
|
+
*
|
|
351
|
+
* Throws when `segment` is not a collection id Firestore accepts: empty,
|
|
352
|
+
* containing `/`, `.` or `..`, matching `__.*__`, or longer than 1500 bytes. An
|
|
353
|
+
* invalid definition is a programming error, not a failure to recover from.
|
|
354
|
+
*
|
|
355
|
+
* @example Defining a collection with a subcollection
|
|
356
|
+
* ```ts
|
|
357
|
+
* import { Collection, Firestore, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
358
|
+
* import { Effect, Schema } from "effect";
|
|
359
|
+
*
|
|
360
|
+
* class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
|
|
361
|
+
* class Post extends FirestoreModel.Class<Post>("Post")({ title: Schema.String }) {}
|
|
362
|
+
*
|
|
363
|
+
* const Users = Collection.make("users", User);
|
|
364
|
+
* const UserPosts = Users.subcollection("posts", Post);
|
|
365
|
+
*
|
|
366
|
+
* const program = Effect.gen(function* () {
|
|
367
|
+
* const firestore = yield* Firestore.Firestore;
|
|
368
|
+
* const alice = Users.id.make("alice");
|
|
369
|
+
*
|
|
370
|
+
* yield* firestore.setDoc(Users.doc(alice), { name: "Alice" });
|
|
371
|
+
* const post = yield* firestore.addDoc(UserPosts.collection(alice), { title: "Hello" });
|
|
372
|
+
* return post.path; // "users/alice/posts/<generated id>"
|
|
373
|
+
* });
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* @see {@link fromPath} for a reference to a document of an existing collection from a path string
|
|
377
|
+
* @see {@link CollectionGroup.make} for reading several collections with one collection id together
|
|
378
|
+
*
|
|
379
|
+
* @stability unstable
|
|
380
|
+
* @category constructors
|
|
381
|
+
* @since 0.1.0
|
|
382
|
+
*/
|
|
383
|
+
export declare const make: <const Segment extends string, S extends Any$1>(segment: Segment, schema: S) => Collection<readonly [readonly [Segment, S]]>;
|
|
384
|
+
/**
|
|
385
|
+
* References the document of `collection` at a path string, such as
|
|
386
|
+
* `users/alice/posts/hello`, or returns `None` when the path is not in that
|
|
387
|
+
* collection.
|
|
388
|
+
*
|
|
389
|
+
* **When to use**
|
|
390
|
+
*
|
|
391
|
+
* Use when a path arrives as a string, such as from a URL, a message or a
|
|
392
|
+
* stored {@link DocumentPath.DocumentPath}, and you know which collection it
|
|
393
|
+
* should be in.
|
|
394
|
+
*
|
|
395
|
+
* **Details**
|
|
396
|
+
*
|
|
397
|
+
* The path is checked against every segment the collection was defined with,
|
|
398
|
+
* so a path into another collection returns `None`, even one with the same
|
|
399
|
+
* last segment. Every id in the path must be non-empty.
|
|
400
|
+
*
|
|
401
|
+
* @example Parsing a path
|
|
402
|
+
* ```ts
|
|
403
|
+
* import { Collection, FirestoreModel } from "@visiolab/effect-firebase/firestore";
|
|
404
|
+
* import { Option, Schema } from "effect";
|
|
405
|
+
*
|
|
406
|
+
* class User extends FirestoreModel.Class<User>("User")({ name: Schema.String }) {}
|
|
407
|
+
* class Post extends FirestoreModel.Class<Post>("Post")({ title: Schema.String }) {}
|
|
408
|
+
*
|
|
409
|
+
* const Users = Collection.make("users", User);
|
|
410
|
+
* const UserPosts = Users.subcollection("posts", Post);
|
|
411
|
+
*
|
|
412
|
+
* Option.isSome(Collection.fromPath(UserPosts, "users/alice/posts/hello")); // true
|
|
413
|
+
* Option.isSome(Collection.fromPath(UserPosts, "orgs/acme/posts/hello")); // false
|
|
414
|
+
* ```
|
|
415
|
+
*
|
|
416
|
+
* @see {@link Collection.doc} for a reference built from typed ids
|
|
417
|
+
*
|
|
418
|
+
* @stability unstable
|
|
419
|
+
* @category constructors
|
|
420
|
+
* @since 0.2.0
|
|
421
|
+
*/
|
|
422
|
+
export declare const fromPath: <C extends Any>(collection: C, path: string) => Option.Option<DocumentReference<C>>;
|
|
423
|
+
export { Collection_d_exports };
|