diff --git a/packages/alchemy/src/Cloudflare/Containers/ContainerApplication.ts b/packages/alchemy/src/Cloudflare/Containers/ContainerApplication.ts index 16df280127..156ce4448e 100644 --- a/packages/alchemy/src/Cloudflare/Containers/ContainerApplication.ts +++ b/packages/alchemy/src/Cloudflare/Containers/ContainerApplication.ts @@ -53,6 +53,24 @@ export namespace ContainerApplication { >[number]; export type Constraints = { tier?: number; + /** + * Limit container placement to specific geographic regions. + */ + regions?: ( + | "ENAM" + | "WNAM" + | "EEUR" + | "WEUR" + | "APAC" + | "SAM" + | "ME" + | "OC" + | "AFR" + )[]; + /** + * Restrict container placement to a compliance/data-residency boundary. + */ + jurisdiction?: "eu" | "fedramp"; }; export type Affinities = { colocation?: "datacenter"; @@ -639,9 +657,11 @@ export type ContainerShape = Main; * ``` * * `schedulingPolicy` selects the control-plane placement strategy, - * `constraints.tier` restricts which capacity tier instances may land on, and - * `affinities.colocation` keeps related instances in the same datacenter to - * reduce inter-instance latency. + * `constraints.tier` restricts which capacity tier instances may land on, + * `constraints.regions`/`constraints.jurisdiction` restrict placement to + * geographic regions or a compliance boundary (e.g. `jurisdiction: "eu"` for + * data residency), and `affinities.colocation` keeps related instances in the + * same datacenter to reduce inter-instance latency. * * ### Rollouts * When an update changes the configuration, `rollout` controls how the new diff --git a/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts b/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts index 466b5e3609..4e3a1880aa 100644 --- a/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts +++ b/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts @@ -111,6 +111,36 @@ describe("ContainerApplication", () => { }).pipe(logLevel), ); + // Placement constraints are a typed pass-through: `constraints.regions` + // and `constraints.jurisdiction` are forwarded verbatim to + // `createContainerApplication` and read back unchanged. This does not + // assert an instance actually lands in the requested region/jurisdiction + // (capacity is account/plan-dependent) — only that the Alchemy API + // boundary threads the fields through without dropping or renaming them. + test.provider("placement constraints round-trip", (scratch) => + Effect.gen(function* () { + yield* scratch.destroy(); + + const deployed = yield* scratch.deploy( + Effect.gen(function* () { + return { + app: yield* Cloudflare.Container("ConstrainedPlacement", { + image: "mendhak/http-https-echo:latest", + constraints: { regions: ["WEUR", "EEUR"], jurisdiction: "eu" }, + }).Application, + }; + }), + ); + + expect(deployed.app.constraints).toEqual({ + regions: ["WEUR", "EEUR"], + jurisdiction: "eu", + }); + + yield* scratch.destroy(); + }).pipe(logLevel), + ); + // Issue #953 (2): an `image` that already references the target registry // (e.g. pushed by CI) is deployed as-is — no docker pull/tag/push // round-trip. The first deploy pushes a public image into the account