Skip to content

Commit 9ba8551

Browse files
committed
🎉 feat: support Bun native static response per method for Bun >= 1.2.14
1 parent 0792b06 commit 9ba8551

File tree

10 files changed

+192
-113
lines changed

10 files changed

+192
-113
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.3.2 - 24 May 2025
2+
Feature:
3+
- Support Bun native static response per method for Bun >= 1.2.14
4+
15
# 1.3.1 - 8 May 2025
26
Bug fix:
37
- [#1200](https://github.com/elysiajs/elysia/issues/1200) limited Bun Router to supported method

bun.lock

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"fast-decode-uri-component": "^1.0.1",
1010
},
1111
"devDependencies": {
12-
"@types/bun": "^1.2.9",
12+
"@types/bun": "^1.2.14",
1313
"@types/cookie": "^1.0.0",
1414
"@types/fast-decode-uri-component": "^1.0.0",
1515
"@typescript-eslint/eslint-plugin": "^8.30.1",
@@ -33,7 +33,7 @@
3333
"@sinclair/typebox": ">= 0.34.0",
3434
"exact-mirror": ">= 0.0.9",
3535
"file-type": ">= 20.0.0",
36-
"file-type": ">= 20.0.0",
36+
"openapi-types": ">= 12.0.0",
3737
"typescript": ">= 5.0.0",
3838
},
3939
},
@@ -183,7 +183,7 @@
183183

184184
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
185185

186-
"@types/bun": ["@types/bun@1.2.12", "", { "dependencies": { "bun-types": "1.2.12" } }, "sha512-lY/GQTXDGsolT/TiH72p1tuyUORuRrdV7VwOTOjDOt8uTBJQOJc5zz3ufwwDl0VBaoxotSk4LdP0hhjLJ6ypIQ=="],
186+
"@types/bun": ["@types/bun@1.2.14", "", { "dependencies": { "bun-types": "1.2.14" } }, "sha512-VsFZKs8oKHzI7zwvECiAJ5oSorWndIWEVhfbYqZd4HI/45kzW7PN2Rr5biAzvGvRuNmYLSANY+H59ubHq8xw7Q=="],
187187

188188
"@types/cookie": ["@types/cookie@1.0.0", "", { "dependencies": { "cookie": "*" } }, "sha512-mGFXbkDQJ6kAXByHS7QAggRXgols0mAdP4MuXgloGY1tXokvzaFFM4SMqWvf7AH0oafI7zlFJwoGWzmhDqTZ9w=="],
189189

@@ -249,7 +249,7 @@
249249

250250
"builtin-modules": ["builtin-modules@3.3.0", "", {}, "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="],
251251

252-
"bun-types": ["bun-types@1.2.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-tvWMx5vPqbRXgE8WUZI94iS1xAYs8bkqESR9cxBB1Wi+urvfTrF1uzuDgBHFAdO0+d2lmsbG3HmeKMvUyj6pWA=="],
252+
"bun-types": ["bun-types@1.2.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-Kuh4Ub28ucMRWeiUUWMHsT9Wcbr4H3kLIO72RZZElSDxSu7vpetRvxIUDUaW6QtaIeixIpm7OXtNnZPf82EzwA=="],
253253

254254
"bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="],
255255

@@ -525,6 +525,8 @@
525525

526526
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
527527

528+
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
529+
528530
"joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="],
529531

530532
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],

example/a.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
import { Elysia, t } from '../src'
2-
import { req } from '../test/utils'
1+
import Elysia, { t } from '../src'
32

4-
const app = new Elysia()
5-
.onRequest(() => 'a')
6-
.get('/:hash', async ({ params: { hash }, error, set }) => {
7-
const file = Bun.file('example/teapot.webp')
3+
const strict = new Elysia({ strictPath: true })
4+
.get('', '')
85

9-
return file
10-
})
11-
.listen(3000)
6+
console.log(strict.router.response)
127

13-
console.log(app.routes[0].compile().toString())
8+
// console.log(app.router.response)

example/stress/instance.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Elysia, t } from '../../src'
33
const total = 100
44
const sub = 5
55

6-
const app = new Elysia()
6+
const app = new Elysia({ precompile: true })
77

88
const memory = process.memoryUsage().heapTotal / 1024 / 1024
99
const t1 = performance.now()

example/stress/multiple-routes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Elysia, t } from '../../src'
33
const total = 500
44

