From 65f1092281866333118e5e8ebf0f5234bf695baf Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 15 Mar 2026 23:00:18 +0000 Subject: [PATCH 1/2] Fix autoconfig package installation always failing at workspace roots (#12676) Co-authored-by: Pete Bacon Darwin --- .changeset/fifty-radios-nail.md | 9 +++++ .../get-details-for-auto-config.test.ts | 38 +++++++++++++++++- .../src/autoconfig/c3-vendor/packages.ts | 40 ++++++++++++++++++- packages/wrangler/src/autoconfig/details.ts | 27 +++++++++---- .../src/autoconfig/frameworks/angular.ts | 9 ++++- .../src/autoconfig/frameworks/index.ts | 1 + .../src/autoconfig/frameworks/nuxt.ts | 2 + .../src/autoconfig/frameworks/react-router.ts | 3 ++ .../src/autoconfig/frameworks/sveltekit.ts | 2 + .../src/autoconfig/frameworks/tanstack.ts | 2 + .../src/autoconfig/frameworks/vike.ts | 3 ++ .../src/autoconfig/frameworks/vite.ts | 2 + .../src/autoconfig/frameworks/waku.ts | 2 + packages/wrangler/src/autoconfig/run.ts | 6 ++- packages/wrangler/src/autoconfig/types.ts | 2 + 15 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 .changeset/fifty-radios-nail.md diff --git a/.changeset/fifty-radios-nail.md b/.changeset/fifty-radios-nail.md new file mode 100644 index 000000000000..41c9e522d132 --- /dev/null +++ b/.changeset/fifty-radios-nail.md @@ -0,0 +1,9 @@ +--- +"wrangler": patch +--- + +Fix autoconfig package installation always failing at workspace roots + +When running autoconfig at the root of a monorepo workspace, package installation commands now include the appropriate workspace root flags (`--workspace-root` for pnpm, `-W` for yarn). This prevents errors like "Running this command will add the dependency to the workspace root" that previously occurred when configuring projects at the workspace root. + +Additionally, autoconfig now allows running at the workspace root if the root directory itself is listed as a workspace package (e.g., `workspaces: ["packages/*", "."]`). diff --git a/packages/wrangler/src/__tests__/autoconfig/details/get-details-for-auto-config.test.ts b/packages/wrangler/src/__tests__/autoconfig/details/get-details-for-auto-config.test.ts index fdc4be0a7ed5..ce405e7cb767 100644 --- a/packages/wrangler/src/__tests__/autoconfig/details/get-details-for-auto-config.test.ts +++ b/packages/wrangler/src/__tests__/autoconfig/details/get-details-for-auto-config.test.ts @@ -118,10 +118,46 @@ describe("autoconfig details - getDetailsForAutoConfig()", () => { await expect( details.getDetailsForAutoConfig() ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: The Wrangler application detection logic has been run in the root of a workspace, this is not supported. Change your working directory to one of the applications in the workspace and try again.]` + `[Error: The Wrangler application detection logic has been run in the root of a workspace instead of targeting a specific project. Change your working directory to one of the applications in the workspace and try again.]` ); }); + it("should not bail when run in the root of a workspace if the root is included as a workspace package", async ({ + expect, + }) => { + await seed({ + "pnpm-workspace.yaml": "packages:\n - 'packages/*'\n - '.'\n", + "package.json": JSON.stringify({ + name: "my-workspace", + workspaces: ["packages/*", "."], + }), + "index.html": "

Hello World

", + "packages/my-app/package.json": JSON.stringify({ name: "my-app" }), + "packages/my-app/index.html": "

Hello World

", + }); + + const result = await details.getDetailsForAutoConfig(); + + expect(result.isWorkspaceRoot).toBe(true); + expect(result.framework?.id).toBe("static"); + }); + + it("should set isWorkspaceRoot to false for non-workspace projects", async ({ + expect, + }) => { + await seed({ + "package.json": JSON.stringify({ + name: "my-app", + }), + "package-lock.json": JSON.stringify({ lockfileVersion: 3 }), + "index.html": "

Hello World

", + }); + + const result = await details.getDetailsForAutoConfig(); + + expect(result.isWorkspaceRoot).toBe(false); + }); + it("should warn when no lock file is detected (project may be inside a workspace)", async ({ expect, }) => { diff --git a/packages/wrangler/src/autoconfig/c3-vendor/packages.ts b/packages/wrangler/src/autoconfig/c3-vendor/packages.ts index da25ac19c4c4..6eedbc716b57 100644 --- a/packages/wrangler/src/autoconfig/c3-vendor/packages.ts +++ b/packages/wrangler/src/autoconfig/c3-vendor/packages.ts @@ -11,6 +11,7 @@ type InstallConfig = { doneText?: string; dev?: boolean; force?: boolean; + isWorkspaceRoot?: boolean; }; /** @@ -30,6 +31,7 @@ export const installPackages = async ( ) => { const { type } = packageManager; const { force, dev, startText, doneText } = config; + const isWorkspaceRoot = config.isWorkspaceRoot ?? false; if (packages.length === 0) { let cmd; @@ -50,8 +52,10 @@ export const installPackages = async ( ...packages, ...(type === "pnpm" ? ["--no-frozen-lockfile"] : []), ...(force === true ? ["--force"] : []), + ...getWorkspaceInstallRootFlag(type, isWorkspaceRoot), ], { + cwd: process.cwd(), startText, doneText, silent: true, @@ -74,7 +78,6 @@ export const installPackages = async ( saveFlag = dev ? "--save-dev" : ""; break; } - await runCommand( [ type, @@ -82,6 +85,7 @@ export const installPackages = async ( ...(saveFlag ? [saveFlag] : []), ...packages, ...(force === true ? ["--force"] : []), + ...getWorkspaceInstallRootFlag(type, isWorkspaceRoot), ], { startText, @@ -113,15 +117,47 @@ export const installPackages = async ( } }; +/** + * Returns the potential flag(/s) that need to be added to a package manager's install command when it is + * run at the root of a workspace. + * + * @param packageManagerType The type of package manager + * @param isWorkspaceRoot Flag indicating whether the install command is being run at the root of a workspace + * @returns an array containing the flag(/s) to use, or an empty array if not supported or not running in the workspace root. + */ +const getWorkspaceInstallRootFlag = ( + packageManagerType: PackageManager["type"], + isWorkspaceRoot: boolean +): string[] => { + if (!isWorkspaceRoot) { + return []; + } + + switch (packageManagerType) { + case "pnpm": + return ["--workspace-root"]; + case "yarn": + return ["-W"]; + case "npm": + case "bun": + // npm and bun don't have the workspace check + return []; + } +}; + /** * Installs the latest version of wrangler in the project directory if it isn't already. */ -export const installWrangler = async (packageManager: PackageManager) => { +export const installWrangler = async ( + packageManager: PackageManager, + isWorkspaceRoot: boolean +) => { const { type } = packageManager; // Even if Wrangler is already installed, make sure we install the latest version, as some framework CLIs are pinned to an older version await installPackages(packageManager, [`wrangler@latest`], { dev: true, + isWorkspaceRoot, startText: `Installing wrangler ${dim( "A command line tool for building Cloudflare Workers" )}`, diff --git a/packages/wrangler/src/autoconfig/details.ts b/packages/wrangler/src/autoconfig/details.ts index d69ce0e61974..01a690e055ec 100644 --- a/packages/wrangler/src/autoconfig/details.ts +++ b/packages/wrangler/src/autoconfig/details.ts @@ -206,6 +206,7 @@ async function detectFramework( ): Promise<{ detectedFramework: DetectedFramework | undefined; packageManager: PackageManager; + isWorkspaceRoot?: boolean; }> { const fs = new NodeFS(); @@ -220,10 +221,20 @@ async function detectFramework( const buildSettings = await project.getBuildSettings(); - if (project.workspace?.isRoot) { - throw new UserError( - "The Wrangler application detection logic has been run in the root of a workspace, this is not supported. Change your working directory to one of the applications in the workspace and try again." + const isWorkspaceRoot = !!project.workspace?.isRoot; + + if (isWorkspaceRoot) { + const resolvedProjectPath = resolve(projectPath); + + const workspaceRootIncludesProject = project.workspace?.packages.some( + (pkg) => resolve(pkg.path) === resolvedProjectPath ); + + if (!workspaceRootIncludesProject) { + throw new UserError( + "The Wrangler application detection logic has been run in the root of a workspace instead of targeting a specific project. Change your working directory to one of the applications in the workspace and try again." + ); + } } const detectedFramework = findDetectedFramework(buildSettings); @@ -259,7 +270,7 @@ async function detectFramework( }; } - return { detectedFramework, packageManager }; + return { detectedFramework, packageManager, isWorkspaceRoot }; } /** @@ -402,10 +413,8 @@ export async function getDetailsForAutoConfig({ }; } - const { detectedFramework, packageManager } = await detectFramework( - projectPath, - wranglerConfig - ); + const { detectedFramework, packageManager, isWorkspaceRoot } = + await detectFramework(projectPath, wranglerConfig); const framework = getFramework(detectedFramework?.framework?.id); const packageJsonPath = resolve(projectPath, "package.json"); @@ -456,6 +465,7 @@ export async function getDetailsForAutoConfig({ return { ...baseDetails, configured: true, + isWorkspaceRoot, }; } @@ -498,6 +508,7 @@ export async function getDetailsForAutoConfig({ ...baseDetails, outputDir, configured: false, + isWorkspaceRoot, }; } diff --git a/packages/wrangler/src/autoconfig/frameworks/angular.ts b/packages/wrangler/src/autoconfig/frameworks/angular.ts index 193ae77d33fb..55999929bed8 100644 --- a/packages/wrangler/src/autoconfig/frameworks/angular.ts +++ b/packages/wrangler/src/autoconfig/frameworks/angular.ts @@ -15,11 +15,12 @@ export class Angular extends Framework { outputDir, dryRun, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { if (!dryRun) { await updateAngularJson(workerName); await overrideServerFile(); - await installAdditionalDependencies(packageManager); + await installAdditionalDependencies(packageManager, isWorkspaceRoot); } return { wranglerConfig: { @@ -80,11 +81,15 @@ async function overrideServerFile() { ); } -async function installAdditionalDependencies(packageManager: PackageManager) { +async function installAdditionalDependencies( + packageManager: PackageManager, + isWorkspaceRoot: boolean +) { await installPackages(packageManager, ["xhr2"], { dev: true, startText: "Installing additional dependencies", doneText: `${brandColor("installed")}`, + isWorkspaceRoot, }); } diff --git a/packages/wrangler/src/autoconfig/frameworks/index.ts b/packages/wrangler/src/autoconfig/frameworks/index.ts index 028da9b5165c..b0388496d702 100644 --- a/packages/wrangler/src/autoconfig/frameworks/index.ts +++ b/packages/wrangler/src/autoconfig/frameworks/index.ts @@ -8,6 +8,7 @@ export type ConfigurationOptions = { workerName: string; dryRun: boolean; packageManager: PackageManager; + isWorkspaceRoot: boolean; }; export type PackageJsonScriptsOverrides = { diff --git a/packages/wrangler/src/autoconfig/frameworks/nuxt.ts b/packages/wrangler/src/autoconfig/frameworks/nuxt.ts index c0bc99ccab13..d26a84124a18 100644 --- a/packages/wrangler/src/autoconfig/frameworks/nuxt.ts +++ b/packages/wrangler/src/autoconfig/frameworks/nuxt.ts @@ -56,12 +56,14 @@ export class Nuxt extends Framework { dryRun, projectPath, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { if (!dryRun) { await installPackages(packageManager, ["nitro-cloudflare-dev"], { dev: true, startText: "Installing the Cloudflare dev module", doneText: `${brandColor(`installed`)} ${dim("nitro-cloudflare-dev")}`, + isWorkspaceRoot, }); updateNuxtConfig(projectPath); } diff --git a/packages/wrangler/src/autoconfig/frameworks/react-router.ts b/packages/wrangler/src/autoconfig/frameworks/react-router.ts index cfbc9799077b..5add1341d615 100644 --- a/packages/wrangler/src/autoconfig/frameworks/react-router.ts +++ b/packages/wrangler/src/autoconfig/frameworks/react-router.ts @@ -147,6 +147,7 @@ export class ReactRouter extends Framework { dryRun, projectPath, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { const viteEnvironmentKey = configPropertyName(projectPath); if (!dryRun) { @@ -154,6 +155,7 @@ export class ReactRouter extends Framework { dev: true, startText: "Installing the Cloudflare Vite plugin", doneText: `${brandColor(`installed`)} ${dim("@cloudflare/vite-plugin")}`, + isWorkspaceRoot, }); mkdirSync("workers"); @@ -191,6 +193,7 @@ export class ReactRouter extends Framework { dev: true, startText: "Installing the isbot package", doneText: `${brandColor(`installed`)} ${dim("isbot")}`, + isWorkspaceRoot, }); if (!existsSync("app/entry.server.tsx")) { diff --git a/packages/wrangler/src/autoconfig/frameworks/sveltekit.ts b/packages/wrangler/src/autoconfig/frameworks/sveltekit.ts index 94705ec50f28..ef7f7b05f93c 100644 --- a/packages/wrangler/src/autoconfig/frameworks/sveltekit.ts +++ b/packages/wrangler/src/autoconfig/frameworks/sveltekit.ts @@ -9,6 +9,7 @@ export class SvelteKit extends Framework { async configure({ dryRun, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { const { dlx } = packageManager; if (!dryRun) { @@ -34,6 +35,7 @@ export class SvelteKit extends Framework { await installPackages(packageManager, [], { startText: "Installing packages", doneText: `${brandColor("installed")}`, + isWorkspaceRoot, }); } return { diff --git a/packages/wrangler/src/autoconfig/frameworks/tanstack.ts b/packages/wrangler/src/autoconfig/frameworks/tanstack.ts index 55bc2c46cc5f..ae15b88ab654 100644 --- a/packages/wrangler/src/autoconfig/frameworks/tanstack.ts +++ b/packages/wrangler/src/autoconfig/frameworks/tanstack.ts @@ -9,12 +9,14 @@ export class TanstackStart extends Framework { dryRun, projectPath, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { if (!dryRun) { await installPackages(packageManager, ["@cloudflare/vite-plugin"], { dev: true, startText: "Installing the Cloudflare Vite plugin", doneText: `${brandColor(`installed`)} ${dim("@cloudflare/vite-plugin")}`, + isWorkspaceRoot, }); transformViteConfig(projectPath, { viteEnvironmentName: "ssr" }); diff --git a/packages/wrangler/src/autoconfig/frameworks/vike.ts b/packages/wrangler/src/autoconfig/frameworks/vike.ts index b3b0b3b8be70..1c5898a0f0ff 100644 --- a/packages/wrangler/src/autoconfig/frameworks/vike.ts +++ b/packages/wrangler/src/autoconfig/frameworks/vike.ts @@ -18,6 +18,7 @@ export class Vike extends Framework { projectPath, dryRun, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { const vikeServerIsInstalled = isPackageInstalled( "vike-server", @@ -41,10 +42,12 @@ export class Vike extends Framework { { startText: "Installing vike-photon and @photonjs/cloudflare", doneText: `${brandColor(`installed`)} photon packages`, + isWorkspaceRoot, } ); await installPackages(packageManager, ["@cloudflare/vite-plugin"], { dev: true, + isWorkspaceRoot, }); addVikePhotonToConfigFile(projectPath); diff --git a/packages/wrangler/src/autoconfig/frameworks/vite.ts b/packages/wrangler/src/autoconfig/frameworks/vite.ts index a6e69464615e..cbabbeae035f 100644 --- a/packages/wrangler/src/autoconfig/frameworks/vite.ts +++ b/packages/wrangler/src/autoconfig/frameworks/vite.ts @@ -16,12 +16,14 @@ export class Vite extends Framework { dryRun, projectPath, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { if (!dryRun) { await installPackages(packageManager, ["@cloudflare/vite-plugin"], { dev: true, startText: "Installing the Cloudflare Vite plugin", doneText: `${brandColor(`installed`)} ${dim("@cloudflare/vite-plugin")}`, + isWorkspaceRoot, }); transformViteConfig(projectPath); diff --git a/packages/wrangler/src/autoconfig/frameworks/waku.ts b/packages/wrangler/src/autoconfig/frameworks/waku.ts index e0c98ac79836..dd2e5bf3d39c 100644 --- a/packages/wrangler/src/autoconfig/frameworks/waku.ts +++ b/packages/wrangler/src/autoconfig/frameworks/waku.ts @@ -23,6 +23,7 @@ export class Waku extends Framework { dryRun, projectPath, packageManager, + isWorkspaceRoot, }: ConfigurationOptions): Promise { validateMinimumWakuVersion(projectPath); @@ -34,6 +35,7 @@ export class Waku extends Framework { dev: true, startText: "Installing additional dependencies", doneText: `${brandColor("installed")}`, + isWorkspaceRoot, } ); diff --git a/packages/wrangler/src/autoconfig/run.ts b/packages/wrangler/src/autoconfig/run.ts index 62c83f7be528..be68fbfb1d07 100644 --- a/packages/wrangler/src/autoconfig/run.ts +++ b/packages/wrangler/src/autoconfig/run.ts @@ -103,11 +103,14 @@ export async function runAutoConfig( const { packageManager } = autoConfigDetails; + const isWorkspaceRoot = autoConfigDetails.isWorkspaceRoot ?? false; + const dryRunConfigurationResults = await autoConfigDetails.framework.configure({ outputDir: autoConfigDetails.outputDir, projectPath: autoConfigDetails.projectPath, workerName: autoConfigDetails.workerName, + isWorkspaceRoot, dryRun: true, packageManager, }); @@ -165,13 +168,14 @@ export async function runAutoConfig( ); if (autoConfigSummary.wranglerInstall && enableWranglerInstallation) { - await installWrangler(packageManager); + await installWrangler(packageManager, isWorkspaceRoot); } const configurationResults = await autoConfigDetails.framework.configure({ outputDir: autoConfigDetails.outputDir, projectPath: autoConfigDetails.projectPath, workerName: autoConfigDetails.workerName, + isWorkspaceRoot, dryRun: false, packageManager, }); diff --git a/packages/wrangler/src/autoconfig/types.ts b/packages/wrangler/src/autoconfig/types.ts index 16e2d06b21b7..e28b85baa9d3 100644 --- a/packages/wrangler/src/autoconfig/types.ts +++ b/packages/wrangler/src/autoconfig/types.ts @@ -20,6 +20,8 @@ type AutoConfigDetailsBase = { outputDir: string; /** The detected package manager for the project */ packageManager: PackageManager; + /** Whether the current path is at the root of a workspace */ + isWorkspaceRoot?: boolean; }; export type AutoConfigDetailsForConfiguredProject = Optional< From 876108a04b19f6577843f7cf9884639e17d37fb7 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 15 Mar 2026 23:01:57 +0000 Subject: [PATCH 2/2] [vite-plugin] Fix crash when plugins send HMR events before runner initialization (#12859) Co-authored-by: Edmund Hung --- .changeset/evil-crabs-start.md | 7 +++ .../src/__tests__/hmr-events.spec.ts | 59 +++++++++++++++++++ .../src/cloudflare-environment.ts | 23 ++++++-- 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 .changeset/evil-crabs-start.md create mode 100644 packages/vite-plugin-cloudflare/src/__tests__/hmr-events.spec.ts diff --git a/.changeset/evil-crabs-start.md b/.changeset/evil-crabs-start.md new file mode 100644 index 000000000000..65a3d38fc6a5 --- /dev/null +++ b/.changeset/evil-crabs-start.md @@ -0,0 +1,7 @@ +--- +"@cloudflare/vite-plugin": patch +--- + +Fix crash when plugins send HMR events before runner initialization + +Previously, if another Vite plugin (such as `vite-plugin-vue-devtools`) sent HMR events during `configureServer` before the Cloudflare plugin had initialized its runner, the dev server would crash with `AssertionError: The WebSocket is undefined`. The environment's WebSocket send operations are now deferred until the runner is fully initialized, allowing early HMR events to be handled gracefully. diff --git a/packages/vite-plugin-cloudflare/src/__tests__/hmr-events.spec.ts b/packages/vite-plugin-cloudflare/src/__tests__/hmr-events.spec.ts new file mode 100644 index 000000000000..654b20a4f320 --- /dev/null +++ b/packages/vite-plugin-cloudflare/src/__tests__/hmr-events.spec.ts @@ -0,0 +1,59 @@ +import { fileURLToPath } from "node:url"; +import { cloudflare } from "@cloudflare/vite-plugin"; +import { createServer } from "vite"; +import { afterEach, describe, test } from "vitest"; +import type { Plugin, ViteDevServer } from "vite"; + +const fixturesPath = fileURLToPath(new URL("./fixtures", import.meta.url)); + +describe("HMR events", () => { + let server: ViteDevServer | undefined; + + afterEach(async () => { + await server?.close(); + server = undefined; + }); + + // Reference: https://github.com/cloudflare/workers-sdk/issues/11063 + test("the environment handles early HMR events before runner initialization", async ({ + expect, + }) => { + // Create a plugin that triggers HMR events early in configureServer. + // This mimics what vite-plugin-vue-devtools does - it sends HMR events + // before the cloudflare plugin has had a chance to call initRunner(). + // Before the fix, this would crash with "AssertionError: The WebSocket is undefined" + const earlyHmrPlugin: Plugin = { + name: "early-hmr-plugin", + configureServer(viteDevServer) { + // Access the worker environment and try to send HMR events + // BEFORE the cloudflare plugin has called initRunner() + const workerEnv = viteDevServer.environments.my_worker; + if (workerEnv) { + workerEnv.hot.send("test-event", { data: "test" }); + } + }, + }; + + server = await createServer({ + root: fixturesPath, + logLevel: "silent", + plugins: [ + // Place the early HMR plugin BEFORE cloudflare to trigger HMR + // events before the runner is initialized + earlyHmrPlugin, + cloudflare({ inspectorPort: false, persistState: false }), + ], + }); + + await server.listen(); + + // Verify the server is responsive by making a request. + const address = server.resolvedUrls?.local[0]; + if (!address) { + throw new Error("Server address is undefined"); + } + + const response = await fetch(address); + expect(response.ok).toBe(true); + }); +}); diff --git a/packages/vite-plugin-cloudflare/src/cloudflare-environment.ts b/packages/vite-plugin-cloudflare/src/cloudflare-environment.ts index 9c9159b9949c..fc22adf10808 100644 --- a/packages/vite-plugin-cloudflare/src/cloudflare-environment.ts +++ b/packages/vite-plugin-cloudflare/src/cloudflare-environment.ts @@ -27,6 +27,7 @@ export const MAIN_ENTRY_NAME = "index"; interface WebSocketContainer { webSocket?: WebSocket; + messageBuffers?: string[]; } const webSocketUndefinedError = "The WebSocket is undefined"; @@ -57,9 +58,15 @@ function createHotChannel( return { send(payload) { const webSocket = webSocketContainer.webSocket; - assert(webSocket, webSocketUndefinedError); + const message = JSON.stringify(payload); - webSocket.send(JSON.stringify(payload)); + if (!webSocket) { + webSocketContainer.messageBuffers ??= []; + webSocketContainer.messageBuffers.push(message); + return; + } + + webSocket.send(message); }, on(event: string, listener: vite.HotChannelListener) { const listeners = listenersMap.get(event) ?? new Set(); @@ -86,11 +93,11 @@ function createHotChannel( } export class CloudflareDevEnvironment extends vite.DevEnvironment { - #webSocketContainer: { webSocket?: WebSocket }; + #webSocketContainer: WebSocketContainer; constructor(name: string, config: vite.ResolvedConfig) { // It would be good if we could avoid passing this object around and mutating it - const webSocketContainer = {}; + const webSocketContainer: WebSocketContainer = {}; super(name, config, { hot: true, transport: createHotChannel(webSocketContainer), @@ -124,6 +131,14 @@ export class CloudflareDevEnvironment extends vite.DevEnvironment { assert(webSocket, "Failed to establish WebSocket"); webSocket.accept(); this.#webSocketContainer.webSocket = webSocket; + + if (this.#webSocketContainer.messageBuffers) { + for (const bufferedMessage of this.#webSocketContainer.messageBuffers) { + webSocket.send(bufferedMessage); + } + + delete this.#webSocketContainer.messageBuffers; + } } async fetchWorkerExportTypes(