Skip to content

Commit 08deb9d

Browse files
committed
🔧 fix: separate optional comma flag between closure
1 parent 36a33fa commit 08deb9d

File tree

8 files changed

+449
-216
lines changed

8 files changed

+449
-216
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.0.1 - 3 Feb 2025
2+
Bug fix:
3+
- Separate optional comma flag between closure
4+
15
# 0.0.0 - 3 Feb 2025
26
Feature:
37
- Initial release

benchmarks/large.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { t } from 'elysia'
2+
import { benchmark } from './utils'
3+
4+
benchmark(
5+
t.Array(
6+
t.Object({
7+
id: t.Number(),
8+
name: t.String(),
9+
bio: t.String(),
10+
user: t.Object({
11+
name: t.String(),
12+
password: t.String(),
13+
email: t.Optional(t.String({ format: 'email' })),
14+
age: t.Optional(t.Number()),
15+
avatar: t.Optional(t.String({ format: 'uri' })),
16+
cover: t.Optional(t.String({ format: 'uri' }))
17+
}),
18+
playing: t.Optional(t.String()),
19+
wishlist: t.Optional(t.Array(t.Number())),
20+
games: t.Array(
21+
t.Object({
22+
id: t.Number(),
23+
name: t.String(),
24+
hoursPlay: t.Optional(t.Number({ default: 0 })),
25+
tags: t.Array(
26+
t.Object({
27+
name: t.String(),
28+
count: t.Number()
29+
})
30+
)
31+
})
32+
),
33+
metadata: t.Intersect([
34+
t.Object({
35+
alias: t.String()
36+
}),
37+
t.Object({
38+
country: t.Nullable(t.String()),
39+
region: t.Optional(t.String())
40+
})
41+
]),
42+
social: t.Optional(
43+
t.Object({
44+
facebook: t.Optional(t.String()),
45+
twitter: t.Optional(t.String()),
46+
youtube: t.Optional(t.String())
47+
})
48+
)
49+
})
50+
),
51+
[
52+
{
53+
id: 1,
54+
name: 'SaltyAom',
55+
bio: 'I like train',
56+
user: {
57+
name: 'SaltyAom',
58+
password: '123456',
59+
avatar: 'https://avatars.githubusercontent.com/u/35027979?v=4',
60+
cover: 'https://saltyaom.com/cosplay/pekomama.webp'
61+
},
62+
playing: 'Strinova',
63+
wishlist: [4_154_456, 2_345_345],
64+
games: [
65+
{
66+
id: 4_154_456,
67+
name: 'MiSide',
68+
hoursPlay: 17,
69+
tags: [
70+
{ name: 'Psychological Horror', count: 236_432 },
71+
{ name: 'Cute', count: 495_439 },
72+
{ name: 'Dating Sim', count: 395_532 }
73+
]
74+
},
75+
{
76+
id: 4_356_345,
77+
name: 'Strinova',
78+
hoursPlay: 365,
79+
tags: [
80+
{ name: 'Free to Play', count: 205_593 },
81+
{ name: 'Anime', count: 504_304 },
82+
{ name: 'Third-Person Shooter', count: 395_532 }
83+
]
84+
},
85+
{
86+
id: 2_345_345,
87+
name: "Tom Clancy's Rainbow Six Siege",
88+
hoursPlay: 287,
89+
tags: [
90+
{ name: 'FPS', count: 855_324 },
91+
{ name: 'Multiplayer', count: 456_567 },
92+
{ name: 'Tactical', count: 544_467 }
93+
]
94+
}
95+
],
96+
metadata: {
97+
alias: 'SaltyAom',
98+
country: 'Thailand',
99+
region: 'Asia'
100+
},
101+
social: {
102+
twitter: 'SaltyAom'
103+
}
104+
},
105+
{
106+
id: 2,
107+
name: 'VLost',
108+
bio: 'ไม่พี่คืองี้',
109+
user: {
110+
name: 'nattapon_kub',
111+
password: '123456'
112+
},
113+
games: [
114+
{
115+
id: 4_154_456,
116+
name: 'MiSide',
117+
hoursPlay: 17,
118+
tags: [
119+
{ name: 'Psychological Horror', count: 236_432 },
120+
{ name: 'Cute', count: 495_439 },
121+
{ name: 'Dating Sim', count: 395_532 }
122+
]
123+
},
124+
{
125+
id: 4_356_345,
126+
name: 'Strinova',
127+
hoursPlay: 365,
128+
tags: [
129+
{ name: 'Free to Play', count: 205_593 },
130+
{ name: 'Anime', count: 504_304 },
131+
{ name: 'Third-Person Shooter', count: 395_532 }
132+
]
133+
}
134+
],
135+
metadata: {
136+
alias: 'vlost',
137+
country: 'Thailand'
138+
}
139+
},
140+
{
141+
id: 2,
142+
name: 'eika',
143+
bio: 'こんにちわ!',
144+
user: {
145+
name: 'ei_ka',
146+
password: '123456'
147+
},
148+
games: [
149+
{
150+
id: 4_356_345,
151+
name: 'Strinova',
152+
hoursPlay: 365,
153+
tags: [
154+
{ name: 'Free to Play', count: 205_593 },
155+
{ name: 'Anime', count: 504_304 },
156+
{ name: 'Third-Person Shooter', count: 395_532 }
157+
]
158+
}
159+
],
160+
metadata: {
161+
alias: 'eika',
162+
country: 'Japan'
163+
}
164+
}
165+
]
166+
)

