@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,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Errors of Callables: Firebase's error codes, built-in errors that carry
|
|
3
|
+
* them, and the error a Callable Client fails with when a call goes wrong in a
|
|
4
|
+
* way the contract doesn't declare.
|
|
5
|
+
*
|
|
6
|
+
* @stability unstable
|
|
7
|
+
* @since 0.3.0
|
|
8
|
+
*/
|
|
9
|
+
import { Schema } from "effect";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Firebase's error codes, as `firebase-functions` and the client SDK name
|
|
13
|
+
* them, without `ok`.
|
|
14
|
+
*
|
|
15
|
+
* @stability unstable
|
|
16
|
+
* @category schemas
|
|
17
|
+
* @since 0.3.0
|
|
18
|
+
*/
|
|
19
|
+
export const ErrorCode = Schema.Literals([
|
|
20
|
+
"cancelled",
|
|
21
|
+
"unknown",
|
|
22
|
+
"invalid-argument",
|
|
23
|
+
"deadline-exceeded",
|
|
24
|
+
"not-found",
|
|
25
|
+
"already-exists",
|
|
26
|
+
"permission-denied",
|
|
27
|
+
"resource-exhausted",
|
|
28
|
+
"failed-precondition",
|
|
29
|
+
"aborted",
|
|
30
|
+
"out-of-range",
|
|
31
|
+
"unimplemented",
|
|
32
|
+
"internal",
|
|
33
|
+
"unavailable",
|
|
34
|
+
"data-loss",
|
|
35
|
+
"unauthenticated",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* One of Firebase's error codes, such as `not-found`.
|
|
40
|
+
*
|
|
41
|
+
* @stability unstable
|
|
42
|
+
* @category models
|
|
43
|
+
* @since 0.3.0
|
|
44
|
+
*/
|
|
45
|
+
export type ErrorCode = typeof ErrorCode.Type;
|
|
46
|
+
|
|
47
|
+
declare module "effect/Schema" {
|
|
48
|
+
namespace Annotations {
|
|
49
|
+
interface Annotations {
|
|
50
|
+
/**
|
|
51
|
+
* The Firebase error code a Callable answers with when it fails with
|
|
52
|
+
* this error. Set it with `Callable.errorCode` or in a `TaggedError`'s
|
|
53
|
+
* annotations, and read it with `Callable.getErrorCode`.
|
|
54
|
+
*/
|
|
55
|
+
readonly callableErrorCode?: ErrorCode | undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The resource the call asks for doesn't exist. Its code is `not-found`.
|
|
62
|
+
*
|
|
63
|
+
* @example Failing with a built-in error
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { Effect, Schema } from "effect";
|
|
66
|
+
* import { Callable, CallableError } from "@visiolab/effect-firebase/functions";
|
|
67
|
+
*
|
|
68
|
+
* const GetUser = Callable.make("getUser", {
|
|
69
|
+
* payload: { id: Schema.String },
|
|
70
|
+
* success: Schema.String,
|
|
71
|
+
* error: CallableError.NotFound,
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* const GetUserLive = Callable.toLayer(GetUser, ({ id }) =>
|
|
75
|
+
* Effect.fail(new CallableError.NotFound({ message: `No user ${id}` })),
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @stability unstable
|
|
80
|
+
* @category built-in errors
|
|
81
|
+
* @since 0.3.0
|
|
82
|
+
*/
|
|
83
|
+
export class NotFound extends Schema.TaggedError<NotFound, { readonly brand: unique symbol }>()(
|
|
84
|
+
"NotFound",
|
|
85
|
+
{ message: Schema.optional(Schema.String) },
|
|
86
|
+
{ callableErrorCode: "not-found" },
|
|
87
|
+
) {}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The caller may not do what the call asks. Its code is `permission-denied`.
|
|
91
|
+
*
|
|
92
|
+
* @see {@link Unauthenticated} for a caller who isn't signed in
|
|
93
|
+
*
|
|
94
|
+
* @stability unstable
|
|
95
|
+
* @category built-in errors
|
|
96
|
+
* @since 0.3.0
|
|
97
|
+
*/
|
|
98
|
+
export class PermissionDenied extends Schema.TaggedError<
|
|
99
|
+
PermissionDenied,
|
|
100
|
+
{ readonly brand: unique symbol }
|
|
101
|
+
>()(
|
|
102
|
+
"PermissionDenied",
|
|
103
|
+
{ message: Schema.optional(Schema.String) },
|
|
104
|
+
{ callableErrorCode: "permission-denied" },
|
|
105
|
+
) {}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The caller isn't signed in. Its code is `unauthenticated`.
|
|
109
|
+
*
|
|
110
|
+
* @see {@link PermissionDenied} for a signed-in caller who may not do this
|
|
111
|
+
*
|
|
112
|
+
* @stability unstable
|
|
113
|
+
* @category built-in errors
|
|
114
|
+
* @since 0.3.0
|
|
115
|
+
*/
|
|
116
|
+
export class Unauthenticated extends Schema.TaggedError<
|
|
117
|
+
Unauthenticated,
|
|
118
|
+
{ readonly brand: unique symbol }
|
|
119
|
+
>()(
|
|
120
|
+
"Unauthenticated",
|
|
121
|
+
{ message: Schema.optional(Schema.String) },
|
|
122
|
+
{ callableErrorCode: "unauthenticated" },
|
|
123
|
+
) {}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The Payload is well-formed but its values are wrong. Its code is
|
|
127
|
+
* `invalid-argument`.
|
|
128
|
+
*
|
|
129
|
+
* **Details**
|
|
130
|
+
*
|
|
131
|
+
* A Payload that doesn't decode is answered with `invalid-argument` already,
|
|
132
|
+
* without this error. Use it for checks the Payload's schema can't express.
|
|
133
|
+
*
|
|
134
|
+
* @see {@link FailedPrecondition} for a valid Payload the current state rejects
|
|
135
|
+
*
|
|
136
|
+
* @stability unstable
|
|
137
|
+
* @category built-in errors
|
|
138
|
+
* @since 0.3.0
|
|
139
|
+
*/
|
|
140
|
+
export class InvalidArgument extends Schema.TaggedError<
|
|
141
|
+
InvalidArgument,
|
|
142
|
+
{ readonly brand: unique symbol }
|
|
143
|
+
>()(
|
|
144
|
+
"InvalidArgument",
|
|
145
|
+
{ message: Schema.optional(Schema.String) },
|
|
146
|
+
{ callableErrorCode: "invalid-argument" },
|
|
147
|
+
) {}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* What the call would create exists already. Its code is `already-exists`.
|
|
151
|
+
*
|
|
152
|
+
* @stability unstable
|
|
153
|
+
* @category built-in errors
|
|
154
|
+
* @since 0.3.0
|
|
155
|
+
*/
|
|
156
|
+
export class AlreadyExists extends Schema.TaggedError<
|
|
157
|
+
AlreadyExists,
|
|
158
|
+
{ readonly brand: unique symbol }
|
|
159
|
+
>()(
|
|
160
|
+
"AlreadyExists",
|
|
161
|
+
{ message: Schema.optional(Schema.String) },
|
|
162
|
+
{ callableErrorCode: "already-exists" },
|
|
163
|
+
) {}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The system isn't in the state the call needs. Its code is
|
|
167
|
+
* `failed-precondition`.
|
|
168
|
+
*
|
|
169
|
+
* @see {@link InvalidArgument} for a Payload that is wrong whatever the state
|
|
170
|
+
*
|
|
171
|
+
* @stability unstable
|
|
172
|
+
* @category built-in errors
|
|
173
|
+
* @since 0.3.0
|
|
174
|
+
*/
|
|
175
|
+
export class FailedPrecondition extends Schema.TaggedError<
|
|
176
|
+
FailedPrecondition,
|
|
177
|
+
{ readonly brand: unique symbol }
|
|
178
|
+
>()(
|
|
179
|
+
"FailedPrecondition",
|
|
180
|
+
{ message: Schema.optional(Schema.String) },
|
|
181
|
+
{ callableErrorCode: "failed-precondition" },
|
|
182
|
+
) {}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A quota or rate limit ran out. Its code is `resource-exhausted`.
|
|
186
|
+
*
|
|
187
|
+
* @stability unstable
|
|
188
|
+
* @category built-in errors
|
|
189
|
+
* @since 0.3.0
|
|
190
|
+
*/
|
|
191
|
+
export class ResourceExhausted extends Schema.TaggedError<
|
|
192
|
+
ResourceExhausted,
|
|
193
|
+
{ readonly brand: unique symbol }
|
|
194
|
+
>()(
|
|
195
|
+
"ResourceExhausted",
|
|
196
|
+
{ message: Schema.optional(Schema.String) },
|
|
197
|
+
{ callableErrorCode: "resource-exhausted" },
|
|
198
|
+
) {}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The service can't answer right now, and the call may succeed if retried.
|
|
202
|
+
* Its code is `unavailable`.
|
|
203
|
+
*
|
|
204
|
+
* @stability unstable
|
|
205
|
+
* @category built-in errors
|
|
206
|
+
* @since 0.3.0
|
|
207
|
+
*/
|
|
208
|
+
export class Unavailable extends Schema.TaggedError<
|
|
209
|
+
Unavailable,
|
|
210
|
+
{ readonly brand: unique symbol }
|
|
211
|
+
>()(
|
|
212
|
+
"Unavailable",
|
|
213
|
+
{ message: Schema.optional(Schema.String) },
|
|
214
|
+
{ callableErrorCode: "unavailable" },
|
|
215
|
+
) {}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* No response arrived: the request never reached the server, the connection
|
|
219
|
+
* broke before it answered, or the client gave up waiting.
|
|
220
|
+
*
|
|
221
|
+
* **Details**
|
|
222
|
+
*
|
|
223
|
+
* The client SDK appends the HTTP status of an answer to its error's message
|
|
224
|
+
* as ` [<status>]`, and reports a network failure as status 0. The errors the
|
|
225
|
+
* SDK makes itself carry no status: its timeout (`deadline-exceeded`), a
|
|
226
|
+
* cancelled call, and a deleted Functions instance. `CallableClient` treats
|
|
227
|
+
* an error without a status, or with status 0, as a `TransportError`, except
|
|
228
|
+
* for an answer the SDK can't read, which is a {@link DecodeError}.
|
|
229
|
+
*
|
|
230
|
+
* @see {@link ResponseError} for an error the server answered with
|
|
231
|
+
*
|
|
232
|
+
* @stability unstable
|
|
233
|
+
* @category errors
|
|
234
|
+
* @since 0.3.0
|
|
235
|
+
*/
|
|
236
|
+
export class TransportError extends Schema.TaggedError<
|
|
237
|
+
TransportError,
|
|
238
|
+
{ readonly brand: unique symbol }
|
|
239
|
+
>()("TransportError", {
|
|
240
|
+
/** What the SDK threw. */
|
|
241
|
+
cause: Schema.Defect(),
|
|
242
|
+
}) {}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The server answered with an error the contract doesn't declare.
|
|
246
|
+
*
|
|
247
|
+
* **Details**
|
|
248
|
+
*
|
|
249
|
+
* `code` is Firebase's error code without its `functions/` prefix, such as
|
|
250
|
+
* `not-found`. `not-found` usually means the client calls the wrong region or
|
|
251
|
+
* a name the server doesn't export.
|
|
252
|
+
*
|
|
253
|
+
* Only an error the server answered with, with a non-zero HTTP status, is a
|
|
254
|
+
* `ResponseError`. A `deadline-exceeded` the server sends is one; the client
|
|
255
|
+
* SDK's own timeout, which has the same code, is a {@link TransportError}.
|
|
256
|
+
*
|
|
257
|
+
* @see {@link TransportError} for a call no answer arrived for
|
|
258
|
+
*
|
|
259
|
+
* @stability unstable
|
|
260
|
+
* @category errors
|
|
261
|
+
* @since 0.3.0
|
|
262
|
+
*/
|
|
263
|
+
export class ResponseError extends Schema.TaggedError<
|
|
264
|
+
ResponseError,
|
|
265
|
+
{ readonly brand: unique symbol }
|
|
266
|
+
>()("ResponseError", {
|
|
267
|
+
/** Firebase's error code, such as `internal`. */
|
|
268
|
+
code: ErrorCode,
|
|
269
|
+
/** The error's message, without the SDK's ` [status]` suffix. */
|
|
270
|
+
message: Schema.String,
|
|
271
|
+
/** The error's `details`, as the server sent them. */
|
|
272
|
+
details: Schema.Unknown,
|
|
273
|
+
}) {}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The Payload didn't encode through its `Schema.toCodecJson`, so nothing was
|
|
277
|
+
* sent.
|
|
278
|
+
*
|
|
279
|
+
* @stability unstable
|
|
280
|
+
* @category errors
|
|
281
|
+
* @since 0.3.0
|
|
282
|
+
*/
|
|
283
|
+
export class EncodeError extends Schema.TaggedError<
|
|
284
|
+
EncodeError,
|
|
285
|
+
{ readonly brand: unique symbol }
|
|
286
|
+
>()("EncodeError", {
|
|
287
|
+
/** The `Schema.SchemaError` of the encode. */
|
|
288
|
+
cause: Schema.Defect(),
|
|
289
|
+
}) {}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The server's answer didn't decode against the contract, as after the server
|
|
293
|
+
* and the client drift apart.
|
|
294
|
+
*
|
|
295
|
+
* @stability unstable
|
|
296
|
+
* @category errors
|
|
297
|
+
* @since 0.3.0
|
|
298
|
+
*/
|
|
299
|
+
export class DecodeError extends Schema.TaggedError<
|
|
300
|
+
DecodeError,
|
|
301
|
+
{ readonly brand: unique symbol }
|
|
302
|
+
>()("DecodeError", {
|
|
303
|
+
/** The `Schema.SchemaError` of the decode. */
|
|
304
|
+
cause: Schema.Defect(),
|
|
305
|
+
}) {}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* What went wrong with a call, as one of the reasons above.
|
|
309
|
+
*
|
|
310
|
+
* @stability unstable
|
|
311
|
+
* @category models
|
|
312
|
+
* @since 0.3.0
|
|
313
|
+
*/
|
|
314
|
+
export type Reason = TransportError | ResponseError | EncodeError | DecodeError;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* A call to a Callable failed in a way the contract doesn't declare.
|
|
318
|
+
*
|
|
319
|
+
* **When to use**
|
|
320
|
+
*
|
|
321
|
+
* Use `Effect.catchReason` on its `reason` to recover from one kind of
|
|
322
|
+
* failure, such as a missing response.
|
|
323
|
+
*
|
|
324
|
+
* @example Recovering from a missing response
|
|
325
|
+
* ```ts
|
|
326
|
+
* import { Effect } from "effect";
|
|
327
|
+
* import type { CallableError } from "@visiolab/effect-firebase/functions";
|
|
328
|
+
*
|
|
329
|
+
* declare const call: Effect.Effect<string, CallableError.CallableError>;
|
|
330
|
+
*
|
|
331
|
+
* const program = call.pipe(
|
|
332
|
+
* Effect.catchReason("CallableError", "TransportError", () => Effect.succeed("offline")),
|
|
333
|
+
* );
|
|
334
|
+
* ```
|
|
335
|
+
*
|
|
336
|
+
* @stability unstable
|
|
337
|
+
* @category errors
|
|
338
|
+
* @since 0.3.0
|
|
339
|
+
*/
|
|
340
|
+
export class CallableError extends Schema.TaggedError<
|
|
341
|
+
CallableError,
|
|
342
|
+
{ readonly brand: unique symbol }
|
|
343
|
+
>()("CallableError", {
|
|
344
|
+
/** The deployed name of the Callable Function that was called. */
|
|
345
|
+
callable: Schema.String,
|
|
346
|
+
/** What went wrong. */
|
|
347
|
+
reason: Schema.Union([TransportError, ResponseError, EncodeError, DecodeError]),
|
|
348
|
+
}) {}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callable Middleware: what runs around every call to the Callables it is
|
|
3
|
+
* attached to, before the Payload is decoded.
|
|
4
|
+
*
|
|
5
|
+
* A middleware is declared once, with what it provides to the handler, what it
|
|
6
|
+
* requires from middleware outside it and the errors it can fail with. It is
|
|
7
|
+
* attached to a Callable with `.middleware(M)`, and implemented as a Layer of a
|
|
8
|
+
* function that wraps the call. The implementation is provided where the
|
|
9
|
+
* chain runs: to `Functions` from `@visiolab/effect-firebase-functions`,
|
|
10
|
+
* or to `Callable.handler`.
|
|
11
|
+
*
|
|
12
|
+
* @example Declaring, attaching and implementing a middleware
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { Context, Effect, Layer, Schema } from "effect";
|
|
15
|
+
* import { Callable, CallableError, CallableMiddleware, CallableRequest } from "@visiolab/effect-firebase/functions";
|
|
16
|
+
*
|
|
17
|
+
* class Tenant extends Context.Service<Tenant, { readonly id: string }>()("Tenant") {}
|
|
18
|
+
*
|
|
19
|
+
* class WithTenant extends CallableMiddleware.Service<WithTenant, { provides: Tenant }>()(
|
|
20
|
+
* "WithTenant",
|
|
21
|
+
* { error: CallableError.PermissionDenied },
|
|
22
|
+
* ) {}
|
|
23
|
+
*
|
|
24
|
+
* const ListUsers = Callable.make("listUsers", { success: Schema.Array(Schema.String) }).middleware(
|
|
25
|
+
* WithTenant,
|
|
26
|
+
* );
|
|
27
|
+
*
|
|
28
|
+
* const WithTenantLive = Layer.succeed(WithTenant, (effect) =>
|
|
29
|
+
* Effect.gen(function* () {
|
|
30
|
+
* const { auth } = yield* CallableRequest.CallableRequest;
|
|
31
|
+
* const id = auth?.token["tenant"];
|
|
32
|
+
* if (typeof id !== "string") return yield* new CallableError.PermissionDenied();
|
|
33
|
+
* return yield* Effect.provideService(effect, Tenant, { id });
|
|
34
|
+
* }),
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @stability unstable
|
|
39
|
+
* @since 0.3.0
|
|
40
|
+
*/
|
|
41
|
+
import type { Context, Effect, Schema } from "effect";
|
|
42
|
+
import type { unhandled } from "effect/Types";
|
|
43
|
+
import type * as Callable from "./Callable.ts";
|
|
44
|
+
// Only the documentation links to it.
|
|
45
|
+
// oxlint-disable-next-line no-unused-vars
|
|
46
|
+
import type * as CallableRequest from "./CallableRequest.ts";
|
|
47
|
+
import * as internal from "./internal/callableMiddleware.ts";
|
|
48
|
+
|
|
49
|
+
const TypeId = internal.TypeId;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The errors a middleware can fail with: one schema or an array.
|
|
53
|
+
*
|
|
54
|
+
* @stability unstable
|
|
55
|
+
* @category models
|
|
56
|
+
* @since 0.3.0
|
|
57
|
+
*/
|
|
58
|
+
export type ErrorConstraint = Schema.Top | ReadonlyArray<Schema.Top>;
|
|
59
|
+
|
|
60
|
+
type ErrorSchemaFromConstraint<E> =
|
|
61
|
+
E extends ReadonlyArray<Schema.Top> ? E[number] : E extends Schema.Top ? E : never;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* What a middleware's implementation is told about the call it wraps.
|
|
65
|
+
*
|
|
66
|
+
* @stability unstable
|
|
67
|
+
* @category models
|
|
68
|
+
* @since 0.3.0
|
|
69
|
+
*/
|
|
70
|
+
export interface Options {
|
|
71
|
+
/** The Callable being called. */
|
|
72
|
+
readonly callable: Callable.Any;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The implementation of a middleware: a function that wraps the rest of the
|
|
77
|
+
* call.
|
|
78
|
+
*
|
|
79
|
+
* **Details**
|
|
80
|
+
*
|
|
81
|
+
* `effect` is the rest of the call: the middleware inside this one, the
|
|
82
|
+
* Payload's decoding and the handler. It needs what this middleware
|
|
83
|
+
* `Provides`. The implementation may run it, fail instead, or catch what it
|
|
84
|
+
* fails with. It may require what middleware outside it provides, and always
|
|
85
|
+
* the {@link CallableRequest.CallableRequest}.
|
|
86
|
+
*
|
|
87
|
+
* A Payload that doesn't decode fails `effect` too. An implementation that
|
|
88
|
+
* catches every failure catches that one as well, and the caller then no
|
|
89
|
+
* longer gets `invalid-argument`. Catch only the errors you mean to.
|
|
90
|
+
*
|
|
91
|
+
* @stability unstable
|
|
92
|
+
* @category models
|
|
93
|
+
* @since 0.3.0
|
|
94
|
+
*/
|
|
95
|
+
export type CallableMiddleware<Provides, E extends ErrorConstraint, Requires> = (
|
|
96
|
+
effect: Effect.Effect<unknown, unhandled, Provides>,
|
|
97
|
+
options: Options,
|
|
98
|
+
) => Effect.Effect<
|
|
99
|
+
unknown,
|
|
100
|
+
unhandled | ErrorSchemaFromConstraint<E>["Type"],
|
|
101
|
+
Requires | CallableRequest.CallableRequest
|
|
102
|
+
>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Any middleware, as the type of its service class's instances: what it
|
|
106
|
+
* provides, requires and fails with.
|
|
107
|
+
*
|
|
108
|
+
* @stability unstable
|
|
109
|
+
* @category models
|
|
110
|
+
* @since 0.3.0
|
|
111
|
+
*/
|
|
112
|
+
export interface AnyId {
|
|
113
|
+
readonly [TypeId]: {
|
|
114
|
+
readonly provides: any;
|
|
115
|
+
readonly requires: any;
|
|
116
|
+
readonly error: ErrorConstraint;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Any middleware's service key, as `.middleware` takes it.
|
|
122
|
+
*
|
|
123
|
+
* @stability unstable
|
|
124
|
+
* @category models
|
|
125
|
+
* @since 0.3.0
|
|
126
|
+
*/
|
|
127
|
+
export interface AnyKey extends Context.Key<any, any> {
|
|
128
|
+
readonly [TypeId]: typeof TypeId;
|
|
129
|
+
/** The schemas of the errors the middleware can fail with. */
|
|
130
|
+
readonly error: ReadonlySet<Schema.Top>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The services a middleware provides to what it wraps.
|
|
135
|
+
*
|
|
136
|
+
* @stability unstable
|
|
137
|
+
* @category type-level
|
|
138
|
+
* @since 0.3.0
|
|
139
|
+
*/
|
|
140
|
+
export type Provides<A> = A extends { readonly [TypeId]: { readonly provides: infer P } }
|
|
141
|
+
? P
|
|
142
|
+
: never;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The services a middleware requires from the middleware outside it.
|
|
146
|
+
*
|
|
147
|
+
* @stability unstable
|
|
148
|
+
* @category type-level
|
|
149
|
+
* @since 0.3.0
|
|
150
|
+
*/
|
|
151
|
+
export type Requires<A> = A extends { readonly [TypeId]: { readonly requires: infer R } }
|
|
152
|
+
? R
|
|
153
|
+
: never;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The services still required after wrapping `R` in the middleware `A`: `A`'s
|
|
157
|
+
* `provides` are removed and its `requires` added.
|
|
158
|
+
*
|
|
159
|
+
* @stability unstable
|
|
160
|
+
* @category type-level
|
|
161
|
+
* @since 0.3.0
|
|
162
|
+
*/
|
|
163
|
+
export type ApplyServices<A extends AnyId, R> = Exclude<R, Provides<A>> | Requires<A>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The union of the error schemas of a middleware.
|
|
167
|
+
*
|
|
168
|
+
* @stability unstable
|
|
169
|
+
* @category type-level
|
|
170
|
+
* @since 0.3.0
|
|
171
|
+
*/
|
|
172
|
+
export type ErrorSchema<A> = A extends { readonly [TypeId]: { readonly error: infer E } }
|
|
173
|
+
? ErrorSchemaFromConstraint<E>
|
|
174
|
+
: never;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The errors a middleware can fail with.
|
|
178
|
+
*
|
|
179
|
+
* @stability unstable
|
|
180
|
+
* @category type-level
|
|
181
|
+
* @since 0.3.0
|
|
182
|
+
*/
|
|
183
|
+
export type Error<A> = ErrorSchema<A>["Type"];
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The class {@link Service} returns: a service key whose instances carry the
|
|
187
|
+
* middleware's types.
|
|
188
|
+
*
|
|
189
|
+
* @stability unstable
|
|
190
|
+
* @category models
|
|
191
|
+
* @since 0.3.0
|
|
192
|
+
*/
|
|
193
|
+
export type ServiceClass<
|
|
194
|
+
Self,
|
|
195
|
+
Id extends string,
|
|
196
|
+
Config extends { requires: any; provides: any; error: ErrorConstraint },
|
|
197
|
+
> = Context.Service<
|
|
198
|
+
Self,
|
|
199
|
+
CallableMiddleware<Config["provides"], Config["error"], Config["requires"]>
|
|
200
|
+
> & {
|
|
201
|
+
new (_: never): Context.ServiceClass.Shape<
|
|
202
|
+
Id,
|
|
203
|
+
CallableMiddleware<Config["provides"], Config["error"], Config["requires"]>
|
|
204
|
+
> & {
|
|
205
|
+
readonly [TypeId]: {
|
|
206
|
+
readonly error: Config["error"];
|
|
207
|
+
readonly requires: Config["requires"];
|
|
208
|
+
readonly provides: Config["provides"];
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
readonly [TypeId]: typeof TypeId;
|
|
212
|
+
/** The schemas of the errors the middleware can fail with. */
|
|
213
|
+
readonly error: ReadonlySet<Schema.Top>;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Declares a Callable Middleware as a class.
|
|
218
|
+
*
|
|
219
|
+
* **When to use**
|
|
220
|
+
*
|
|
221
|
+
* Use to state a requirement of a Callable, such as "the caller is a member of
|
|
222
|
+
* a tenant", as part of its contract. For "the caller is signed in", use the
|
|
223
|
+
* built-in `Caller.Authenticated`.
|
|
224
|
+
*
|
|
225
|
+
* **Details**
|
|
226
|
+
*
|
|
227
|
+
* `provides` is what the middleware gives the handler: the handler's Layer no
|
|
228
|
+
* longer needs it. `requires` is what it needs from middleware outside it; a
|
|
229
|
+
* `requires` that nothing outside provides is left for the Layers where the
|
|
230
|
+
* chain runs. The {@link CallableRequest.CallableRequest} is always there and
|
|
231
|
+
* is never listed.
|
|
232
|
+
*
|
|
233
|
+
* `error` is one schema or an array. A middleware's errors join the error
|
|
234
|
+
* union of every Callable it is attached to. They are sent like the
|
|
235
|
+
* Callable's declared errors, and the client decodes them back.
|
|
236
|
+
*
|
|
237
|
+
* The middleware runs before the Payload is decoded, so a call it rejects
|
|
238
|
+
* learns nothing from the Payload's decoding. The decoding runs inside the
|
|
239
|
+
* chain, so an implementation that catches every failure also catches a
|
|
240
|
+
* Payload that doesn't decode.
|
|
241
|
+
*
|
|
242
|
+
* @example Declaring a middleware
|
|
243
|
+
* ```ts
|
|
244
|
+
* import { Context } from "effect";
|
|
245
|
+
* import { CallableError, CallableMiddleware } from "@visiolab/effect-firebase/functions";
|
|
246
|
+
*
|
|
247
|
+
* class Tenant extends Context.Service<Tenant, { readonly id: string }>()("Tenant") {}
|
|
248
|
+
*
|
|
249
|
+
* class WithTenant extends CallableMiddleware.Service<WithTenant, { provides: Tenant }>()(
|
|
250
|
+
* "WithTenant",
|
|
251
|
+
* { error: CallableError.PermissionDenied },
|
|
252
|
+
* ) {}
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* @see {@link Callable.Callable.middleware} for attaching it to a Callable
|
|
256
|
+
* @see {@link Caller.Authenticated} for the built-in middleware
|
|
257
|
+
*
|
|
258
|
+
* @stability unstable
|
|
259
|
+
* @category constructors
|
|
260
|
+
* @since 0.3.0
|
|
261
|
+
*/
|
|
262
|
+
export const Service =
|
|
263
|
+
<
|
|
264
|
+
Self,
|
|
265
|
+
Config extends { requires?: any; provides?: any } = { requires: never; provides: never },
|
|
266
|
+
>() =>
|
|
267
|
+
<const Id extends string, const Error extends ErrorConstraint = never>(
|
|
268
|
+
id: Id,
|
|
269
|
+
options?: { readonly error?: Error | undefined },
|
|
270
|
+
): ServiceClass<
|
|
271
|
+
Self,
|
|
272
|
+
Id,
|
|
273
|
+
{
|
|
274
|
+
requires: "requires" extends keyof Config ? Config["requires"] : never;
|
|
275
|
+
provides: "provides" extends keyof Config ? Config["provides"] : never;
|
|
276
|
+
error: Error;
|
|
277
|
+
}
|
|
278
|
+
> =>
|
|
279
|
+
internal.Service(id, options);
|