Skip to content

Commit 78f4828

Browse files
authored
Merge pull request #1529 from elysiajs/next
patch: 1.4.16
2 parents 3e69b82 + cc01281 commit 78f4828

File tree

15 files changed

+281
-53
lines changed

15 files changed

+281
-53
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ jobs:
2727
- name: Test
2828
run: bun run test
2929

30+
- name: Test
31+
run: bun run test:cf
32+
3033
- name: Publish Preview
3134
if: github.event_name == 'pull_request'
3235
run: bunx pkg-pr-new publish

.github/workflows/publish.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ jobs:
4343
- name: Test
4444
run: bun run test
4545

46+
- name: Test
47+
run: bun run test:cf
48+
4649
- name: 'Publish'
4750
env:
4851
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
# 1.4.16 - 13 Nov 2025
2+
Improvement:
3+
- ValidationError: add `messageValue` as an alias of `errorValue`
4+
- ValidationError.detail now accept optional 2nd parameter `allowUnsafeValidatorDetails`
5+
- macro: add `introspect`
6+
- prevent redundant route compilation
7+
- merge multiple macro resolve response
8+
9+
Bug fix:
10+
- [#1543](https://github.com/elysiajs/elysia/pull/1524) respect toResponse() method on Error classes
11+
- [#1537](https://github.com/elysiajs/elysia/issues/1537) websocket: ping/pong not being called
12+
- [#1536](https://github.com/elysiajs/elysia/pull/1536) export ExtractErrorFromHandle
13+
- [#1535](https://github.com/elysiajs/elysia/pull/1535) skip response validation for generators and streams
14+
- [#1531](https://github.com/elysiajs/elysia/pull/1531) typo in ElysiaTypeCustomErrorCallback: valdation to validation
15+
- [#1528](https://github.com/elysiajs/elysia/issues/1528) error in parsing request body validation errors with Zod
16+
- [#1527](https://github.com/elysiajs/elysia/issues/1527) bracket handling in exact mirror
17+
- [#1524](https://github.com/elysiajs/elysia/pull/1524) head request handler not working
18+
119
# 1.4.15 - 3 Nov 2025
220
Bug fix:
321
- 1.4.14 regression with Eden Treaty, and OpenAPI type gen

example/a.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
import Elysia, { t } from '../src'
1+
import { Elysia, status, t } from '../src'
22

3-
new Elysia()
4-
.post(
5-
'/mirror',
6-
async ({ status, body }) => status(201, { success: false }),
7-
{
8-
body: t.Object({
9-
code: t.String()
10-
}),
11-
response: {
12-
200: t.Object({
13-
success: t.Literal(true)
14-
}),
15-
201: t.Object({
16-
success: t.Literal(false)
17-
})
3+
const auth = (app: Elysia) =>
4+
app.derive(({ headers, status }) => {
5+
try {
6+
const token = headers['authorization']?.replace('Bearer ', '') || ''
7+
return {
8+
isAuthenticated: true
189
}
10+
} catch (e) {
11+
const error = e as Error
12+
console.error('Authentication error:', error.message)
13+
return status(401, 'Unauthorized')
1914
}
20-
)
21-
.listen(3333)
15+
})
16+
17+
const app = new Elysia()
18+
.use(auth)
19+
.get('/', ({ isAuthenticated }) => isAuthenticated)
20+
.listen(5000)
21+
22+
app['~Routes']

package.json

Lines changed: 3 additions & 3 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.4.15",
4+
"version": "1.4.16",
55
"author": {
66
"name": "saltyAom",
77
"url": "https://github.com/SaltyAom",
@@ -178,7 +178,7 @@
178178
],
179179
"license": "MIT",
180180
"scripts": {
181-
"test": "bun run test:functionality && bun run test:types && bun run test:node && bun run test:cf",
181+
"test": "bun run test:functionality && bun run test:types && bun run test:node",
182182
"test:functionality": "bun test && bun run test:imports",
183183
"test:imports": "bun run test/type-system/import.ts",
184184
"test:types": "tsc --project tsconfig.test.json",
@@ -191,7 +191,7 @@
191191
},
192192
"dependencies": {
193193
"cookie": "^1.0.2",
194-
"exact-mirror": "0.2.2",
194+
"exact-mirror": "0.2.3",
195195
"fast-decode-uri-component": "^1.0.1",
196196
"memoirist": "^0.4.0"
197197
},

src/adapter/bun/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { createBunRouteHandler } from './compose'
1010
import { createNativeStaticHandler } from './handler-native'
1111

1212
import { serializeCookie } from '../../cookies'
13-
import { isProduction, ValidationError } from '../../error'
13+
import { isProduction, status, ValidationError } from '../../error'
1414
import { getSchemaValidator } from '../../schema'
1515
import {
1616
hasHeaderShorthand,
@@ -627,8 +627,7 @@ export const BunAdapter: ElysiaAdapter = {
627627
)
628628
return
629629

630-
set.status = 400
631-
return 'Expected a websocket connection'
630+
return status(400, 'Expected a websocket connection')
632631
},
633632
{
634633
...rest,

src/adapter/web-standard/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,8 @@ export const WebStandardAdapter: ElysiaAdapter = {
9393
fnLiteral +=
9494
`const u=r.url,` +
9595
`s=u.indexOf('/',${standardHostname ? 11 : 7}),` +
96-
`qi=u.indexOf('?',s+1)\n` +
97-
`let p\n` +
98-
`if(qi===-1)p=u.substring(s)\n` +
99-
`else p=u.substring(s, qi)\n`
96+
`qi=u.indexOf('?',s+1),` +
97+
`p=u.substring(s,qi===-1?undefined:qi)\n`
10098

10199
if (hasTrace) fnLiteral += `const id=randomId()\n`
102100

src/error.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const emptyHttpStatus = {
3939
308: undefined
4040
} as const
4141

42-
export type SelectiveStatus<Res> = <
42+
export type SelectiveStatus<in out Res> = <
4343
const Code extends
4444
| keyof Res
4545
| InvertedStatusMap[Extract<keyof InvertedStatusMap, keyof Res>]
@@ -91,7 +91,7 @@ export const status = <
9191
>(
9292
code: Code,
9393
response?: T
94-
) => new ElysiaCustomStatusResponse<Code, T>(code, response as any)
94+
) => new ElysiaCustomStatusResponse<Code, T>(code, response as T)
9595

9696
export class InternalServerError extends Error {
9797
code = 'INTERNAL_SERVER_ERROR'
@@ -153,9 +153,15 @@ export const mapValueError = (error: ValueError | undefined): MapValueError => {
153153
summary: undefined
154154
}
155155

156-
const { message, path, value, type } = error
156+
let { message, path, value, type } = error
157+
158+
if (Array.isArray(path)) path = path[0]
159+
160+
const property =
161+
typeof path === 'string'
162+
? path.slice(1).replaceAll('/', '.')
163+
: 'unknown'
157164

158-
const property = path.slice(1).replaceAll('/', '.')
159165
const isRoot = path === ''
160166

161167
switch (type) {
@@ -271,8 +277,29 @@ export class ValidationError extends Error {
271277
code = 'VALIDATION'
272278
status = 422
273279

280+
/**
281+
* An actual value of `message`
282+
*
283+
* Since `message` is string
284+
* use this instead of message
285+
*/
274286
valueError?: ValueError
287+
288+
/**
289+
* Alias of `valueError`
290+
*/
291+
get messageValue() {
292+
return this.valueError
293+
}
294+
295+
/**
296+
* Expected value of the schema
297+
*/
275298
expected?: unknown
299+
300+
/**
301+
* Custom error if provided
302+
*/
276303
customError?: string
277304

278305
constructor(
@@ -282,6 +309,9 @@ export class ValidationError extends Error {
282309
| TypeCheck<any>
283310
| ElysiaTypeCheck<any>
284311
| StandardSchemaV1Like,
312+
/**
313+
* Input value
314+
*/
285315
public value: unknown,
286316
private allowUnsafeValidationDetails = false,
287317
errors?: ValueErrorIterator
@@ -535,15 +565,17 @@ export class ValidationError extends Error {
535565
* })
536566
* ```
537567
*/
538-
detail(message: unknown) {
568+
detail(
569+
message: unknown,
570+
allowUnsafeValidatorDetails = this.allowUnsafeValidationDetails
571+
) {
539572
if (!this.customError) return this.message
540573

541-
const validator = this.validator
542574
const value = this.value
543575
const expected = this.expected
544576
const errors = this.all
545577

546-
return isProduction && !this.allowUnsafeValidationDetails
578+
return isProduction && !allowUnsafeValidatorDetails
547579
? {
548580
type: 'validation',
549581
on: this.type,

src/index.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -909,11 +909,8 @@ export default class Elysia<
909909

910910
addResponsePath(path)
911911

912-
let _compiled: ComposedHandler
913912
const compile = () => {
914-
if (_compiled) return _compiled
915-
916-
return (_compiled = composeHandler({
913+
const compiled = composeHandler({
917914
app: this,
918915
path,
919916
method,
@@ -926,7 +923,12 @@ export default class Elysia<
926923
: handle,
927924
allowMeta,
928925
inference: this.inference
929-
}))
926+
})
927+
928+
if (this.router.history[index])
929+
this.router.history[index].composed = compiled
930+
931+
return compiled
930932
}
931933

932934
let oldIndex: number | undefined
@@ -941,22 +943,22 @@ export default class Elysia<
941943
else this.routeTree[`${method}_${path}`] = this.router.history.length
942944

943945
const index = oldIndex ?? this.router.history.length
946+
const route = this.router.history
944947

945948
const mainHandler = shouldPrecompile
946949
? compile()
947950
: (ctx: Context) =>
948-
(
949-
(this.router.history[index].composed =
950-
compile!()) as ComposedHandler
951-
)(ctx)
951+
((route[index].composed = compile!()) as ComposedHandler)(
952+
ctx
953+
)
952954

953955
if (oldIndex !== undefined)
954956
this.router.history[oldIndex] = Object.assign(
955957
{
956958
method,
957959
path,
958960
composed: mainHandler,
959-
compile: compile!,
961+
compile,
960962
handler: handle,
961963
hooks
962964
},
@@ -976,7 +978,7 @@ export default class Elysia<
976978
method,
977979
path,
978980
composed: mainHandler,
979-
compile: compile!,
981+
compile,
980982
handler: handle,
981983
hooks
982984
},
@@ -987,7 +989,9 @@ export default class Elysia<
987989
)
988990

989991
const handler = {
990-
handler: shouldPrecompile ? mainHandler : undefined,
992+
handler: shouldPrecompile
993+
? (route[index].composed as ComposedHandler)
994+
: undefined,
991995
compile() {
992996
return (this.handler = compile!())
993997
}
@@ -5235,6 +5239,10 @@ export default class Elysia<
52355239
>,
52365240
const Property extends MaybeValueOrVoidFunction<
52375241
MacroProperty<
5242+
Metadata['macro'] &
5243+
InputSchema<keyof Definitions['typebox'] & string> & {
5244+
[name in Name]?: boolean
5245+
},
52385246
Schema & MacroContext,
52395247
Singleton & {
52405248
derive: Partial<Ephemeral['derive'] & Volatile['derive']>
@@ -5277,6 +5285,8 @@ export default class Elysia<
52775285
const Input extends Metadata['macro'] &
52785286
InputSchema<keyof Definitions['typebox'] & string>,
52795287
const NewMacro extends Macro<
5288+
Metadata['macro'] &
5289+
InputSchema<keyof Definitions['typebox'] & string>,
52805290
Input,
52815291
IntersectIfObjectSchema<
52825292
MergeSchema<
@@ -5321,6 +5331,7 @@ export default class Elysia<
53215331
const NewMacro extends MaybeFunction<
53225332
Macro<
53235333
Input,
5334+
// @ts-ignore trust me bro
53245335
IntersectIfObjectSchema<
53255336
MergeSchema<
53265337
UnwrapRoute<Input, Definitions['typebox'], BasePath>,
@@ -5417,6 +5428,13 @@ export default class Elysia<
54175428
continue
54185429
}
54195430

5431+
if (k === 'introspect') {
5432+
value?.(localHook)
5433+
5434+
delete localHook[key]
5435+
continue
5436+
}
5437+
54205438
if (k === 'detail') {
54215439
if (!localHook.detail) localHook.detail = {}
54225440
localHook.detail = mergeDeep(localHook.detail, value, {

0 commit comments

Comments
 (0)