Skip to content

Commit dc34afa

Browse files
cursoragentabhi
andcommitted
feat: Add Bun integration and custom bundler support
Co-authored-by: abhi <abhi@mastra.ai>
1 parent eaa03dd commit dc34afa

File tree

9 files changed

+433
-15
lines changed

9 files changed

+433
-15
lines changed

.github/workflows/test-bun.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Bun Integration Tests
2+
3+
on:
4+
pull_request:
5+
branches: [main, 0.x]
6+
paths:
7+
- 'packages/bundler-bun/**'
8+
- 'packages/cli/**'
9+
- 'packages/core/**'
10+
- 'packages/deployer/**'
11+
- '.github/workflows/test-bun.yml'
12+
push:
13+
branches: [main]
14+
paths:
15+
- 'packages/bundler-bun/**'
16+
- 'packages/cli/**'
17+
- 'packages/core/**'
18+
- 'packages/deployer/**'
19+
20+
jobs:
21+
test-bun-bundler:
22+
name: Test Bun Bundler
23+
# Only run on the main repository, not on forks
24+
if: ${{ github.repository == 'mastra-ai/mastra' }}
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v5
28+
29+
- name: Setup Bun
30+
uses: oven-sh/setup-bun@v2
31+
with:
32+
bun-version: latest
33+
34+
- name: Setup pnpm and Node.js
35+
uses: ./.github/actions/setup-pnpm-node
36+
37+
- name: Install dependencies
38+
run: pnpm install
39+
40+
- name: Build core packages
41+
run: pnpm turbo build --filter "@mastra/core"
42+
43+
- name: Build bundler-bun
44+
run: pnpm turbo build --filter "@mastra/bundler-bun"
45+
46+
- name: Run bundler-bun tests with Bun
47+
working-directory: packages/bundler-bun
48+
run: bun test
49+
env:
50+
NODE_OPTIONS: '--max_old_space_size=8096'
51+
52+
test-bun-create:
53+
name: Test bun create mastra
54+
# Only run on the main repository, not on forks
55+
if: ${{ github.repository == 'mastra-ai/mastra' }}
56+
runs-on: ubuntu-latest
57+
steps:
58+
- uses: actions/checkout@v5
59+
60+
- name: Setup Bun
61+
uses: oven-sh/setup-bun@v2
62+
with:
63+
bun-version: latest
64+
65+
- name: Setup pnpm and Node.js
66+
uses: ./.github/actions/setup-pnpm-node
67+
68+
- name: Install dependencies
69+
run: pnpm install
70+
71+
- name: Build CLI
72+
run: pnpm turbo build --filter "mastra"
73+
74+
- name: Run Bun detection tests
75+
run: pnpm test:cli -- --run src/commands/create/bun-detection.test.ts
76+
env:
77+
NODE_OPTIONS: '--max_old_space_size=8096'

