Skip to content

feat: add Bun adapter - #16695

Merged
Rich-Harris merged 102 commits into
sveltejs:version-3from
Black-Hack:feat/bun-adapter
Aug 21, 2026
Merged

feat: add Bun adapter#16695
Rich-Harris merged 102 commits into
sveltejs:version-3from
Black-Hack:feat/bun-adapter

Conversation

@Black-Hack

Copy link
Copy Markdown
Contributor

closes #16070

Summary

This PR adds an official @sveltejs/adapter-bun package that builds SvelteKit applications into standalone Bun-native servers using Bun.serve, Web API requests/responses, native routes, and Bun.file.

What changed

  • Adds SSR, hydration, static assets, prerendered pages/endpoints, redirects, base paths, immutable caching, and non-GET fallthrough to SvelteKit.
  • Supports $app/server reads, instrumentation.server.js, server-sent events, graceful shutdown, request-origin normalization, and trusted proxy headers.
  • Exposes the original Request and Bun Server through event.platform, including Bun metrics and requestIP.
  • Supports regular JavaScript builds and self-contained executables with asset embedding, cross-compilation, minification, bytecode, and source maps.

Key differences from adapter-node

Area adapter-bun adapter-node
Runtime Native Bun.serve and Web APIs Node HTTP, Polka, and request conversion
Static files Native Bun routes and file responses sirv with Brotli/gzip precompression
Build output JavaScript server or compiled executable JavaScript server only
Customization Bun server context through event.platform Reusable handler.js and Node request context
Lifecycle Bun timeouts and second-signal forced exit Systemd activation and Node-specific timeout controls

Application imports follow Bun's bundler behavior rather than adapter-node's dependencies/devDependencies externalization policy.

Decisions and tradeoffs

  • Native routes preserve Bun's optimized file serving, conditional requests, ranges, streaming, and automatic HEAD behavior.
  • Filesystem assets remain unbuffered in regular builds; compiled builds embed them into the executable.
  • The generated server owns fetch and routes to preserve SvelteKit behavior.
  • SvelteKit receives a normalized public URL while event.platform.request retains the original Bun request required by native APIs.
  • Node-specific behavior is not emulated where Bun provides a different native model.

Known limitations and future work

  • Bun treats * as a route wildcard, so filenames containing a literal * are currently rejected at build time.
  • WebSockets, TLS, custom error handlers, HTTP/3, and explicit HTTP/1 configuration require a custom Bun integration.
  • This PR does not provide a reusable custom-server handler or built-in gzip/Brotli precompression.
  • Systemd socket activation and Node-specific shutdown, keep-alive, and header timeouts are out of scope.
  • Follow-ups may expand buildOptions, dependency externalization controls, MIME handling, and literal-wildcard support.

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

Edits

  • Please ensure that 'Allow edits from maintainers' is checked. PRs without this option may be closed.

… configuration

- Add env.js for managing environment variables with validation and fallback options.
- Introduce handler.js to manage the Bun-native SvelteKit request handling.
- Create index.js to configure server options based on environment variables.
- Implement static.js for serving static files and handling prerendered paths.
- Add utils.js for utility functions related to byte parsing and header management.
- Create tests for environment variable functions and utility functions.
- Set up a basic SvelteKit application with routes, static files, and event streams.
- Configure Playwright for end-to-end testing of the application.
…asset handling