benchmarks/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ export const benchmark = <T extends TAnySchema>(
1111
const fastJsonStringify = fastJson(model)
1212
const encode = createAccelerator(model)
1313

14-
if (encode(value) !== JSON.stringify(value))
14+
if (encode(value) !== JSON.stringify(value)) {
15+
console.log(encode(value))
1516
throw new Error('Invalid result')
17+
}
1618

1719
barplot(() => {
1820
summary(() => {

example/index.ts

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,15 @@ import { t } from 'elysia'
22
import { createAccelerator } from '../src/index'
33

44
const shape = t.Object({
5-
a: t.Literal('a'),
6-
b: t.Number(),
7-
c: t.Object({
8-
a: t.String()
9-
}),
10-
d: t.Array(
11-
t.Object({
12-
a: t.Array(t.String())
13-
})
14-
),
15-
e: t.Intersect([
16-
t.Object({
17-
a: t.String()
18-
}),
19-
t.Object({
20-
b: t.Optional(t.Number())
21-
})
22-
])
5+
name: t.String(),
6+
id: t.Number()
237
})
248

9+
const value = {
10+
id: 0,
11+
name: 'saltyaom'
12+
} satisfies typeof shape.static
13+
2514
const stringify = createAccelerator(shape)
2615

27-
console.log(
28-
stringify({
29-
a: 'a',
30-
b: 1,
31-
c: { a: 'a' },
32-
d: [{ a: ['a', 'b'] }, { a: ['a', 'a'] }],
33-
e: { a: 'a' }
34-
} satisfies typeof shape.static)
35-
)
16+
console.log(stringify(value))

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "json-accelerator",
3-
"version": "0.0.0",
3+
"version": "0.0.1",
44
"description": "Speed up JSON stringification by providing OpenAPI/TypeBox model",
55
"license": "MIT",
66
"scripts": {

src/index.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export const mergeObjectIntersection = (schema: TAnySchema): TAnySchema => {
9191

9292
interface Instruction {
9393
array: number
94-
optional: boolean
94+
optional: number
9595
properties: string[]
9696
}
9797

@@ -153,20 +153,24 @@ const accelerate = (
153153

154154
let init = true
155155
let hasOptional = false
156-
for (const key in schema.properties) {
157-
const isOptional = OptionalKind in schema.properties[key]
158-
if (isOptional && !hasOptional) {
156+
let op = `op[${instruction.optional}]`
157+
158+
for (const key in schema.properties)
159+
if (OptionalKind in schema.properties[key]) {
160+
instruction.optional++
159161
hasOptional = true
160-
v += `\${(op=false)||''}`
162+
break
161163
}
164+
165+
for (const key in schema.properties) {
166+
const isOptional = OptionalKind in schema.properties[key]
162167
const name = joinProperty(property, key)
163168
const hasShortName =
164169
schema.properties[key].type === 'object' &&
165170
!name.startsWith('ar')
166171

167172
const i = instruction.properties.length
168173
if (hasShortName) instruction.properties.push(name)
169-
if (!instruction.optional) instruction.optional = true
170174

171175
const k = encodeProperty(key)
172176
const p = accelerate(
@@ -175,15 +179,19 @@ const accelerate = (
175179
instruction
176180
)
177181

178-
const comma = `\${op?',':(op=true)&&''}`
182+
const comma = `\${${op}?',':(${op}=true)&&''}`
179183

180184
v += isOptional
181185
? `\${(${name}!==undefined?\`${comma}${k}:${p}\`:'')}`
182-
: `${!init ? ',' : '\${(op=true)&&""}'}${k}:${p}`
186+
: hasOptional
187+
? `${!init ? ',' : `\${(${op}=true)&&""}`}${k}:${p}`
188+
: `${!init ? ',' : ''}${k}:${p}`
183189

184190
init = false
185191
}
186192

193+
if (hasOptional) v += `\${(${op}=false)||''}`
194+
187195
v += '}'
188196

189197
if (nullableCondition) v += `\`}`
@@ -194,7 +202,6 @@ const accelerate = (
194202
const i = instruction.array
195203

196204
instruction.array++
197-
if (!instruction.optional) instruction.optional = true
198205

199206
if (schema.items.type === 'string') {
200207
if (nullableCondition)
@@ -250,7 +257,8 @@ const accelerate = (
250257

251258
let setup = ''
252259

253-
if (instruction.optional) setup += `let op=false\n`
260+
if (instruction.optional)
261+
setup += `let op=new Array(${instruction.optional})\n`
254262

255263
for (let i = 0; i < instruction.properties.length; i++) {
256264
const key = `s${i}`
@@ -270,7 +278,7 @@ export const createAccelerator = <T extends TAnySchema>(
270278
): ((v: T['static']) => string) => {
271279
const f = accelerate(schema, 'v', {
272280
array: 0,
273-
optional: false,
281+
optional: 0,
274282
properties: []
275283
})
276284

0 commit comments

Comments
 (0)