@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.
Files changed (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -2
  3. package/dist/_virtual/_rolldown/runtime.mjs +12 -0
  4. package/dist/app/App.d.mts +27 -0
  5. package/dist/app/App.mjs +27 -0
  6. package/dist/app/index.d.mts +2 -0
  7. package/dist/app/index.mjs +1 -0
  8. package/dist/firestore/Collection.d.mts +423 -0
  9. package/dist/firestore/Collection.mjs +133 -0
  10. package/dist/firestore/CollectionGroup.d.mts +164 -0
  11. package/dist/firestore/CollectionGroup.mjs +84 -0
  12. package/dist/firestore/DocumentPath.d.mts +95 -0
  13. package/dist/firestore/DocumentPath.mjs +71 -0
  14. package/dist/firestore/DocumentSnapshot.d.mts +239 -0
  15. package/dist/firestore/DocumentSnapshot.mjs +134 -0
  16. package/dist/firestore/FieldValue.d.mts +332 -0
  17. package/dist/firestore/FieldValue.mjs +256 -0
  18. package/dist/firestore/Firestore.d.mts +1165 -0
  19. package/dist/firestore/Firestore.mjs +274 -0
  20. package/dist/firestore/FirestoreModel.d.mts +1232 -0
  21. package/dist/firestore/FirestoreModel.mjs +586 -0
  22. package/dist/firestore/FirestoreSchema.d.mts +567 -0
  23. package/dist/firestore/FirestoreSchema.mjs +419 -0
  24. package/dist/firestore/FirestoreValue.d.mts +145 -0
  25. package/dist/firestore/FirestoreValue.mjs +87 -0
  26. package/dist/firestore/GeoPoint.d.mts +90 -0
  27. package/dist/firestore/GeoPoint.mjs +75 -0
  28. package/dist/firestore/Patch.d.mts +73 -0
  29. package/dist/firestore/Patch.mjs +57 -0
  30. package/dist/firestore/QuerySnapshot.d.mts +107 -0
  31. package/dist/firestore/QuerySnapshot.mjs +43 -0
  32. package/dist/firestore/Timestamp.d.mts +273 -0
  33. package/dist/firestore/Timestamp.mjs +255 -0
  34. package/dist/firestore/index.d.mts +14 -0
  35. package/dist/firestore/index.mjs +14 -0
  36. package/dist/firestore/internal/collection.mjs +156 -0
  37. package/dist/firestore/internal/collectionGroup.mjs +64 -0
  38. package/dist/firestore/internal/documentPath.d.mts +4 -0
  39. package/dist/firestore/internal/documentPath.mjs +42 -0
  40. package/dist/firestore/internal/documentSnapshot.mjs +63 -0
  41. package/dist/firestore/internal/fieldValue.d.mts +11 -0
  42. package/dist/firestore/internal/fieldValue.mjs +88 -0
  43. package/dist/firestore/internal/firestore.mjs +145 -0
  44. package/dist/firestore/internal/firestoreError.d.mts +29 -0
  45. package/dist/firestore/internal/firestoreError.mjs +30 -0
  46. package/dist/firestore/internal/firestoreModel.mjs +719 -0
  47. package/dist/firestore/internal/firestoreSchema.mjs +123 -0
  48. package/dist/firestore/internal/firestoreValue.mjs +108 -0
  49. package/dist/firestore/internal/firestoreValueWalk.d.mts +58 -0
  50. package/dist/firestore/internal/firestoreValueWalk.mjs +223 -0
  51. package/dist/firestore/internal/geoPoint.d.mts +4 -0
  52. package/dist/firestore/internal/geoPoint.mjs +39 -0
  53. package/dist/firestore/internal/patch.mjs +19 -0
  54. package/dist/firestore/internal/querySnapshot.mjs +24 -0
  55. package/dist/firestore/internal/schemaAST.mjs +35 -0
  56. package/dist/firestore/internal/snapshotExtra.mjs +35 -0
  57. package/dist/firestore/internal/streamFromListener.mjs +12 -0
  58. package/dist/firestore/internal/timestamp.d.mts +4 -0
  59. package/dist/firestore/internal/timestamp.mjs +66 -0
  60. package/dist/firestore/internal/timestampSchema.mjs +31 -0
  61. package/dist/firestore/internal/transactionFromCallback.mjs +66 -0
  62. package/dist/functions/Callable.d.mts +654 -0
  63. package/dist/functions/Callable.mjs +450 -0
  64. package/dist/functions/CallableError.d.mts +297 -0
  65. package/dist/functions/CallableError.mjs +256 -0
  66. package/dist/functions/CallableMiddleware.d.mts +213 -0
  67. package/dist/functions/CallableMiddleware.mjs +52 -0
  68. package/dist/functions/CallableRequest.d.mts +166 -0
  69. package/dist/functions/CallableRequest.mjs +18 -0
  70. package/dist/functions/Caller.d.mts +103 -0
  71. package/dist/functions/Caller.mjs +115 -0
  72. package/dist/functions/DocumentTrigger.d.mts +795 -0
  73. package/dist/functions/DocumentTrigger.mjs +566 -0
  74. package/dist/functions/FirebaseFunction.d.mts +79 -0
  75. package/dist/functions/FirebaseFunction.mjs +4 -0
  76. package/dist/functions/FunctionGroup.d.mts +175 -0
  77. package/dist/functions/FunctionGroup.mjs +64 -0
  78. package/dist/functions/index.d.mts +9 -0
  79. package/dist/functions/index.mjs +9 -0
  80. package/dist/functions/internal/callable.mjs +167 -0
  81. package/dist/functions/internal/callableMiddleware.d.mts +19 -0
  82. package/dist/functions/internal/callableMiddleware.mjs +15 -0
  83. package/dist/functions/internal/callableRequest.mjs +6 -0
  84. package/dist/functions/internal/documentTrigger.d.mts +10 -0
  85. package/dist/functions/internal/documentTrigger.mjs +73 -0
  86. package/dist/functions/internal/firebaseFunction.d.mts +5 -0
  87. package/dist/functions/internal/firebaseFunction.mjs +24 -0
  88. package/dist/functions/internal/functionGroup.mjs +36 -0
  89. package/package.json +72 -3
  90. package/src/app/App.ts +24 -0
  91. package/src/app/index.ts +7 -0
  92. package/src/firestore/Collection.ts +520 -0
  93. package/src/firestore/CollectionGroup.ts +245 -0
  94. package/src/firestore/DocumentPath.ts +114 -0
  95. package/src/firestore/DocumentSnapshot.ts +308 -0
  96. package/src/firestore/FieldValue.ts +375 -0
  97. package/src/firestore/Firestore.ts +1432 -0
  98. package/src/firestore/FirestoreModel.ts +1884 -0
  99. package/src/firestore/FirestoreSchema.ts +643 -0
  100. package/src/firestore/FirestoreValue.ts +207 -0
  101. package/src/firestore/GeoPoint.ts +105 -0
  102. package/src/firestore/Patch.ts +117 -0
  103. package/src/firestore/QuerySnapshot.ts +150 -0
  104. package/src/firestore/Timestamp.ts +302 -0
  105. package/src/firestore/index.ts +67 -0
  106. package/src/firestore/internal/collection.ts +246 -0
  107. package/src/firestore/internal/collectionGroup.ts +81 -0
  108. package/src/firestore/internal/documentPath.ts +53 -0
  109. package/src/firestore/internal/documentSnapshot.ts +117 -0
  110. package/src/firestore/internal/fieldValue.ts +119 -0
  111. package/src/firestore/internal/firestore.ts +322 -0
  112. package/src/firestore/internal/firestoreError.ts +36 -0
  113. package/src/firestore/internal/firestoreModel.ts +1242 -0
  114. package/src/firestore/internal/firestoreSchema.ts +218 -0
  115. package/src/firestore/internal/firestoreValue.ts +202 -0
  116. package/src/firestore/internal/firestoreValueWalk.ts +349 -0
  117. package/src/firestore/internal/geoPoint.ts +59 -0
  118. package/src/firestore/internal/patch.ts +21 -0
  119. package/src/firestore/internal/querySnapshot.ts +39 -0
  120. package/src/firestore/internal/schemaAST.ts +83 -0
  121. package/src/firestore/internal/snapshotExtra.ts +47 -0
  122. package/src/firestore/internal/streamFromListener.ts +27 -0
  123. package/src/firestore/internal/timestamp.ts +119 -0
  124. package/src/firestore/internal/timestampSchema.ts +42 -0
  125. package/src/firestore/internal/transactionFromCallback.ts +106 -0
  126. package/src/functions/Callable.ts +855 -0
  127. package/src/functions/CallableError.ts +348 -0
  128. package/src/functions/CallableMiddleware.ts +279 -0
  129. package/src/functions/CallableRequest.ts +207 -0
  130. package/src/functions/Caller.ts +141 -0
  131. package/src/functions/DocumentTrigger.ts +927 -0
  132. package/src/functions/FirebaseFunction.ts +96 -0
  133. package/src/functions/FunctionGroup.ts +225 -0
  134. package/src/functions/index.ts +62 -0
  135. package/src/functions/internal/callable.ts +272 -0
  136. package/src/functions/internal/callableMiddleware.ts +54 -0
  137. package/src/functions/internal/callableRequest.ts +8 -0
  138. package/src/functions/internal/documentTrigger.ts +143 -0
  139. package/src/functions/internal/firebaseFunction.ts +58 -0
  140. package/src/functions/internal/functionGroup.ts +47 -0
@@ -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);