1import * as Lambda from "@/AWS/Lambda";
2import * as Config from "effect/Config";
3import * as Effect from "effect/Effect";
4import * as Redacted from "effect/Redacted";
5import { HttpServerRequest } from "effect/http/HttpServerRequest";
6import * as HttpServerResponse from "effect/http/HttpServerResponse";
7import path from "pathe";
8
9const main = path.resolve(import.meta.dirname, "handler.ts");
10
11/**
12 * Hard-coded values the integ test asserts against to prove the
13 * deploy-time bindings flow all the way through to the runtime.
14 */
15export const LITERAL_SECRET_VALUE = "sk-aws-literal-secret-abc";
16export const STRING_VAR_VALUE = "plain-aws-string-value";
17export const NUMBER_VAR_VALUE = 9090;
18export const OBJECT_VAR_VALUE = {
19 region: "us-east-1",
20 flags: { canary: true },
21};
22
23/**
24 * Name of the deploy-time `process.env` variable the test populates
25 * before deploying — sourced via `Config.String(...)` inside the
26 * Lambda's init phase.
27 */
28export const CONFIG_SECRET_ENV_KEY = "ALCHEMY_AWS_SECRET_TEST_SOURCE";
29
30export class SecretsTestFunction extends Lambda.Function<SecretsTestFunction>()(
31 "SecretsTestFunction",
32) {}
33
34export const SecretsTestFunctionLive = SecretsTestFunction.make(
35 {
36 main,
37 functionUrl: true,
38 },
39 Effect.gen(function* () {
40 // Secret from a literal — `Alchemy.Secret` coerces the literal to
41 // `Redacted` and the Lambda runtime accessor rebuilds the wrapper
42 // from the JSON marker stored in env.
43 const literalSecret = yield* Config.Redacted("LITERAL_SECRET").pipe(
44 Config.withDefault(Redacted.make(LITERAL_SECRET_VALUE)),
45 );
46
47 // Secret from a `Config` — resolved against the active
48 // `ConfigProvider` (process.env) at deploy time. The test populates
49 // `process.env[CONFIG_SECRET_ENV_KEY]` before deploying, so source
50 // from that same key (not a hard-coded literal).
51 const configSecret = yield* Config.Redacted(CONFIG_SECRET_ENV_KEY);
52
53 // Plain string variable — string round-trip.
54 const stringVar = yield* Config.String("STRING_VAR").pipe(
55 Config.withDefault(STRING_VAR_VALUE),
56 );
57
58 // Number variable — non-string values JSON.stringify on `set` and
59 // JSON.parse on the runtime accessor, so the accessor returns the
60 // original number.
61 const numberVar = yield* Config.Number("NUMBER_VAR").pipe(
62 Config.withDefault(NUMBER_VAR_VALUE),
63 );
64
65 // Object variable — same JSON round-trip as above for nested data.
66 const objectVar = yield* Config.String("OBJECT_VAR").pipe(
67 Config.withDefault(OBJECT_VAR_VALUE),
68 );
69
70 return {
71 fetch: Effect.gen(function* () {
72 const request = yield* HttpServerRequest;
73 const url = new URL(request.originalUrl);
74 const pathname = url.pathname;
75
76 switch (pathname) {
77 case "/ready":
78 return HttpServerResponse.text("ok");
79 case "/secret/literal": {
80 return yield* HttpServerResponse.json({
81 isRedacted: Redacted.isRedacted(literalSecret),
82 value: Redacted.value(literalSecret),
83 });
84 }
85 case "/secret/config": {
86 return yield* HttpServerResponse.json({
87 isRedacted: Redacted.isRedacted(configSecret),
88 value: Redacted.value(configSecret),
89 });
90 }
91 case "/var/string": {
92 return yield* HttpServerResponse.json({
93 type: typeof stringVar,
94 value: stringVar,
95 });
96 }
97 case "/var/number": {
98 return yield* HttpServerResponse.json({
99 type: typeof numberVar,
100 value: numberVar,
101 });
102 }
103 case "/var/object": {
104 return yield* HttpServerResponse.json({
105 type: typeof objectVar,
106 value: objectVar,
107 });
108 }
109 default:
110 return yield* HttpServerResponse.json(
111 { error: "Not found", pathname },
112 { status: 404 },
113 );
114 }
115 }).pipe(Effect.orDie),
116 };
117 }),
118);
119
120export default SecretsTestFunctionLive;