From 2c82e227c6a8c7d40a3546f224e639ea7d940562 Mon Sep 17 00:00:00 2001 From: svelte-triage-bot Date: Fri, 28 Aug 2026 16:52:28 +0000 Subject: [PATCH] fix: dispose Cloudflare proxy on Vite server close --- .changeset/fresh-workers-retire.md | 5 ++ packages/adapter-cloudflare/index.js | 32 +++++++++++- packages/adapter-cloudflare/index.spec.js | 59 +++++++++++++++++++++++ packages/adapter-cloudflare/tsconfig.json | 1 + 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 .changeset/fresh-workers-retire.md create mode 100644 packages/adapter-cloudflare/index.spec.js diff --git a/.changeset/fresh-workers-retire.md b/.changeset/fresh-workers-retire.md new file mode 100644 index 000000000000..dfc73e4ac6a9 --- /dev/null +++ b/.changeset/fresh-workers-retire.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': patch +--- + +fix: dispose Cloudflare platform proxies when Vite servers close diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 68f731cdc25d..0ae0195d4695 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -219,18 +219,46 @@ export default function (options = {}) { * @returns {Plugin} */ function virtual_workers_module(options, stub_import) { + /** @type {import('wrangler').PlatformProxy | undefined} */ + let proxy; + const setup = async () => { if (globalThis.__sveltekit_cloudflare_platform) return; - const proxy = await getPlatformProxy(options); + proxy = await getPlatformProxy(options); // We store the platform proxy on globalThis so that our virtual workers module // can access the same instance that we use here to populate `caches` and `cf` (above). globalThis.__sveltekit_cloudflare_platform = proxy; /** @type {any} */ (globalThis).caches = proxy.caches; }; + + const dispose = async () => { + if (!proxy) return; + + const current = proxy; + proxy = undefined; + if (globalThis.__sveltekit_cloudflare_platform === current) { + // @ts-expect-error allow another dev or preview server to create a new proxy + globalThis.__sveltekit_cloudflare_platform = undefined; + } + await current.dispose(); + }; + return { name: 'vite-plugin-sveltekit-adapter-cloudflare-virtual-workers-module', configureServer: setup, - configurePreviewServer: setup, + async configurePreviewServer(server) { + await setup(); + + const close = server.close; + server.close = async () => { + try { + await close.call(server); + } finally { + await dispose(); + } + }; + }, + closeBundle: dispose, resolveId: { filter: { id: exactRegex('cloudflare:workers') }, handler() { diff --git a/packages/adapter-cloudflare/index.spec.js b/packages/adapter-cloudflare/index.spec.js new file mode 100644 index 000000000000..e0ecca01aa21 --- /dev/null +++ b/packages/adapter-cloudflare/index.spec.js @@ -0,0 +1,59 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import { getPlatformProxy } from 'wrangler'; +import adapter from './index.js'; + +vi.mock('wrangler', () => ({ + getPlatformProxy: vi.fn(), + unstable_readConfig: vi.fn() +})); + +/** @type {Awaited> | undefined} */ +let proxy; + +beforeEach(() => { + const value = /** @type {Awaited>} */ ( + /** @type {unknown} */ ({ + env: {}, + ctx: {}, + caches: {}, + cf: {}, + dispose: vi.fn() + }) + ); + proxy = value; + vi.mocked(getPlatformProxy).mockResolvedValue(value); +}); + +afterEach(() => { + // @ts-expect-error reset the global populated by the plugin + globalThis.__sveltekit_cloudflare_platform = undefined; +}); + +describe('virtual workers module', () => { + test('disposes the platform proxy when the dev server closes', async () => { + const plugin = adapter().vite?.plugins?.pre?.[0]; + const configure_server = /** @type {Function} */ (plugin?.configureServer); + const close_bundle = /** @type {Function} */ (plugin?.closeBundle); + + await configure_server({}); + await close_bundle(); + await close_bundle(); + + expect(proxy?.dispose).toHaveBeenCalledOnce(); + expect(globalThis.__sveltekit_cloudflare_platform).toBeUndefined(); + }); + + test('disposes the platform proxy when the preview server closes', async () => { + const plugin = adapter().vite?.plugins?.pre?.[0]; + const configure_preview_server = /** @type {Function} */ (plugin?.configurePreviewServer); + const close = vi.fn(); + const server = { close }; + + await configure_preview_server(server); + await server.close(); + + expect(close).toHaveBeenCalledOnce(); + expect(proxy?.dispose).toHaveBeenCalledOnce(); + expect(globalThis.__sveltekit_cloudflare_platform).toBeUndefined(); + }); +}); diff --git a/packages/adapter-cloudflare/tsconfig.json b/packages/adapter-cloudflare/tsconfig.json index 2e16130e2b76..e9e10322ab67 100644 --- a/packages/adapter-cloudflare/tsconfig.json +++ b/packages/adapter-cloudflare/tsconfig.json @@ -17,6 +17,7 @@ }, "include": [ "index.js", + "index.spec.js", "src", "utils.js", "utils.spec.js",