-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[WIP] feat: add Bun runtime compatibility #5843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -184,6 +184,61 @@ | |||||||||||||||||
| with: | ||||||||||||||||||
| use_oidc: true | ||||||||||||||||||
|
|
||||||||||||||||||
| test-bun: | ||||||||||||||||||
| strategy: | ||||||||||||||||||
| fail-fast: false | ||||||||||||||||||
| matrix: | ||||||||||||||||||
| os: ['ubuntu-latest', 'macos-latest'] | ||||||||||||||||||
| bun: ['latest'] | ||||||||||||||||||
| shardIndex: [1, 2, 3] | ||||||||||||||||||
| shardTotal: [3] | ||||||||||||||||||
|
|
||||||||||||||||||
| name: Test Bun (${{ matrix.os }}, ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}) | ||||||||||||||||||
| runs-on: ${{ matrix.os }} | ||||||||||||||||||
|
|
||||||||||||||||||
| concurrency: | ||||||||||||||||||
| group: test-bun-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }}-(${{ matrix.os }}, ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}) | ||||||||||||||||||
| cancel-in-progress: true | ||||||||||||||||||
|
|
||||||||||||||||||
| steps: | ||||||||||||||||||
| - name: Checkout repository | ||||||||||||||||||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Start Redis | ||||||||||||||||||
| uses: shogo82148/actions-setup-redis@cff708d63a30aebc0bfaa7276fb709d173f36cb6 # v1 | ||||||||||||||||||
| with: | ||||||||||||||||||
| redis-version: '7' | ||||||||||||||||||
| auto-start: 'true' | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Start MySQL | ||||||||||||||||||
| uses: shogo82148/actions-setup-mysql@27e74fac04c136a9f4c2dc2ed457df57331b3e0c # v1 | ||||||||||||||||||
| with: | ||||||||||||||||||
| mysql-version: '8' | ||||||||||||||||||
| auto-start: 'true' | ||||||||||||||||||
| - name: Init DB | ||||||||||||||||||
| run: | | ||||||||||||||||||
| mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;" | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Install pnpm | ||||||||||||||||||
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Set up Node.js | ||||||||||||||||||
| uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 | ||||||||||||||||||
| with: | ||||||||||||||||||
| node-version: '24' | ||||||||||||||||||
| cache: 'pnpm' | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Set up Bun | ||||||||||||||||||
| uses: oven-sh/setup-bun@v2 | ||||||||||||||||||
| with: | ||||||||||||||||||
| bun-version: ${{ matrix.bun }} | ||||||||||||||||||
|
Comment on lines
+231
to
+234
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: The latest commit hash for oven-sh/setup-bun v2 is 0c5077e. This is the commit associated with the latest v2 release, v2.2.0, released on 2026-03-14. GitHub Actions typically reference releases by tag (e.g., oven-sh/setup-bun@v2), which points to this commit. The repository's main branch last push was on 2026-03-14T09:38:41Z, matching the release time, suggesting no later commits on main. Citations: Pin the Using 🔧 Proposed fix - name: Set up Bun
- uses: oven-sh/setup-bun@v2
+ uses: oven-sh/setup-bun@0c5077e # v2.2.0
with:
bun-version: ${{ matrix.bun }}📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: CodeQL[warning] 232-232: Unpinned tag for a non-immutable Action in workflow 🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| - name: Install dependencies | ||||||||||||||||||
| run: pnpm install --frozen-lockfile | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Run tests with Bun | ||||||||||||||||||
| run: bun ./node_modules/vitest/vitest.mjs run --bail 1 --retry 2 --testTimeout 20000 --hookTimeout 20000 --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} | ||||||||||||||||||
|
|
||||||||||||||||||
| test-egg-bin: | ||||||||||||||||||
| strategy: | ||||||||||||||||||
| fail-fast: false | ||||||||||||||||||
|
|
@@ -278,6 +333,7 @@ | |||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||
| needs: | ||||||||||||||||||
| - test | ||||||||||||||||||
| - test-bun | ||||||||||||||||||
| - test-egg-bin | ||||||||||||||||||
| - test-egg-scripts | ||||||||||||||||||
| - typecheck | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { ms } from 'humanize-ms'; | ||
| import { | ||
| HttpClient as RawHttpClient, | ||
| HttpClientRequestTimeoutError, | ||
| type RequestURL as HttpClientRequestURL, | ||
| type RequestOptions, | ||
| type ClientOptions as HttpClientOptions, | ||
|
|
@@ -48,6 +49,29 @@ export class HttpClient extends RawHttpClient { | |
| } else { | ||
| options.tracer = options.tracer ?? this.#app.tracer; | ||
| } | ||
| // Bun's undici doesn't honor headersTimeout/bodyTimeout, | ||
| // use AbortSignal.timeout as a fallback to enforce request timeout | ||
| if (process.versions.bun && !options.signal) { | ||
| const rawTimeout = options.timeout ?? this.#app.config.httpclient?.request?.timeout; | ||
| // urllib supports timeout as number or [connectTimeout, responseTimeout]. | ||
| // Use the shorter (connect) timeout for AbortSignal — if headers haven't | ||
| // arrived within connectTimeout the request should fail, matching Node's | ||
| // headersTimeout semantics as closely as possible. | ||
| const timeoutMs = Array.isArray(rawTimeout) | ||
| ? Math.min(...rawTimeout.filter((t): t is number => typeof t === 'number' && t > 0)) | ||
| : rawTimeout; | ||
|
Comment on lines
+60
to
+62
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edge case: If Consider adding a guard for this edge case: Proposed fix const timeoutMs = Array.isArray(rawTimeout)
- ? Math.min(...rawTimeout.filter((t): t is number => typeof t === 'number' && t > 0))
+ ? rawTimeout.filter((t): t is number => typeof t === 'number' && t > 0).reduce((a, b) => Math.min(a, b), 0)
: rawTimeout;
+ if (typeof timeoutMs === 'number' && timeoutMs > 0 && Number.isFinite(timeoutMs)) {
- if (typeof timeoutMs === 'number' && timeoutMs > 0) {🤖 Prompt for AI Agents |
||
| if (typeof timeoutMs === 'number' && timeoutMs > 0) { | ||
| options.signal = AbortSignal.timeout(timeoutMs); | ||
| try { | ||
| return await super.request<T>(url, options); | ||
|
Comment on lines
+55
to
+66
|
||
| } catch (err: any) { | ||
| if (err?.name === 'TimeoutError' || err?.code === 'ABORT_ERR') { | ||
| throw new HttpClientRequestTimeoutError(timeoutMs, { cause: err }); | ||
| } | ||
| throw err; | ||
| } | ||
| } | ||
| } | ||
| return await super.request<T>(url, options); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -268,6 +268,11 @@ export class Application extends Emitter { | |
| if (statuses.empty[code]) { | ||
| // strip headers | ||
| ctx.body = null; | ||
| // explicitly remove content headers from the raw response | ||
| // to ensure they are not sent (some runtimes like Bun don't strip them automatically) | ||
| res.removeHeader('Content-Type'); | ||
| res.removeHeader('Content-Length'); | ||
| res.removeHeader('Transfer-Encoding'); | ||
|
Comment on lines
+271
to
+275
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, in Node.js HTTP ServerResponse, calling response.removeHeader(name) after headers have already been sent throws Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client. Citations:
🏁 Script executed: cd packages/koa && wc -l src/application.tsRepository: eggjs/egg Length of output: 75 🏁 Script executed: cd packages/koa && sed -n '250,300p' src/application.tsRepository: eggjs/egg Length of output: 1460 🏁 Script executed: cd packages/koa && sed -n '200,250p' src/application.tsRepository: eggjs/egg Length of output: 1633 Guard raw header removal with At lines 273-275, calling Suggested fix- res.removeHeader('Content-Type');
- res.removeHeader('Content-Length');
- res.removeHeader('Transfer-Encoding');
+ if (!res.headersSent) {
+ res.removeHeader('Content-Type');
+ res.removeHeader('Content-Length');
+ res.removeHeader('Transfer-Encoding');
+ }🤖 Prompt for AI Agents |
||
| res.end(); | ||
| return; | ||
| } | ||
|
|
||
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium