1/// <reference types="@cloudflare/workers-types" />
2
3import type { Pipeline } from "cloudflare:pipelines";
4import type * as Effect from "effect/Effect";
5import type { Redacted } from "effect/Redacted";
6import type * as Stream from "effect/Stream";
7import type { Output } from "../../Output.ts";
8// Aliased so the ambient Cloudflare `Rpc` namespace (from
9// @cloudflare/workers-types, referenced above) stays reachable for
10// `Rpc.DurableObjectBranded`.
11import type { Rpc as AlchemyRpc } from "../../Rpc.ts";
12import type { WorkflowBinding, WorkflowLike } from "../Workflows/Workflow.ts";
13// NOTE: import the service modules directly rather than `import * as Cloudflare
14// from "../index.ts"`. Importing the whole Cloudflare barrel here creates a
15// circular re-export when the barrel does `export * from "./Workers/index.ts"`
16// (the barrel must enumerate this module, which imports the barrel back), which
17// collapses inference of the large `providers()` collection to `unknown`.
18import type * as AI from "../AI/index.ts";
19import type * as AnalyticsEngine from "../AnalyticsEngine/index.ts";
20import type * as ArtifactsNs from "../Artifacts/index.ts";
21import type { Container } from "../Containers/Container.ts";
22import type * as D1 from "../D1/index.ts";
23import type * as Email from "../Email/index.ts";
24import type * as FlagshipNs from "../Flagship/index.ts";
25import type * as HyperdriveNs from "../Hyperdrive/index.ts";
26import type * as ImagesNs from "../Images/index.ts";
27import type * as KV from "../KV/index.ts";
28import type * as PipelinesNs from "../Pipelines/index.ts";
29import type * as Queues from "../Queues/index.ts";
30import type * as R2 from "../R2/index.ts";
31import type * as StreamNs from "../Stream/index.ts";
32import type { VpcService } from "../VpcService/VpcService.ts";
33import type { VpcServiceLookup } from "../VpcService/VpcServiceLookup.ts";
34import type { DispatchNamespace as DispatchNamespaceResource } from "../WorkersForPlatforms/DispatchNamespace.ts";
35import type { AIBinding } from "./AIBinding.ts";
36import type { Assets } from "./Assets.ts";
37import type * as WorkerOnlyBinding from "./Binding.ts";
38import type { BrowserBinding } from "./BrowserBinding.ts";
39import type { DurableObjectLike } from "./DurableObject.ts";
40import type { RateLimitBinding } from "./RateLimitBinding.ts";
41import type { RpcErrorEnvelope, RpcStreamEnvelope } from "./Rpc.ts";
42import type { SecretKeyBinding } from "./SecretKeyBinding.ts";
43import type { VersionMetadataBinding } from "./VersionMetadataBinding.ts";
44import type { Worker } from "./Worker.ts";
45import type { WorkerEntrypointBinding } from "./WorkerEntrypoint.ts";
46import type { WorkerLoader as WorkerLoaderResource } from "./WorkerLoader.ts";
47
48export type InferEnv<W> =
49 W extends Effect.Effect<infer A, infer _E, infer _R>
50 ? InferEnv<A>
51 : W extends Worker<any>
52 ? InferEnv<Exclude<W["Props"]["env"], undefined>>
53 : W extends { readonly "~alchemy/WorkerEnv": infer Env }
54 ? InferEnv<Env>
55 : {
56 [k in keyof W]: GetBindingType<W[k]>;
57 };
58
59export type GetBindingType<T> =
60 // A named-entrypoint service binding (`Cloudflare.WorkerEntrypoint`).
61 // Tested first: the marker *contains* a Worker, so the later Worker
62 // branches must never see it.
63 T extends WorkerEntrypointBinding<infer Entrypoint>
64 ? Service<Entrypoint>
65 : // A Container bound in `env` is a container-backed Durable Object class —
66 // the runtime binding is the class's namespace. Must be tested BEFORE the
67 // generic Effect unwrap: a Container declaration is itself an Effect.
68 T extends Container.Decl<any, any, any, any, infer DOShape>
69 ? DurableObjectNamespace<DOShape & Rpc.DurableObjectBranded>
70 : // A Worker-only binding lifted by `.pipe(Alchemy.remote())` — the brand
71 // carries the underlying binding value type; recurse on it. Must also
72 // be tested BEFORE the generic Effect unwrap (its Effect `A` is the
73 // runtime client, not the binding value).
74 T extends WorkerOnlyBinding.BindingEffect<infer B>
75 ? GetBindingType<B>
76 : // `R2.S3Credentials` is an Effect whose `A` is the Effect-native
77 // accessor; on an async Worker's `env` it arrives as a JSON string
78 // (a `secret_text` when deployed). Tested BEFORE the Effect unwrap.
79 T extends R2.S3Credentials
80 ? string
81 : // `Worker.URL` (an Effect resolving to a deferred string accessor) needs
82 // no case of its own: the generic Effect unwrap below reduces it to
83 // `string` via the fallthrough.
84 T extends
85 | Output<infer A, infer _Req>
86 | Effect.Effect<infer A, infer _E, infer _R>
87 ? GetBindingType<A>
88 : T extends FlagshipNs.App
89 ? Flagship
90 : T extends Assets
91 ? Service
92 : T extends AlchemyRpc<infer Shape extends object>
93 ? RpcWireShape<Shape> & Service
94 : T extends D1.Database
95 ? D1Database
96 : T extends R2.Bucket
97 ? R2Bucket
98 : T extends KV.Namespace
99 ? KVNamespace
100 : T extends DispatchNamespaceResource
101 ? DispatchNamespace
102 : T extends Queues.Queue
103 ? Queue<unknown>
104 : T extends AI.Gateway
105 ? Ai
106 : T extends AIBinding
107 ? Ai
108 : T extends AI.Search
109 ? AiSearchInstance
110 : T extends AI.SearchNamespace
111 ? AiSearchNamespace
112 : T extends Email.SendEmail
113 ? SendEmail
114 : T extends AnalyticsEngine.Dataset
115 ? AnalyticsEngineDataset
116 : T extends ArtifactsNs.Namespace
117 ? Artifacts
118 : T extends RateLimitBinding
119 ? RateLimit
120 : T extends SecretKeyBinding
121 ? CryptoKey
122 : T extends ImagesNs.ImagesBinding
123 ? ImagesBinding
124 : T extends BrowserBinding
125 ? BrowserRun
126 : // The ambient global `StreamBinding` from
127 // @cloudflare/workers-types (the alchemy binding value
128 // type of the same name is only reachable as
129 // `StreamNs.StreamBinding`).
130 T extends StreamNs.StreamBinding
131 ? StreamBinding
132 : T extends HyperdriveNs.Connection
133 ? Hyperdrive
134 : T extends VersionMetadataBinding
135 ? WorkerVersionMetadata
136 : T extends WorkerLoaderResource
137 ? WorkerLoader
138 : T extends
139 | WorkflowLike<
140 infer Params
141 >
142 | WorkflowBinding<
143 infer Params
144 >
145 ? Workflow<Params>
146 : T extends DurableObjectLike
147 ? DurableObjectNamespace<
148 Exclude<
149 T["Shape"],
150 undefined
151 >
152 >
153 : T extends
154 | VpcService
155 | VpcServiceLookup
156 ? Fetcher
157 : T extends
158 | PipelinesNs.Stream
159 | PipelinesNs.LegacyPipeline
160 ? Pipeline
161 : T extends Redacted<any>
162 ? // redacteds are always stored as secret_text, so are always string
163 // we JSON.stringify when not a Redacted<string>
164 string
165 : T;
166
167/**
168 * Cloudflare service-binding wire shape for an Effect-native Worker.
169 *
170 * Effect/Stream return values are encoded as envelopes on the wire (see
171 * `RpcErrorEnvelope`, `RpcStreamEnvelope`), so the mapped types reflect what
172 * the raw binding actually resolves to. `fetch` is dropped from the user
173 * shape and re-introduced via `Service` so callers get the standard
174 * `(input, init?) => Promise<Response>` signature.
175 *
176 * Use {@link toRpcAsync} to wrap a binding into a Promise<T>-flavored view
177 * where envelopes are decoded for you.
178 */
179export type RpcWireShape<Shape> = {
180 [K in keyof Shape as K extends "fetch" ? never : K]: Shape[K] extends (
181 ...args: infer A
182 ) => Effect.Effect<infer T, any, any>
183 ? (...args: A) => Promise<T | RpcErrorEnvelope>
184 : Shape[K] extends (...args: infer A) => Stream.Stream<any, any, any>
185 ? (...args: A) => Promise<RpcStreamEnvelope | RpcErrorEnvelope>
186 : Shape[K] extends Effect.Effect<infer T, any, any>
187 ? Promise<T | RpcErrorEnvelope>
188 : Shape[K] extends Stream.Stream<any, any, any>
189 ? Promise<RpcStreamEnvelope | RpcErrorEnvelope>
190 : Shape[K] extends (...args: infer A) => infer R
191 ? (...args: A) => Promise<Awaited<R>>
192 : Promise<Shape[K]>;
193};