Skip to content

Commit 9c6571e

Browse files
committed
🧹 chore: merge mani
2 parents 0d2e399 + 4ed9465 commit 9c6571e

File tree

9 files changed

+252
-16
lines changed

9 files changed

+252
-16
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 1.1.27 - 23 Dec 2024
2+
Bug fix:
3+
- [#963](https://github.com/elysiajs/elysia/pull/963) array parser on query string when AOT is off
4+
- [#961](https://github.com/elysiajs/elysia/pull/961) literal handler when AOT is off
5+
16
# 1.1.26 - 4 Dev 2024
27
Bug fix:
38
- [#907](https://github.com/elysiajs/elysia/issues/907), [#872](https://github.com/elysiajs/elysia/issues/872), [#926](https://github.com/elysiajs/elysia/issues/926) BooleanString is not behave correctly if property is not provided

package.json

Lines changed: 1 addition & 1 deletion
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.2.0-exp.52",
4+
"version": "1.2.0-rc.0",
55
"author": {
66
"name": "saltyAom",
77
"url": "https://github.com/SaltyAom",

src/dynamic-handle.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99

1010
import type { Context } from './context'
1111

12-
import { parseQuery, parseQueryFromURL } from './fast-querystring'
12+
import { parseQuery } from './fast-querystring'
1313

1414
import { redirect, signCookie, StatusMap } from './utils'
1515
import { parseCookie } from './cookies'
@@ -19,7 +19,7 @@ import { TransformDecodeError } from '@sinclair/typebox/value'
1919

2020
// JIT Handler
2121
export type DynamicHandler = {
22-
handle: Handler<any, any>
22+
handle: unknown | Handler<any, any>
2323
content?: string
2424
hooks: LifeCycleStore
2525
validator?: SchemaValidator
@@ -175,8 +175,10 @@ export const createDynamicHandler = (app: AnyElysia) => {
175175

176176
context.body = body
177177
context.params = handler?.params || undefined
178+
179+
// @ts-ignore
178180
context.query =
179-
qi === -1 ? {} : parseQueryFromURL(url.substring(qi + 1))
181+
qi === -1 ? {} : parseQuery(url.substring(qi + 1))
180182

181183
context.headers = {}
182184
for (const [key, value] of request.headers.entries())
@@ -332,7 +334,7 @@ export const createDynamicHandler = (app: AnyElysia) => {
332334
}
333335
}
334336

335-
let response = handle(context)
337+
let response = typeof handle === 'function' ? handle(context) : handle
336338
if (response instanceof Promise) response = await response
337339

338340
if (!hooks.afterHandle.length) {

src/type-system.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
TArray,
77
TDate,
88
TUnsafe,
9-
TypeRegistry
9+
TypeRegistry,
10+
TInteger,
11+
IntegerOptions
1012
} from '@sinclair/typebox'
1113
import { TypeSystem } from '@sinclair/typebox/system'
1214
import {
@@ -261,6 +263,12 @@ const Files: ElysiaFiles =
261263
if (!FormatRegistry.Has('numeric'))
262264
FormatRegistry.Set('numeric', (value) => !!value && !isNaN(+value))
263265

266+
if (!FormatRegistry.Has('integer'))
267+
FormatRegistry.Set(
268+
'integer',
269+
(value) => !!value && Number.isInteger(+value)
270+
)
271+
264272
if (!FormatRegistry.Has('boolean'))
265273
FormatRegistry.Set(
266274
'boolean',
@@ -354,6 +362,32 @@ export const ElysiaType = {
354362
})
355363
.Encode((value) => value) as any as TNumber
356364
},
365+
Integer: (property?: IntegerOptions): TInteger => {
366+
const schema = Type.Integer(property)
367+
368+
return t
369+
.Transform(
370+
t.Union(
371+
[
372+
t.String({
373+
format: 'integer',
374+
default: 0
375+
}),
376+
t.Number(property)
377+
],
378+
property
379+
)
380+
)
381+
.Decode((value) => {
382+
const number = +value
383+
384+
if (!Value.Check(schema, number))
385+
throw new ValidationError('property', schema, number)
386+
387+
return number
388+
})
389+
.Encode((value) => value) as any as TInteger
390+
},
357391
Date: (property?: DateOptions) => {
358392
const schema = Type.Date(property)
359393

@@ -637,6 +671,7 @@ declare module '@sinclair/typebox' {
637671
ObjectString: typeof ElysiaType.ObjectString
638672
ArrayString: typeof ElysiaType.ArrayString
639673
Numeric: typeof ElysiaType.Numeric
674+
Integer: typeof ElysiaType.Integer
640675
File: typeof ElysiaType.File
641676
Files: typeof ElysiaType.Files
642677
Nullable: typeof ElysiaType.Nullable
@@ -663,7 +698,7 @@ declare module '@sinclair/typebox' {
663698
/**
664699
* A Boolean string
665700
*
666-
* Will be parse to Boolean
701+
* Will be parsed to a Boolean
667702
*/
668703
t.BooleanString = ElysiaType.BooleanString
669704
t.ObjectString = ElysiaType.ObjectString
@@ -672,9 +707,10 @@ t.ArrayString = ElysiaType.ArrayString
672707
/**
673708
* A Numeric string
674709
*
675-
* Will be parse to Number
710+
* Will be parsed to a Number
676711
*/
677712
t.Numeric = ElysiaType.Numeric
713+
t.Integer = ElysiaType.Integer
678714

679715
t.File = (arg = {}) =>
680716
ElysiaType.File({

test/core/dynamic.test.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ describe('Dynamic Mode', () => {
1313
expect(res).toBe('Hi')
1414
})
1515

16+
it('handle literal', async () => {
17+
const app = new Elysia({ aot: false }).get('/', 'Hi');
18+
19+
const response = await app.handle(req('/')).then((x) => x.text());
20+
21+
expect(response).toBe('Hi');
22+
})
23+
1624
it('handle body', async () => {
1725
const app = new Elysia({
1826
aot: false
@@ -144,15 +152,14 @@ describe('Dynamic Mode', () => {
144152
})
145153

146154
it('validate', async () => {
147-
const app = new Elysia({
148-
// aot: false
149-
}).post('/', ({ query: { id } }) => id.toString(), {
155+
const app = new Elysia({ aot: false }).post('/', ({ query: { id, arr } }) => `${id} - ${arr}`, {
150156
body: t.Object({
151157
username: t.String(),
152158
password: t.String()
153159
}),
154160
query: t.Object({
155-
id: t.String()
161+
id: t.String(),
162+
arr: t.Array(t.String()),
156163
}),
157164
response: {
158165
200: t.String()
@@ -161,13 +168,14 @@ describe('Dynamic Mode', () => {
161168

162169
const res = await app
163170
.handle(
164-
post('/?id=me', {
171+
post('/?id=me&arr=v1&arr=v2', {
165172
username: 'username',
166173
password: 'password'
167174
})
168175
)
169176
.then((x) => x.text())
170-
expect(res).toBe('me')
177+
178+
expect(res).toBe('me - v1,v2')
171179
})
172180

173181
it('handle non query fallback', async () => {

test/validator/body.test.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,60 @@ describe('Body Validator', () => {
140140
expect(res.status).toBe(200)
141141
})
142142

143+
it('parse single integer', async () => {
144+
const app = new Elysia().post('/', ({ body }) => body, {
145+
body: t.Object({
146+
name: t.String(),
147+
job: t.String(),
148+
trait: t.Optional(t.String()),
149+
age: t.Integer()
150+
})
151+
})
152+
const res = await app.handle(
153+
post('/', {
154+
name: 'sucrose',
155+
job: 'alchemist',
156+
age: '16'
157+
})
158+
)
159+
160+
expect(await res.json()).toEqual({
161+
name: 'sucrose',
162+
job: 'alchemist',
163+
age: 16
164+
})
165+
166+
expect(res.status).toBe(200)
167+
})
168+
169+
it('parse multiple integers', async () => {
170+
const app = new Elysia().post('/', ({ body }) => body, {
171+
body: t.Object({
172+
name: t.String(),
173+
job: t.String(),
174+
trait: t.Optional(t.String()),
175+
age: t.Integer(),
176+
rank: t.Integer()
177+
})
178+
})
179+
const res = await app.handle(
180+
post('/', {
181+
name: 'sucrose',
182+
job: 'alchemist',
183+
age: '16',
184+
rank: '4'
185+
})
186+
)
187+
188+
expect(await res.json()).toEqual({
189+
name: 'sucrose',
190+
job: 'alchemist',
191+
age: 16,
192+
rank: 4
193+
})
194+
expect(res.status).toBe(200)
195+
})
196+
143197
it('validate empty body', async () => {
144198
const app = new Elysia().post('/', ({ body }) => body, {
145199
body: t.Union([
@@ -416,7 +470,7 @@ describe('Body Validator', () => {
416470
expect(value).toBe('number')
417471
})
418472

419-
it("coerce number to numeric", async () => {
473+
it('coerce number to numeric', async () => {
420474
const app = new Elysia().post('/', ({ body }) => typeof body, {
421475
body: t.Number()
422476
})
@@ -450,7 +504,7 @@ describe('Body Validator', () => {
450504
expect(response.status).toBe(422)
451505
})
452506

453-
it("coerce string to boolean", async () => {
507+
it('coerce string to boolean', async () => {
454508
const app = new Elysia().post('/', ({ body }) => typeof body, {
455509
body: t.Boolean()
456510
})

test/validator/header.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,49 @@ describe('Header Validator', () => {
150150
expect(res.status).toBe(200)
151151
})
152152

153+
it('parse single integer', async () => {
154+
const app = new Elysia().get('/', ({ headers }) => headers, {
155+
headers: t.Object({
156+
limit: t.Integer()
157+
})
158+
})
159+
const res = await app.handle(
160+
req('/', {
161+
headers: {
162+
limit: '16'
163+
}
164+
})
165+
)
166+
167+
expect(await res.json()).toEqual({
168+
limit: 16
169+
})
170+
expect(res.status).toBe(200)
171+
})
172+
173+
it('parse multiple integers', async () => {
174+
const app = new Elysia().get('/', ({ headers }) => headers, {
175+
headers: t.Object({
176+
limit: t.Integer(),
177+
offset: t.Integer()
178+
})
179+
})
180+
const res = await app.handle(
181+
req('/', {
182+
headers: {
183+
limit: '16',
184+
offset: '4'
185+
}
186+
})
187+
)
188+
189+
expect(await res.json()).toEqual({
190+
limit: 16,
191+
offset: 4
192+
})
193+
expect(res.status).toBe(200)
194+
})
195+
153196
it('validate partial', async () => {
154197
const app = new Elysia().get('/', ({ headers }) => headers, {
155198
headers: t.Partial(

test/validator/params.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,71 @@ describe('Params Validator', () => {
8989
expect(res.status).toBe(200)
9090
})
9191

92+
it('parse single integer', async () => {
93+
const app = new Elysia().get('/id/:id', ({ params }) => params, {
94+
params: t.Object({
95+
id: t.Integer()
96+
})
97+
})
98+
const res = await app.handle(req('/id/617'))
99+
expect(await res.json()).toEqual({
100+
id: 617
101+
})
102+
expect(res.status).toBe(200)
103+
})
104+
105+
it('parse malformed integer', async () => {
106+
const app = new Elysia().get('/id/:id', ({ params }) => params, {
107+
params: t.Object({
108+
id: t.Integer()
109+
})
110+
})
111+
112+
const res = await app.handle(req('/id/617.1234'))
113+
expect(await res.json()).toEqual({
114+
errors: [
115+
{
116+
errors: [],
117+
message: 'Expected integer',
118+
path: '',
119+
schema: {
120+
type: 'integer'
121+
},
122+
summary: 'Expected integer',
123+
type: 27,
124+
value: 617.1234
125+
}
126+
],
127+
expected: 0,
128+
found: 617.1234,
129+
message: 'Expected integer',
130+
on: 'property',
131+
property: 'root',
132+
summary: 'Expected integer',
133+
type: 'validation'
134+
})
135+
expect(res.status).toBe(422)
136+
})
137+
138+
it('parse multiple integer', async () => {
139+
const app = new Elysia().get(
140+
'/id/:id/chapter/:chapterId',
141+
({ params }) => params,
142+
{
143+
params: t.Object({
144+
id: t.Integer(),
145+
chapterId: t.Integer()
146+
})
147+
}
148+
)
149+
const res = await app.handle(req('/id/617/chapter/12'))
150+
expect(await res.json()).toEqual({
151+
id: 617,
152+
chapterId: 12
153+
})
154+
expect(res.status).toBe(200)
155+
})
156+
92157
it('create default string params', async () => {
93158
const app = new Elysia().get('/:name', ({ params }) => params, {
94159
params: t.Object({

0 commit comments

Comments
 (0)