@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.
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 +66 -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,855 @@
1
+ /**
2
+ * Callables: server functions the client calls by name, declared once and
3
+ * shared by the server and the client.
4
+ *
5
+ * A {@link Callable} is a contract: its name, its Payload and what it succeeds
6
+ * with. The server implements it with {@link toLayer} and serves it with
7
+ * `Functions.make` from `@visiolab/effect-firebase-functions`. The client
8
+ * calls it with `CallableClient` from `@visiolab/effect-firebase-client`. This
9
+ * module imports neither SDK, so the contract can live in code shared with the
10
+ * browser.
11
+ *
12
+ * Values cross the wire as `Schema.toCodecJson` output, on both sides, so a
13
+ * `Date` or a `bigint` arrives as itself.
14
+ *
15
+ * @example Declaring a Callable and implementing it
16
+ * ```ts
17
+ * import { Effect, Schema } from "effect";
18
+ * import { Callable } from "@visiolab/effect-firebase/functions";
19
+ *
20
+ * const Greet = Callable.make("greet", {
21
+ * payload: { name: Schema.String },
22
+ * success: Schema.String,
23
+ * });
24
+ *
25
+ * const GreetLive = Callable.toLayer(Greet, ({ name }) => Effect.succeed(`Hello, ${name}!`));
26
+ * ```
27
+ *
28
+ * @stability unstable
29
+ * @since 0.3.0
30
+ */
31
+ import { type Context, type Effect, type Layer, type Pipeable, Schema } from "effect";
32
+ import type * as CallableError from "./CallableError.ts";
33
+ import type * as CallableMiddleware from "./CallableMiddleware.ts";
34
+ import type * as CallableRequest from "./CallableRequest.ts";
35
+ import type * as FirebaseFunction from "./FirebaseFunction.ts";
36
+ import type * as FunctionGroup from "./FunctionGroup.ts";
37
+ import * as internal from "./internal/callable.ts";
38
+ import type * as internalGroup from "./internal/functionGroup.ts";
39
+ import { isFunctionGroup } from "./internal/functionGroup.ts";
40
+ import type * as internalMiddleware from "./internal/callableMiddleware.ts";
41
+
42
+ const TypeId = internal.TypeId;
43
+
44
+ /**
45
+ * The group and the Callable that the arguments of an overload taking
46
+ * `(callable, ...)` or `(group, name, ...)` name.
47
+ */
48
+ const target = (
49
+ args: ReadonlyArray<unknown>,
50
+ ): readonly [internalGroup.Fields | undefined, internal.Fields] =>
51
+ isFunctionGroup(args[0])
52
+ ? [args[0], args[0].functions[args[1] as string] as internal.Fields]
53
+ : [undefined, args[0] as internal.Fields];
54
+
55
+ /**
56
+ * A Callable: a server function's name, its Payload and what it succeeds with.
57
+ *
58
+ * **Details**
59
+ *
60
+ * A Callable is the kind of {@link FirebaseFunction.FirebaseFunction} the
61
+ * client calls by name, and its `kind` is `"Callable"`.
62
+ *
63
+ * The name is the Callable's identity. A Callable in no group is deployed
64
+ * under its name, so it is a literal type that the server's exports and the
65
+ * client's functions are keyed on.
66
+ *
67
+ * @see {@link DocumentTrigger.DocumentTrigger} for a Function a change to a document runs
68
+ *
69
+ * @stability unstable
70
+ * @category models
71
+ * @since 0.3.0
72
+ */
73
+ export interface Callable<
74
+ out Name extends string,
75
+ out Payload extends Schema.Top = Schema.Void,
76
+ out Success extends Schema.Top = Schema.Void,
77
+ out Error extends Schema.Top = never,
78
+ out Middleware = never,
79
+ out MiddlewareServices = never,
80
+ >
81
+ extends FirebaseFunction.FirebaseFunction<"Callable", Name>, Pipeable.Pipeable {
82
+ readonly [TypeId]: typeof TypeId;
83
+ /** The Callable's name, which a Callable in no group is deployed under. */
84
+ readonly name: Name;
85
+ /** The schema of what the client sends. */
86
+ readonly payload: Payload;
87
+ /** The schema of what the handler succeeds with. */
88
+ readonly success: Success;
89
+ /** The schemas of the errors the handler can fail with. */
90
+ readonly error: ReadonlySet<Error>;
91
+ /** The Callable's own middleware, in the order it was attached. */
92
+ readonly middlewares: ReadonlySet<CallableMiddleware.AnyKey>;
93
+ /** The middleware attached to the Callable, as a union. Types only. */
94
+ readonly "~Middleware": Middleware;
95
+ /** What the middleware chain still requires. Types only. */
96
+ readonly "~MiddlewareServices": MiddlewareServices;
97
+
98
+ /**
99
+ * Attaches a Callable Middleware to the Callable.
100
+ *
101
+ * **Details**
102
+ *
103
+ * Returns a new Callable. The middleware runs around every call, before
104
+ * the Payload is decoded. A middleware attached later wraps the ones
105
+ * attached before it, so it can provide what they require. A Function
106
+ * Group carries no middleware, so a member of a group runs only its own.
107
+ *
108
+ * Its `provides` are removed from what the handler's Layer requires, and
109
+ * its errors join the Callable's error union. Its implementation is
110
+ * required where the chain runs: by `Functions` and by
111
+ * {@link handler}.
112
+ *
113
+ * The Payload is decoded inside the chain, so a middleware that catches
114
+ * every failure also catches a Payload that doesn't decode, and the caller
115
+ * then no longer gets `invalid-argument`.
116
+ *
117
+ * @see {@link CallableMiddleware.Service} for declaring a middleware
118
+ */
119
+ middleware<I extends CallableMiddleware.AnyId, S>(
120
+ middleware: Context.Key<I, S>,
121
+ ): Callable<
122
+ Name,
123
+ Payload,
124
+ Success,
125
+ Error,
126
+ Middleware | I,
127
+ CallableMiddleware.ApplyServices<I, MiddlewareServices>
128
+ >;
129
+ }
130
+
131
+ /**
132
+ * Any {@link Callable}, whatever its name, schemas and middleware.
133
+ *
134
+ * @stability unstable
135
+ * @category models
136
+ * @since 0.3.0
137
+ */
138
+ export interface Any extends FirebaseFunction.FirebaseFunction<"Callable", string> {
139
+ readonly [TypeId]: typeof TypeId;
140
+ readonly payload: Schema.Top;
141
+ readonly success: Schema.Top;
142
+ readonly error: ReadonlySet<Schema.Top>;
143
+ readonly middlewares: ReadonlySet<CallableMiddleware.AnyKey>;
144
+ }
145
+
146
+ /**
147
+ * The middleware attached to a {@link Callable}, as a union of the middleware
148
+ * service types.
149
+ *
150
+ * @stability unstable
151
+ * @category type-level
152
+ * @since 0.3.0
153
+ */
154
+ export type Middleware<C> = internalMiddleware.Attached<C>;
155
+
156
+ /**
157
+ * What the middleware chain of a {@link Callable} still requires: the
158
+ * `requires` of each middleware that no middleware outside it provides.
159
+ *
160
+ * @stability unstable
161
+ * @category type-level
162
+ * @since 0.3.0
163
+ */
164
+ export type MiddlewareServices<C> = Exclude<
165
+ internalMiddleware.AttachedServices<C>,
166
+ CallableRequest.CallableRequest
167
+ >;
168
+
169
+ /**
170
+ * What the middleware of a {@link Callable} provides to its handler.
171
+ *
172
+ * @stability unstable
173
+ * @category type-level
174
+ * @since 0.3.0
175
+ */
176
+ export type MiddlewareProvides<C> = CallableMiddleware.Provides<Middleware<C>>;
177
+
178
+ /**
179
+ * The Payload type of a {@link Callable}: what its handler receives.
180
+ *
181
+ * @stability unstable
182
+ * @category type-level
183
+ * @since 0.3.0
184
+ */
185
+ export type Payload<C extends Any> = C["payload"]["Type"];
186
+
187
+ /**
188
+ * The success type of a {@link Callable}: what its handler succeeds with.
189
+ *
190
+ * @stability unstable
191
+ * @category type-level
192
+ * @since 0.3.0
193
+ */
194
+ export type Success<C extends Any> = C["success"]["Type"];
195
+
196
+ /**
197
+ * The union of the error schemas of a {@link Callable}.
198
+ *
199
+ * @stability unstable
200
+ * @category type-level
201
+ * @since 0.3.0
202
+ */
203
+ export type ErrorSchema<C extends Any> =
204
+ | (C["error"] extends ReadonlySet<infer S extends Schema.Top> ? S : never)
205
+ | CallableMiddleware.ErrorSchema<Middleware<C>>;
206
+
207
+ /**
208
+ * The error type of a {@link Callable}: what its handler can fail with, and
209
+ * what its client decodes back. It includes the errors of its middleware.
210
+ *
211
+ * @stability unstable
212
+ * @category type-level
213
+ * @since 0.3.0
214
+ */
215
+ export type Error<C extends Any> = ErrorSchema<C>["Type"];
216
+
217
+ /**
218
+ * Options for {@link make}.
219
+ *
220
+ * @stability unstable
221
+ * @category models
222
+ * @since 0.3.0
223
+ */
224
+ export interface MakeOptions<
225
+ Payload extends Schema.Top | Schema.Struct.Fields,
226
+ Success extends Schema.Top,
227
+ Error extends Schema.Top | ReadonlyArray<Schema.Top> = never,
228
+ > {
229
+ /**
230
+ * What the client sends: a schema, or the fields of a struct. Leave it out
231
+ * for a Callable that takes nothing.
232
+ */
233
+ readonly payload?: Payload | undefined;
234
+ /**
235
+ * What the handler succeeds with. Leave it out for a Callable that answers
236
+ * with nothing.
237
+ */
238
+ readonly success?: Success | undefined;
239
+ /**
240
+ * The errors the handler can fail with: one schema or an array. Leave it
241
+ * out for a Callable that never fails with an error of its own.
242
+ */
243
+ readonly error?: Error | undefined;
244
+ }
245
+
246
+ /**
247
+ * Declares a Callable.
248
+ *
249
+ * **When to use**
250
+ *
251
+ * Use to declare a server function once, in code that the server and the
252
+ * client share.
253
+ *
254
+ * **Details**
255
+ *
256
+ * `payload` takes a schema or the fields of a struct, and defaults to
257
+ * `Schema.Void`. `success` defaults to `Schema.Void`. `error` takes one
258
+ * schema or an array; without it the Callable never fails with an error of
259
+ * its own, and its client fails only with `CallableError`.
260
+ *
261
+ * All of them are sent through `Schema.toCodecJson`, so they must round-trip
262
+ * through JSON. `Schema.Unknown` is the safe choice for "any JSON".
263
+ * `Schema.Any` skips every check and is an escape hatch. A schema that can't
264
+ * round-trip fails at runtime, not at compile time.
265
+ *
266
+ * An error is sent with the Firebase error code of its schema, `internal`
267
+ * unless {@link errorCode} or its `callableErrorCode` annotation sets one.
268
+ *
269
+ * @example Declaring a Callable with a Payload and a success value
270
+ * ```ts
271
+ * import { Schema } from "effect";
272
+ * import { Callable } from "@visiolab/effect-firebase/functions";
273
+ *
274
+ * const Greet = Callable.make("greet", {
275
+ * payload: { name: Schema.String, at: Schema.Date },
276
+ * success: Schema.Struct({ message: Schema.String }),
277
+ * });
278
+ * ```
279
+ *
280
+ * @example Declaring the errors a Callable can fail with
281
+ * ```ts
282
+ * import { Schema } from "effect";
283
+ * import { Callable, CallableError } from "@visiolab/effect-firebase/functions";
284
+ *
285
+ * class NameTaken extends Schema.TaggedError<NameTaken>()(
286
+ * "NameTaken",
287
+ * { name: Schema.String },
288
+ * { callableErrorCode: "already-exists" },
289
+ * ) {}
290
+ *
291
+ * const Register = Callable.make("register", {
292
+ * payload: { name: Schema.String },
293
+ * error: [NameTaken, CallableError.PermissionDenied],
294
+ * });
295
+ * ```
296
+ *
297
+ * @see {@link toLayer} for implementing the Callable on the server
298
+ * @see {@link errorCode} for giving an error schema its code
299
+ * @see {@link FunctionGroup.make} for collecting Functions under one prefix
300
+ * @see {@link DocumentTrigger.created} for declaring a Function a change to a document runs
301
+ *
302
+ * @stability unstable
303
+ * @category constructors
304
+ * @since 0.3.0
305
+ */
306
+ export const make = <
307
+ const Name extends string,
308
+ Payload extends Schema.Top | Schema.Struct.Fields = Schema.Void,
309
+ Success extends Schema.Top = Schema.Void,
310
+ const Error extends Schema.Top | ReadonlyArray<Schema.Top> = never,
311
+ >(
312
+ name: Name,
313
+ options?: MakeOptions<Payload, Success, Error>,
314
+ ): Callable<
315
+ Name,
316
+ Payload extends Schema.Top
317
+ ? Payload
318
+ : Payload extends Schema.Struct.Fields
319
+ ? Schema.Struct<Payload>
320
+ : never,
321
+ Success,
322
+ Error extends ReadonlyArray<Schema.Top> ? Error[number] : Error
323
+ > => {
324
+ const payload = options?.payload;
325
+ const error: Schema.Top | ReadonlyArray<Schema.Top> | undefined = options?.error;
326
+ return internal.make({
327
+ kind: internal.kind,
328
+ error: new Set(error === undefined ? [] : Schema.isSchema(error) ? [error] : error),
329
+ middlewares: new Set(),
330
+ name,
331
+ payload:
332
+ payload === undefined
333
+ ? Schema.Void
334
+ : Schema.isSchema(payload)
335
+ ? payload
336
+ : Schema.Struct(payload as Schema.Struct.Fields),
337
+ success: options?.success ?? Schema.Void,
338
+ }) as never;
339
+ };
340
+
341
+ /**
342
+ * Marks the {@link Handler} of a Callable in no group. The same marker as
343
+ * {@link FirebaseFunction.NoGroup}, which every kind of Function shares.
344
+ *
345
+ * @stability unstable
346
+ * @category models
347
+ * @since 0.3.0
348
+ */
349
+ export type NoGroup = FirebaseFunction.NoGroup;
350
+
351
+ /**
352
+ * The service a {@link toLayer} Layer provides: the Callable Handler of the
353
+ * Callable `Name`, as served in `Group`.
354
+ *
355
+ * **Details**
356
+ *
357
+ * A handler is keyed by its group and its name, so the handler of `greet` in
358
+ * no group is `Handler<NoGroup, "greet">`. A Layer that serves a Callable must
359
+ * provide the handler under exactly that key. It is the
360
+ * {@link FirebaseFunction.Handler} every kind of Function is served by.
361
+ *
362
+ * @stability unstable
363
+ * @category models
364
+ * @since 0.3.0
365
+ */
366
+ export type Handler<Group, Name extends string> = FirebaseFunction.Handler<Group, Name>;
367
+
368
+ /**
369
+ * The {@link Handler} of a Callable in no group.
370
+ *
371
+ * @stability unstable
372
+ * @category type-level
373
+ * @since 0.3.0
374
+ */
375
+ export type HandlerFor<C extends Any> = FirebaseFunction.HandlerFor<C>;
376
+
377
+ /**
378
+ * What implements a Callable: a function from the decoded Payload to an
379
+ * Effect of the success value, failing only with the Callable's declared
380
+ * errors.
381
+ *
382
+ * @stability unstable
383
+ * @category models
384
+ * @since 0.3.0
385
+ */
386
+ export type HandlerFunction<C extends Any, R> = (
387
+ payload: Payload<C>,
388
+ ) => Effect.Effect<Success<C>, Error<C>, R>;
389
+
390
+ /**
391
+ * What a handler that requires `R` leaves for its {@link toLayer} Layer to
392
+ * require: `R` without what the Callable's middleware provides and without
393
+ * the {@link CallableRequest.CallableRequest}, which every call provides.
394
+ *
395
+ * @stability unstable
396
+ * @category type-level
397
+ * @since 0.3.0
398
+ */
399
+ export type HandlerServices<C, R> = Exclude<
400
+ R,
401
+ MiddlewareProvides<C> | CallableRequest.CallableRequest
402
+ >;
403
+
404
+ /**
405
+ * What {@link handler} yields: the Callable's handler behind its middleware
406
+ * chain, run in process.
407
+ *
408
+ * @stability unstable
409
+ * @category models
410
+ * @since 0.3.0
411
+ */
412
+ export type HandlerCall<C extends Any> = (
413
+ payload: Payload<C>,
414
+ request: CallableRequest.Input,
415
+ ) => Effect.Effect<Success<C>, Error<C>>;
416
+
417
+ /**
418
+ * What running the middleware chain of a {@link Callable} requires: its
419
+ * handler, each middleware implementation, and what the chain still
420
+ * requires.
421
+ *
422
+ * @stability unstable
423
+ * @category type-level
424
+ * @since 0.3.0
425
+ */
426
+ export type ChainServices<C extends Any, H> = H | Middleware<C> | MiddlewareServices<C>;
427
+
428
+ /**
429
+ * Implements a Callable as a Layer.
430
+ *
431
+ * **When to use**
432
+ *
433
+ * Use to write the server side of a Callable. The Layer is what
434
+ * `Functions.make` or `Functions.group` from
435
+ * `@visiolab/effect-firebase-functions` serves.
436
+ *
437
+ * **Details**
438
+ *
439
+ * Pass the Callable for a Callable in no group. Pass its group and its name
440
+ * for a Callable member of a {@link FunctionGroup.FunctionGroup}: the Layer then
441
+ * provides `Handler<"users", "create">`, which serves the member in that group
442
+ * only. A Layer from the Callable on its own doesn't serve it in a group, and
443
+ * a Callable in two groups needs a Layer for each.
444
+ *
445
+ * Takes the handler itself, or an Effect that builds it. The Effect runs once,
446
+ * when the Layer is built, so do per-instance setup there.
447
+ *
448
+ * The Layer requires what the handler needs on each call and what building it
449
+ * needs. Both are provided where the Layer is built: services built with the
450
+ * Layer are shared by every call. What the Callable's middleware provides,
451
+ * such as the `Caller`, and the {@link CallableRequest.CallableRequest} are
452
+ * provided per call instead, so the Layer doesn't require them. Nor does it
453
+ * require the middleware's implementations: those are required where the
454
+ * chain runs.
455
+ *
456
+ * @example Implementing a Callable
457
+ * ```ts
458
+ * import { Effect, Schema } from "effect";
459
+ * import { Callable } from "@visiolab/effect-firebase/functions";
460
+ *
461
+ * const Greet = Callable.make("greet", {
462
+ * payload: { name: Schema.String },
463
+ * success: Schema.String,
464
+ * });
465
+ *
466
+ * const GreetLive = Callable.toLayer(Greet, ({ name }) => Effect.succeed(`Hello, ${name}!`));
467
+ * ```
468
+ *
469
+ * @example Building the handler once per instance
470
+ * ```ts
471
+ * import { Effect, Schema } from "effect";
472
+ * import { Callable } from "@visiolab/effect-firebase/functions";
473
+ *
474
+ * const Greet = Callable.make("greet", {
475
+ * payload: { name: Schema.String },
476
+ * success: Schema.String,
477
+ * });
478
+ *
479
+ * const GreetLive = Callable.toLayer(
480
+ * Greet,
481
+ * Effect.gen(function* () {
482
+ * const greeting = yield* Effect.succeed("Hello");
483
+ * return ({ name }) => Effect.succeed(`${greeting}, ${name}!`);
484
+ * }),
485
+ * );
486
+ * ```
487
+ *
488
+ * @example Implementing a member of a group
489
+ * ```ts
490
+ * import { Effect, Schema } from "effect";
491
+ * import { Callable, FunctionGroup } from "@visiolab/effect-firebase/functions";
492
+ *
493
+ * const CreateUser = Callable.make("create", {
494
+ * payload: { name: Schema.String },
495
+ * success: Schema.String,
496
+ * });
497
+ *
498
+ * class Users extends FunctionGroup.make("users").add(CreateUser) {}
499
+ *
500
+ * const CreateUserLive = Callable.toLayer(Users, "create", ({ name }) => Effect.succeed(name));
501
+ * ```
502
+ *
503
+ * @see {@link make} for declaring the Callable
504
+ * @see {@link serve} for running a call against the handler
505
+ * @see {@link DocumentTrigger.toLayer} for implementing a Document Trigger
506
+ * @see {@link FunctionGroup.make} for collecting Functions under one prefix
507
+ *
508
+ * @stability unstable
509
+ * @category constructors
510
+ * @since 0.3.0
511
+ */
512
+ export const toLayer: {
513
+ <C extends Any, R = never, EB = never, RB = never>(
514
+ callable: C,
515
+ handler: HandlerFunction<C, R> | Effect.Effect<HandlerFunction<C, R>, EB, RB>,
516
+ ): Layer.Layer<HandlerFor<C>, EB, HandlerServices<C, R> | RB>;
517
+ <
518
+ G extends FunctionGroup.Any,
519
+ const Name extends FunctionGroup.Names<G, Any>,
520
+ R = never,
521
+ EB = never,
522
+ RB = never,
523
+ >(
524
+ group: G,
525
+ name: Name,
526
+ handler:
527
+ | HandlerFunction<FunctionGroup.Member<G, Name, Any>, R>
528
+ | Effect.Effect<HandlerFunction<FunctionGroup.Member<G, Name, Any>, R>, EB, RB>,
529
+ ): Layer.Layer<
530
+ Handler<G["identifier"], Name>,
531
+ EB,
532
+ HandlerServices<FunctionGroup.Member<G, Name, Any>, R> | RB
533
+ >;
534
+ } = ((...args: ReadonlyArray<unknown>) => {
535
+ const [group, callable] = target(args);
536
+ // The handler's requirements are checked by the signatures, and provided
537
+ // where the Layer is built.
538
+ return internal.toLayer(
539
+ group?.identifier,
540
+ callable.name,
541
+ args[group === undefined ? 1 : 2] as internal.HandlerInput,
542
+ );
543
+ }) as never;
544
+
545
+ /**
546
+ * Runs one call to a Callable, as it arrives on the wire. A building block for
547
+ * the functions package. Most apps don't need it.
548
+ *
549
+ * **When to use**
550
+ *
551
+ * Use to serve Callables from a transport of your own. `Functions`
552
+ * from `@visiolab/effect-firebase-functions` serves them through
553
+ * `firebase-functions`.
554
+ *
555
+ * **Details**
556
+ *
557
+ * Pass the Callable for a Callable in no group, or its group and its name for
558
+ * a Callable member of a {@link FunctionGroup.FunctionGroup}.
559
+ *
560
+ * Returns a function of the call's {@link CallableRequest.CallableRequest}. It
561
+ * runs the middleware chain, the same one {@link handler} runs. Inside it, it
562
+ * decodes the raw `data` with the Payload's `Schema.toCodecJson` and runs the
563
+ * handler. It then encodes the success value with the success schema's
564
+ * `Schema.toCodecJson`, so its result is plain JSON. The request is provided
565
+ * to the chain. Give it a `rawRequest` built from your transport's HTTP
566
+ * request, with `HttpServerRequest.fromWeb` from `effect/http/HttpServerRequest`.
567
+ *
568
+ * Requires the handler, every middleware implementation, and what the chain
569
+ * still requires. They are read once, so a call needs nothing else.
570
+ *
571
+ * Every failure is a `CallableError` whose `ResponseError` reason is what to
572
+ * answer with:
573
+ *
574
+ * - A declared error, or an error of a middleware, is sent with its schema's
575
+ * code, the error's `message` if it is a non-empty string, else its `_tag`,
576
+ * and the error encoded with its schema's `Schema.toCodecJson` as
577
+ * `details`.
578
+ * - A raw Payload that doesn't decode is sent as `invalid-argument` with a
579
+ * generic message and no details. The `Schema.SchemaError` is logged at
580
+ * Debug.
581
+ *
582
+ * A defect, or a success or error value that doesn't encode, is a defect.
583
+ * Answer it with a bare `internal`, so nothing of it reaches the caller.
584
+ *
585
+ * The `CallableError`'s `callable` is the deployed name: the Callable's name,
586
+ * or `<group>-<name>` for a member of a group.
587
+ *
588
+ * @see {@link toLayer} for providing the handler
589
+ * @see {@link handler} for running the handler in process, with typed values
590
+ * @see {@link Functions.make} for serving through `firebase-functions`
591
+ * @see {@link deployment} for the name and span attributes a transport uses
592
+ * @see {@link DocumentTrigger.serve} for running an event of a Document Trigger
593
+ *
594
+ * @stability unstable
595
+ * @category serving
596
+ * @since 0.3.0
597
+ */
598
+ export const serve: {
599
+ <C extends Any>(
600
+ callable: C,
601
+ ): Effect.Effect<
602
+ (
603
+ request: CallableRequest.CallableRequest,
604
+ ) => Effect.Effect<unknown, CallableError.CallableError>,
605
+ never,
606
+ ChainServices<C, HandlerFor<C>>
607
+ >;
608
+ <G extends FunctionGroup.Any, const Name extends FunctionGroup.Names<G, Any>>(
609
+ group: G,
610
+ name: Name,
611
+ ): Effect.Effect<
612
+ (
613
+ request: CallableRequest.CallableRequest,
614
+ ) => Effect.Effect<unknown, CallableError.CallableError>,
615
+ never,
616
+ ChainServices<FunctionGroup.Member<G, Name, Any>, Handler<G["identifier"], Name>>
617
+ >;
618
+ } = ((...args: ReadonlyArray<unknown>) => internal.serve(...target(args))) as never;
619
+
620
+ /**
621
+ * Runs a Callable's handler in process, behind its middleware chain.
622
+ *
623
+ * **When to use**
624
+ *
625
+ * Use to test a handler and its middleware without the emulator and without
626
+ * `firebase-functions`. Provide the same Layers you serve it with, or stub a
627
+ * middleware with a Layer of your own.
628
+ *
629
+ * **Details**
630
+ *
631
+ * Pass the Callable for a Callable in no group, or its group and its name for
632
+ * a Callable member of a {@link FunctionGroup.FunctionGroup}.
633
+ *
634
+ * Requires the handler, every middleware implementation, and what the chain
635
+ * still requires. A middleware whose
636
+ * `requires` no middleware outside it provides leaves that requirement here,
637
+ * so a chain in the wrong order is a type error where the Layers are
638
+ * provided.
639
+ *
640
+ * Yields a function of the Payload and a {@link CallableRequest.Input}: `{}`
641
+ * for a signed-out call, `{ auth: { uid, token } }` for a signed-in one. It
642
+ * encodes the Payload with `Schema.toCodecJson` as the request's raw `data`,
643
+ * so middleware sees what it would see on the wire. The request's
644
+ * `rawRequest` is the input's, or else a JSON `POST` of the encoded Payload to
645
+ * `http://localhost/<deployed name>` (see
646
+ * {@link CallableRequest.Input.rawRequest}). It runs the chain that
647
+ * `Functions` runs, and fails with the Callable's declared errors and
648
+ * those of its middleware. The success value is the handler's, not encoded.
649
+ *
650
+ * @example Running a handler behind Authenticated
651
+ * ```ts
652
+ * import { Effect, Layer, Schema } from "effect";
653
+ * import { Callable, Caller } from "@visiolab/effect-firebase/functions";
654
+ *
655
+ * const WhoAmI = Callable.make("whoAmI", { success: Schema.String }).middleware(
656
+ * Caller.Authenticated,
657
+ * );
658
+ *
659
+ * const WhoAmILive = Callable.toLayer(WhoAmI, () =>
660
+ * Effect.map(Effect.service(Caller.Caller), ({ uid }) => uid),
661
+ * );
662
+ *
663
+ * const program = Effect.gen(function* () {
664
+ * const whoAmI = yield* Callable.handler(WhoAmI);
665
+ * // "ada"
666
+ * return yield* whoAmI(undefined, { auth: { uid: "ada", token: {} } });
667
+ * }).pipe(Effect.provide(Layer.merge(WhoAmILive, Caller.Authenticated.layer)));
668
+ * ```
669
+ *
670
+ * @see {@link serve} for running a call as it arrives on the wire
671
+ * @see {@link toLayer} for providing the handler
672
+ * @see {@link Functions.make} for serving the same chain through `firebase-functions`
673
+ *
674
+ * @stability unstable
675
+ * @category serving
676
+ * @since 0.3.0
677
+ */
678
+ export const handler: {
679
+ <C extends Any>(
680
+ callable: C,
681
+ ): Effect.Effect<HandlerCall<C>, never, ChainServices<C, HandlerFor<C>>>;
682
+ <G extends FunctionGroup.Any, const Name extends FunctionGroup.Names<G, Any>>(
683
+ group: G,
684
+ name: Name,
685
+ ): Effect.Effect<
686
+ HandlerCall<FunctionGroup.Member<G, Name, Any>>,
687
+ never,
688
+ ChainServices<FunctionGroup.Member<G, Name, Any>, Handler<G["identifier"], Name>>
689
+ >;
690
+ } = ((...args: ReadonlyArray<unknown>) => internal.handler(...target(args))) as never;
691
+
692
+ /**
693
+ * Sets the Firebase error code a Callable answers with when it fails with
694
+ * errors of this schema.
695
+ *
696
+ * **When to use**
697
+ *
698
+ * Use to give a code to an error schema you don't own. For an error class of
699
+ * your own, set `callableErrorCode` in its annotations instead, so every
700
+ * Callable that lists it uses the same code.
701
+ *
702
+ * **Details**
703
+ *
704
+ * An error schema without a code is sent as `internal`. The client reads the
705
+ * code to find the declared errors to decode the answer against, so the code
706
+ * is part of the contract.
707
+ *
708
+ * @example Giving codes to error schemas
709
+ * ```ts
710
+ * import { Schema } from "effect";
711
+ * import { Callable } from "@visiolab/effect-firebase/functions";
712
+ *
713
+ * class NameTaken extends Schema.TaggedError<NameTaken>()(
714
+ * "NameTaken",
715
+ * { name: Schema.String },
716
+ * { callableErrorCode: "already-exists" },
717
+ * ) {}
718
+ *
719
+ * class Banned extends Schema.TaggedError<Banned>()("Banned", {}) {}
720
+ *
721
+ * const Register = Callable.make("register", {
722
+ * payload: { name: Schema.String },
723
+ * error: [NameTaken, Banned.pipe(Callable.errorCode("permission-denied"))],
724
+ * });
725
+ * ```
726
+ *
727
+ * @see {@link getErrorCode} for reading the code back
728
+ * @see {@link make} for declaring the errors a Callable fails with
729
+ *
730
+ * @stability unstable
731
+ * @category errors
732
+ * @since 0.3.0
733
+ */
734
+ export const errorCode =
735
+ (code: CallableError.ErrorCode) =>
736
+ <S extends Schema.Top>(self: S): S["Rebuild"] =>
737
+ self.annotate({ callableErrorCode: code });
738
+
739
+ /**
740
+ * Reads the Firebase error code of an error schema: what {@link errorCode} or
741
+ * its `callableErrorCode` annotation set, else `internal`. A building block
742
+ * for the functions and client packages. Most apps don't need it.
743
+ *
744
+ * **When to use**
745
+ *
746
+ * Use when serving or calling Callables from a transport of your own.
747
+ *
748
+ * @see {@link errorCode} for setting the code
749
+ * @see {@link errorSchemas} for the schemas a call can be answered with
750
+ *
751
+ * @stability unstable
752
+ * @category errors
753
+ * @since 0.3.0
754
+ */
755
+ export const getErrorCode: (schema: Schema.Top) => CallableError.ErrorCode = internal.getErrorCode;
756
+
757
+ /**
758
+ * The schemas of every error a call can be answered with: the Callable's
759
+ * declared errors, then those of its middleware. A building block for the
760
+ * client package. Most apps don't need it.
761
+ *
762
+ * **When to use**
763
+ *
764
+ * Use when calling Callables from a transport of your own, to decode an
765
+ * error's `details` against the schemas that carry its code.
766
+ *
767
+ * **Details**
768
+ *
769
+ * Pass the Callable for a Callable in no group, or its group and its name for
770
+ * a Callable member of a {@link FunctionGroup.FunctionGroup}.
771
+ *
772
+ * @see {@link getErrorCode} for the code of each schema
773
+ * @see {@link deployment} for the name a transport calls
774
+ *
775
+ * @stability unstable
776
+ * @category errors
777
+ * @since 0.3.0
778
+ */
779
+ export const errorSchemas: {
780
+ (callable: Any): ReadonlyArray<Schema.Top>;
781
+ <G extends FunctionGroup.Any>(
782
+ group: G,
783
+ name: FunctionGroup.Names<G, Any>,
784
+ ): ReadonlyArray<Schema.Top>;
785
+ } = ((...args: ReadonlyArray<unknown>) => internal.errorSchemas(target(args)[1])) as never;
786
+
787
+ /**
788
+ * Where a Callable is served: the name it is deployed under and the
789
+ * attributes of each call's spans.
790
+ *
791
+ * @stability unstable
792
+ * @category models
793
+ * @since 0.3.0
794
+ */
795
+ export interface Deployment {
796
+ /**
797
+ * The name the Callable Function is exported under and the client calls:
798
+ * the Callable's name, or `<group>-<name>` for a member of a group.
799
+ */
800
+ readonly name: string;
801
+ /**
802
+ * The attributes of the server's and the client's span of a call:
803
+ * `rpc.system: "firebase_callable"`, `rpc.method` (the Callable's name),
804
+ * `rpc.service` (the group, for a member) and `faas.name` (the deployed
805
+ * name).
806
+ */
807
+ readonly attributes: Readonly<Record<string, string>>;
808
+ }
809
+
810
+ /**
811
+ * Returns the {@link Deployment} of a Callable: its deployed name and its span
812
+ * attributes. A building block of the functions and client packages, which
813
+ * agree on both through it. Most apps don't need it.
814
+ *
815
+ * **When to use**
816
+ *
817
+ * Use when serving or calling Callables from a transport of your own.
818
+ * `Functions` and `CallableClient` already use it.
819
+ *
820
+ * **Details**
821
+ *
822
+ * Pass the Callable for a Callable in no group, or its group and its name for
823
+ * a Callable member of a {@link FunctionGroup.FunctionGroup}.
824
+ *
825
+ * @example Reading the deployed name of a member
826
+ * ```ts
827
+ * import { Schema } from "effect";
828
+ * import { Callable, FunctionGroup } from "@visiolab/effect-firebase/functions";
829
+ *
830
+ * const CreateUser = Callable.make("create", { payload: { name: Schema.String } });
831
+ *
832
+ * class Users extends FunctionGroup.make("users").add(CreateUser) {}
833
+ *
834
+ * // "users-create"
835
+ * const { name } = Callable.deployment(Users, "create");
836
+ * ```
837
+ *
838
+ * @see {@link serve} for running a call on the server
839
+ * @see {@link errorSchemas} for the errors a call can be answered with
840
+ * @see {@link DocumentTrigger.deployment} for where a Document Trigger is deployed
841
+ *
842
+ * @stability unstable
843
+ * @category serving
844
+ * @since 0.3.0
845
+ */
846
+ export const deployment: {
847
+ (callable: Any): Deployment;
848
+ <G extends FunctionGroup.Any>(group: G, name: FunctionGroup.Names<G, Any>): Deployment;
849
+ } = ((...args: ReadonlyArray<unknown>) => {
850
+ const [group, callable] = target(args);
851
+ return {
852
+ name: internal.deployedName(group, callable),
853
+ attributes: internal.spanAttributes(group, callable),
854
+ };
855
+ }) as never;