- Added TLS configuration options in serverOptions, allowing for certificate and key specification.
- Introduced embedded asset handling for serving static files with metadata (size, type, lastModified, etag).
- Updated file_route to support embedded assets and conditional requests.
- Enhanced compile options to reserve runtime target and module format, ensuring user configurations are safely composed.
- Added tests for TLS options and embedded asset functionality to ensure robustness.
- Improved documentation to clarify usage of new features and options.
Comment thread packages/adapter-bun/src/routes-util.js
Bun.build emits a copy of a side-effect-only chunk (import 'x'; export {};)
per importer and every copy targets the same output path, failing real-world
apps with 'Multiple files share the same output path'. Resolve each importer's
copy to a distinct synthetic module so the copies no longer collide.
Nic-Polumeyv added a commit to Nic-Polumeyv/kit that referenced this pull request Aug 14, 2026
workspace:^/catalog: specifiers only resolve inside the kit monorepo; bun
resolves a file: directory dependency's devDependencies, so installing the
package from a submodule checkout fails on them. Pin branch for
polumeyv production use of PR sveltejs#16695; rebase onto the PR head to update.
}
```

`platform.request` remains the original request even when the adapter normalizes the request URL to a configured or proxy-derived public origin before passing it to SvelteKit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the rationale for passing this through? does platform.request have methods/properties that event.request doesn't (like request.cf on Cloudflare)? if not I don't really see why we'd pass this along, we don't for any other platform

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's identity, not extra properties. server.upgrade(request) and server.requestIP(request) only work with the request object Bun passed to fetch. Behind a proxy the adapter hands SvelteKit new Request(public_origin + pathname, request) so the origin matches, which makes event.request a copy: on 1.4.0, upgrade(copy) returns false and the socket never opens, requestIP(copy) is null. Without platform.request a route behind a reverse proxy cannot upgrade a WebSocket. The docs example shows the least useful property of it. I'll reword it around server.upgrade.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: the generated server never sets websocket, so upgrade() throws there with either request and that argument is moot. What identity still buys is server.timeout(request, seconds) and server.requestIP(request), and getClientAddress() already covers the second. That's thin. I'll drop platform.request and keep platform.server unless you'd rather keep it for timeout.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never regretted being more restrictive at first and then exposing new stuff as and when a need is demonstrated. In this case I think the global idleTimeout option is probably sufficient... though that said AFAICT we're not currently setting a value other than the default, which IIUC is 10 seconds? That might not be sufficient, given that we chose 30 seconds as the (probably somewhat arbitrary) threshold for sending keep-alive comments for query.live in #16063.

We could either change the default here or there. But given that it's an experimental feature it needn't prevent us from merging this and allowing early adopters to start kicking the tyres

The generated server never sets websocket, so server.upgrade() throws with either request; getClientAddress() already wraps requestIP with the original. Nothing left justifies a second copy of the request on platform.
Nic-Polumeyv added a commit to Nic-Polumeyv/kit that referenced this pull request Aug 21, 2026
workspace:^/catalog: specifiers only resolve inside the kit monorepo; bun
resolves a file: directory dependency's devDependencies, so installing the
package from a submodule checkout fails on them. Pin branch for
polumeyv production use of PR sveltejs#16695; rebase onto the PR head to update.
Nic-Polumeyv added a commit to Nic-Polumeyv/kit that referenced this pull request Aug 21, 2026
workspace:^/catalog: specifiers only resolve inside the kit monorepo; bun
resolves a file: directory dependency's devDependencies, so installing the
package from a submodule checkout fails on them. Pin branch for
polumeyv production use of PR sveltejs#16695; rebase onto the PR head to update.

@Rich-Harris Rich-Harris left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outstanding, thank you!

@svelte-docs-bot

Copy link
Copy Markdown

@Rich-Harris

Copy link
Copy Markdown
Member

Once this gets released we can update the docs — for now I put a bunch of @errors annotations to get them building

@Rich-Harris
Rich-Harris merged commit 863a663 into sveltejs:version-3 Aug 21, 2026
36 of 37 checks passed
Rich-Harris pushed a commit that referenced this pull request Aug 21, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to version-3, this PR
will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`version-3` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `version-3`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## @sveltejs/adapter-bun@1.0.0-next.1

### Minor Changes

- feat: add a Bun-native adapter with static file serving and
single-executable support
([#16695](#16695))

### Patch Changes

- Updated dependencies
[[`9b3d195`](9b3d195),
[`385d378`](385d378),
[`3782448`](3782448),
[`d0d3a33`](d0d3a33),
[`4b7a483`](4b7a483),
[`e325d7d`](e325d7d),
[`4f63c79`](4f63c79)]:
  - @sveltejs/kit@3.0.0-next.25
## @sveltejs/kit@3.0.0-next.25

### Minor Changes

- feat: add an `applyReroute` helper for adapters that support split
serverless function deployments
([#16665](#16665))

### Patch Changes

- chore: build streamed responses from async generators
([#16847](#16847))

- fix: tweak response logging for remote requests
([#16865](#16865))

- fix: discard invalidation results when a navigation completes while
they load ([#16852](#16852))

- fix: route dev-server response logging through Vite's logger so it
respects `logLevel` and `customLogger`
([#16858](#16858))

- chore: read build-time config from defines on the server instead of
carrying it in `options`
([#16873](#16873))

- chore: read `options` from a single module instead of passing it
through the server runtime
([#16871](#16871))
## @sveltejs/adapter-netlify@7.0.0-next.10

### Patch Changes

- fix: correctly apply `reroute` results for apps configured with split
serverless functions
([#16665](#16665))
- Updated dependencies
[[`9b3d195`](9b3d195),
[`385d378`](385d378),
[`3782448`](3782448),
[`d0d3a33`](d0d3a33),
[`4b7a483`](4b7a483),
[`e325d7d`](e325d7d),
[`4f63c79`](4f63c79)]:
  - @sveltejs/kit@3.0.0-next.25
## @sveltejs/adapter-vercel@7.0.0-next.8

### Patch Changes

- fix: omit ISR data endpoints for server-only routes
([#16731](#16731))

- fix: correctly apply `reroute` results for apps configured with split
serverless functions
([#16665](#16665))
- Updated dependencies
[[`9b3d195`](9b3d195),
[`385d378`](385d378),
[`3782448`](3782448),
[`d0d3a33`](d0d3a33),
[`4b7a483`](4b7a483),
[`e325d7d`](e325d7d),
[`4f63c79`](4f63c79)]:
  - @sveltejs/kit@3.0.0-next.25

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@Rich-Harris

Copy link
Copy Markdown
Member

opened sveltejs/svelte.dev#2186 which will allows us to remove the @errors annotations

@Black-Hack

Black-Hack commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

I am thrilled that this was merged! This was my first PR ever for SvelteKit. Any feedback for future PRs?

@teemingc

Copy link
Copy Markdown
Member

Any feedback for future PRs?

Thank you. Some things that we might want to change in follow-up PRs:

  • the test files are written in ts but our eslint script only searches js files. We should change them to js or ignore them in the eslint config (otherwise errors appear in the IDE while the CI lint doesn’t error)
  • there are two test folders: test and tests. We can probably name these better or consolidate them.
  • we should try to reduce the number of mocks in the unit tests if possible
  • the bun platform tests won’t run unless the github workflow is in the main branch too. Might have to open up another pr based against main that copies the same ones over

@Black-Hack

Copy link
Copy Markdown
Contributor Author

Noted @teemingc. Will keep these in mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Bun adapter

4 participants