Skip to content

Commit 3fb45d6

Browse files
committed
🔧 fix: hasTransform doesn't detect when used in Intersect
1 parent 86ec566 commit 3fb45d6

File tree

7 files changed

+196
-123
lines changed

7 files changed

+196
-123
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.2.21 - 22 Feb 2025
2+
Bug fix:
3+
- [#671](https://github.com/elysiajs/elysia/issues/671#issuecomment-2676263442) Transform inside t.Intersect isn't detected
4+
15
# 1.2.20 - 22 Feb 2025
26
Bug fix:
37
- [#671](https://github.com/elysiajs/elysia/issues/671#issuecomment-2675777040) Transform query schema check fails

example/a.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
import { Elysia, t } from '../src'
2+
import { hasRef, hasTransform } from '../src/compose'
23
import { req } from '../test/utils'
34

4-
const app = new Elysia().get(
5-
'/test',
6-
({ query: { id } }) => ({
7-
id,
8-
type: typeof id
9-
}),
10-
{
11-
query: t.Object({
12-
id: t
13-
.Transform(t.Array(t.UnionEnum(['test', 'foo'])))
14-
.Decode((id) => ({ value: id }))
15-
.Encode((id) => id.value)
5+
const app = new Elysia().post('/test', ({ body }) => body, {
6+
body: t.Intersect([
7+
t.Object({ foo: t.String() }),
8+
t.Object({
9+
field: t
10+
.Transform(t.String())
11+
.Decode((decoded) => ({ decoded }))
12+
.Encode((v) => v.decoded)
1613
})
17-
}
18-
)
14+
])
15+
})
1916

20-
app.handle(req('/test?id=test'))
21-
.then((x) =>
22-
x.json().then((v) =>
23-
console.dir(v, {
24-
depth: null
25-
})
26-
)
17+
app.handle(
18+
new Request('http://localhost/test', {
19+
method: 'POST',
20+
headers: {
21+
'Content-Type': 'application/json'
22+
},
23+
body: JSON.stringify({ field: 'bar', foo: 'test' })
24+
})
2725
)
26+
.then((x) => x.json())
27+
.then(console.log)

src/compose.ts

Lines changed: 51 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -363,62 +363,45 @@ const TransformSymbol = Symbol.for('TypeBox.Transform')
363363
export const hasRef = (schema: TAnySchema): boolean => {
364364
if (!schema) return false
365365

366-
if (schema.type === 'object' && schema.properties) {
367-
const properties = schema.properties as Record<string, TAnySchema>
368-
for (const key of Object.keys(properties)) {
369-
const property = properties[key]
366+
if (schema.oneOf)
367+
for (let i = 0; i < schema.oneOf.length; i++)
368+
if (hasRef(schema.oneOf[i])) return true
370369

371-
if (property.type === 'object') {
372-
if (hasRef(property)) return true
373-
} else {
374-
if (property.anyOf) {
375-
for (let i = 0; i < property.anyOf.length; i++)
376-
if (hasRef(property.anyOf[i])) return true
377-
}
370+
if (schema.anyOf)
371+
for (let i = 0; i < schema.anyOf.length; i++)
372+
if (hasRef(schema.anyOf[i])) return true
378373

379-
if (property.oneOf) {
380-
for (let i = 0; i < property.oneOf.length; i++)
381-
if (hasRef(property.oneOf[i])) return true
382-
}
383-
384-
if (property.allOf) {
385-
for (let i = 0; i < property.allOf.length; i++)
386-
if (hasRef(property.allOf[i])) return true
387-
}
388-
}
374+
if (schema.oneOf)
375+
for (let i = 0; i < schema.oneOf.length; i++)
376+
if (hasRef(schema.oneOf[i])) return true
389377

390-
if (property[Kind] === 'Ref' && '$ref' in property) return true
378+
if (schema.allOf)
379+
for (let i = 0; i < schema.allOf.length; i++)
380+
if (hasRef(schema.allOf[i])) return true
391381

392-
if (property.type === 'array' && property.items) {
393-
const item = property.items
382+
if (schema.not && hasRef(schema.not)) return true
394383

395-
if (item.type === 'object') {
396-
if (hasRef(item)) return true
397-
} else {
398-
if (item.anyOf) {
399-
for (let i = 0; i < item.anyOf.length; i++)
400-
if (hasRef(item.anyOf[i])) return true
401-
}
384+
if (schema.type === 'object' && schema.properties) {
385+
const properties = schema.properties as Record<string, TAnySchema>
402386

403-
if (item.oneOf) {
404-
for (let i = 0; i < item.oneOf.length; i++)
405-
if (hasRef(item.oneOf[i])) return true
406-
}
387+
for (const key of Object.keys(properties)) {
388+
const property = properties[key]
407389

408-
if (item.allOf) {
409-
for (let i = 0; i < item.allOf.length; i++)
410-
if (hasRef(item.allOf[i])) return true
411-
}
412-
}
390+
if (hasRef(property)) return true
413391

414-
if (item[Kind] === 'Ref' && '$ref' in item) return true
415-
}
392+
if (
393+
property.type === 'array' &&
394+
property.items &&
395+
hasRef(property.items)
396+
)
397+
return true
416398
}
417399

418400
return false
419401
}
420402

421-
if (schema.type === 'array' && schema.items) return hasRef(schema.items)
403+
if (schema.type === 'array' && schema.items && hasRef(schema.items))
404+
return true
422405

423406
return schema[Kind] === 'Ref' && '$ref' in schema
424407
}
@@ -429,67 +412,46 @@ export const hasTransform = (schema: TAnySchema): boolean => {
429412
if (schema.$ref && schema.$defs && schema.$ref in schema.$defs)
430413
return hasTransform(schema.$defs[schema.$ref])
431414

432-
if (schema.type === 'object' && schema.properties) {
433-
const properties = schema.properties as Record<string, TAnySchema>
434-
for (const key of Object.keys(properties)) {
435-
const property = properties[key]
436-
437-
if (property.type === 'object') {
438-
if (hasTransform(property)) return true
439-
} else {
440-
if (property.anyOf) {
441-
for (let i = 0; i < property.anyOf.length; i++)
442-
if (hasTransform(property.anyOf[i])) return true
443-
}
415+
if (schema.oneOf)
416+
for (let i = 0; i < schema.oneOf.length; i++)
417+
if (hasTransform(schema.oneOf[i])) return true
444418

445-
if (property.allOf) {
446-
for (let i = 0; i < property.allOf.length; i++)
447-
if (hasTransform(property.allOf[i])) return true
448-
}
449-
450-
if (property.oneOf) {
451-
for (let i = 0; i < property.oneOf.length; i++)
452-
if (hasTransform(property.oneOf[i])) return true
453-
}
454-
}
419+
if (schema.anyOf) {
420+
for (let i = 0; i < schema.anyOf.length; i++)
421+
if (hasTransform(schema.anyOf[i])) return true
422+
}
455423

456-
if (TransformSymbol in property) return true
424+
if (schema.allOf)
425+
for (let i = 0; i < schema.allOf.length; i++)
426+
if (hasTransform(schema.allOf[i])) return true
457427

458-
if (property.type === 'array' && property.items) {
459-
const item = property.items
428+
if (schema.not && hasTransform(schema.not)) return true
460429

461-
if (item.type === 'object') {
462-
if (hasTransform(item)) return true
463-
} else {
464-
if (item.anyOf) {
465-
for (let i = 0; i < item.anyOf.length; i++)
466-
if (hasTransform(item.anyOf[i])) return true
467-
}
430+
if (schema.type === 'object' && schema.properties) {
431+
const properties = schema.properties as Record<string, TAnySchema>
468432

469-
if (item.oneOf) {
470-
for (let i = 0; i < item.oneOf.length; i++)
471-
if (hasTransform(item.oneOf[i])) return true
472-
}
433+
for (const key of Object.keys(properties)) {
434+
const property = properties[key]
473435

474-
if (item.allOf) {
475-
for (let i = 0; i < item.allOf.length; i++)
476-
if (hasTransform(item.allOf[i])) return true
477-
}
478-
}
436+
if (hasTransform(property)) return true
479437

480-
if (TransformSymbol in item) return true
481-
}
438+
if (
439+
property.type === 'array' &&
440+
property.items &&
441+
hasTransform(property.items)
442+
)
443+
return true
482444
}
483445

484446
return false
485447
}
486448

487-
if (schema.type === 'array' && schema.items)
488-
return hasTransform(schema.items)
449+
if (schema.type === 'array' && schema.items && hasTransform(schema.items))
450+
return true
489451

490452
return (
491453
TransformSymbol in schema ||
492-
(schema.properties && TransformSymbol in schema.properties)
454+
(!!schema.properties && TransformSymbol in schema.properties)
493455
)
494456
}
495457

src/utils.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,7 @@ const _replaceSchemaType = (
352352
return schema
353353
}
354354

355-
if (schema.not) {
356-
for (let i = 0; i < schema.not.length; i++)
357-
schema.not[i] = _replaceSchemaType(schema.not[i], options, root)
358-
359-
return schema
360-
}
355+
if (schema.not) return _replaceSchemaType(schema.not, options, root)
361356

362357
const isRoot = root && !!options.excludeRoot
363358

@@ -466,9 +461,7 @@ const _replaceSchemaType = (
466461
else if (to.allOf)
467462
for (let i = 0; i < to.allOf.length; i++)
468463
to.allOf[i] = composeProperties(to.allOf[i])
469-
else if (to.not)
470-
for (let i = 0; i < to.not.length; i++)
471-
to.not[i] = composeProperties(to.not[i])
464+
else if (to.not) to.not = composeProperties(to.not)
472465

473466
if (transform) to[TransformKind as any] = transform[TransformKind]
474467

@@ -524,9 +517,7 @@ const _replaceSchemaType = (
524517
else if (to.allOf)
525518
for (let i = 0; i < to.allOf.length; i++)
526519
to.allOf[i] = { ...rest, ...to.allOf[i] }
527-
else if (to.not)
528-
for (let i = 0; i < to.not.length; i++)
529-
to.not[i] = { ...rest, ...to.not[i] }
520+
else if (to.not) to.not = { ...rest, ...to.not }
530521

531522
properties[key] = {
532523
...rest,

test/units/has-ref.test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { describe, expect, it } from 'bun:test'
44

55
import { hasRef } from '../../src/compose'
66

7-
describe('has transform', () => {
7+
describe('has Ref', () => {
88
it('return true if object property has ref', () => {
99
expect(
1010
hasRef(
@@ -100,4 +100,47 @@ describe('has transform', () => {
100100
)
101101
).toBe(true)
102102
})
103+
104+
it('return true if Ref is inside Union', () => {
105+
expect(
106+
hasRef(
107+
t.Union([
108+
t.Object({ foo: t.String() }),
109+
t.Object({
110+
field: t.Ref('b')
111+
})
112+
])
113+
)
114+
).toEqual(true)
115+
})
116+
117+
it('return true if Ref is inside Intersect', () => {
118+
expect(
119+
hasRef(
120+
t.Intersect([
121+
t.Object({ foo: t.String() }),
122+
t.Object({
123+
field: t.Ref('b')
124+
})
125+
])
126+
)
127+
).toEqual(true)
128+
})
129+
130+
it('return true if Ref is inside Transform', () => {
131+
expect(
132+
hasRef(
133+
t
134+
.Transform(t.Object({ id: t.Ref('b') }))
135+
.Decode((value) => value.id)
136+
.Encode((value) => ({
137+
id: value
138+
}))
139+
)
140+
).toBe(true)
141+
})
142+
143+
it('return true if Ref is the root', () => {
144+
expect(hasRef(t.Ref('b'))).toBe(true)
145+
})
103146
})

test/units/has-transform.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,49 @@ describe('has transform', () => {
100100
)
101101
).toBe(true)
102102
})
103+
104+
it('return true if Transform is inside Intersect', () => {
105+
expect(
106+
hasTransform(
107+
t.Intersect([
108+
t.Object({ foo: t.String() }),
109+
t.Object({
110+
field: t
111+
.Transform(t.String())
112+
.Decode((decoded) => ({ decoded }))
113+
.Encode((v) => v.decoded)
114+
})
115+
])
116+
)
117+
).toEqual(true)
118+
})
119+
120+
it('return true if Transform is inside Union', () => {
121+
expect(
122+
hasTransform(
123+
t.Union([
124+
t.Object({ foo: t.String() }),
125+
t.Object({
126+
field: t
127+
.Transform(t.String())
128+
.Decode((decoded) => ({ decoded }))
129+
.Encode((v) => v.decoded)
130+
})
131+
])
132+
)
133+
).toEqual(true)
134+
})
135+
136+
it('return true when Transform is the root', () => {
137+
expect(
138+
hasTransform(
139+
t
140+
.Transform(t.Object({ id: t.Integer() }))
141+
.Decode((value) => value.id)
142+
.Encode((value) => ({
143+
id: value
144+
}))
145+
)
146+
).toBe(true)
147+
})
103148
})

0 commit comments

Comments
 (0)