Skip to content

Commit 133c042

Browse files
authored
[wrangler] feat: add support for placement in wrangler config (#3095)
* [wrangler] feat: add support for placement in wrangler config Allows a placement object in the wrangler config with a mode of off or smart to configure Smart placement. * increase specificity for workers placement * Add example to placement changeset
1 parent a97344d commit 133c042

11 files changed

+77
-1
lines changed

.changeset/short-bottles-smell.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
feat: add support for placement in wrangler config
6+
7+
Allows a `placement` object in the wrangler config with a mode of `off` or `smart` to configure [Smart placement](https://developers.cloudflare.com/workers/platform/smart-placement/). Enabling Smart Placement can be done in your `wrangler.toml` like:
8+
9+
```toml
10+
[placement]
11+
mode = "smart"
12+
```

packages/wrangler/src/__tests__/configuration.test.ts

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ describe("normalizeAndValidateConfig()", () => {
8484
first_party_worker: undefined,
8585
keep_vars: undefined,
8686
logpush: undefined,
87+
placement: undefined,
8788
});
8889
expect(diagnostics.hasErrors()).toBe(false);
8990
expect(diagnostics.hasWarnings()).toBe(false);
@@ -955,6 +956,9 @@ describe("normalizeAndValidateConfig()", () => {
955956
node_compat: true,
956957
first_party_worker: true,
957958
logpush: true,
959+
placement: {
960+
mode: "smart",
961+
},
958962
};
959963

960964
const { config, diagnostics } = normalizeAndValidateConfig(
@@ -1030,6 +1034,9 @@ describe("normalizeAndValidateConfig()", () => {
10301034
node_compat: "INVALID",
10311035
first_party_worker: "INVALID",
10321036
logpush: "INVALID",
1037+
placement: {
1038+
mode: "INVALID",
1039+
},
10331040
} as unknown as RawEnvironment;
10341041

10351042
const { config, diagnostics } = normalizeAndValidateConfig(
@@ -1093,6 +1100,7 @@ describe("normalizeAndValidateConfig()", () => {
10931100
- Expected \\"name\\" to be of type string, alphanumeric and lowercase with dashes only but got 111.
10941101
- Expected \\"main\\" to be of type string but got 1333.
10951102
- Expected \\"usage_model\\" field to be one of [\\"bundled\\",\\"unbound\\"] but got \\"INVALID\\".
1103+
- Expected \\"placement.mode\\" field to be one of [\\"off\\",\\"smart\\"] but got \\"INVALID\\".
10961104
- The field \\"define.DEF1\\" should be a string but got 1777.
10971105
- Expected \\"no_bundle\\" to be of type boolean but got \\"INVALID\\".
10981106
- Expected \\"minify\\" to be of type boolean but got \\"INVALID\\".

packages/wrangler/src/api/pages/create-worker-bundle-contents.ts

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ function createWorkerBundleFormData(workerBundle: BundleResult): FormData {
7070
usage_model: undefined,
7171
keepVars: undefined,
7272
logpush: undefined,
73+
placement: undefined,
7374
};
7475

7576
return createWorkerUploadForm(worker);

packages/wrangler/src/config/environment.ts

+7
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,13 @@ interface EnvironmentInheritable {
265265
* @inheritable
266266
*/
267267
logpush: boolean | undefined;
268+
269+
/**
270+
* Specify how the worker should be located to minimize round-trip time.
271+
*
272+
* More details: https://developers.cloudflare.com/workers/platform/smart-placement/
273+
*/
274+
placement: { mode: "off" | "smart" } | undefined;
268275
}
269276

270277
export type DurableObjectBindings = {

packages/wrangler/src/config/validation.ts

+27
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,32 @@ function validateRoutes(
873873
);
874874
}
875875

876+
function normalizeAndValidatePlacement(
877+
diagnostics: Diagnostics,
878+
topLevelEnv: Environment | undefined,
879+
rawEnv: RawEnvironment
880+
): Config["placement"] {
881+
if (rawEnv.placement) {
882+
validateRequiredProperty(
883+
diagnostics,
884+
"placement",
885+
"mode",
886+
rawEnv.placement.mode,
887+
"string",
888+
["off", "smart"]
889+
);
890+
}
891+
892+
return inheritable(
893+
diagnostics,
894+
topLevelEnv,
895+
rawEnv,
896+
"placement",
897+
() => true,
898+
undefined
899+
);
900+
}
901+
876902
/**
877903
* Validate top-level environment configuration and return the normalized values.
878904
*/
@@ -1060,6 +1086,7 @@ function normalizeAndValidateEnvironment(
10601086
isOneOf("bundled", "unbound"),
10611087
undefined
10621088
),
1089+
placement: normalizeAndValidatePlacement(diagnostics, topLevelEnv, rawEnv),
10631090
build,
10641091
workers_dev,
10651092
// Not inherited fields

packages/wrangler/src/create-worker-upload-form.ts

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
CfWorkerInit,
55
CfModuleType,
66
CfDurableObjectMigrations,
7+
CfPlacement,
78
} from "./worker.js";
89

910
export function toMimeType(type: CfModuleType): string {
@@ -71,6 +72,7 @@ export interface WorkerMetadata {
7172
bindings: WorkerMetadataBinding[];
7273
keep_bindings?: WorkerMetadataBinding["type"][];
7374
logpush?: boolean;
75+
placement?: CfPlacement;
7476
// Allow unsafe.metadata to add arbitary properties at runtime
7577
[key: string]: unknown;
7678
}
@@ -89,6 +91,7 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
8991
compatibility_flags,
9092
keepVars,
9193
logpush,
94+
placement,
9295
} = worker;
9396

9497
let { modules } = worker;
@@ -320,6 +323,7 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
320323
capnp_schema: bindings.logfwdr?.schema,
321324
...(keepVars && { keep_bindings: ["plain_text", "json"] }),
322325
...(logpush !== undefined && { logpush }),
326+
...(placement && { placement }),
323327
};
324328

325329
if (bindings.unsafe?.metadata !== undefined) {

packages/wrangler/src/dev/remote.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ async function createRemoteWorkerInit(props: {
576576
usage_model: props.usageModel,
577577
keepVars: true,
578578
logpush: false,
579+
placement: undefined, // no placement in dev
579580
};
580581

581582
return init;

packages/wrangler/src/init.ts

+5
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export type ServiceMetadataRes = {
8080
usage_model: "bundled" | "unbound";
8181
compatibility_date: string;
8282
last_deployed_from?: "wrangler" | "dash" | "api";
83+
placement_mode?: "smart";
8384
};
8485
};
8586
created_on: string;
@@ -868,6 +869,10 @@ async function getWorkerConfig(
868869
new Date().toISOString().substring(0, 10),
869870
...routeOrRoutesToConfig,
870871
usage_model: serviceEnvMetadata.script.usage_model,
872+
placement:
873+
serviceEnvMetadata.script.placement_mode === "smart"
874+
? { mode: "smart" }
875+
: undefined,
871876
...(durableObjectClassNames.length
872877
? {
873878
migrations: [

packages/wrangler/src/publish/publish.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import type {
3535
import type { Entry } from "../entry";
3636
import type { PutConsumerBody } from "../queues/client";
3737
import type { AssetPaths } from "../sites";
38-
import type { CfWorkerInit } from "../worker";
38+
import type { CfWorkerInit, CfPlacement } from "../worker";
3939

4040
type Props = {
4141
config: Config;
@@ -571,6 +571,10 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
571571
});
572572
}
573573

574+
// The upload API only accepts an empty string or no specified placement for the "off" mode.
575+
const placement: CfPlacement | undefined =
576+
config.placement?.mode === "smart" ? { mode: "smart" } : undefined;
577+
574578
const worker: CfWorkerInit = {
575579
name: scriptName,
576580
main: {
@@ -586,6 +590,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
586590
usage_model: config.usage_model,
587591
keepVars,
588592
logpush: props.logpush !== undefined ? props.logpush : config.logpush,
593+
placement,
589594
};
590595

591596
// As this is not deterministic for testing, we detect if in a jest environment and run asynchronously

packages/wrangler/src/secret/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export const secret = (secretYargs: CommonYargsArgv) => {
123123
usage_model: undefined,
124124
keepVars: false, // this doesn't matter since it's a new script anyway
125125
logpush: false,
126+
placement: undefined,
126127
}),
127128
}
128129
);

packages/wrangler/src/worker.ts

+5
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ export interface CfDurableObjectMigrations {
203203
}[];
204204
}
205205

206+
export interface CfPlacement {
207+
mode: "smart";
208+
}
209+
206210
/**
207211
* Options for creating a `CfWorker`.
208212
*/
@@ -246,6 +250,7 @@ export interface CfWorkerInit {
246250
usage_model: "bundled" | "unbound" | undefined;
247251
keepVars: boolean | undefined;
248252
logpush: boolean | undefined;
253+
placement: CfPlacement | undefined;
249254
}
250255

251256
export interface CfWorkerContext {

0 commit comments

Comments
 (0)