Skip to content

Commit 6e31e66

Browse files
committed
fix: routes fetches based on worker routes
1 parent e98f76a commit 6e31e66

7 files changed

Lines changed: 103 additions & 48 deletions

File tree

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,67 @@ describe("createServer", { sequential: true }, () => {
135135
);
136136
});
137137

138+
it("routes fetches based on worker routes", async ({ expect }) => {
139+
await helper.seed({
140+
"wrangler.primary.jsonc": dedent`
141+
{
142+
"name": "primary-worker",
143+
"main": "src/primary.ts",
144+
"compatibility_date": "2026-05-20",
145+
"routes": ["primary.example.com/*"]
146+
}
147+
`,
148+
"src/primary.ts": dedent`
149+
export default {
150+
fetch(request) {
151+
return Response.json({ name: "primary", url: request.url });
152+
}
153+
};
154+
`,
155+
"src/auxiliary.ts": dedent`
156+
export default {
157+
fetch(request) {
158+
return Response.json({ name: "auxiliary", url: request.url });
159+
}
160+
};
161+
`,
162+
});
163+
164+
const server = createServer({
165+
root: helper.tmpPath,
166+
workers: [
167+
{ configPath: "./wrangler.primary.jsonc" },
168+
{
169+
config: {
170+
name: "auxiliary-worker",
171+
main: "src/auxiliary.ts",
172+
compatibility_date: "2026-05-20",
173+
routes: ["auxiliary.example.com/*"],
174+
},
175+
},
176+
],
177+
});
178+
onTestFinished(server.close);
179+
180+
await server.listen();
181+
182+
const primaryResponse = await server.fetch(
183+
"http://primary.example.com/path?value=1"
184+
);
185+
await expect(primaryResponse.json()).resolves.toEqual({
186+
name: "primary",
187+
url: "http://primary.example.com/path?value=1",
188+
});
189+
190+
const auxiliaryResponse = await server.fetch(
191+
"http://auxiliary.example.com/path?value=2"
192+
);
193+
await expect(auxiliaryResponse.json()).resolves.toEqual({
194+
name: "auxiliary",
195+
url: "http://auxiliary.example.com/path?value=2",
196+
});
197+
});
198+
138199
it("supports overriding fetch for outbound requests", async ({ expect }) => {
139200
await helper.seed({
140201
"wrangler.jsonc": dedent`

packages/wrangler/src/api/server.ts

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@ import type {
1818
LogLevel,
1919
ServiceFetch,
2020
StartDevWorkerInput,
21-
StartDevWorkerOptions,
2221
} from "./startDevWorker/types";
2322
import type {
2423
FetcherScheduledOptions,
2524
FetcherScheduledResult,
2625
} from "@cloudflare/workers-types/experimental";
27-
import type { Config, RawConfig } from "@cloudflare/workers-utils";
26+
import type { Config, RawConfig, Trigger } from "@cloudflare/workers-utils";
2827
import type { DispatchFetch, Json, RequestInfo } from "miniflare";
2928

3029
export type InlineConfig = Omit<RawConfig, "env">;
@@ -131,6 +130,28 @@ function normalizeInlineWorkerConfig(
131130
return normalizedConfig;
132131
}
133132

133+
type ConfigRoute = NonNullable<Config["route"]> | NonNullable<Config["routes"]>[number];
134+
135+
function routeToTrigger(route: ConfigRoute): Extract<Trigger, { type: "route" }> {
136+
return typeof route === "string"
137+
? { type: "route", pattern: route }
138+
: { type: "route", ...route };
139+
}
140+
141+
function getInlineConfigTriggers(config: Config): Trigger[] {
142+
const routes = [
143+
...(config.route ? [config.route] : []),
144+
...(config.routes ?? []),
145+
].map(routeToTrigger);
146+
const crons =
147+
config.triggers.crons?.map<Extract<Trigger, { type: "cron" }>>((cron) => ({
148+
type: "cron",
149+
cron,
150+
})) ?? [];
151+
152+
return [...routes, ...crons];
153+
}
154+
134155
async function resolveFetchInput(
135156
input: RequestInfo,
136157
session: ServerSession
@@ -195,10 +216,7 @@ function resolveWorkerInputs(
195216
bindings,
196217
migrations: inlineConfig?.migrations,
197218
containers: inlineConfig?.containers,
198-
triggers: inlineConfig?.triggers?.crons?.map((cron) => ({
199-
type: "cron" as const,
200-
cron,
201-
})),
219+
triggers: inlineConfig ? getInlineConfigTriggers(inlineConfig) : undefined,
202220
tailConsumers: inlineConfig?.tail_consumers,
203221
streamingTailConsumers: inlineConfig?.streaming_tail_consumers,
204222
assets: inlineConfig?.assets?.directory,
@@ -216,7 +234,8 @@ function resolveWorkerInputs(
216234
((request) => {
217235
return globalThis.fetch(request.url, request);
218236
}),
219-
multiworkerPrimary: isPrimaryWorker && isMultiworker ? true : undefined,
237+
multiworkerPrimary: isMultiworker ? isPrimaryWorker : undefined,
238+
inferOriginFromRoutes: false,
220239
},
221240
build: {
222241
nodejsCompatMode: (config) => {
@@ -298,44 +317,6 @@ async function updateConfig(
298317
}
299318
}
300319

301-
// TODO: Do we want this?
302-
function maybePrintScheduledWorkerWarning(
303-
serverSession: ServerSession,
304-
url: URL
305-
): void {
306-
const workersWithCronTriggers = serverSession.devEnvs
307-
.map((devEnv) => devEnv.config.latestConfig)
308-
.filter((config): config is StartDevWorkerOptions => config !== undefined)
309-
.filter((config) =>
310-
config.triggers?.some((trigger) => trigger.type === "cron")
311-
);
312-
313-
if (workersWithCronTriggers.length === 0) {
314-
return;
315-
}
316-
317-
const testScheduled = workersWithCronTriggers.every(
318-
(config) => config.dev.testScheduled
319-
);
320-
if (testScheduled) {
321-
return;
322-
}
323-
324-
const host =
325-
url.hostname === "0.0.0.0" || url.hostname === "::"
326-
? "localhost"
327-
: url.hostname.includes(":")
328-
? `[${url.hostname}]`
329-
: url.hostname;
330-
331-
logger.once.warn(
332-
`Scheduled Workers are not automatically triggered during local development.\n` +
333-
`To manually trigger a scheduled event, run:\n` +
334-
` curl "http://${host}:${url.port}/cdn-cgi/handler/scheduled"\n` +
335-
`For more details, see https://developers.cloudflare.com/workers/configuration/cron-triggers/#test-cron-triggers-locally`
336-
);
337-
}
338-
339320
/**
340321
* Creates a worker server with a small, migration-focused API surface.
341322
*
@@ -415,9 +396,8 @@ export function createServer(options: ServerOptions): WorkerServer {
415396
const session = await createSession(currentOptions, serverAuthHook);
416397

417398
try {
418-
const ready = await waitForPrimaryReady(session);
399+
await waitForPrimaryReady(session);
419400
serverSession = session;
420-
maybePrintScheduledWorkerWarning(session, ready.url);
421401
} catch (error) {
422402
await teardownSession(session);
423403
throw error;

packages/wrangler/src/api/startDevWorker/ConfigController.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,11 @@ async function resolveDevConfig(
149149
origin: {
150150
secure:
151151
input.dev?.origin?.secure ?? config.dev.upstream_protocol === "https",
152-
hostname: host ?? getInferredHost(routes, config.configPath),
152+
hostname:
153+
host ??
154+
(input.dev?.inferOriginFromRoutes
155+
? getInferredHost(routes, config.configPath)
156+
: undefined),
153157
},
154158
watch: input.dev?.watch,
155159
liveReload: input.dev?.liveReload || false,
@@ -159,6 +163,7 @@ async function resolveDevConfig(
159163
persist: localPersistencePath,
160164
registry: input.dev?.registry,
161165
multiworkerPrimary: input.dev?.multiworkerPrimary,
166+
inferOriginFromRoutes: input.dev?.inferOriginFromRoutes,
162167
enableContainers:
163168
input.dev?.enableContainers ?? config.dev.enable_containers,
164169
dockerPath: input.dev?.dockerPath ?? getDockerPath(),

packages/wrangler/src/api/startDevWorker/LocalRuntimeController.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,13 @@ export async function convertToConfigBundle(
112112
const bindings: Record<string, Binding> = { ...event.config.bindings };
113113

114114
const crons = [];
115+
const routes = [];
115116
const queueConsumers = [];
116117
for (const trigger of event.config.triggers ?? []) {
117118
if (trigger.type === "cron") {
118119
crons.push(trigger.cron);
120+
} else if (trigger.type === "route") {
121+
routes.push(trigger.pattern);
119122
} else if (trigger.type === "queue-consumer") {
120123
const { type: _, ...consumer } = trigger;
121124
queueConsumers.push(consumer);
@@ -191,6 +194,7 @@ export async function convertToConfigBundle(
191194
localPersistencePath: event.config.dev.persist,
192195
liveReload: event.config.dev?.liveReload ?? false,
193196
crons,
197+
routes,
194198
queueConsumers,
195199
outboundService: event.config.dev.outboundService,
196200
localProtocol: event.config.dev?.server?.secure ? "https" : "http",

packages/wrangler/src/api/startDevWorker/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ export interface StartDevWorkerInput {
173173

174174
/** Treat this as the primary worker in a multiworker setup (i.e. the first Worker in Miniflare's options) */
175175
multiworkerPrimary?: boolean;
176+
/** Whether to infer the local request origin from configured routes. */
177+
inferOriginFromRoutes?: boolean;
176178

177179
containerBuildId?: string;
178180
/** Whether to build and connect to containers during local dev. Requires Docker daemon to be running. Defaults to true. */

packages/wrangler/src/dev/miniflare/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export interface ConfigBundle {
8686
localPersistencePath: string | false;
8787
liveReload: boolean;
8888
crons: Config["triggers"]["crons"];
89+
routes: string[] | undefined;
8990
queueConsumers: Config["queues"]["consumers"];
9091
localProtocol: "http" | "https";
9192
httpsKeyPath: string | undefined;
@@ -1090,6 +1091,7 @@ export async function buildMiniflareOptions(
10901091
...bindingOptions,
10911092
...sitesOptions,
10921093
...assetOptions,
1094+
routes: config.routes,
10931095
outboundService: config.outboundService,
10941096
containerEngine: config.containerEngine,
10951097
zone: config.zone,

packages/wrangler/src/dev/start-dev.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ async function setupDevEnv(
265265
logLevel: args.logLevel,
266266
registry: args.disableDevRegistry ? undefined : getRegistryPath(),
267267
multiworkerPrimary: args.multiworkerPrimary,
268+
inferOriginFromRoutes: true,
268269
enableContainers: args.enableContainers,
269270
dockerPath: args.dockerPath,
270271
// initialise with a random id

0 commit comments

Comments
 (0)