diff --git a/documentation/docs/25-build-and-deploy/99-writing-adapters.md b/documentation/docs/25-build-and-deploy/99-writing-adapters.md
index 204445e5e889..e3b76df224aa 100644
--- a/documentation/docs/25-build-and-deploy/99-writing-adapters.md
+++ b/documentation/docs/25-build-and-deploy/99-writing-adapters.md
@@ -41,8 +41,11 @@ export default function (options) {
}
},
vite: {
- plugins: [
- // add plugins here to integrate with Vite
+ pre_plugins: [
+ // add plugins here...
+ ],
+ post_plugins: [
+ // ...or here to integrate with Vite
]
}
};
diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js
index 185c964ceafc..e10020400569 100644
--- a/packages/adapter-cloudflare/index.js
+++ b/packages/adapter-cloudflare/index.js
@@ -10,13 +10,19 @@ import {
parse_redirects,
append_headers
} from './utils.js';
-
-const name = '@sveltejs/adapter-cloudflare';
+import { ServerResponse } from 'node:http';
+import { coupleWebSocket } from 'miniflare';
+import { WebSocketServer } from 'ws';
+import { cloudflare } from '@cloudflare/vite-plugin';
+import { exactRegex } from '@rolldown/pluginutils';
+// @ts-expect-error types are private
+import { dedent } from '@sveltejs/kit/internal';
/** @type {import('./index.js').default} */
export default function (options = {}) {
+ const node_ws_server = new WebSocketServer({ noServer: true });
return {
- name,
+ name: '@sveltejs/adapter-cloudflare',
async adapt(builder) {
if (
existsSync('_routes.json') ||
@@ -182,7 +188,13 @@ export default function (options = {}) {
// we want to invoke `getPlatformProxy` only once, but await it only when it is accessed.
// If we would await it here, it would hang indefinitely because the platform proxy only resolves once a request happens
const get_emulated = async () => {
- const proxy = await getPlatformProxy(options.platformProxy);
+ // TODO - need access to the vite dev server here
+ // const runner = get_runner(vite, server);
+ // runner.import('cloudflare:workers');
+ const proxy = await getPlatformProxy({
+ configPath: '.svelte-kit/cloudflare-tmp/wrangler.json',
+ ...options.platformProxy
+ });
const platform = {
env: proxy.env,
ctx: proxy.ctx,
@@ -215,10 +227,174 @@ export default function (options = {}) {
supports: {
read: () => true,
instrumentation: () => true
+ },
+ /**
+ *
+ * @param {WebSocketServerResponse} res
+ * @param {Response & { webSocket?: import('miniflare').WebSocket }} response
+ */
+ setResponse(res, response) {
+ if (!response.webSocket || !res.socket || !res[WEBSOCKET_HEAD]) return false;
+
+ const socket = res.socket;
+ const worker_socket = response.webSocket;
+ res.detachSocket(socket);
+ node_ws_server.handleUpgrade(res.req, socket, res[WEBSOCKET_HEAD], (client_socket) => {
+ void coupleWebSocket(client_socket, worker_socket);
+ node_ws_server.emit('connection', client_socket, res.req);
+ });
+
+ return true;
+ },
+ vite: {
+ pre_plugins: [listener_plugin, virtual_modules_plugin(options)],
+ post_plugins: [
+ // Cloudflare's plugin needs to be after SvelteKit so sveltekit's middleware
+ // can read the requests first and ignore them if necessary.
+ ...cloudflare({
+ configPath: options.config,
+ config: (user_config) => {
+ // Assets are handled by SvelteKit
+ delete user_config.assets;
+ return user_config;
+ }
+ })
+ ]
+ }
+ };
+}
+
+/**
+ * @param {import('./index.js').AdapterOptions} options
+ * @returns {import('vite').Plugin}
+ * */
+function virtual_modules_plugin(options) {
+ const modules = ['cloudflare:workers', 'virtual:todo-name-cloudflare-handler'];
+ /** @type {string} */
+ let out_dir;
+ const { wrangler_config } = validate_wrangler_config(options.config);
+ const worker_name = wrangler_config.name ?? 'worker';
+ // Rename the worker because two workers are running at once
+ wrangler_config.name = worker_name + '-sveltekit';
+ // Point all durable objects at the worker ran by @cloudflare/vite-plugin
+ for (const binding of wrangler_config.durable_objects?.bindings ?? []) {
+ if (binding.script_name === undefined) {
+ binding.script_name = worker_name;
+ }
+ }
+ // squash warnings
+ if (Object.keys(wrangler_config.unsafe).length === 0) {
+ delete /** @type {{ unsafe?: {} }} */ (wrangler_config).unsafe;
+ }
+
+ return {
+ name: 'vite-plugin-sveltekit-adapter-cloudflare-virtual-modules',
+ configResolved(config) {
+ const plugin = config.plugins.find((plugin) => plugin.name === 'vite-plugin-sveltekit-setup');
+ const options = plugin?.api?.options;
+ if (!options) throw new Error('vite-plugin-sveltekit-setup not found');
+ out_dir = options.kit.outDir;
+ },
+ resolveId: {
+ filter: {
+ id: modules.map((m) => exactRegex(m))
+ },
+ handler(id) {
+ return '\0' + id;
+ }
+ },
+ load: {
+ filter: {
+ id: modules.map((m) => exactRegex('\0' + m))
+ },
+ handler(id) {
+ if (id === '\0cloudflare:workers') {
+ return dedent`
+ import { getPlatformProxy } from 'wrangler';
+ import { writeFileSync, mkdirSync } from 'node:fs';
+
+ const tmp_dir = ${JSON.stringify(out_dir + '/cloudflare-tmp')};
+ mkdirSync(tmp_dir, { recursive: true });
+ const tmp_config_file = tmp_dir + '/wrangler.json';
+ writeFileSync(tmp_config_file, ${JSON.stringify(JSON.stringify(wrangler_config))});
+ const proxy = await getPlatformProxy({
+ ...${JSON.stringify(options.platformProxy ?? {})},
+ configPath: tmp_config_file
+ });
+ export const env = proxy.env;
+ export const waitUntil = () => {};
+ // TODO - stub other exports
+ `;
+ }
+ }
}
};
}
+
+const WEBSOCKET_HEAD = Symbol('websocketHead');
+/** @typedef {import('http').ServerResponse & { [WEBSOCKET_HEAD]?: Buffer }} WebSocketServerResponse */
+/** @type {import('vite').Plugin} */
+const listener_plugin = {
+ name: 'vite-plugin-sveltekit-adapter-cloudflare-listeners',
+ enforce: 'post',
+ configureServer(server) {
+ return () => {
+ if (server.httpServer) {
+ const upgrade_listeners = server.httpServer
+ .listeners('upgrade')
+ .filter((listener) => listener.name !== 'hmrServerWsListener');
+
+ for (const listener of upgrade_listeners) {
+ server.httpServer.removeListener('upgrade', /** @type {() => void} */ (listener));
+ }
+
+ /**
+ * @param {import('vite').Connect.IncomingMessage} req
+ * @param {import('net').Socket} socket
+ * @param {Buffer} head
+ */
+ const upgrade_handler = (req, socket, head) => {
+ if (req.headers['x-sveltekit-cloudflare-handle']) {
+ delete req.headers['x-sveltekit-cloudflare-handle'];
+ req.originalUrl = req.url;
+
+ const res = new ServerResponse(req);
+ res.assignSocket(socket);
+ /** @type {WebSocketServerResponse} */ (res)[WEBSOCKET_HEAD] = head;
+ handler(req, res);
+ return;
+ }
+ for (const listener of upgrade_listeners) {
+ listener(req, socket, head);
+ }
+ };
+
+ server.httpServer.on('upgrade', upgrade_handler);
+ }
+ const sveltekit_dev_middleware = server.middlewares.stack.find(
+ (middleware) =>
+ /** @type {Function} */ (middleware.handle).name === 'sveltekitDevMiddleware'
+ );
+ if (!sveltekit_dev_middleware) {
+ throw new Error('@sveltekit/adapter-cloudflare could not find sveltekitDevMiddleware');
+ }
+ const handler = /** @type {import('vite').Connect.SimpleHandleFunction} */ (
+ sveltekit_dev_middleware.handle
+ );
+ /** @type {import('vite').Connect.NextHandleFunction} */
+ sveltekit_dev_middleware.handle = (req, res, next) => {
+ if (req.headers['x-sveltekit-cloudflare-handle']) {
+ delete req.headers['x-sveltekit-cloudflare-handle'];
+ handler(req, res);
+ return;
+ }
+ next();
+ };
+ };
+ }
+};
+
/**
* @param {string} app_dir
* @param {string | undefined} content existing `_headers` file content
@@ -277,7 +453,7 @@ _redirects
* building_for_cloudflare_pages: boolean
* }}
*/
-function validate_wrangler_config(config_file = undefined) {
+function validate_wrangler_config(config_file) {
const wrangler_config = unstable_readConfig({ config: config_file });
const building_for_cloudflare_pages = is_building_for_cloudflare_pages(wrangler_config);
diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json
index 50f117f3fb8f..0ee3589bf7bd 100644
--- a/packages/adapter-cloudflare/package.json
+++ b/packages/adapter-cloudflare/package.json
@@ -23,6 +23,10 @@
"types": "./index.d.ts",
"import": "./index.js"
},
+ "./worker": {
+ "types": "./worker.d.ts",
+ "import": "./worker.js"
+ },
"./package.json": "./package.json"
},
"types": "index.d.ts",
@@ -46,13 +50,19 @@
},
"devDependencies": {
"@playwright/test": "catalog:",
+ "@rolldown/pluginutils": "^1.0.1",
"@sveltejs/kit": "workspace:^",
"@types/node": "catalog:",
+ "@types/ws": "^8.18.1",
+ "miniflare": "5.20260801.0-alpha",
"typescript": "catalog:",
- "vitest": "catalog:"
+ "vite": "catalog:",
+ "vitest": "catalog:",
+ "ws": "^8.21.2"
},
"peerDependencies": {
+ "@cloudflare/vite-plugin": "^1.51.0",
"@sveltejs/kit": "^3.0.0-next.0",
- "wrangler": "^4.118.0"
+ "wrangler": "^4.119.0"
}
}
diff --git a/packages/adapter-cloudflare/test/apps/workers/.gitignore b/packages/adapter-cloudflare/test/apps/workers/.gitignore
index de2e15da78f4..63d9a515f164 100644
--- a/packages/adapter-cloudflare/test/apps/workers/.gitignore
+++ b/packages/adapter-cloudflare/test/apps/workers/.gitignore
@@ -5,3 +5,4 @@ node_modules
# Cloudflare
.wrangler
/dist
+worker-configuration.d.ts
diff --git a/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc b/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc
index 8c2d83f8c21a..d0ad76b1637f 100644
--- a/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc
+++ b/packages/adapter-cloudflare/test/apps/workers/config/wrangler.jsonc
@@ -2,10 +2,31 @@
// to test that the adapter still resolves the paths correctly
{
"$schema": "../node_modules/wrangler/config-schema.json",
- "main": "../dist/index.js",
+ "name": "adapter-cloudflare-test",
+ "main": "../src/worker.ts",
"compatibility_date": "2026-06-24",
"assets": {
"directory": "../dist/public",
"binding": "ASSETS"
- }
+ },
+ "durable_objects": {
+ "bindings": [
+ {
+ "name": "DO",
+ "class_name": "DO"
+ }
+ ]
+ },
+ "exports": {
+ "DO": {
+ "type": "durable-object",
+ "storage": "sqlite"
+ }
+ },
+ "kv_namespaces": [
+ {
+ "binding": "KV",
+ "remote": false
+ }
+ ]
}
diff --git a/packages/adapter-cloudflare/test/apps/workers/package.json b/packages/adapter-cloudflare/test/apps/workers/package.json
index 2414fec7cb50..00e7521c5ba5 100644
--- a/packages/adapter-cloudflare/test/apps/workers/package.json
+++ b/packages/adapter-cloudflare/test/apps/workers/package.json
@@ -6,7 +6,7 @@
"dev": "vite dev",
"build": "vite build",
"preview": "wrangler dev dist/index.js --config config/wrangler.jsonc",
- "prepare": "svelte-kit sync || echo ''",
+ "prepare": "svelte-kit sync || echo ''; wrangler types --config config/wrangler.jsonc || echo ''",
"test:dev": "DEV=true playwright test",
"test:build": "playwright test",
"test": "pnpm test:dev && pnpm test:build"
diff --git a/packages/adapter-cloudflare/test/apps/workers/src/app.d.ts b/packages/adapter-cloudflare/test/apps/workers/src/app.d.ts
new file mode 100644
index 000000000000..4448bb67727c
--- /dev/null
+++ b/packages/adapter-cloudflare/test/apps/workers/src/app.d.ts
@@ -0,0 +1,10 @@
+declare global {
+ namespace App {
+ interface Platform {
+ env: Cloudflare.Env;
+ ctx: Cloudflare.ExecutionContext;
+ }
+ }
+}
+
+export {};
diff --git a/packages/adapter-cloudflare/test/apps/workers/src/routes/+page.svelte b/packages/adapter-cloudflare/test/apps/workers/src/routes/+page.svelte
index d5e339683387..7cd008c06705 100644
--- a/packages/adapter-cloudflare/test/apps/workers/src/routes/+page.svelte
+++ b/packages/adapter-cloudflare/test/apps/workers/src/routes/+page.svelte
@@ -1,5 +1,20 @@
-
Sum: {data.sum}
+
+WebSocket message: {ws_message}
diff --git a/packages/adapter-cloudflare/test/apps/workers/src/routes/ws/+server.js b/packages/adapter-cloudflare/test/apps/workers/src/routes/ws/+server.js
new file mode 100644
index 000000000000..b62a401ecd83
--- /dev/null
+++ b/packages/adapter-cloudflare/test/apps/workers/src/routes/ws/+server.js
@@ -0,0 +1,6 @@
+import { env } from 'cloudflare:workers';
+
+export const GET = async ({ request }) => {
+ const stub = env.DO.getByName('stub');
+ return stub.fetch(request.url, request);
+}
diff --git a/packages/adapter-cloudflare/test/apps/workers/src/worker.ts b/packages/adapter-cloudflare/test/apps/workers/src/worker.ts
new file mode 100644
index 000000000000..200cd77650b1
--- /dev/null
+++ b/packages/adapter-cloudflare/test/apps/workers/src/worker.ts
@@ -0,0 +1,24 @@
+import { DurableObject } from 'cloudflare:workers';
+import { handler } from '../../../../worker.js';
+
+export class DO extends DurableObject {
+ async fetch(_req: Request): Promise {
+ const { 0: client, 1: server } = new WebSocketPair();
+
+ this.ctx.acceptWebSocket(server);
+ setInterval(() => {
+ server.send(new Date().toISOString());
+ }, 1000);
+
+ return new Response(null, {
+ status: 101,
+ webSocket: client,
+ });
+ }
+}
+
+export default {
+ async fetch(request) {
+ return handler(request);
+ },
+} satisfies ExportedHandler;
diff --git a/packages/adapter-cloudflare/test/apps/workers/tsconfig.json b/packages/adapter-cloudflare/test/apps/workers/tsconfig.json
index 0c2092500732..bfb714a809aa 100644
--- a/packages/adapter-cloudflare/test/apps/workers/tsconfig.json
+++ b/packages/adapter-cloudflare/test/apps/workers/tsconfig.json
@@ -1,13 +1,4 @@
{
- "compilerOptions": {
- "allowJs": true,
- "checkJs": true,
- "esModuleInterop": true,
- "forceConsistentCasingInFileNames": true,
- "resolveJsonModule": true,
- "skipLibCheck": true,
- "sourceMap": true,
- "moduleResolution": "bundler"
- },
- "extends": "$app/tsconfig"
+ "extends": "$app/tsconfig",
+ "include": ["src", "worker-configuration.d.ts"]
}
diff --git a/packages/adapter-cloudflare/test/apps/workers/vite.config.js b/packages/adapter-cloudflare/test/apps/workers/vite.config.js
index 195f6c80776e..2e7d6f775655 100644
--- a/packages/adapter-cloudflare/test/apps/workers/vite.config.js
+++ b/packages/adapter-cloudflare/test/apps/workers/vite.config.js
@@ -11,7 +11,7 @@ const config = {
adapter: adapter({
config: 'config/wrangler.jsonc'
})
- })
+ }),
]
};
diff --git a/packages/adapter-cloudflare/tsconfig.json b/packages/adapter-cloudflare/tsconfig.json
index ce628354cbad..afcbf9c5ce74 100644
--- a/packages/adapter-cloudflare/tsconfig.json
+++ b/packages/adapter-cloudflare/tsconfig.json
@@ -15,5 +15,5 @@
"lib": ["es2024"],
"types": ["node"]
},
- "include": ["index.js", "utils.js", "utils.spec.js", "vitest.config.js", "test/utils.js"]
+ "include": ["index.js", "worker.js", "utils.js", "utils.spec.js", "vitest.config.js", "test/utils.js"]
}
diff --git a/packages/adapter-cloudflare/worker.d.ts b/packages/adapter-cloudflare/worker.d.ts
new file mode 100644
index 000000000000..9cd11c778278
--- /dev/null
+++ b/packages/adapter-cloudflare/worker.d.ts
@@ -0,0 +1 @@
+export function handler(request: Request): Response;
diff --git a/packages/adapter-cloudflare/worker.js b/packages/adapter-cloudflare/worker.js
new file mode 100644
index 000000000000..5b2d09db92cd
--- /dev/null
+++ b/packages/adapter-cloudflare/worker.js
@@ -0,0 +1,9 @@
+/**
+ * @param {Request} request
+ * @returns Response
+ */
+export function handler(request) {
+ const headers = new Headers(request.headers);
+ headers.set('x-sveltekit-cloudflare-handle', 'true');
+ return fetch(request, { headers });
+}
diff --git a/packages/kit/src/exports/internal/server/index.js b/packages/kit/src/exports/internal/server/index.js
index a48db594f0db..1b81e1393748 100644
--- a/packages/kit/src/exports/internal/server/index.js
+++ b/packages/kit/src/exports/internal/server/index.js
@@ -34,4 +34,6 @@ export { init_remote_functions } from './remote-functions.js';
export { init_tracing, otel, record_span } from './telemetry.js';
+export { dedent } from '../../../core/sync/utils.js';
+
export * from '../shared.js';
diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts
index 64c852afbf78..c615d89b6d57 100644
--- a/packages/kit/src/exports/public.d.ts
+++ b/packages/kit/src/exports/public.d.ts
@@ -35,6 +35,9 @@ export { PrerenderOption } from '../types/private.js';
// @ts-ignore this is an optional peer dependency so could be missing. Written like this so dts-buddy preserves the ts-ignore
type Span = import('@opentelemetry/api').Span;
+// @ts-ignore see above
+type ServerResponse = import('http').ServerResponse;
+
type AppErrorWithOptionalStatus = Omit & { status?: App.Error['status'] };
/**
@@ -50,6 +53,12 @@ export interface Adapter {
* @param builder An object provided by SvelteKit that contains methods for adapting the app
*/
adapt: (builder: Builder) => MaybePromise;
+ /**
+ * This function allows adapters to handle the low-level process of transforming a `Response` into
+ * the format that vite understands.
+ * @returns Whether the response was handled
+ */
+ setResponse?: (res: ServerResponse, response: Response) => boolean;
/**
* Checks called during dev and build to determine whether specific features will work in production with this adapter.
*/
@@ -73,10 +82,15 @@ export interface Adapter {
emulate?: () => MaybePromise;
vite?: {
/**
- * Plugins provided by the adapter are placed before any of SvelteKit's own plugins.
+ * Vite plugins placed before any of SvelteKit's own plugins.
+ * @since 3.0.0
+ */
+ pre_plugins?: Plugin[];
+ /**
+ * Vite plugins placed after any of SvelteKit's own plugins.
* @since 3.0.0
*/
- plugins?: Plugin[];
+ post_plugins?: Plugin[];
};
}
diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js
index 2dec6d74d782..b2783bdf9ad7 100644
--- a/packages/kit/src/exports/vite/dev/index.js
+++ b/packages/kit/src/exports/vite/dev/index.js
@@ -502,7 +502,8 @@ export async function dev(
// serving routes with those names. See https://github.com/vitejs/vite/issues/7363
remove_static_middlewares(vite_dev_server.middlewares);
- vite_dev_server.middlewares.use(async (req, res) => {
+ // eslint-disable-next-line prefer-arrow-callback
+ vite_dev_server.middlewares.use(async function sveltekitDevMiddleware(req, res) {
// Vite throws a Cannot read properties of undefined (reading 'wrapDynamicImport')
// if you try to run ssr.runner.import before the server has started so
// we do it inside here to avoid that
@@ -651,7 +652,13 @@ export async function dev(
});
} else {
log_response(rendered.status, request);
- setResponse(res, rendered);
+ let adapter_set_response = false;
+ if (svelte_config.kit.adapter?.setResponse) {
+ adapter_set_response = svelte_config.kit.adapter.setResponse(res, rendered);
+ }
+ if (!adapter_set_response) {
+ setResponse(res, rendered);
+ }
}
} catch (e) {
const error = coalesce_to_error(e);
diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js
index a37f5e64fa6c..8b3785807abc 100644
--- a/packages/kit/src/exports/vite/index.js
+++ b/packages/kit/src/exports/vite/index.js
@@ -2056,7 +2056,7 @@ function kit({ svelte_config }) {
return /** @type {Plugin[]} */ (
[
- svelte_config.kit.adapter?.vite?.plugins,
+ svelte_config.kit.adapter?.vite?.pre_plugins,
plugin_resolve_root,
plugin_setup,
plugin_remote_guard,
@@ -2066,7 +2066,8 @@ function kit({ svelte_config }) {
plugin_service_worker,
plugin_service_worker_env,
plugin_compile,
- plugin_adapter
+ plugin_adapter,
+ svelte_config.kit.adapter?.vite?.post_plugins,
].filter(Boolean)
);
}
diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js
index 4d640769dcc4..fc6d244939fa 100644
--- a/packages/kit/src/runtime/server/endpoint.js
+++ b/packages/kit/src/runtime/server/endpoint.js
@@ -45,11 +45,12 @@ export async function render_endpoint(event, state, mod) {
handler(/** @type {import('@sveltejs/kit').RequestEvent>} */ (event))
);
- if (!(response instanceof Response)) {
- throw new Error(
- `Invalid response from route ${event.url.pathname}: handler should return a Response object`
- );
- }
+ // TODO: need to accept Response-like objects here, such as from a different undici version
+ // if (!(response instanceof Response)) {
+ // throw new Error(
+ // `Invalid response from route ${event.url.pathname}: handler should return a Response object`
+ // );
+ // }
if (state.prerendering && (!state.prerendering.inside_reroute || prerender)) {
// The returned Response might have immutable Headers
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index 6122f53e10aa..7472d78c9b46 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -9,6 +9,9 @@ declare module '@sveltejs/kit' {
// @ts-ignore this is an optional peer dependency so could be missing. Written like this so dts-buddy preserves the ts-ignore
type Span = import('@opentelemetry/api').Span;
+ // @ts-ignore see above
+ type ServerResponse = import('http').ServerResponse;
+
type AppErrorWithOptionalStatus = Omit & { status?: App.Error['status'] };
/**
@@ -24,6 +27,12 @@ declare module '@sveltejs/kit' {
* @param builder An object provided by SvelteKit that contains methods for adapting the app
*/
adapt: (builder: Builder) => MaybePromise;
+ /**
+ * This function allows adapters to handle the low-level process of transforming a `Response` into
+ * the format that vite understands.
+ * @returns Whether the response was handled
+ */
+ setResponse?: (res: ServerResponse, response: Response) => boolean;
/**
* Checks called during dev and build to determine whether specific features will work in production with this adapter.
*/
@@ -47,10 +56,15 @@ declare module '@sveltejs/kit' {
emulate?: () => MaybePromise;
vite?: {
/**
- * Plugins provided by the adapter are placed before any of SvelteKit's own plugins.
+ * Vite plugins placed before any of SvelteKit's own plugins.
+ * @since 3.0.0
+ */
+ pre_plugins?: Plugin[];
+ /**
+ * Vite plugins placed after any of SvelteKit's own plugins.
* @since 3.0.0
*/
- plugins?: Plugin[];
+ post_plugins?: Plugin[];
};
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a2a675ac1bdc..995458026e7f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -94,8 +94,8 @@ catalogs:
specifier: ^4.1.10
version: 4.1.10
wrangler:
- specifier: ^4.118.0
- version: 4.118.0
+ specifier: ^4.119.0
+ version: 4.119.0
importers:
@@ -143,28 +143,46 @@ importers:
packages/adapter-cloudflare:
dependencies:
+ '@cloudflare/vite-plugin':
+ specifier: ^1.51.0
+ version: 1.51.0(vite@8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0))(wrangler@4.119.0(@cloudflare/workers-types@5.20260804.1))
'@cloudflare/workers-types':
specifier: ^5.20260730.1
version: 5.20260804.1
wrangler:
- specifier: ^4.118.0
- version: 4.118.0(@cloudflare/workers-types@5.20260804.1)
+ specifier: ^4.119.0
+ version: 4.119.0(@cloudflare/workers-types@5.20260804.1)
devDependencies:
'@playwright/test':
specifier: 'catalog:'
version: 1.62.1
+ '@rolldown/pluginutils':
+ specifier: ^1.0.1
+ version: 1.0.1
'@sveltejs/kit':
specifier: workspace:^
version: link:../kit
'@types/node':
specifier: 'catalog:'
version: 22.19.19
+ '@types/ws':
+ specifier: ^8.18.1
+ version: 8.18.1
+ miniflare:
+ specifier: 5.20260801.0-alpha
+ version: 5.20260801.0-alpha
typescript:
specifier: 'catalog:'
version: 6.0.3
+ vite:
+ specifier: 'catalog:'
+ version: 8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0)
vitest:
specifier: 'catalog:'
version: 4.1.10(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.10)(jsdom@30.0.1)(vite@8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0))
+ ws:
+ specifier: ^8.21.2
+ version: 8.21.2
packages/adapter-cloudflare/test/apps/pages:
devDependencies:
@@ -185,7 +203,7 @@ importers:
version: 8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0)
wrangler:
specifier: 'catalog:'
- version: 4.118.0(@cloudflare/workers-types@5.20260804.1)
+ version: 4.119.0(@cloudflare/workers-types@5.20260804.1)
packages/adapter-cloudflare/test/apps/workers:
devDependencies:
@@ -206,7 +224,7 @@ importers:
version: 8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0)
wrangler:
specifier: 'catalog:'
- version: 4.118.0(@cloudflare/workers-types@5.20260804.1)
+ version: 4.119.0(@cloudflare/workers-types@5.20260804.1)
packages/adapter-netlify:
dependencies:
@@ -1662,32 +1680,39 @@ packages:
workerd:
optional: true
- '@cloudflare/workerd-darwin-64@1.20260730.1':
- resolution: {integrity: sha512-+MBHmPaiTe2KajryW0T24rZvWFxb41hD3d8anNzQqHzft6vSEb18+sp0znSwxgij7ApPhSM1+vhkNg4f3YMguA==}
+ '@cloudflare/vite-plugin@1.51.0':
+ resolution: {integrity: sha512-8ltb2RA6U1i7KGDzVsRBDa7+GvWRBNGBx0mjTxdsHh/6zgCcUPLT3I8Kwp/xUkIbInojnwh8GAtnbuLJPfcK6w==}
+ hasBin: true
+ peerDependencies:
+ vite: ^6.1.0 || ^7.0.0 || ^8.0.0
+ wrangler: ^4.119.0
+
+ '@cloudflare/workerd-darwin-64@1.20260801.1':
+ resolution: {integrity: sha512-wuJWbXpKvncJi1P0GKS+iYpN5tHdb7JPJJ/+6ZQe8zzovHHVMkLJPNBsgWpqeUhpM3g9qTwEKd2rglNKejuh5A==}
engines: {node: '>=16'}
cpu: [x64]
os: [darwin]
- '@cloudflare/workerd-darwin-arm64@1.20260730.1':
- resolution: {integrity: sha512-SBHKntPkKvNPgaCrTe99xC1CAl8ygJDzlYfK0LbuJ1muKadIw35WnhO0wu894fKBtllsVQdNzDLee+cm0ppLSQ==}
+ '@cloudflare/workerd-darwin-arm64@1.20260801.1':
+ resolution: {integrity: sha512-kwoZiTpnhNrF3+APx84Q/oAqvJ3sU9yefGagwm/ASaH/2W19x0vghkW/r4qCoHCK0WW7EPugZ+aXjgPMRtlq1Q==}
engines: {node: '>=16'}
cpu: [arm64]
os: [darwin]
- '@cloudflare/workerd-linux-64@1.20260730.1':
- resolution: {integrity: sha512-ouyPOSMbiKPeSwUJUvxtMcxGAXs2J4aPE4T5ABIYX5ClcQx5j5bbHTmnqOQEY8sAuLTPjH7dY+iB6UI5ISlwwA==}
+ '@cloudflare/workerd-linux-64@1.20260801.1':
+ resolution: {integrity: sha512-r0vAxCZH+Jih9Unm1yoyiByPNWNgawcKciOHDm5Q37ZVGOkKLsT9AtLe3yLSaul76WrKqtf+JP2n0WW32VBLJg==}
engines: {node: '>=16'}
cpu: [x64]
os: [linux]
- '@cloudflare/workerd-linux-arm64@1.20260730.1':
- resolution: {integrity: sha512-YQ+Mi78U3TPdgBPtwq+Sm6rJU+Ihl2y0pjYtuuKkdmUbYzL7oLR6Xqq9wljhasnuCFICssDJaqhMep5WizYoEQ==}
+ '@cloudflare/workerd-linux-arm64@1.20260801.1':
+ resolution: {integrity: sha512-zWgpdZtSozvIgzQNmQiDSF8yEOQJUkRAWNsDXXzAAoy+fCn8YUoSibj3mpFSbZRvbUldeBEaW6SCdC2VEMkhNQ==}
engines: {node: '>=16'}
cpu: [arm64]
os: [linux]
- '@cloudflare/workerd-windows-64@1.20260730.1':
- resolution: {integrity: sha512-27fAN+vUECW1oYVc1KOcHYpkL8COM2Uxtxql7TL595kxbjoqS5yckw7NLz7bTf2pALFCZWjqXDjZGJ/xbG4ZKQ==}
+ '@cloudflare/workerd-windows-64@1.20260801.1':
+ resolution: {integrity: sha512-2oQz+Ksu4ji6e/+ZoYX+tWQEcxAii2p7l+iR8kx48W1llMalaufAsmVxTlk+3/vrM7D3/2c0iK448e0UQTcIMg==}
engines: {node: '>=16'}
cpu: [x64]
os: [win32]
@@ -2921,6 +2946,9 @@ packages:
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+ '@types/ws@8.18.1':
+ resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+
'@typescript-eslint/eslint-plugin@8.58.0':
resolution: {integrity: sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3655,8 +3683,8 @@ packages:
mdn-data@2.27.1:
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
- miniflare@5.20260730.0-alpha:
- resolution: {integrity: sha512-8/dspSXDshP6nSkCpjKO7BYc2qZoYSXm7iM+QxY7qJyJpAB3onnQSaiu0cvKJlfuMGwULl55hG69FJCcCMXU1Q==}
+ miniflare@5.20260801.0-alpha:
+ resolution: {integrity: sha512-AfMrnQbJg81ESsGkGhOkHBtwTqCG+mosR+3GE+qDrhF1I/ieIvgg3ICxNyBvgHBZ3iapy6cysBQHNPykkzrfgw==}
engines: {node: '>=22.0.0'}
minimatch@10.2.5:
@@ -4296,17 +4324,17 @@ packages:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
- workerd@1.20260730.1:
- resolution: {integrity: sha512-zmfNIjwYSWFY5chGBOjWtH3xAE7p97FTC6vR4Ep98290ho6AeAR/NVcBD274YCLEUYzqm8yxdtZlxMybU8a3jA==}
+ workerd@1.20260801.1:
+ resolution: {integrity: sha512-/g9JGTyqnHtoIscpBHqKD8swE2V4StBs2i69PmLiOhH45OP95jCFICl4F1hKlgN57rqfni5LiCitttoX5OOkVA==}
engines: {node: '>=16'}
hasBin: true
- wrangler@4.118.0:
- resolution: {integrity: sha512-9pkBw/b8zWqGx2S+oLhgHMR1M/4VOE8SynUFABnGWiSFGlcOQ4xiI/B71Xf66RYP2xzngU37IQFPtUruij3lYw==}
+ wrangler@4.119.0:
+ resolution: {integrity: sha512-ookClf+zly4DTc8pBMNrwGQzZKH8IpIYTXkjDw3XS7ZvBQ5mLYH6eOvfD5BEpk3U63zTbv91WRlo1UeRSKXa0g==}
engines: {node: '>=22.0.0'}
hasBin: true
peerDependencies:
- '@cloudflare/workers-types': ^5.20260730.1
+ '@cloudflare/workers-types': ^5.20260801.1
peerDependenciesMeta:
'@cloudflare/workers-types':
optional: true
@@ -4327,6 +4355,18 @@ packages:
utf-8-validate:
optional: true
+ ws@8.21.2:
+ resolution: {integrity: sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
@@ -4522,25 +4562,38 @@ snapshots:
'@cloudflare/kv-asset-handler@0.5.0': {}
- '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260730.1)':
+ '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260801.1)':
dependencies:
unenv: 2.0.0-rc.24
optionalDependencies:
- workerd: 1.20260730.1
+ workerd: 1.20260801.1
- '@cloudflare/workerd-darwin-64@1.20260730.1':
+ '@cloudflare/vite-plugin@1.51.0(vite@8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0))(wrangler@4.119.0(@cloudflare/workers-types@5.20260804.1))':
+ dependencies:
+ '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260801.1)
+ miniflare: 5.20260801.0-alpha
+ unenv: 2.0.0-rc.24
+ vite: 8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0)
+ workerd: 1.20260801.1
+ wrangler: 4.119.0(@cloudflare/workers-types@5.20260804.1)
+ ws: 8.21.0
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ '@cloudflare/workerd-darwin-64@1.20260801.1':
optional: true
- '@cloudflare/workerd-darwin-arm64@1.20260730.1':
+ '@cloudflare/workerd-darwin-arm64@1.20260801.1':
optional: true
- '@cloudflare/workerd-linux-64@1.20260730.1':
+ '@cloudflare/workerd-linux-64@1.20260801.1':
optional: true
- '@cloudflare/workerd-linux-arm64@1.20260730.1':
+ '@cloudflare/workerd-linux-arm64@1.20260801.1':
optional: true
- '@cloudflare/workerd-windows-64@1.20260730.1':
+ '@cloudflare/workerd-windows-64@1.20260801.1':
optional: true
'@cloudflare/workers-types@5.20260804.1': {}
@@ -5530,6 +5583,10 @@ snapshots:
'@types/trusted-types@2.0.7': {}
+ '@types/ws@8.18.1':
+ dependencies:
+ '@types/node': 22.19.19
+
'@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.8.0(jiti@2.4.2)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.4.2)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
@@ -5665,7 +5722,7 @@ snapshots:
sirv: 3.0.2
tinyrainbow: 3.1.0
vitest: 4.1.10(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.10)(jsdom@30.0.1)(vite@8.1.5(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0))
- ws: 8.21.0
+ ws: 8.21.2
transitivePeerDependencies:
- bufferutil
- msw
@@ -6302,12 +6359,12 @@ snapshots:
mdn-data@2.27.1: {}
- miniflare@5.20260730.0-alpha:
+ miniflare@5.20260801.0-alpha:
dependencies:
'@cspotcode/source-map-support': 0.8.1
sharp: 0.35.2
undici: 7.28.0
- workerd: 1.20260730.1
+ workerd: 1.20260801.1
ws: 8.21.0
youch: 4.1.0-beta.10
transitivePeerDependencies:
@@ -6921,24 +6978,24 @@ snapshots:
word-wrap@1.2.5: {}
- workerd@1.20260730.1:
+ workerd@1.20260801.1:
optionalDependencies:
- '@cloudflare/workerd-darwin-64': 1.20260730.1
- '@cloudflare/workerd-darwin-arm64': 1.20260730.1
- '@cloudflare/workerd-linux-64': 1.20260730.1
- '@cloudflare/workerd-linux-arm64': 1.20260730.1
- '@cloudflare/workerd-windows-64': 1.20260730.1
+ '@cloudflare/workerd-darwin-64': 1.20260801.1
+ '@cloudflare/workerd-darwin-arm64': 1.20260801.1
+ '@cloudflare/workerd-linux-64': 1.20260801.1
+ '@cloudflare/workerd-linux-arm64': 1.20260801.1
+ '@cloudflare/workerd-windows-64': 1.20260801.1
- wrangler@4.118.0(@cloudflare/workers-types@5.20260804.1):
+ wrangler@4.119.0(@cloudflare/workers-types@5.20260804.1):
dependencies:
'@cloudflare/kv-asset-handler': 0.5.0
- '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260730.1)
+ '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260801.1)
blake3-wasm: 2.1.5
esbuild: 0.28.1
- miniflare: 5.20260730.0-alpha
+ miniflare: 5.20260801.0-alpha
path-to-regexp: 6.3.0
unenv: 2.0.0-rc.24
- workerd: 1.20260730.1
+ workerd: 1.20260801.1
optionalDependencies:
'@cloudflare/workers-types': 5.20260804.1
fsevents: 2.3.3
@@ -6954,6 +7011,8 @@ snapshots:
ws@8.21.0: {}
+ ws@8.21.2: {}
+
xml-name-validator@5.0.0: {}
xmlchars@2.2.0: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 3bfac531a678..f0a9f5eef2ba 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -78,7 +78,7 @@ catalog:
typescript: ^6.0.3
valibot: ^1.4.2
vite: ^8.1.5
- wrangler: ^4.118.0
+ wrangler: ^4.119.0
# unit test deps that should be upgraded together
'@vitest/browser-playwright': ^4.1.10