@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,175 @@
1
+ import { Any as Any$1, Handler } from "./FirebaseFunction.mjs";
2
+ import { Pipeable } from "effect";
3
+ declare namespace FunctionGroup_d_exports {
4
+ export { Any, FunctionGroup, Handlers, Member, Members, Names, isFunctionGroup, make };
5
+ }
6
+ declare const TypeId = "~@visiolab/effect-firebase/functions/FunctionGroup";
7
+ /**
8
+ * A Function Group: a name and the Functions it deploys under that name.
9
+ *
10
+ * **Details**
11
+ *
12
+ * A group is a class, so `class Users extends FunctionGroup.make("users")`
13
+ * gives it a name of its own in types and in hovers. Its name is
14
+ * `identifier`, since a class already has a `name` of its own.
15
+ *
16
+ * A group holds Functions of any kind, so a group can follow a domain, such
17
+ * as `posts`, rather than a kind. A Function's name is unique within its
18
+ * group. `.add` checks this when the module loads; the types don't. A
19
+ * Function can be in more than one group, and each group serves and
20
+ * implements it separately. Groups don't nest.
21
+ *
22
+ * A group is only a name and its members. It carries no middleware: attach a
23
+ * Callable Middleware to each Callable that needs it with
24
+ * {@link Callable.Callable.middleware}.
25
+ *
26
+ * @stability unstable
27
+ * @category models
28
+ * @since 0.3.0
29
+ */
30
+ export interface FunctionGroup<out Identifier extends string, in out Functions extends Any$1 = never> extends Pipeable.Pipeable {
31
+ new (_: never): {};
32
+ readonly [TypeId]: typeof TypeId;
33
+ /** The group's name: the prefix its members deploy under. */
34
+ readonly identifier: Identifier;
35
+ /** The group's members, keyed by their names. */
36
+ readonly functions: { readonly [F in Functions as F["name"]]: F; };
37
+ /**
38
+ * Adds Functions of any kind to the group.
39
+ *
40
+ * **Details**
41
+ *
42
+ * Returns a new group. Throws when a Function's name is already taken in
43
+ * the group, so a clash fails when the module loads, before anything
44
+ * deploys. The types don't catch a clash.
45
+ */
46
+ add<const A extends ReadonlyArray<Any$1>>(...functions: A): FunctionGroup<Identifier, Functions | A[number]>;
47
+ }
48
+ /**
49
+ * Any {@link FunctionGroup}, whatever its name and members.
50
+ *
51
+ * @stability unstable
52
+ * @category models
53
+ * @since 0.3.0
54
+ */
55
+ export interface Any {
56
+ readonly [TypeId]: typeof TypeId;
57
+ readonly identifier: string;
58
+ readonly functions: {
59
+ readonly [name: string]: Any$1;
60
+ };
61
+ }
62
+ /**
63
+ * The members of a {@link FunctionGroup}, as a union of Functions.
64
+ *
65
+ * **When to use**
66
+ *
67
+ * Use to read every member of a group. Pass the `Any` of a kind as `K`, such
68
+ * as `Members<typeof Users, Callable.Any>`, to keep only the members of that
69
+ * kind.
70
+ *
71
+ * @see {@link Names} for the names of the members
72
+ * @see {@link Member} for the member of one name
73
+ *
74
+ * @stability unstable
75
+ * @category type-level
76
+ * @since 0.3.0
77
+ */
78
+ export type Members<G, K extends Any$1 = Any$1> = G extends {
79
+ readonly functions: infer Record;
80
+ } ? Extract<Record[keyof Record], K> : never;
81
+ /**
82
+ * The names of the members of a {@link FunctionGroup}.
83
+ *
84
+ * **When to use**
85
+ *
86
+ * Use to key something on a group's members. Pass the `Any` of a kind as `K`
87
+ * to keep only the names of the members of that kind.
88
+ *
89
+ * @see {@link Members} for the members themselves
90
+ *
91
+ * @stability unstable
92
+ * @category type-level
93
+ * @since 0.3.0
94
+ */
95
+ export type Names<G, K extends Any$1 = Any$1> = Members<G, K>["name"];
96
+ /**
97
+ * The member of a {@link FunctionGroup} named `Name`.
98
+ *
99
+ * **When to use**
100
+ *
101
+ * Use to read one member's type by its name. Pass the `Any` of a kind as `K`
102
+ * to get `never` unless the member is of that kind.
103
+ *
104
+ * @see {@link Members} for every member
105
+ *
106
+ * @stability unstable
107
+ * @category type-level
108
+ * @since 0.3.0
109
+ */
110
+ export type Member<G, Name extends string, K extends Any$1 = Any$1> = Extract<Members<G, K>, {
111
+ readonly name: Name;
112
+ }>;
113
+ /**
114
+ * The {@link FirebaseFunction.Handler}s that serving a {@link FunctionGroup}
115
+ * needs: one per member, keyed by the group.
116
+ *
117
+ * @stability unstable
118
+ * @category type-level
119
+ * @since 0.3.0
120
+ */
121
+ export type Handlers<G extends Any> = Names<G> extends (infer Name) ? Name extends string ? Handler<G["identifier"], Name> : never : never;
122
+ /**
123
+ * Checks whether a value is a {@link FunctionGroup}. A building block for the
124
+ * functions and client packages, which take a Function or a group. Most apps
125
+ * don't need it.
126
+ *
127
+ * @stability unstable
128
+ * @category guards
129
+ * @since 0.3.0
130
+ */
131
+ export declare const isFunctionGroup: (u: unknown) => u is Any;
132
+ /**
133
+ * Starts a Function Group with no members.
134
+ *
135
+ * **When to use**
136
+ *
137
+ * Use to collect related Functions, of any kind, under one prefix, and to
138
+ * deploy them as one. A Function that stands alone needs no group.
139
+ *
140
+ * **Details**
141
+ *
142
+ * Add members with `.add`. Extend the result as a class, so the group has a
143
+ * name of its own. Each member deploys as `<identifier>-<name>`.
144
+ *
145
+ * A client built from a Callable on its own, rather than from its group,
146
+ * calls the name without the prefix and fails with `not-found`. Nothing
147
+ * checks this at runtime.
148
+ *
149
+ * @example Serving one Callable in two groups
150
+ * ```ts
151
+ * import { Schema } from "effect";
152
+ * import { Callable, FunctionGroup } from "@visiolab/effect-firebase/functions";
153
+ *
154
+ * const GetUser = Callable.make("get", {
155
+ * payload: { id: Schema.String },
156
+ * success: Schema.String,
157
+ * });
158
+ *
159
+ * // Deploys as `users-get` and `admins-get`, each with a handler of its own.
160
+ * class Users extends FunctionGroup.make("users").add(GetUser) {}
161
+ * class Admins extends FunctionGroup.make("admins").add(GetUser) {}
162
+ * ```
163
+ *
164
+ * @see {@link Callable.make} for declaring a Callable member
165
+ * @see {@link Callable.toLayer} for implementing a Callable member
166
+ * @see {@link DocumentTrigger.created} for declaring a Document Trigger member
167
+ * @see {@link DocumentTrigger.toLayer} for implementing a Document Trigger member
168
+ * @see {@link Functions.group} for deploying the group
169
+ *
170
+ * @stability unstable
171
+ * @category constructors
172
+ * @since 0.3.0
173
+ */
174
+ export declare const make: <const Identifier extends string>(identifier: Identifier) => FunctionGroup<Identifier>;
175
+ export { FunctionGroup_d_exports };
@@ -0,0 +1,64 @@
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
2
+ import { isFunctionGroup as isFunctionGroup$1, make as make$1 } from "./internal/functionGroup.mjs";
3
+ // src/functions/FunctionGroup.ts
4
+ var FunctionGroup_exports = /* @__PURE__ */ __exportAll({
5
+ isFunctionGroup: () => isFunctionGroup,
6
+ make: () => make
7
+ });
8
+ /**
9
+ * Checks whether a value is a {@link FunctionGroup}. A building block for the
10
+ * functions and client packages, which take a Function or a group. Most apps
11
+ * don't need it.
12
+ *
13
+ * @stability unstable
14
+ * @category guards
15
+ * @since 0.3.0
16
+ */
17
+ const isFunctionGroup = isFunctionGroup$1;
18
+ /**
19
+ * Starts a Function Group with no members.
20
+ *
21
+ * **When to use**
22
+ *
23
+ * Use to collect related Functions, of any kind, under one prefix, and to
24
+ * deploy them as one. A Function that stands alone needs no group.
25
+ *
26
+ * **Details**
27
+ *
28
+ * Add members with `.add`. Extend the result as a class, so the group has a
29
+ * name of its own. Each member deploys as `<identifier>-<name>`.
30
+ *
31
+ * A client built from a Callable on its own, rather than from its group,
32
+ * calls the name without the prefix and fails with `not-found`. Nothing
33
+ * checks this at runtime.
34
+ *
35
+ * @example Serving one Callable in two groups
36
+ * ```ts
37
+ * import { Schema } from "effect";
38
+ * import { Callable, FunctionGroup } from "@visiolab/effect-firebase/functions";
39
+ *
40
+ * const GetUser = Callable.make("get", {
41
+ * payload: { id: Schema.String },
42
+ * success: Schema.String,
43
+ * });
44
+ *
45
+ * // Deploys as `users-get` and `admins-get`, each with a handler of its own.
46
+ * class Users extends FunctionGroup.make("users").add(GetUser) {}
47
+ * class Admins extends FunctionGroup.make("admins").add(GetUser) {}
48
+ * ```
49
+ *
50
+ * @see {@link Callable.make} for declaring a Callable member
51
+ * @see {@link Callable.toLayer} for implementing a Callable member
52
+ * @see {@link DocumentTrigger.created} for declaring a Document Trigger member
53
+ * @see {@link DocumentTrigger.toLayer} for implementing a Document Trigger member
54
+ * @see {@link Functions.group} for deploying the group
55
+ *
56
+ * @stability unstable
57
+ * @category constructors
58
+ * @since 0.3.0
59
+ */
60
+ const make = (identifier) => make$1({
61
+ identifier,
62
+ functions: {}
63
+ });
64
+ export { FunctionGroup_exports, isFunctionGroup, make };
@@ -0,0 +1,9 @@
1
+ import { CallableError_d_exports } from "./CallableError.mjs";
2
+ import { CallableRequest_d_exports } from "./CallableRequest.mjs";
3
+ import { CallableMiddleware_d_exports } from "./CallableMiddleware.mjs";
4
+ import { FirebaseFunction_d_exports } from "./FirebaseFunction.mjs";
5
+ import { FunctionGroup_d_exports } from "./FunctionGroup.mjs";
6
+ import { Callable_d_exports } from "./Callable.mjs";
7
+ import { Caller_d_exports } from "./Caller.mjs";
8
+ import { DocumentTrigger_d_exports } from "./DocumentTrigger.mjs";
9
+ export { Callable_d_exports as Callable, CallableError_d_exports as CallableError, CallableMiddleware_d_exports as CallableMiddleware, CallableRequest_d_exports as CallableRequest, Caller_d_exports as Caller, DocumentTrigger_d_exports as DocumentTrigger, FirebaseFunction_d_exports as FirebaseFunction, FunctionGroup_d_exports as FunctionGroup };
@@ -0,0 +1,9 @@
1
+ import { CallableError_exports } from "./CallableError.mjs";
2
+ import { Callable_exports } from "./Callable.mjs";
3
+ import { CallableMiddleware_exports } from "./CallableMiddleware.mjs";
4
+ import { CallableRequest_exports } from "./CallableRequest.mjs";
5
+ import { Caller_exports } from "./Caller.mjs";
6
+ import { DocumentTrigger_exports } from "./DocumentTrigger.mjs";
7
+ import { FirebaseFunction_exports } from "./FirebaseFunction.mjs";
8
+ import { FunctionGroup_exports } from "./FunctionGroup.mjs";
9
+ export { Callable_exports as Callable, CallableError_exports as CallableError, CallableMiddleware_exports as CallableMiddleware, CallableRequest_exports as CallableRequest, Caller_exports as Caller, DocumentTrigger_exports as DocumentTrigger, FirebaseFunction_exports as FirebaseFunction, FunctionGroup_exports as FunctionGroup };
@@ -0,0 +1,167 @@
1
+ import { CallableError, ResponseError } from "../CallableError.mjs";
2
+ import { CallableRequest } from "./callableRequest.mjs";
3
+ import { deployedName as deployedName$1, handlerKey as handlerKey$1 } from "./firebaseFunction.mjs";
4
+ import { Context, Effect, Layer, Pipeable, Schema } from "effect";
5
+ import * as HttpServerRequest from "effect/http/HttpServerRequest";
6
+ // src/functions/internal/callable.ts
7
+ /** @internal */
8
+ /** @internal */
9
+ const TypeId = "~@visiolab/effect-firebase/functions/Callable";
10
+ /** @internal */
11
+ const kind = "Callable";
12
+ const Proto = {
13
+ [TypeId]: TypeId,
14
+ middleware(middleware) {
15
+ return make({
16
+ kind,
17
+ name: this.name,
18
+ payload: this.payload,
19
+ success: this.success,
20
+ error: this.error,
21
+ middlewares: /* @__PURE__ */ new Set([...this.middlewares, middleware])
22
+ });
23
+ },
24
+ pipe() {
25
+ return Pipeable.pipeArguments(this, arguments);
26
+ }
27
+ };
28
+ /** @internal */
29
+ const make = (fields) => Object.assign(Object.create(Proto), fields);
30
+ /**
31
+ * The key of the handler of the Callable `name` in `group`, or in no group.
32
+ *
33
+ * @internal
34
+ */
35
+ const handlerKey = (group, name) => handlerKey$1(group, name);
36
+ /** @internal */
37
+ const toLayer = (group, name, handler) => Layer.effect(handlerKey(group, name), Effect.gen(function* () {
38
+ const run = Effect.isEffect(handler) ? yield* handler : handler;
39
+ const built = yield* Effect.context();
40
+ return (payload) => Effect.updateContext(run(payload), (call) => Context.merge(built, call));
41
+ }));
42
+ /**
43
+ * Schemas with their services erased: a Callable's codecs need none that the
44
+ * types track, and `Schema.Top` leaves them `unknown`.
45
+ */
46
+ const codec = (schema) => Schema.toCodecJson(schema);
47
+ /** @internal */
48
+ const getErrorCode = (schema) => Schema.resolveAnnotations(schema)?.callableErrorCode ?? "internal";
49
+ /**
50
+ * The name `callable` is deployed under: its own in no group, or
51
+ * `<group>-<name>` in `group`. The server exports it under this name, and the
52
+ * client calls it.
53
+ *
54
+ * @internal
55
+ */
56
+ const deployedName = deployedName$1;
57
+ /**
58
+ * The attributes of the server's and the client's span of a call to
59
+ * `callable` in `group`, or in no group.
60
+ *
61
+ * @internal
62
+ */
63
+ const spanAttributes = (group, callable) => ({
64
+ "rpc.system": "firebase_callable",
65
+ "rpc.method": callable.name,
66
+ ...group === void 0 ? {} : { "rpc.service": group.identifier },
67
+ "faas.name": deployedName(group, callable)
68
+ });
69
+ /**
70
+ * The schemas of every error a call to `callable` can be answered with: its
71
+ * declared errors, then those of its middleware.
72
+ *
73
+ * @internal
74
+ */
75
+ const errorSchemas = (callable) => [...callable.error, ...[...callable.middlewares].flatMap((middleware) => [...middleware.error])];
76
+ /**
77
+ * Turns a failure of the call into the answer that carries it: the code of
78
+ * the error schema it matches, a readable message, and the encoded error. A
79
+ * failure no schema matches, or one that doesn't encode, is a defect.
80
+ */
81
+ const encodeFailure = (schemas, error) => Effect.gen(function* () {
82
+ for (const schema of schemas) {
83
+ if (!Schema.is(schema)(error)) continue;
84
+ const code = getErrorCode(schema);
85
+ const details = yield* Effect.orDie(Schema.encodeUnknownEffect(codec(schema))(error));
86
+ const fields = error;
87
+ const message = typeof fields.message === "string" && fields.message !== "" ? fields.message : typeof fields._tag === "string" ? fields._tag : code;
88
+ return new ResponseError({
89
+ code,
90
+ message,
91
+ details
92
+ });
93
+ }
94
+ return yield* Effect.die(error);
95
+ });
96
+ /**
97
+ * The failure of a raw Payload that doesn't decode, as it leaves the chain.
98
+ * Middleware sees it like any other failure.
99
+ */
100
+ var PayloadDecodeError = class {
101
+ cause;
102
+ constructor(cause) {
103
+ this.cause = cause;
104
+ }
105
+ };
106
+ /**
107
+ * The middleware chain of `callable` in `group`, or in no group, around the
108
+ * Payload's decoding and the handler. `serve`, which `Functions` runs,
109
+ * and `handler` both run this one chain.
110
+ *
111
+ * The middleware implementations are read from the context once. The context
112
+ * the chain was read in sits under that of each call, and the call's request
113
+ * on top of both.
114
+ *
115
+ * @internal
116
+ */
117
+ const chain = (group, callable) => Effect.gen(function* () {
118
+ const handler = yield* Effect.service(handlerKey(group?.identifier, callable.name));
119
+ const context = yield* Effect.context();
120
+ const implementations = [...callable.middlewares].map((key) => Context.getUnsafe(context, key));
121
+ const decode = Schema.decodeUnknownEffect(codec(callable.payload));
122
+ return (request) => {
123
+ let effect = decode(request.data).pipe(Effect.mapError((cause) => new PayloadDecodeError(cause)), Effect.flatMap(handler));
124
+ for (const implementation of implementations) effect = implementation(effect, { callable });
125
+ return effect.pipe(Effect.provideService(CallableRequest, request), Effect.updateContext((call) => Context.merge(context, call)));
126
+ };
127
+ });
128
+ /** @internal */
129
+ const serve = (group, callable) => Effect.map(chain(group, callable), (run) => {
130
+ const encode = Schema.encodeUnknownEffect(codec(callable.success));
131
+ const schemas = errorSchemas(callable);
132
+ const fail = (reason) => new CallableError({
133
+ callable: deployedName(group, callable),
134
+ reason
135
+ });
136
+ return (request) => run(request).pipe(Effect.catch((error) => error instanceof PayloadDecodeError ? Effect.logDebug("The Payload of a Callable didn't decode", error.cause).pipe(Effect.andThen(Effect.fail(fail(new ResponseError({
137
+ code: "invalid-argument",
138
+ message: "The Payload didn't decode",
139
+ details: void 0
140
+ }))))) : Effect.flatMap(encodeFailure(schemas, error), (reason) => Effect.fail(fail(reason)))), Effect.flatMap((value) => Effect.orDie(encode(value))));
141
+ });
142
+ /**
143
+ * The HTTP request of a call `handler` runs with no request given: a JSON
144
+ * `POST` of the encoded Payload to the deployed name.
145
+ */
146
+ const syntheticRequest = (name, data) => HttpServerRequest.fromWeb(new Request(`http://localhost/${name}`, {
147
+ method: "POST",
148
+ headers: { "content-type": "application/json" },
149
+ body: JSON.stringify(data)
150
+ }));
151
+ /** @internal */
152
+ const handler = (group, callable) => Effect.map(chain(group, callable), (run) => {
153
+ const encode = Schema.encodeUnknownEffect(codec(callable.payload));
154
+ const name = deployedName(group, callable);
155
+ return (payload, input) => encode(payload).pipe(Effect.orDie, Effect.flatMap((data) => run({
156
+ auth: input.auth === void 0 ? void 0 : {
157
+ uid: input.auth.uid,
158
+ token: input.auth.token,
159
+ rawToken: input.auth.rawToken ?? ""
160
+ },
161
+ app: input.app,
162
+ instanceIdToken: input.instanceIdToken,
163
+ data,
164
+ rawRequest: input.rawRequest ?? syntheticRequest(name, data)
165
+ })), Effect.catchIf((error) => error instanceof PayloadDecodeError, (error) => Effect.die(error.cause)));
166
+ });
167
+ export { TypeId, chain, deployedName, errorSchemas, getErrorCode, handler, handlerKey, kind, make, serve, spanAttributes, toLayer };
@@ -0,0 +1,19 @@
1
+ import { Context, Effect, Schema } from "effect";
2
+ /**
3
+ * The middleware attached to a Callable, as a union, from its phantom
4
+ * `~Middleware` field.
5
+ *
6
+ * @internal
7
+ */
8
+ export type Attached<X> = X extends {
9
+ readonly "~Middleware": infer M;
10
+ } ? M : never;
11
+ /**
12
+ * What the middleware attached to a Callable still requires, from its phantom
13
+ * `~MiddlewareServices` field.
14
+ *
15
+ * @internal
16
+ */
17
+ export type AttachedServices<X> = X extends {
18
+ readonly "~MiddlewareServices": infer R;
19
+ } ? R : never;
@@ -0,0 +1,15 @@
1
+ import { Context } from "effect";
2
+ // src/functions/internal/callableMiddleware.ts
3
+ /** @internal */
4
+ /** @internal */
5
+ const TypeId = "~@visiolab/effect-firebase/functions/CallableMiddleware";
6
+ /** @internal */
7
+ const Service = (id, options) => {
8
+ class Service extends Context.Service()(id) {}
9
+ const self = Service;
10
+ self[TypeId] = TypeId;
11
+ const error = options?.error;
12
+ self.error = new Set(error === void 0 ? [] : Array.isArray(error) ? error : [error]);
13
+ return self;
14
+ };
15
+ export { Service, TypeId };
@@ -0,0 +1,6 @@
1
+ import { Context } from "effect";
2
+ // src/functions/internal/callableRequest.ts
3
+ /** @internal */
4
+ /** @internal */
5
+ const CallableRequest = Context.Service("@visiolab/effect-firebase/functions/CallableRequest");
6
+ export { CallableRequest };
@@ -0,0 +1,10 @@
1
+ import "../../firestore/DocumentSnapshot.mjs";
2
+ import "../../firestore/Collection.mjs";
3
+ import "./firebaseFunction.mjs";
4
+ import { Context, Effect, Layer } from "effect";
5
+ /**
6
+ * The changes to a document a trigger can listen to.
7
+ *
8
+ * @internal
9
+ */
10
+ export type EventKind = "created" | "deleted" | "updated" | "written";
@@ -0,0 +1,73 @@
1
+ import { deployedName, handlerKey as handlerKey$1 } from "./firebaseFunction.mjs";
2
+ import { Context, Effect, Layer, Pipeable } from "effect";
3
+ // src/functions/internal/documentTrigger.ts
4
+ /** @internal */
5
+ /** @internal */
6
+ const TypeId = "~@visiolab/effect-firebase/functions/DocumentTrigger";
7
+ /** @internal */
8
+ const kind = "DocumentTrigger";
9
+ const Proto = {
10
+ [TypeId]: TypeId,
11
+ pipe() {
12
+ return Pipeable.pipeArguments(this, arguments);
13
+ }
14
+ };
15
+ /** @internal */
16
+ const make = (fields) => Object.assign(Object.create(Proto), fields);
17
+ /**
18
+ * The key of the handler of the Document Trigger `name` in `group`, or in no
19
+ * group.
20
+ *
21
+ * @internal
22
+ */
23
+ const handlerKey = (group, name) => handlerKey$1(group, name);
24
+ /** @internal */
25
+ const toLayer = (group, name, handler) => Layer.effect(handlerKey(group, name), Effect.gen(function* () {
26
+ const run = Effect.isEffect(handler) ? yield* handler : handler;
27
+ const built = yield* Effect.context();
28
+ return (event) => Effect.updateContext(run(event), (call) => Context.merge(built, call));
29
+ }));
30
+ /** @internal */
31
+ const serve = (group, trigger) => Effect.service(handlerKey(group?.identifier, trigger.name));
32
+ /**
33
+ * The path pattern of the documents of `collection`, with a wildcard of its
34
+ * own for each ancestor id and for the document id:
35
+ * `users/{id0}/posts/{id1}`. The wildcards are only ever read back through
36
+ * the event's document path, so their names are free.
37
+ *
38
+ * @internal
39
+ */
40
+ const documentPattern = (collection) => collection.path.map((segment, i) => `${segment}/{id${i}}`).join("/");
41
+ /** @internal */
42
+ const deployment = (group, trigger) => ({
43
+ name: deployedName(group, trigger),
44
+ document: documentPattern(trigger.collection)
45
+ });
46
+ /** @internal */
47
+ const createdEvent = (input) => ({
48
+ id: input.id,
49
+ time: input.time,
50
+ ref: input.snapshot.ref,
51
+ snapshot: input.snapshot
52
+ });
53
+ /**
54
+ * A `deleted` event has the shape of a `created` one, with the document as it
55
+ * was just before it was deleted.
56
+ *
57
+ * @internal
58
+ */
59
+ const deletedEvent = createdEvent;
60
+ /**
61
+ * The event of a change with a document on each side. Both sides are at the
62
+ * same reference; `ref` is taken from `after`.
63
+ *
64
+ * @internal
65
+ */
66
+ const changeEvent = (input) => ({
67
+ id: input.id,
68
+ time: input.time,
69
+ ref: input.after.ref,
70
+ before: input.before,
71
+ after: input.after
72
+ });
73
+ export { TypeId, changeEvent, createdEvent, deletedEvent, deployment, documentPattern, handlerKey, kind, make, serve, toLayer };
@@ -0,0 +1,5 @@
1
+ import { Context } from "effect";
2
+ /** @internal */
3
+ export declare const NoGroupTypeId = "~@visiolab/effect-firebase/functions/FirebaseFunction/NoGroup";
4
+ /** @internal */
5
+ export declare const HandlerTypeId = "~@visiolab/effect-firebase/functions/FirebaseFunction/Handler";
@@ -0,0 +1,24 @@
1
+ import { Context } from "effect";
2
+ // src/functions/internal/firebaseFunction.ts
3
+ /** @internal */
4
+ /**
5
+ * The key of the handler of the Function `name` in `group`, or in no group,
6
+ * whatever its kind. A kind decides what it holds.
7
+ *
8
+ * Context keys are compared by their string, so every call with the same group
9
+ * and name reaches the same service. Deployed names can't contain `/`, so the
10
+ * two forms can't collide. Names are unique within a group, and a Function in
11
+ * no group is deployed under its name, so two kinds never share a key they
12
+ * could both be served under.
13
+ *
14
+ * @internal
15
+ */
16
+ const handlerKey = (group, name) => Context.Service(group === void 0 ? `@visiolab/effect-firebase/functions/Handler/${name}` : `@visiolab/effect-firebase/functions/Handler/${group}/${name}`);
17
+ /**
18
+ * The name `fn` is deployed under: its own in no group, or `<group>-<name>`
19
+ * in `group`.
20
+ *
21
+ * @internal
22
+ */
23
+ const deployedName = (group, fn) => group === void 0 ? fn.name : `${group.identifier}-${fn.name}`;
24
+ export { deployedName, handlerKey };
@@ -0,0 +1,36 @@
1
+ import { Pipeable } from "effect";
2
+ // src/functions/internal/functionGroup.ts
3
+ /** @internal */
4
+ /** @internal */
5
+ const TypeId = "~@visiolab/effect-firebase/functions/FunctionGroup";
6
+ /** @internal */
7
+ const isFunctionGroup = (u) => (typeof u === "function" || typeof u === "object" && u !== null) && "~@visiolab/effect-firebase/functions/FunctionGroup" in u;
8
+ const Proto = {
9
+ [TypeId]: TypeId,
10
+ add(...toAdd) {
11
+ const functions = { ...this.functions };
12
+ for (const fn of toAdd) {
13
+ if (Object.hasOwn(functions, fn.name)) throw new Error(`The Function Group "${this.identifier}" already has a Function named "${fn.name}"`);
14
+ functions[fn.name] = fn;
15
+ }
16
+ return make({
17
+ identifier: this.identifier,
18
+ functions
19
+ });
20
+ },
21
+ pipe() {
22
+ return Pipeable.pipeArguments(this, arguments);
23
+ }
24
+ };
25
+ /**
26
+ * A function, so that a group can be the base of a class. Its `name` is the
27
+ * function's own, so the group's name is its `identifier`.
28
+ *
29
+ * @internal
30
+ */
31
+ const make = (fields) => {
32
+ function FunctionGroup() {}
33
+ Object.setPrototypeOf(FunctionGroup, Proto);
34
+ return Object.assign(FunctionGroup, fields);
35
+ };
36
+ export { TypeId, isFunctionGroup, make };