1import * as GCP from "@/GCP";
2import * as Effect from "effect/Effect";
3import * as Stream from "effect/Stream";
4import * as HttpServerResponse from "effect/http/HttpServerResponse";
5
6/** Bucket the function writes to from `fetch` and from topic deliveries. */
7export const Marker = GCP.Storage.Bucket("FnMarker", { forceDestroy: true });
8/** Topic pushed to the function through a push subscription. */
9export const Pings = GCP.PubSub.Topic("FnPings", {});
10
11/**
12 * Effect-native Cloud Function: an HTTP `fetch` using Storage bindings,
13 * plus a Pub/Sub push event source that records each message.
14 * Deployed from {@link ../EffectNative.test.ts}.
15 */
16export default class EffectFunction extends GCP.CloudFunctions.Function<EffectFunction>()(
17 "EffectFunction",
18 { main: import.meta.url, location: "us-central1" },
19 Effect.gen(function* () {
20 const bucket = yield* Marker;
21 const putObject = yield* GCP.Storage.PutObject(bucket);
22 const getObject = yield* GCP.Storage.GetObject(bucket);
23
24 yield* GCP.PubSub.consumeTopicMessages(yield* Pings, (messages) =>
25 messages.pipe(
26 Stream.runForEach(({ message }) =>
27 putObject({
28 name: `ping-${message.messageId}.txt`,
29 body: atob(message.data ?? ""),
30 }).pipe(Effect.orDie),
31 ),
32 ),
33 );
34
35 return {
36 fetch: Effect.gen(function* () {
37 yield* putObject({ name: "hello.txt", body: "from-function" }).pipe(
38 Effect.orDie,
39 );
40 const { body } = yield* getObject({ object: "hello.txt" }).pipe(
41 Effect.orDie,
42 );
43 return yield* HttpServerResponse.json({
44 read: new TextDecoder().decode(body),
45 node: yield* Effect.sync(() => process.versions.node),
46 });
47 }),
48 };
49 }).pipe(
50 Effect.provide(GCP.Storage.PutObjectHttp),
51 Effect.provide(GCP.Storage.GetObjectHttp),
52 Effect.provide(GCP.CloudFunctions.TopicEventSource),
53 ),
54) {}