1import * as AWS from "@/AWS/index.ts";
2import * as Context from "effect/Context";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import * as HttpClient from "effect/http/HttpClient";
6import * as HttpServerResponse from "effect/http/HttpServerResponse";
7
8/**
9 * A service whose Layer performs real async I/O during the sandbox's init
10 * (cold start) — the "fetch a value once and cache it for every
11 * invocation" pattern.
12 */
13class TraceConfig extends Context.Service<
14 TraceConfig,
15 { trace: string; nonce: string }
16>()("InitIO.TraceConfig") {}
17
18const TraceConfigLive = Layer.effect(
19 TraceConfig,
20 Effect.gen(function* () {
21 // Counted per sandbox so every response can assert the layer's I/O ran
22 // exactly once no matter how many invocations the sandbox has served.
23 yield* Effect.sync(() => {
24 (globalThis as any).__initFetches =
25 ((globalThis as any).__initFetches ?? 0) + 1;
26 });
27 const client = yield* HttpClient.HttpClient;
28 const response = yield* client.get(
29 "https://www.cloudflare.com/cdn-cgi/trace",
30 );
31 const trace = yield* response.text;
32 return { trace, nonce: crypto.randomUUID() };
33 }).pipe(
34 // A failed init fetch is a defect: the sandbox is useless without its
35 // config, and init's typed error channel is reserved for ConfigError.
36 Effect.orDie,
37 ),
38);
39
40/**
41 * Pins that one-shot async I/O during init / layer build works on Lambda:
42 * the layer's fetch runs exactly once per sandbox (at cold start, under the
43 * instance scope) and the cached plain value serves every invocation.
44 */
45export default class InitIOProbe extends AWS.Lambda.Function<InitIOProbe>()(
46 "InitIOProbe",
47 {
48 main: import.meta.url,
49 functionUrl: true,
50 },
51 Effect.gen(function* () {
52 const config = yield* TraceConfig;
53
54 return {
55 fetch: Effect.gen(function* () {
56 return yield* HttpServerResponse.json({
57 nonce: config.nonce,
58 traceLength: config.trace.length,
59 hasUag: config.trace.includes("uag="),
60 initFetches: (globalThis as any).__initFetches ?? 0,
61 });
62 }),
63 };
64 }).pipe(Effect.provide(TraceConfigLive)),
65) {}