@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
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The call a Callable is answering: who made it, from which app, the raw
|
|
3
|
+
* Payload as it arrived and the HTTP request it came in.
|
|
4
|
+
*
|
|
5
|
+
* Every call runs with a {@link CallableRequest} in scope. Callable Middleware
|
|
6
|
+
* reads it to decide whether the call may go on, and a handler may read it
|
|
7
|
+
* too. It is portable: `Functions` from
|
|
8
|
+
* `@visiolab/effect-firebase-functions` provides it from Firebase's request,
|
|
9
|
+
* and `Callable.handler` provides it from a {@link Input} of your own.
|
|
10
|
+
*
|
|
11
|
+
* @example Rate limiting by the caller's address
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { Effect, Layer, Option } from "effect";
|
|
14
|
+
* import { CallableError, CallableMiddleware, CallableRequest } from "@visiolab/effect-firebase/functions";
|
|
15
|
+
*
|
|
16
|
+
* class RateLimit extends CallableMiddleware.Service<RateLimit>()("RateLimit", {
|
|
17
|
+
* error: CallableError.ResourceExhausted,
|
|
18
|
+
* }) {}
|
|
19
|
+
*
|
|
20
|
+
* const RateLimitLive = Layer.sync(RateLimit, () => {
|
|
21
|
+
* const calls = new Map<string, number>();
|
|
22
|
+
* return (effect) =>
|
|
23
|
+
* Effect.gen(function* () {
|
|
24
|
+
* const { rawRequest } = yield* CallableRequest.CallableRequest;
|
|
25
|
+
* const address = Option.getOrElse(rawRequest.remoteAddress, () => "unknown");
|
|
26
|
+
* const count = (calls.get(address) ?? 0) + 1;
|
|
27
|
+
* calls.set(address, count);
|
|
28
|
+
* if (count > 100) return yield* new CallableError.ResourceExhausted();
|
|
29
|
+
* return yield* effect;
|
|
30
|
+
* });
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @stability unstable
|
|
35
|
+
* @since 0.3.0
|
|
36
|
+
*/
|
|
37
|
+
import type { Context } from "effect";
|
|
38
|
+
import type * as HttpServerRequest from "effect/http/HttpServerRequest";
|
|
39
|
+
import * as internal from "./internal/callableRequest.ts";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Who made a call: the signed-in user's uid, the claims of their ID token and
|
|
43
|
+
* the raw ID token.
|
|
44
|
+
*
|
|
45
|
+
* @stability unstable
|
|
46
|
+
* @category models
|
|
47
|
+
* @since 0.3.0
|
|
48
|
+
*/
|
|
49
|
+
export interface Auth {
|
|
50
|
+
/** The signed-in user's uid. */
|
|
51
|
+
readonly uid: string;
|
|
52
|
+
/** The decoded claims of the user's ID token. */
|
|
53
|
+
readonly token: Readonly<Record<string, unknown>>;
|
|
54
|
+
/** The ID token as the client sent it. */
|
|
55
|
+
readonly rawToken: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The app a call came from, as App Check attested it.
|
|
60
|
+
*
|
|
61
|
+
* @stability unstable
|
|
62
|
+
* @category models
|
|
63
|
+
* @since 0.3.0
|
|
64
|
+
*/
|
|
65
|
+
export interface App {
|
|
66
|
+
/** The app ID of the attested Firebase app. */
|
|
67
|
+
readonly appId: string;
|
|
68
|
+
/** The decoded claims of the App Check token. */
|
|
69
|
+
readonly token: Readonly<Record<string, unknown>>;
|
|
70
|
+
/** Whether the App Check token was already used, when replay protection is on. */
|
|
71
|
+
readonly alreadyConsumed?: boolean | undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The call a Callable is answering.
|
|
76
|
+
*
|
|
77
|
+
* **Details**
|
|
78
|
+
*
|
|
79
|
+
* `data` is the raw Payload, the `Schema.toCodecJson` output the client sent.
|
|
80
|
+
* Callable Middleware runs before the Payload is decoded, so it sees only
|
|
81
|
+
* `data`.
|
|
82
|
+
*
|
|
83
|
+
* `rawRequest` is the HTTP request the call came in, as Effect's
|
|
84
|
+
* `HttpServerRequest`. `Functions` builds it from Firebase's request,
|
|
85
|
+
* streaming its body without copying the bytes, and `Callable.handler` takes
|
|
86
|
+
* it from {@link Input.rawRequest}.
|
|
87
|
+
*
|
|
88
|
+
* @stability unstable
|
|
89
|
+
* @category models
|
|
90
|
+
* @since 0.3.0
|
|
91
|
+
*/
|
|
92
|
+
export interface CallableRequest {
|
|
93
|
+
/** The signed-in caller, absent for a signed-out call. */
|
|
94
|
+
readonly auth?: Auth | undefined;
|
|
95
|
+
/** The app the call came from, absent without App Check. */
|
|
96
|
+
readonly app?: App | undefined;
|
|
97
|
+
/** An unverified Firebase Instance ID token, if the client sent one. */
|
|
98
|
+
readonly instanceIdToken?: string | undefined;
|
|
99
|
+
/** The raw Payload, before it is decoded. */
|
|
100
|
+
readonly data: unknown;
|
|
101
|
+
/**
|
|
102
|
+
* The HTTP request the call came in: its headers, method, URL, body and the
|
|
103
|
+
* caller's `remoteAddress`.
|
|
104
|
+
*
|
|
105
|
+
* **Details**
|
|
106
|
+
*
|
|
107
|
+
* The Firebase web SDK's `httpsCallable` can't send custom headers, so a
|
|
108
|
+
* header of your own comes only from callers that don't use it, such as
|
|
109
|
+
* another server. Headers the browser or the SDK sets, such as `user-agent`
|
|
110
|
+
* and `accept-language`, are there for every caller.
|
|
111
|
+
*
|
|
112
|
+
* `remoteAddress` is the caller's IP address as the platform reports it,
|
|
113
|
+
* `Option.none()` when it is unknown.
|
|
114
|
+
*/
|
|
115
|
+
readonly rawRequest: HttpServerRequest.HttpServerRequest;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The service key of the {@link CallableRequest}.
|
|
120
|
+
*
|
|
121
|
+
* **Details**
|
|
122
|
+
*
|
|
123
|
+
* Every call provides it, so Callable Middleware may read it without listing
|
|
124
|
+
* it in `requires`.
|
|
125
|
+
*
|
|
126
|
+
* @stability unstable
|
|
127
|
+
* @category services
|
|
128
|
+
* @since 0.3.0
|
|
129
|
+
*/
|
|
130
|
+
export const CallableRequest: Context.Service<CallableRequest, CallableRequest> =
|
|
131
|
+
internal.CallableRequest;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* What `Callable.handler` takes about a call: who makes it, from which app and
|
|
135
|
+
* over which HTTP request. `Callable.handler` fills `data` by encoding the
|
|
136
|
+
* Payload.
|
|
137
|
+
*
|
|
138
|
+
* **Details**
|
|
139
|
+
*
|
|
140
|
+
* Pass `{}` for a signed-out call and `{ auth: { uid, token } }` for a
|
|
141
|
+
* signed-in one. `rawToken` defaults to an empty string, since a call made in
|
|
142
|
+
* process has no real ID token.
|
|
143
|
+
*
|
|
144
|
+
* @stability unstable
|
|
145
|
+
* @category models
|
|
146
|
+
* @since 0.3.0
|
|
147
|
+
*/
|
|
148
|
+
export interface Input {
|
|
149
|
+
/** The signed-in caller, absent for a signed-out call. */
|
|
150
|
+
readonly auth?:
|
|
151
|
+
| {
|
|
152
|
+
readonly uid: string;
|
|
153
|
+
readonly token: Readonly<Record<string, unknown>>;
|
|
154
|
+
readonly rawToken?: string | undefined;
|
|
155
|
+
}
|
|
156
|
+
| undefined;
|
|
157
|
+
/** The app the call came from. */
|
|
158
|
+
readonly app?: App | undefined;
|
|
159
|
+
/** A Firebase Instance ID token. */
|
|
160
|
+
readonly instanceIdToken?: string | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* The HTTP request the call comes in, for middleware that reads headers or
|
|
163
|
+
* the caller's address.
|
|
164
|
+
*
|
|
165
|
+
* **Details**
|
|
166
|
+
*
|
|
167
|
+
* Left out, it is a `POST` of the encoded Payload as JSON to
|
|
168
|
+
* `http://localhost/<deployed name>`, with `content-type:
|
|
169
|
+
* application/json`, no other headers and no `remoteAddress`. Build one with
|
|
170
|
+
* `HttpServerRequest.fromWeb` from `effect/http/HttpServerRequest`, and set
|
|
171
|
+
* `remoteAddress` with its `modify`.
|
|
172
|
+
*
|
|
173
|
+
* @example Calling with a request of your own
|
|
174
|
+
* ```ts
|
|
175
|
+
* import { Effect, Layer, Option } from "effect";
|
|
176
|
+
* import * as HttpServerRequest from "effect/http/HttpServerRequest";
|
|
177
|
+
* import { Callable, CallableMiddleware, CallableRequest } from "@visiolab/effect-firebase/functions";
|
|
178
|
+
*
|
|
179
|
+
* class LogLocale extends CallableMiddleware.Service<LogLocale>()("LogLocale") {}
|
|
180
|
+
*
|
|
181
|
+
* const Ping = Callable.make("ping").middleware(LogLocale);
|
|
182
|
+
*
|
|
183
|
+
* const program = Effect.gen(function* () {
|
|
184
|
+
* const ping = yield* Callable.handler(Ping);
|
|
185
|
+
* const rawRequest = HttpServerRequest.fromWeb(
|
|
186
|
+
* new Request("https://example.com/ping", {
|
|
187
|
+
* method: "POST",
|
|
188
|
+
* headers: { "accept-language": "de-CH" },
|
|
189
|
+
* }),
|
|
190
|
+
* ).modify({ remoteAddress: Option.some("203.0.113.7") });
|
|
191
|
+
* yield* ping(undefined, { rawRequest });
|
|
192
|
+
* }).pipe(
|
|
193
|
+
* Effect.provide(
|
|
194
|
+
* Layer.mergeAll(
|
|
195
|
+
* Callable.toLayer(Ping, () => Effect.void),
|
|
196
|
+
* Layer.succeed(LogLocale, (effect) =>
|
|
197
|
+
* Effect.flatMap(Effect.service(CallableRequest.CallableRequest), ({ rawRequest }) =>
|
|
198
|
+
* Effect.andThen(Effect.logInfo("Locale", rawRequest.headers["accept-language"]), effect),
|
|
199
|
+
* ),
|
|
200
|
+
* ),
|
|
201
|
+
* ),
|
|
202
|
+
* ),
|
|
203
|
+
* );
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
readonly rawRequest?: HttpServerRequest.HttpServerRequest | undefined;
|
|
207
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Caller: the signed-in user making a call, and the built-in
|
|
3
|
+
* `Authenticated` middleware that provides it.
|
|
4
|
+
*
|
|
5
|
+
* A Callable that requires a signed-in caller attaches {@link Authenticated}.
|
|
6
|
+
* Its handler then reads the {@link Caller} instead of the raw request, and a
|
|
7
|
+
* signed-out call fails with `CallableError.Unauthenticated` before its
|
|
8
|
+
* Payload is decoded.
|
|
9
|
+
*
|
|
10
|
+
* @example Reading the Caller in a handler
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { Effect, Schema } from "effect";
|
|
13
|
+
* import { Callable, Caller } from "@visiolab/effect-firebase/functions";
|
|
14
|
+
*
|
|
15
|
+
* const WhoAmI = Callable.make("whoAmI", { success: Schema.String }).middleware(
|
|
16
|
+
* Caller.Authenticated,
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* const WhoAmILive = Callable.toLayer(WhoAmI, () =>
|
|
20
|
+
* Effect.map(Effect.service(Caller.Caller), ({ uid }) => uid),
|
|
21
|
+
* );
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @stability unstable
|
|
25
|
+
* @since 0.3.0
|
|
26
|
+
*/
|
|
27
|
+
import { Context, Effect, Layer } from "effect";
|
|
28
|
+
import * as CallableError from "./CallableError.ts";
|
|
29
|
+
import * as CallableMiddleware from "./CallableMiddleware.ts";
|
|
30
|
+
import * as CallableRequest from "./CallableRequest.ts";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The signed-in user making a call.
|
|
34
|
+
*
|
|
35
|
+
* **Details**
|
|
36
|
+
*
|
|
37
|
+
* A portable shape, not `firebase-admin`'s `DecodedIdToken`. There is no
|
|
38
|
+
* constructor: {@link Authenticated} provides it from the request, and a test
|
|
39
|
+
* provides a fake one from a stub middleware Layer.
|
|
40
|
+
*
|
|
41
|
+
* @stability unstable
|
|
42
|
+
* @category models
|
|
43
|
+
* @since 0.3.0
|
|
44
|
+
*/
|
|
45
|
+
export interface Caller {
|
|
46
|
+
/** The user's uid. */
|
|
47
|
+
readonly uid: string;
|
|
48
|
+
/** The decoded claims of the user's ID token. */
|
|
49
|
+
readonly token: Readonly<Record<string, unknown>>;
|
|
50
|
+
/** The ID token as the client sent it. */
|
|
51
|
+
readonly rawToken: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The service key of the {@link Caller}.
|
|
56
|
+
*
|
|
57
|
+
* @stability unstable
|
|
58
|
+
* @category services
|
|
59
|
+
* @since 0.3.0
|
|
60
|
+
*/
|
|
61
|
+
export const Caller: Context.Service<Caller, Caller> = Context.Service<Caller>(
|
|
62
|
+
"@visiolab/effect-firebase/functions/Caller",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The built-in middleware that requires a signed-in caller and provides the
|
|
67
|
+
* {@link Caller}.
|
|
68
|
+
*
|
|
69
|
+
* **When to use**
|
|
70
|
+
*
|
|
71
|
+
* Use on every Callable that only a signed-in user may call. Write a
|
|
72
|
+
* middleware of your own with {@link CallableMiddleware.Service} for other
|
|
73
|
+
* requirements, such as a custom claim.
|
|
74
|
+
*
|
|
75
|
+
* **Details**
|
|
76
|
+
*
|
|
77
|
+
* A call without auth fails with `CallableError.Unauthenticated`, before its
|
|
78
|
+
* Payload is decoded. Provide {@link Authenticated.layer} where the chain
|
|
79
|
+
* runs, or a stub Layer that provides a fake Caller in tests.
|
|
80
|
+
*
|
|
81
|
+
* @example Stubbing the Caller in process
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { Effect, Layer, Schema } from "effect";
|
|
84
|
+
* import { Callable, Caller } from "@visiolab/effect-firebase/functions";
|
|
85
|
+
*
|
|
86
|
+
* const WhoAmI = Callable.make("whoAmI", { success: Schema.String }).middleware(
|
|
87
|
+
* Caller.Authenticated,
|
|
88
|
+
* );
|
|
89
|
+
*
|
|
90
|
+
* const WhoAmILive = Callable.toLayer(WhoAmI, () =>
|
|
91
|
+
* Effect.map(Effect.service(Caller.Caller), ({ uid }) => uid),
|
|
92
|
+
* );
|
|
93
|
+
*
|
|
94
|
+
* const AsAda = Layer.succeed(Caller.Authenticated, (effect) =>
|
|
95
|
+
* Effect.provideService(effect, Caller.Caller, { uid: "ada", token: {}, rawToken: "" }),
|
|
96
|
+
* );
|
|
97
|
+
*
|
|
98
|
+
* const program = Effect.gen(function* () {
|
|
99
|
+
* const whoAmI = yield* Callable.handler(WhoAmI);
|
|
100
|
+
* return yield* whoAmI(undefined, {});
|
|
101
|
+
* }).pipe(Effect.provide(Layer.merge(WhoAmILive, AsAda)));
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @see {@link Caller} for what it provides
|
|
105
|
+
* @see {@link CallableMiddleware.Service} for declaring a middleware of your own
|
|
106
|
+
*
|
|
107
|
+
* @stability unstable
|
|
108
|
+
* @category middleware
|
|
109
|
+
* @since 0.3.0
|
|
110
|
+
*/
|
|
111
|
+
export class Authenticated extends CallableMiddleware.Service<
|
|
112
|
+
Authenticated,
|
|
113
|
+
{ provides: Caller }
|
|
114
|
+
>()("@visiolab/effect-firebase/functions/Caller/Authenticated", {
|
|
115
|
+
error: CallableError.Unauthenticated,
|
|
116
|
+
}) {
|
|
117
|
+
/**
|
|
118
|
+
* The implementation of {@link Authenticated}: provides the {@link Caller}
|
|
119
|
+
* from the request's auth, and fails with `CallableError.Unauthenticated`
|
|
120
|
+
* when there is none.
|
|
121
|
+
*
|
|
122
|
+
* **When to use**
|
|
123
|
+
*
|
|
124
|
+
* Use where the chain runs, in production and in process alike.
|
|
125
|
+
*
|
|
126
|
+
* @stability unstable
|
|
127
|
+
* @category layers
|
|
128
|
+
* @since 0.3.0
|
|
129
|
+
*/
|
|
130
|
+
static readonly layer: Layer.Layer<Authenticated> = Layer.succeed(this, (effect) =>
|
|
131
|
+
Effect.gen(function* () {
|
|
132
|
+
const { auth } = yield* Effect.service(CallableRequest.CallableRequest);
|
|
133
|
+
if (auth === undefined) return yield* new CallableError.Unauthenticated();
|
|
134
|
+
return yield* Effect.provideService(effect, Caller, {
|
|
135
|
+
uid: auth.uid,
|
|
136
|
+
token: auth.token,
|
|
137
|
+
rawToken: auth.rawToken,
|
|
138
|
+
});
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
}
|