55
{
6-
const app = new Elysia()
6+
const app = new Elysia({ precompile: true })
77

88
const t1 = performance.now()
99
const memory = process.memoryUsage().heapTotal / 1024 / 1024

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "elysia",
33
"description": "Ergonomic Framework for Human",
4-
"version": "1.3.1",
4+
"version": "1.3.2",
55
"author": {
66
"name": "saltyAom",
77
"url": "https://github.com/SaltyAom",
@@ -183,7 +183,7 @@
183183
"fast-decode-uri-component": "^1.0.1"
184184
},
185185
"devDependencies": {
186-
"@types/bun": "^1.2.9",
186+
"@types/bun": "^1.2.14",
187187
"@types/cookie": "^1.0.0",
188188
"@types/fast-decode-uri-component": "^1.0.0",
189189
"@typescript-eslint/eslint-plugin": "^8.30.1",

src/adapter/bun/index.ts

Lines changed: 109 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
hasHeaderShorthand,
1818
isNotEmpty,
1919
isNumericString,
20-
randomId
20+
randomId,
21+
supportPerMethodInlineHandler
2122
} from '../../utils'
2223

2324
import {
@@ -216,11 +217,94 @@ export const BunAdapter: ElysiaAdapter = {
216217
options = parseInt(options)
217218
}
218219

219-
const staticRoutes = <Record<string, Response>>{}
220+
const createStaticRoute = <
221+
WithAsync extends boolean | undefined = false
222+
>(
223+
iterator: AnyElysia['router']['response'],
224+
{ withAsync = false }: { withAsync?: WithAsync } = {}
225+
): true extends WithAsync
226+
? Promise<{
227+
[path: string]:
228+
| Response
229+
| { [method: string]: Response }
230+
}>
231+
: {
232+
[path: string]:
233+
| Response
234+
| { [method: string]: Response }
235+
} => {
236+
const staticRoutes = <
237+
{
238+
[path: string]:
239+
| Response
240+
| { [method: string]: Response }
241+
}
242+
>{}
243+
const ops = <Promise<any>[]>[]
244+
245+
for (const [path, route] of Object.entries(iterator)) {
246+
if (supportPerMethodInlineHandler) {
247+
if (!route) continue
248+
249+
for (const [method, value] of Object.entries(route)) {
250+
if (!value || !(method in supportedMethods))
251+
continue
252+
253+
if (value instanceof Promise) {
254+
if (withAsync) {
255+
if (!staticRoutes[path])
256+
staticRoutes[path] = {}
257+
258+
ops.push(
259+
value.then((awaited) => {
260+
if (awaited instanceof Response)
261+
// @ts-ignore
262+
staticRoutes[path][method] =
263+
awaited
264+
})
265+
)
266+
}
267+
268+
continue
269+
}
270+
271+
if (!(value instanceof Response)) continue
220272

221-
for (const [path, route] of Object.entries(app.router.response))
222-
if (route && !(route instanceof Promise))
223-
staticRoutes[path] = route
273+
if (!staticRoutes[path]) staticRoutes[path] = {}
274+
275+
// @ts-ignore
276+
staticRoutes[path][method] = value
277+
}
278+
} else {
279+
if (!route) continue
280+
281+
if (route instanceof Promise) {
282+
if (withAsync) {
283+
if (!staticRoutes[path]) staticRoutes[path] = {}
284+
285+
ops.push(
286+
route.then((awaited) => {
287+
if (awaited instanceof Response)
288+
// @ts-ignore
289+
staticRoutes[path] = awaited
290+
})
291+
)
292+
}
293+
294+
continue
295+
}
296+
297+
if (!(route instanceof Response)) continue
298+
299+
staticRoutes[path] = route
300+
}
301+
}
302+
303+
if (withAsync)
304+
return Promise.all(ops).then(() => staticRoutes) as any
305+
306+
return staticRoutes as any
307+
}
224308

225309
const serve =
226310
typeof options === 'object'
@@ -230,12 +314,14 @@ export const BunAdapter: ElysiaAdapter = {
230314
...(app.config.serve || {}),
231315
...(options || {}),
232316
// @ts-ignore
233-
routes: {
234-
...staticRoutes,
235-
...mapRoutes(app),
236-
// @ts-expect-error
237-
...app.config.serve?.routes
238-
},
317+
routes: mergeRoutes(
318+
mergeRoutes(
319+
createStaticRoute(app.router.response),
320+
mapRoutes(app)
321+
),
322+
// @ts-expect-error private property
323+
app.config.serve?.routes
324+
),
239325
websocket: {
240326
...(app.config.websocket || {}),
241327
...(websocket || {})
@@ -249,7 +335,10 @@ export const BunAdapter: ElysiaAdapter = {
249335
...(app.config.serve || {}),
250336
// @ts-ignore
251337
routes: mergeRoutes(
252-
mergeRoutes(staticRoutes, mapRoutes(app)),
338+
mergeRoutes(
339+
createStaticRoute(app.router.response),
340+
mapRoutes(app)
341+
),
253342
// @ts-expect-error private property
254343
app.config.serve?.routes
255344
),
@@ -283,38 +372,23 @@ export const BunAdapter: ElysiaAdapter = {
283372

284373
// @ts-expect-error private
285374
app.promisedModules.then(async () => {
286-
Bun?.gc(false)
287-
288-
const staticRoutes = <Record<string, Response>>{}
289-
const asyncStaticRoutes = <Promise<Response | undefined>[]>[]
290-
const asyncStaticRoutesPath = <string[]>[]
291-
292-
for (const [path, route] of Object.entries(app.router.response))
293-
if (route instanceof Promise) {
294-
asyncStaticRoutes.push(route)
295-
asyncStaticRoutesPath.push(path)
296-
} else if (route) staticRoutes[path] = route
297-
298-
if (!app.server && !isNotEmpty(asyncStaticRoutes)) return
299-
300-
const promises = await Promise.all(asyncStaticRoutes)
301-
for (let i = 0; i < promises.length; i++) {
302-
const route = promises[i]
303-
const path = asyncStaticRoutesPath[i]
304-
305-
if (route) staticRoutes[path] = route
306-
}
307-
308375
app.server?.reload({
309376
...serve,
310377
fetch: app.fetch,
311378
// @ts-ignore
312379
routes: mergeRoutes(
313-
mergeRoutes(staticRoutes, mapRoutes(app)),
380+
mergeRoutes(
381+
await createStaticRoute(app.router.response, {
382+
withAsync: true
383+
}),
384+
mapRoutes(app)
385+
),
314386
// @ts-expect-error private property
315387
app.config.serve?.routes
316388
)
317389
})
390+
391+
Bun?.gc(false)
318392
})
319393
}
320394
},

src/index.ts

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import {
4040
promoteEvent,
4141
isNotEmpty,
4242
encodePath,
43-
lifeCycleToArray
43+
lifeCycleToArray,
44+
supportPerMethodInlineHandler
4445
} from './utils'
4546

4647
import {
@@ -294,7 +295,11 @@ export default class Elysia<
294295
// Static Router
295296
static: {} as { [path in string]: { [method in string]: number } },
296297
// Native Static Response
297-
response: {} as Record<string, MaybePromise<Response | undefined>>,
298+
response: {} as {
299+
[path: string]:
300+
| MaybePromise<Response | undefined>
301+
| { [method: string]: MaybePromise<Response | undefined> }
302+
},
298303
history: [] as InternalRoute[]
299304
}
300305

@@ -853,12 +858,23 @@ export default class Elysia<
853858
const useNativeStaticResponse =
854859
this.config.nativeStaticResponse === true
855860

856-
if (
857-
useNativeStaticResponse &&
858-
nativeStaticHandler &&
859-
(method === 'GET' || method === 'ALL')
860-
)
861-
this.router.response[path] = nativeStaticHandler()
861+
const addResponsePath = (path: string) => {
862+
if (!useNativeStaticResponse || !nativeStaticHandler) return
863+
864+
if (supportPerMethodInlineHandler) {
865+
if (this.router.response[path])
866+
// @ts-expect-error
867+
this.router.response[path]![method] = nativeStaticHandler()
868+
else
869+
this.router.response[path] = {
870+
[method]: nativeStaticHandler()
871+
}
872+
} else {
873+
this.router.response[path] = nativeStaticHandler()
874+
}
875+
}
876+
877+
addResponsePath(path)
862878

863879
let _compiled: ComposedHandler
864880
const compile = () => {
@@ -984,13 +1000,8 @@ export default class Elysia<
9841000
[method]: index
9851001
} as const
9861002

987-
if (
988-
!this.config.strictPath &&
989-
useNativeStaticResponse &&
990-
nativeStaticHandler &&
991-
(method === 'GET' || method === 'ALL')
992-
)
993-
this.router.response[getLoosePath(path)] = nativeStaticHandler()
1003+
if(!this.config.strictPath)
1004+
addResponsePath(getLoosePath(path))
9941005

9951006
// Static path doesn't need encode as it's done in compilation process
9961007
} else {
@@ -1009,31 +1020,15 @@ export default class Elysia<
10091020
if (!this.config.strictPath) {
10101021
const loosePath = getLoosePath(path)
10111022

1012-
if (
1013-
useNativeStaticResponse &&
1014-
staticHandler &&
1015-
(method === 'GET' || method === 'ALL')
1016-
)
1017-
this.router.response[loosePath] =
1018-
staticHandler() as Response
1019-
1023+
addResponsePath(loosePath)
10201024
this.router.http.add(method, loosePath, handler)
10211025
}
10221026

10231027
const encoded = encodePath(path, { dynamic: true })
10241028
if (path !== encoded) {
10251029
this.router.http.add(method, encoded, handler)
10261030

1027-
if (
1028-
useNativeStaticResponse &&
1029-
staticHandler &&
1030-
(method === 'GET' || method === 'ALL')
1031-
) {
1032-
this.router.response[encoded] = staticHandler() as Response
1033-
1034-
this.router.response[getLoosePath(encoded)] =
1035-
staticHandler() as Response
1036-
}
1031+
addResponsePath(encoded)
10371032
}
10381033
}
10391034
}

0 commit comments

Comments
 (0)