Skip to content

Commit 2ca70b1

Browse files
authored
Register named entrypoints with the dev registry (#11417)
* Add failing tests * Refactor getDevMiniflareOptions and remove unknown tails logging * Add named entrypoints to unsafeDirectSockets * Add changeset
1 parent 5ee3780 commit 2ca70b1

File tree

4 files changed

+117
-120
lines changed

4 files changed

+117
-120
lines changed

.changeset/ready-kids-happen.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@cloudflare/vite-plugin": minor
3+
---
4+
5+
Register named entrypoints with the dev registry.
6+
7+
This enables binding to [named entrypoints](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/rpc/#named-entrypoints) defined in a `vite dev` session from another `vite dev` or `wrangler dev` session running locally.

fixtures/dev-registry/tests/dev-registry.test.ts

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ describe("Dev Registry: vite dev <-> vite dev", () => {
474474
"vite.worker-entrypoint-with-assets.config.ts",
475475
devRegistryPath
476476
);
477+
await runViteDev("vite.worker-entrypoint.config.ts", devRegistryPath);
477478

478479
// Test fallback before exported-handler is started
479480
await vi.waitFor(async () => {
@@ -496,7 +497,7 @@ describe("Dev Registry: vite dev <-> vite dev", () => {
496497
devRegistryPath
497498
);
498499

499-
// Test exported-handler -> worker-entrypoint
500+
// Test exported-handler -> worker-entrypoint-with-assets
500501
await vi.waitFor(async () => {
501502
const searchParams = new URLSearchParams({
502503
"test-service": "worker-entrypoint-with-assets",
@@ -516,7 +517,7 @@ describe("Dev Registry: vite dev <-> vite dev", () => {
516517
expect(await assetResponse.text()).toBe("This is an example asset file");
517518
}, waitForTimeout);
518519

519-
// Test worker-entrypoint -> exported-handler
520+
// Test worker-entrypoint-with-assets -> exported-handler
520521
await vi.waitFor(async () => {
521522
const searchParams = new URLSearchParams({
522523
"test-service": "exported-handler",
@@ -529,55 +530,105 @@ describe("Dev Registry: vite dev <-> vite dev", () => {
529530
expect(await response.text()).toEqual("Hello from exported handler!");
530531
expect(response.status).toBe(200);
531532
}, waitForTimeout);
533+
534+
// Test exported-handler -> named-entrypoint
535+
await vi.waitFor(async () => {
536+
const searchParams = new URLSearchParams({
537+
"test-service": "named-entrypoint",
538+
"test-method": "fetch",
539+
});
540+
const response = await fetch(`${exportedHandler}?${searchParams}`);
541+
542+
expect(await response.text()).toEqual("Hello from Named Entrypoint!");
543+
expect(response.status).toBe(200);
544+
}, waitForTimeout);
545+
546+
// Test exported-handler -> named-entrypoint-with-assets
547+
await vi.waitFor(async () => {
548+
const searchParams = new URLSearchParams({
549+
"test-service": "named-entrypoint-with-assets",
550+
"test-method": "fetch",
551+
});
552+
const response = await fetch(`${exportedHandler}?${searchParams}`);
553+
554+
expect(await response.text()).toEqual("Hello from Named Entrypoint!");
555+
expect(response.status).toBe(200);
556+
}, waitForTimeout);
532557
});
533558

534559
it("supports RPC over service binding", async ({ devRegistryPath }) => {
535-
const workerEntrypoint = await runViteDev(
536-
"vite.worker-entrypoint.config.ts",
560+
const exportedHandler = await runViteDev(
561+
"vite.exported-handler.config.ts",
537562
devRegistryPath
538563
);
564+
await runViteDev("vite.worker-entrypoint.config.ts", devRegistryPath);
539565

566+
// Test fallback before worker-entrypoint-with-assets is started
540567
await vi.waitFor(async () => {
541568
const searchParams = new URLSearchParams({
542569
"test-service": "worker-entrypoint-with-assets",
543570
"test-method": "rpc",
544571
});
545-
const response = await fetch(`${workerEntrypoint}?${searchParams}`);
572+
const response = await fetch(`${exportedHandler}?${searchParams}`);
546573

547574
expect(response.status).toBe(500);
548575
expect(await response.text()).toEqual(
549576
`Cannot access "ping" as we couldn't find a local dev session for the "default" entrypoint of service "worker-entrypoint-with-assets" to proxy to.`
550577
);
551578
}, waitForTimeout);
552579

553-
const workerEntrypointWithAssets = await runViteDev(
580+
await runViteDev(
554581
"vite.worker-entrypoint-with-assets.config.ts",
555582
devRegistryPath
556583
);
557584

585+
// Test exported-handler -> worker-entrypoint RPC
558586
await vi.waitFor(async () => {
559587
const searchParams = new URLSearchParams({
560588
"test-service": "worker-entrypoint",
561589
"test-method": "rpc",
562590
});
563-
const response = await fetch(
564-
`${workerEntrypointWithAssets}?${searchParams}`
565-
);
591+
const response = await fetch(`${exportedHandler}?${searchParams}`);
566592

567593
expect(response.status).toBe(200);
568594
expect(await response.text()).toEqual("Pong");
569595
}, waitForTimeout);
570596

597+
// Test exported-handler -> worker-entrypoint-with-assets RPC
571598
await vi.waitFor(async () => {
572599
const searchParams = new URLSearchParams({
573600
"test-service": "worker-entrypoint-with-assets",
574601
"test-method": "rpc",
575602
});
576-
const response = await fetch(`${workerEntrypoint}?${searchParams}`);
603+
const response = await fetch(`${exportedHandler}?${searchParams}`);
577604

578605
expect(response.status).toBe(200);
579606
expect(await response.text()).toEqual("Pong");
580607
}, waitForTimeout);
608+
609+
// Test exported-handler -> named-entrypoint RPC
610+
await vi.waitFor(async () => {
611+
const searchParams = new URLSearchParams({
612+
"test-service": "named-entrypoint",
613+
"test-method": "rpc",
614+
});
615+
const response = await fetch(`${exportedHandler}?${searchParams}`);
616+
617+
expect(response.status).toBe(200);
618+
expect(await response.text()).toEqual("Pong from Named Entrypoint");
619+
}, waitForTimeout);
620+
621+
// Test exported-handler -> named-entrypoint-with-assets RPC
622+
await vi.waitFor(async () => {
623+
const searchParams = new URLSearchParams({
624+
"test-service": "named-entrypoint-with-assets",
625+
"test-method": "rpc",
626+
});
627+
const response = await fetch(`${exportedHandler}?${searchParams}`);
628+
629+
expect(response.status).toBe(200);
630+
expect(await response.text()).toEqual("Pong from Named Entrypoint");
631+
}, waitForTimeout);
581632
});
582633

583634
it("supports tail handler", async ({ devRegistryPath }) => {

packages/vite-plugin-cloudflare/playground/multi-worker/__tests__/multi-worker.spec.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@ describe.runIf(isBuild)("output directories", () => {
2626
});
2727

2828
describe("multi-worker basic functionality", async () => {
29-
test("warning about non-existent tail is printed", async () => {
30-
expect(serverLogs.warns).toEqual([
31-
expect.stringMatching(
32-
/Make sure you add it to the config or run it in another dev session if you'd like to simulate receiving tail events locally/
33-
),
34-
]);
35-
});
36-
3729
test("entry worker returns a response", async () => {
3830
const result = await getJsonResponse();
3931
expect(result).toEqual({ name: "Worker A" });

packages/vite-plugin-cloudflare/src/miniflare-options.ts

Lines changed: 49 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
LogLevel,
1515
Response as MiniflareResponse,
1616
} from "miniflare";
17-
import colors from "picocolors";
1817
import { globSync } from "tinyglobby";
1918
import * as vite from "vite";
2019
import {
@@ -88,41 +87,6 @@ const VITE_PROXY_WORKER_PATH = "./workers/vite-proxy-worker.js";
8887
const RUNNER_PATH = "./workers/runner-worker.js";
8988
const WRAPPER_PATH = "__VITE_WORKER_ENTRY__";
9089

91-
function logUnknownTails(
92-
tails: WorkerOptions["tails"],
93-
userWorkers: { name?: string }[],
94-
log: (msg: string) => void
95-
) {
96-
// Only connect the tail consumers that represent Workers that are defined in the Vite config. Warn that a tail might be omitted otherwise
97-
// This _differs from service bindings_ because tail consumers are "optional" in a sense, and shouldn't affect the runtime behaviour of a Worker
98-
for (const tailService of tails ?? []) {
99-
let name: string;
100-
if (typeof tailService === "string") {
101-
name = tailService;
102-
} else if (
103-
typeof tailService === "object" &&
104-
"name" in tailService &&
105-
typeof tailService.name === "string"
106-
) {
107-
name = tailService.name;
108-
} else {
109-
// Don't interfere with network-based tail connections (e.g. via the dev registry), or kCurrentWorker
110-
continue;
111-
}
112-
const found = userWorkers.some((w) => w.name === name);
113-
114-
if (!found) {
115-
log(
116-
colors.dim(
117-
colors.yellow(
118-
`Tail consumer "${name}" was not found in your config. Make sure you add it to the config or run it in another dev session if you'd like to simulate receiving tail events locally.`
119-
)
120-
)
121-
);
122-
}
123-
}
124-
}
125-
12690
/** Map that maps worker configPaths to their existing remote proxy session data (if any) */
12791
const remoteProxySessionsDataMap = new Map<
12892
string,
@@ -344,15 +308,46 @@ export async function getDevMiniflareOptions(
344308
}
345309
);
346310

347-
const { externalWorkers } = miniflareWorkerOptions;
311+
const { externalWorkers, workerOptions } = miniflareWorkerOptions;
348312

349-
const workerOptions = miniflareWorkerOptions.workerOptions;
313+
const wrappers = [
314+
`import { createWorkerEntrypointWrapper, createDurableObjectWrapper, createWorkflowEntrypointWrapper } from "${RUNNER_PATH}";`,
315+
`export { __VITE_RUNNER_OBJECT__ } from "${RUNNER_PATH}";`,
316+
`export default createWorkerEntrypointWrapper("default");`,
317+
];
318+
319+
const exportTypes = ctx.workerNameToExportTypesMap.get(
320+
worker.config.name
321+
);
322+
assert(exportTypes, `Expected exportTypes to be defined`);
323+
324+
for (const [name, type] of Object.entries(exportTypes)) {
325+
wrappers.push(
326+
`export const ${name} = create${type}Wrapper("${name}");`
327+
);
328+
}
350329

351330
return {
352331
externalWorkers,
353332
worker: {
354333
...workerOptions,
355-
name: workerOptions.name ?? worker.config.name,
334+
name: worker.config.name,
335+
modulesRoot: miniflareModulesRoot,
336+
modules: [
337+
{
338+
type: "ESModule",
339+
path: path.join(miniflareModulesRoot, WRAPPER_PATH),
340+
contents: wrappers.join("\n"),
341+
},
342+
{
343+
type: "ESModule",
344+
path: path.join(miniflareModulesRoot, RUNNER_PATH),
345+
contents: fs.readFileSync(
346+
fileURLToPath(new URL(RUNNER_PATH, import.meta.url))
347+
),
348+
},
349+
],
350+
unsafeUseModuleFallbackService: true,
356351
unsafeInspectorProxy: inputInspectorPort !== false,
357352
unsafeDirectSockets:
358353
environmentName ===
@@ -362,12 +357,16 @@ export async function getDevMiniflareOptions(
362357
// This exposes the default entrypoint of the asset proxy worker
363358
// on the dev registry with the name of the entry worker
364359
serviceName: VITE_PROXY_WORKER_NAME,
365-
entrypoint: undefined,
366360
proxy: true,
367361
},
362+
...Object.entries(exportTypes)
363+
.filter(([_, type]) => type === "WorkerEntrypoint")
364+
.map(([entrypoint]) => ({
365+
entrypoint,
366+
proxy: true,
367+
})),
368368
]
369369
: [],
370-
modulesRoot: miniflareModulesRoot,
371370
unsafeEvalBinding: "__VITE_UNSAFE_EVAL__",
372371
serviceBindings: {
373372
...workerOptions.serviceBindings,
@@ -394,6 +393,14 @@ export async function getDevMiniflareOptions(
394393
return MiniflareResponse.json(result);
395394
},
396395
},
396+
durableObjects: {
397+
...workerOptions.durableObjects,
398+
__VITE_RUNNER_OBJECT__: {
399+
className: "__VITE_RUNNER_OBJECT__",
400+
unsafeUniqueKey: kUnsafeEphemeralUniqueKey,
401+
unsafePreventEviction: true,
402+
},
403+
},
397404
} satisfies Partial<WorkerOptions>,
398405
};
399406
}
@@ -422,61 +429,7 @@ export async function getDevMiniflareOptions(
422429
resolvedViteConfig.root,
423430
resolvedPluginConfig.persistState
424431
),
425-
workers: [
426-
...assetWorkers,
427-
...externalWorkers,
428-
...userWorkers.map((workerOptions) => {
429-
const wrappers = [
430-
`import { createWorkerEntrypointWrapper, createDurableObjectWrapper, createWorkflowEntrypointWrapper } from "${RUNNER_PATH}";`,
431-
`export { __VITE_RUNNER_OBJECT__ } from "${RUNNER_PATH}";`,
432-
`export default createWorkerEntrypointWrapper("default");`,
433-
];
434-
435-
const exportTypes = ctx.workerNameToExportTypesMap.get(
436-
workerOptions.name
437-
);
438-
assert(exportTypes, `Expected exportTypes to be defined`);
439-
440-
for (const [name, type] of Object.entries(exportTypes)) {
441-
wrappers.push(
442-
`export const ${name} = create${type}Wrapper("${name}");`
443-
);
444-
}
445-
446-
logUnknownTails(
447-
workerOptions.tails,
448-
userWorkers,
449-
viteDevServer.config.logger.warn
450-
);
451-
452-
return {
453-
...workerOptions,
454-
durableObjects: {
455-
...workerOptions.durableObjects,
456-
__VITE_RUNNER_OBJECT__: {
457-
className: "__VITE_RUNNER_OBJECT__",
458-
unsafeUniqueKey: kUnsafeEphemeralUniqueKey,
459-
unsafePreventEviction: true,
460-
},
461-
},
462-
modules: [
463-
{
464-
type: "ESModule",
465-
path: path.join(miniflareModulesRoot, WRAPPER_PATH),
466-
contents: wrappers.join("\n"),
467-
},
468-
{
469-
type: "ESModule",
470-
path: path.join(miniflareModulesRoot, RUNNER_PATH),
471-
contents: fs.readFileSync(
472-
fileURLToPath(new URL(RUNNER_PATH, import.meta.url))
473-
),
474-
},
475-
],
476-
unsafeUseModuleFallbackService: true,
477-
} satisfies WorkerOptions;
478-
}),
479-
],
432+
workers: [...assetWorkers, ...externalWorkers, ...userWorkers],
480433
async unsafeModuleFallbackService(request) {
481434
const url = new URL(request.url);
482435
const rawSpecifier = url.searchParams.get("rawSpecifier");
@@ -632,12 +585,6 @@ export async function getPreviewMiniflareOptions(
632585
const { modulesRules, ...workerOptions } =
633586
miniflareWorkerOptions.workerOptions;
634587

635-
logUnknownTails(
636-
workerOptions.tails,
637-
resolvedPluginConfig.workers,
638-
vitePreviewServer.config.logger.warn
639-
);
640-
641588
return [
642589
{
643590
...workerOptions,

0 commit comments

Comments
 (0)