1import type * as cf from "@cloudflare/workers-types";
2import * as Data from "effect/Data";
3import * as Effect from "effect/Effect";
4import * as Binding from "../../Binding.ts";
5import type { RuntimeContext } from "../../RuntimeContext.ts";
6import type { App } from "./App.ts";
7
8/**
9 * Bind a {@link App} to a Worker and obtain the Effect-native flag-evaluation
10 * client (`get`, `getBooleanValue`, `getStringValue`, …).
11 *
12 * `ReadFlags` is a single identifier that is simultaneously the binding's
13 * Context tag, its type, and the callable —
14 * `yield* Cloudflare.Flagship.ReadFlags(app)`.
15 *
16 * **Example:** Evaluating a flag inside a Worker
17 * ```typescript
18 * const flags = yield* Cloudflare.Flagship.ReadFlags(MyApp);
19 * const enabled = yield* flags.getBooleanValue("new-checkout", false);
20 * ```
21 *
22 * @binding
23 * @product Flagship
24 * @category Developer Platform
25 */
26export interface ReadFlags extends Binding.Service<
27 ReadFlags,
28 "Cloudflare.Flagship.ReadFlags",
29 (app: App) => Effect.Effect<ReadFlagsClient>
30> {}
31
32export const ReadFlags = Binding.Service<ReadFlags>(
33 "Cloudflare.Flagship.ReadFlags",
34);
35
36export class FlagshipError extends Data.TaggedError("FlagshipError")<{
37 message: string;
38 cause: unknown;
39}> {}
40
41/** An Effect produced by a {@link ReadFlagsClient} operation. */
42type FlagshipEffect<A> = Effect.Effect<A, FlagshipError, RuntimeContext>;
43
44// Re-exported so callers don't reach into the `@cloudflare/workers-types`
45// namespace directly.
46export type EvaluationContext = cf.FlagshipEvaluationContext;
47export type EvaluationDetails<T> = cf.FlagshipEvaluationDetails<T>;
48
49/**
50 * Effect-native client for a Cloudflare Flagship (feature flags) binding.
51 *
52 * Mirrors the runtime {@link cf.Flagship} binding one-to-one, translating each
53 * promise-returning method into an Effect. Flagship evaluation never throws —
54 * it falls back to the provided `defaultValue` — so the {@link FlagshipError}
55 * channel only surfaces unexpected runtime failures (e.g. a misconfigured
56 * binding). Use `Cloudflare.Flagship.ReadFlags(app)` inside a Worker's init
57 * phase to obtain it.
58 */
59export interface ReadFlagsClient {
60 /**
61 * Effect resolving to the raw Cloudflare Flagship runtime binding.
62 */
63 raw: Effect.Effect<cf.Flagship, never, RuntimeContext>;
64 /**
65 * Get a flag value without type checking. Use when the flag type is not
66 * known at compile time.
67 */
68 get(
69 flagKey: string,
70 defaultValue?: unknown,
71 context?: EvaluationContext,
72 ): FlagshipEffect<unknown>;
73 /**
74 * Get a boolean flag value, falling back to `defaultValue` when evaluation
75 * fails or the flag type does not match.
76 */
77 getBooleanValue(
78 flagKey: string,
79 defaultValue: boolean,
80 context?: EvaluationContext,
81 ): FlagshipEffect<boolean>;
82 /**
83 * Get a string flag value, falling back to `defaultValue` when evaluation
84 * fails or the flag type does not match.
85 */
86 getStringValue(
87 flagKey: string,
88 defaultValue: string,
89 context?: EvaluationContext,
90 ): FlagshipEffect<string>;
91 /**
92 * Get a number flag value, falling back to `defaultValue` when evaluation
93 * fails or the flag type does not match.
94 */
95 getNumberValue(
96 flagKey: string,
97 defaultValue: number,
98 context?: EvaluationContext,
99 ): FlagshipEffect<number>;
100 /**
101 * Get a typed object flag value, falling back to `defaultValue` when
102 * evaluation fails or the flag type does not match.
103 */
104 getObjectValue<T extends object>(
105 flagKey: string,
106 defaultValue: T,
107 context?: EvaluationContext,
108 ): FlagshipEffect<T>;
109 /**
110 * Get a boolean flag value with full evaluation details (variant, reason,
111 * error code).
112 */
113 getBooleanDetails(
114 flagKey: string,
115 defaultValue: boolean,
116 context?: EvaluationContext,
117 ): FlagshipEffect<EvaluationDetails<boolean>>;
118 /**
119 * Get a string flag value with full evaluation details (variant, reason,
120 * error code).
121 */
122 getStringDetails(
123 flagKey: string,
124 defaultValue: string,
125 context?: EvaluationContext,
126 ): FlagshipEffect<EvaluationDetails<string>>;
127 /**
128 * Get a number flag value with full evaluation details (variant, reason,
129 * error code).
130 */
131 getNumberDetails(
132 flagKey: string,
133 defaultValue: number,
134 context?: EvaluationContext,
135 ): FlagshipEffect<EvaluationDetails<number>>;
136 /**
137 * Get a typed object flag value with full evaluation details (variant,
138 * reason, error code).
139 */
140 getObjectDetails<T extends object>(
141 flagKey: string,
142 defaultValue: T,
143 context?: EvaluationContext,
144 ): FlagshipEffect<EvaluationDetails<T>>;
145}