1import * as Cloudflare from "@/Cloudflare";
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";
7
8/**
9 * Hard-coded values the integ test asserts against to prove the
10 * deploy-time bindings flow all the way through to the runtime.
11 */
12export const LITERAL_SECRET_VALUE = "sk-literal-secret-abc";
13export const STRING_VAR_VALUE = "plain-string-value";
14export const NUMBER_VAR_VALUE = 4242;
15export const OBJECT_VAR_VALUE = { host: "localhost", flags: { beta: true } };
16
17/**
18 * Name of the deploy-time `process.env` variable the test populates
19 * before deploying — sourced via `Config.String(...)` inside the
20 * worker's init phase.
21 */
22
23export default class SecretsTestWorker extends Cloudflare.Worker<SecretsTestWorker>()(
24 "SecretsTestWorker",
25 {
26 main: import.meta.url,
27 workersDev: { enabled: true, previewsEnabled: false },
28 },
29 Effect.gen(function* () {
30 // Secret from a literal — `Alchemy.Secret` coerces the literal to
31 // `Redacted` and the Worker provider deploys it as `secret_text`.
32 const literalSecret = yield* Config.Redacted("LITERAL_SECRET").pipe(
33 Config.withDefault(Redacted.make(LITERAL_SECRET_VALUE)),
34 );
35
36 // Secret from a `Config` — resolved against the active
37 // `ConfigProvider` (process.env) at deploy time.
38 const configSecret = yield* Config.Redacted("CONFIG_SECRET");
39
40 // Plain string variable — `plain_text` binding round-trip.
41 const stringVar = yield* Config.String("STRING_VAR").pipe(
42 Config.withDefault(STRING_VAR_VALUE),
43 );
44
45 // Number variable — non-string values JSON.stringify on `set` and
46 // JSON.parse on the runtime accessor, so the accessor returns the
47 // original number.
48 const numberVar = yield* Config.Number("NUMBER_VAR").pipe(
49 Config.withDefault(NUMBER_VAR_VALUE),
50 );
51
52 // Object variable — same JSON round-trip as above for nested data.
53 const objectVar = yield* Config.String("OBJECT_VAR").pipe(
54 Config.withDefault(OBJECT_VAR_VALUE),
55 );
56
57 return {
58 fetch: Effect.gen(function* () {
59 const request = yield* HttpServerRequest;
60 // `request.url` on Cloudflare workers is the pathname+query
61 // (relative). Use `originalUrl` to get the absolute URL so
62 // `new URL(...)` doesn't throw.
63 const pathname = new URL(request.originalUrl).pathname;
64 switch (pathname) {
65 case "/secret/literal": {
66 return yield* HttpServerResponse.json({
67 isRedacted: Redacted.isRedacted(literalSecret),
68 value: Redacted.value(literalSecret),
69 });
70 }
71 case "/secret/config": {
72 return yield* HttpServerResponse.json({
73 isRedacted: Redacted.isRedacted(configSecret),
74 value: Redacted.value(configSecret),
75 });
76 }
77 case "/var/string": {
78 return yield* HttpServerResponse.json({
79 type: typeof stringVar,
80 value: stringVar,
81 });
82 }
83 case "/var/number": {
84 return yield* HttpServerResponse.json({
85 type: typeof numberVar,
86 value: numberVar,
87 });
88 }
89 case "/var/object": {
90 return yield* HttpServerResponse.json({
91 type: typeof objectVar,
92 value: objectVar,
93 });
94 }
95 default:
96 return HttpServerResponse.text("ok");
97 }
98 }),
99 };
100 }),
101) {}