docs/src/content/en/docs/deployment/mastra-server.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,13 @@ The build process follows these steps:
4949
- **`.build`**: Contains dependency analysis, bundled dependencies, and build configuration files.
5050
- **`output`**: Contains the production-ready application bundle with `index.mjs` and project-specific files.
5151
3. **Copies static assets**: Copies the `public/` folder contents to the `output` directory for serving static files.
52-
4. **Bundles code**: Uses Rollup with tree shaking and source maps for optimization.
52+
4. **Bundles code**: Uses the configured bundler engine (Rollup by default) with tree shaking and source maps for optimization.
5353
5. **Generates server**: Creates a [Hono](https://hono.dev) HTTP server ready for deployment.
5454

55+
### Custom bundler engines
56+
57+
Mastra uses Rollup as the default bundler. If you're running on Bun or want to use a different bundler, you can configure a custom bundler engine in your Mastra configuration. See the [Custom Bundler Engines guide](/guides/v1/deployment/custom-bundler) for details.
58+
5559
### Build output structure
5660

5761
After building, Mastra creates a `.mastra/` directory with the following structure:

docs/src/content/en/docs/deployment/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Mastra applications can be deployed to any Node.js-compatible environment. You c
1414
Mastra can run against any of these runtime environments:
1515

1616
- Node.js `v22.13.0` or later
17-
- Bun
17+
- Bun (with optional native Bun bundler via `@mastra/bundler-bun`)
1818
- Deno
1919
- Cloudflare
2020

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
---
2+
title: "Bun Integration | Deployment"
3+
description: "Learn how to use Bun runtime features in your Mastra applications"
4+
---
5+
6+
# Bun Integration
7+
8+
Mastra supports Bun as a runtime environment with optional native Bun components for bundling and serving. This guide covers how to leverage Bun-specific features in your Mastra applications.
9+
10+
## Bun-Native Server
11+
12+
When running on Bun, you can use the native `Bun.serve()` for better performance instead of `@hono/node-server`.
13+
14+
### Automatic Detection
15+
16+
Use `createServer()` which automatically detects the runtime:
17+
18+
```typescript
19+
import { createServer } from "@mastra/deployer/server";
20+
import { mastra } from "./mastra";
21+
22+
// Automatically uses Bun.serve() on Bun, @hono/node-server on Node.js
23+
const server = await createServer(mastra, { tools: {} });
24+
```
25+
26+
### Explicit Bun Server
27+
28+
For explicit Bun usage:
29+
30+
```typescript
31+
import { createBunServer } from "@mastra/deployer/server";
32+
import { mastra } from "./mastra";
33+
34+
// Throws an error if not running on Bun
35+
const server = await createBunServer(mastra, { tools: {} });
36+
```
37+
38+
## Storage on Bun
39+
40+
### LibSQLStore
41+
42+
The `@mastra/libsql` storage adapter works on Bun without any additional configuration. LibSQL's client library has native Bun support:
43+
44+
```typescript
45+
import { Mastra } from "@mastra/core";
46+
import { LibSQLStore } from "@mastra/libsql";
47+
48+
export const mastra = new Mastra({
49+
storage: new LibSQLStore({
50+
id: "my-store",
51+
url: "file:./data.db", // Works on Bun
52+
}),
53+
});
54+
```
55+
56+
For in-memory databases (useful for development):
57+
58+
```typescript
59+
const storage = new LibSQLStore({
60+
id: "memory-store",
61+
url: ":memory:",
62+
});
63+
```
64+
65+
## Custom Bundler Engines
66+
67+
Mastra uses Rollup as the default bundler for production builds. If you're running on Bun, you can configure the native Bun bundler for faster build times.
68+
69+
## Using the Bun Bundler
70+
71+
If you're running your Mastra application on Bun, you can use the native Bun bundler for faster build times.
72+
73+
### Installation
74+
75+
```bash
76+
bun add @mastra/bundler-bun@beta
77+
```
78+
79+
### Usage
80+
81+
```typescript title="src/mastra/index.ts"
82+
import { Mastra } from "@mastra/core";
83+
import { createBunEngine } from "@mastra/bundler-bun";
84+
85+
export const mastra = new Mastra({
86+
bundler: {
87+
engine: createBunEngine(),
88+
},
89+
});
90+
```
91+
92+
### Configuration Options
93+
94+
The Bun bundler engine accepts these options:
95+
96+
| Option | Type | Default | Description |
97+
|--------|------|---------|-------------|
98+
| `minify` | `boolean` | `true` | Enable minification |
99+
| `sourcemap` | `"external" \| "inline" \| "none"` | `"none"` | Source map generation |
100+
| `target` | `"bun" \| "node" \| "browser"` | `"bun"` | Build target environment |
101+
102+
Example with options:
103+
104+
```typescript title="src/mastra/index.ts"
105+
import { Mastra } from "@mastra/core";
106+
import { createBunEngine } from "@mastra/bundler-bun";
107+
108+
export const mastra = new Mastra({
109+
bundler: {
110+
engine: createBunEngine({
111+
minify: false,
112+
sourcemap: "external",
113+
target: "bun",
114+
}),
115+
},
116+
});
117+
```
118+
119+
## Using the Default Rollup Bundler
120+
121+
The default Rollup bundler is used automatically when no custom engine is specified. You can also explicitly configure it:
122+
123+
```typescript title="src/mastra/index.ts"
124+
import { Mastra } from "@mastra/core";
125+
import { createRollupEngine } from "@mastra/deployer/engines";
126+
127+
export const mastra = new Mastra({
128+
bundler: {
129+
engine: createRollupEngine(),
130+
},
131+
});
132+
```
133+
134+
## Creating a Custom Bundler Engine
135+
136+
You can create custom bundler engines by implementing the `BundlerEngine` interface:
137+
138+
```typescript
139+
import type { BundlerEngine, BundlerEngineOptions, BundlerEngineOutput } from "@mastra/core/bundler";
140+
141+
export class CustomBundlerEngine implements BundlerEngine {
142+
readonly name = "custom";
143+
144+
async bundle(options: BundlerEngineOptions): Promise<BundlerEngineOutput> {
145+
// options.entryPoint - The main entry file path
146+
// options.outputDir - Where to write the bundled output
147+
// options.outputFile - The output filename (e.g., "index.mjs")
148+
// options.externals - Packages to exclude from bundling
149+
// options.sourcemap - Whether to generate source maps
150+
151+
// Implement your bundling logic here
152+
// ...
153+
154+
return {
155+
success: true,
156+
outputPath: `${options.outputDir}/${options.outputFile}`,
157+
};
158+
}
159+
}
160+
```
161+
162+
### BundlerEngineOptions
163+
164+
| Property | Type | Description |
165+
|----------|------|-------------|
166+
| `entryPoint` | `string` | Absolute path to the entry file |
167+
| `outputDir` | `string` | Directory for the bundled output |
168+
| `outputFile` | `string` | Name of the output file |
169+
| `externals` | `string[]` | Packages to exclude from the bundle |
170+
| `sourcemap` | `boolean` | Whether to generate source maps |
171+
172+
### BundlerEngineOutput
173+
174+
| Property | Type | Description |
175+
|----------|------|-------------|
176+
| `success` | `boolean` | Whether the bundling succeeded |
177+
| `outputPath` | `string` | Path to the generated bundle |
178+
| `error` | `Error` (optional) | Error details if bundling failed |
179+
180+
## Runtime Detection
181+
182+
The Bun bundler engine requires the Bun runtime. It will throw an error if used outside of Bun:
183+
184+
```typescript
185+
// This will throw if not running in Bun
186+
const engine = createBunEngine();
187+
```
188+
189+
For projects that need to support multiple runtimes, you can conditionally use the bundler:
190+
191+
```typescript title="src/mastra/index.ts"
192+
import { Mastra } from "@mastra/core";
193+
194+
const getBundlerConfig = async () => {
195+
if (typeof globalThis.Bun !== "undefined") {
196+
const { createBunEngine } = await import("@mastra/bundler-bun");
197+
return { engine: createBunEngine() };
198+
}
199+
return {}; // Use default Rollup bundler
200+
};
201+
202+
export const mastra = new Mastra({
203+
bundler: await getBundlerConfig(),
204+
});
205+
```

docs/src/content/en/guides/getting-started/quickstart.mdx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,6 @@ yarn create mastra@beta
5353
bun create mastra@beta
5454
```
5555

56-
:::warning
57-
58-
Due to a [known issue with Bun](https://github.com/oven-sh/bun/issues/25314) you'll need to run these steps after creating the project:
59-
60-
- `bun add @mastra/server@beta`
61-
- Delete your `node_modules` folder
62-
- Delete your `bun.lock` file
63-
- Run `bun install` to reinstall your packages
64-
65-
:::
66-
6756
</TabItem>
6857
</Tabs>
6958

docs/src/content/en/reference/core/mastra-class.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ export const mastra = new Mastra({
131131
name: "bundler",
132132
type: "BundlerConfig",
133133
description:
134-
"Configuration for the asset bundler with options for externals, sourcemap, and transpilePackages.",
134+
"Configuration for the asset bundler with options for externals, sourcemap, transpilePackages, and custom engine. The engine property allows using alternative bundlers like Bun instead of the default Rollup.",
135135
isOptional: true,
136136
defaultValue:
137-
"{ externals: [], sourcemap: false, transpilePackages: [] }",
137+
"{ externals: [], sourcemap: false, transpilePackages: [], engine: undefined }",
138138
},
139139
{
140140
name: "scorers",

packages/cli/src/commands/create/bun-detection.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,41 @@ describe('Bun Runtime Detection', () => {
135135
expect(mocks.mockExec).toHaveBeenCalledWith(expect.stringContaining('bun add zod@^4'));
136136
});
137137

138+
it('should explicitly install @mastra/server when using bun (workaround for bun issue #25314)', async () => {
139+
process.env.npm_config_user_agent = 'bun/1.0.0';
140+
141+
const { createMastraProject } = await import('./utils');
142+
143+
await createMastraProject({
144+
projectName: 'test-bun-server-workaround',
145+
needsInteractive: false,
146+
});
147+
148+
// Check that @mastra/server is explicitly installed for bun
149+
// This is a workaround for https://github.com/oven-sh/bun/issues/25314
150+
const allCalls = mocks.mockExec.mock.calls.map((c: unknown[]) => c[0]);
151+
const serverCall = allCalls.find((cmd: string) => cmd && cmd.includes('@mastra/server'));
152+
expect(serverCall).toBeDefined();
153+
expect(serverCall).toContain('bun add @mastra/server@');
154+
});
155+
156+
it('should NOT explicitly install @mastra/server when using npm', async () => {
157+
process.env.npm_config_user_agent = 'npm/10.0.0';
158+
159+
const { createMastraProject } = await import('./utils');
160+
161+
await createMastraProject({
162+
projectName: 'test-npm-no-server-workaround',
163+
needsInteractive: false,
164+
});
165+
166+
// npm should NOT have @mastra/server explicitly installed (it works correctly via transitive deps)
167+
const serverCalls = mocks.mockExec.mock.calls.filter(
168+
(call: string[]) => call[0] && call[0].includes('@mastra/server'),
169+
);
170+
expect(serverCalls.length).toBe(0);
171+
});
172+
138173
it('should use npm init when npm is detected', async () => {
139174
process.env.npm_config_user_agent = 'npm/10.0.0';
140175

packages/cli/src/commands/create/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,14 @@ export const createMastraProject = async ({
239239
await installMastraDependency(pm, '@mastra/core', versionTag, false, timeout);
240240
await installMastraDependency(pm, '@mastra/libsql', versionTag, false, timeout);
241241
await installMastraDependency(pm, '@mastra/memory', versionTag, false, timeout);
242+
243+
// Bun workaround: Bun doesn't respect npm's deprecated flag, which can cause
244+
// incorrect versions to be installed. Explicitly install @mastra/server to
245+
// ensure the correct version is used.
246+
// See: https://github.com/oven-sh/bun/issues/25314
247+
if (pm === 'bun') {
248+
await installMastraDependency(pm, '@mastra/server', versionTag, false, timeout);
249+
}
242250
} catch (error) {
243251
throw new Error(
244252
`Failed to install Mastra dependencies: ${error instanceof Error ? error.message : 'Unknown error'}`,

0 commit comments

Comments
 (0)