Skip to content

Commit d1932dd

Browse files
committed
feat: add override support
1 parent 50bb7bf commit d1932dd

3 files changed

Lines changed: 123 additions & 5 deletions

File tree

packages/wrangler/e2e/create-server.test.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,103 @@ describe("createServer", { sequential: true }, () => {
329329
});
330330
});
331331

332+
it("overrides vars and secrets for config path workers", async ({
333+
expect,
334+
}) => {
335+
await helper.seed({
336+
"wrangler.jsonc": dedent`
337+
{
338+
"name": "var-overrides-worker",
339+
"main": "src/index.ts",
340+
"compatibility_date": "2026-05-20",
341+
"vars": { "CONFIG_VAR": "from-config" },
342+
"secrets": { "required": ["API_TOKEN", "SECRET_FROM_FILE"] }
343+
}
344+
`,
345+
".dev.vars": dedent`
346+
API_TOKEN=from-dev-vars
347+
SECRET_FROM_FILE=from-dev-vars
348+
`,
349+
"src/index.ts": dedent`
350+
export default {
351+
fetch(request, env) {
352+
return Response.json({
353+
CONFIG_VAR: env.CONFIG_VAR,
354+
API_TOKEN: env.API_TOKEN,
355+
SECRET_FROM_FILE: env.SECRET_FROM_FILE,
356+
ADDED_VAR: env.ADDED_VAR,
357+
NULL_VAR: env.NULL_VAR,
358+
});
359+
}
360+
};
361+
`,
362+
});
363+
364+
const server = createServer({
365+
root: helper.tmpPath,
366+
workers: [
367+
{
368+
configPath: "./wrangler.jsonc",
369+
overrides: {
370+
vars: {
371+
CONFIG_VAR: "from-override",
372+
ADDED_VAR: "from-override",
373+
NULL_VAR: null,
374+
},
375+
secrets: {
376+
API_TOKEN: "from-override",
377+
},
378+
},
379+
},
380+
],
381+
});
382+
onTestFinished(server.close);
383+
384+
await server.listen();
385+
386+
const response = await server.fetch("/");
387+
await expect(response.json()).resolves.toEqual({
388+
CONFIG_VAR: "from-override",
389+
API_TOKEN: "from-override",
390+
SECRET_FROM_FILE: "from-dev-vars",
391+
ADDED_VAR: "from-override",
392+
NULL_VAR: null,
393+
});
394+
});
395+
396+
it(`supports "nodejs_compat" flag`, async ({ expect }) => {
397+
await helper.seed({
398+
"wrangler.jsonc": dedent`
399+
{
400+
"name": "nodejs-compat-worker",
401+
"main": "src/index.ts",
402+
"compatibility_date": "2026-05-20",
403+
"compatibility_flags": ["nodejs_compat"]
404+
}
405+
`,
406+
"src/index.ts": dedent`
407+
import { Stream } from "node:stream";
408+
409+
export default {
410+
fetch() {
411+
return new Response(String(typeof Stream));
412+
}
413+
};
414+
`,
415+
});
416+
417+
const server = createServer({
418+
root: helper.tmpPath,
419+
workers: [{ configPath: "./wrangler.jsonc" }],
420+
});
421+
onTestFinished(server.close);
422+
423+
await server.listen();
424+
425+
const response = await server.fetch("/");
426+
await expect(response.text()).resolves.toBe("function");
427+
});
428+
332429
it("supports Workers Sites", async ({ expect }) => {
333430
await helper.seed({
334431
"public/hello.txt": "Hello from Workers Sites",

packages/wrangler/src/api/server.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,21 @@ import type {
2525
FetcherScheduledResult,
2626
} from "@cloudflare/workers-types/experimental";
2727
import type { Config, RawConfig } from "@cloudflare/workers-utils";
28-
import type { DispatchFetch, RequestInfo } from "miniflare";
28+
import type { DispatchFetch, Json, RequestInfo } from "miniflare";
2929

3030
export type InlineConfig = Omit<RawConfig, "env">;
3131

32+
export type ConfigOverrides = {
33+
vars?: Record<string, Json>;
34+
secrets?: Record<string, string>;
35+
};
36+
3237
export type WorkerInput =
3338
| {
3439
root?: string;
3540
configPath: string | URL;
3641
env?: string;
42+
overrides?: ConfigOverrides;
3743
}
3844
| {
3945
root?: string;
@@ -166,6 +172,15 @@ function resolveWorkerInputs(
166172
"config" in input
167173
? normalizeInlineWorkerConfig(input.config, root)
168174
: undefined;
175+
const overrides = "configPath" in input ? input.overrides : undefined;
176+
const bindings = convertConfigToBindings(
177+
inlineConfig ?? { vars: overrides?.vars },
178+
{ usePreviewIds: true }
179+
);
180+
181+
for (const [key, value] of Object.entries(overrides?.secrets ?? {})) {
182+
bindings[key] = { type: "secret_text", value };
183+
}
169184

170185
return {
171186
// Uses an empty string to avoid dev env from auto discovering a config file and merging it with the inline config
@@ -177,9 +192,7 @@ function resolveWorkerInputs(
177192
compatibilityFlags: inlineConfig?.compatibility_flags,
178193
complianceRegion: inlineConfig?.compliance_region,
179194
pythonModules: inlineConfig?.python_modules,
180-
bindings: inlineConfig
181-
? convertConfigToBindings(inlineConfig, { usePreviewIds: true })
182-
: undefined,
195+
bindings,
183196
migrations: inlineConfig?.migrations,
184197
containers: inlineConfig?.containers,
185198
triggers: inlineConfig?.triggers?.crons?.map((cron) => ({

packages/wrangler/src/dev.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,21 @@ export function getBindings(
525525
// getVarsForDev returns typed bindings: config vars are plain_text/json,
526526
// while .dev.vars/.env vars are secret_text.
527527
// When secrets is defined, only declared secret keys are loaded from files.
528+
const secrets = configParam.secrets
529+
? {
530+
...configParam.secrets,
531+
required: configParam.secrets?.required?.filter(
532+
(secret) => inputBindings?.[secret]?.type !== "secret_text"
533+
),
534+
}
535+
: undefined;
528536
const vars = getVarsForDev(
529537
configParam.userConfigPath,
530538
envFiles,
531539
configParam.vars,
532540
env,
533541
false,
534-
configParam.secrets
542+
secrets
535543
);
536544
for (const [name, binding] of Object.entries(vars)) {
537545
// Only override plain_text/json/secret_text vars, not other binding types like kv_namespace

0 commit comments

Comments
 (0)