@@ -34,7 +34,14 @@ import {
34
34
} from './error'
35
35
import { ELYSIA_TRACE , type TraceHandler } from './trace'
36
36
37
- import { ElysiaTypeCheck , getCookieValidator , hasType , isUnion } from './schema'
37
+ import {
38
+ coercePrimitiveRoot ,
39
+ ElysiaTypeCheck ,
40
+ getCookieValidator ,
41
+ getSchemaValidator ,
42
+ hasType ,
43
+ isUnion
44
+ } from './schema'
38
45
import { Sucrose , sucrose } from './sucrose'
39
46
import { parseCookie , type CookieOptions } from './cookies'
40
47
import { validateFileExtension } from './type-system/utils'
@@ -1172,6 +1179,8 @@ export const composeHandler = ({
1172
1179
reporter . resolve ( )
1173
1180
}
1174
1181
1182
+ const fileUnions = < ElysiaTypeCheck < any > [ ] > [ ]
1183
+
1175
1184
if ( validator ) {
1176
1185
if ( validator . headers ) {
1177
1186
if ( validator . headers . hasDefault )
@@ -1291,10 +1300,8 @@ export const composeHandler = ({
1291
1300
if ( validator . body . hasTransform || validator . body . isOptional )
1292
1301
fnLiteral += `const isNotEmptyObject=c.body&&(typeof c.body==="object"&&isNotEmpty(c.body))\n`
1293
1302
1294
- const hasFile =
1295
- ! isUnion ( validator . body . schema ) &&
1296
- ( hasType ( 'File' , validator . body . schema ) ||
1297
- hasType ( 'Files' , validator . body . schema ) )
1303
+ const hasUnion = isUnion ( validator . body . schema )
1304
+ let hasNonUnionFileWithDefault = false
1298
1305
1299
1306
if ( validator . body . hasDefault ) {
1300
1307
let value = Value . Default (
@@ -1308,8 +1315,15 @@ export const composeHandler = ({
1308
1315
: undefined
1309
1316
)
1310
1317
1311
- // remove default value of t.File / t.Files
1312
- if ( hasFile && value && typeof value === 'object' ) {
1318
+ if (
1319
+ ! hasUnion &&
1320
+ value &&
1321
+ typeof value === 'object' &&
1322
+ ( hasType ( 'File' , validator . body . schema ) ||
1323
+ hasType ( 'Files' , validator . body . schema ) )
1324
+ ) {
1325
+ hasNonUnionFileWithDefault = true
1326
+
1313
1327
for ( const [ k , v ] of Object . entries ( value ) )
1314
1328
if ( v === 'File' || v === 'Files' )
1315
1329
// @ts -ignore
@@ -1377,26 +1391,90 @@ export const composeHandler = ({
1377
1391
if ( validator . body . hasTransform )
1378
1392
fnLiteral += `if(isNotEmptyObject)c.body=validator.body.Decode(c.body)\n`
1379
1393
1380
- if ( hasFile ) {
1394
+ if ( hasUnion && validator . body . schema . anyOf ?. length ) {
1395
+ const iterator = Object . values (
1396
+ validator . body . schema . anyOf
1397
+ ) as TAnySchema [ ]
1398
+
1399
+ for ( let i = 0 ; i < iterator . length ; i ++ ) {
1400
+ const type = iterator [ i ]
1401
+
1402
+ if ( hasType ( 'File' , type ) || hasType ( 'Files' , type ) ) {
1403
+ const candidate = getSchemaValidator ( type , {
1404
+ // @ts -expect-error private property
1405
+ modules : app . definitions . typebox ,
1406
+ dynamic : ! app . config . aot ,
1407
+ // @ts -expect-error private property
1408
+ models : app . definitions . type ,
1409
+ normalize : app . config . normalize ,
1410
+ additionalCoerce : coercePrimitiveRoot ( ) ,
1411
+ sanitize : ( ) => app . config . sanitize
1412
+ } )
1413
+
1414
+ if ( candidate ) {
1415
+ const isFirst = fileUnions . length === 0
1416
+
1417
+ const iterator = Object . entries (
1418
+ type . properties
1419
+ ) as [ string , TSchema ] [ ]
1420
+
1421
+ let validator = isFirst ? '\n' : ' else '
1422
+ validator += `if(fileUnions[${ fileUnions . length } ].Check(c.body)){`
1423
+
1424
+ let validateFile = ''
1425
+ let validatorLength = 0
1426
+ for ( let i = 0 ; i < iterator . length ; i ++ ) {
1427
+ const [ k , v ] = iterator [ i ]
1428
+
1429
+ if (
1430
+ ! v . extension ||
1431
+ ( v [ Kind ] !== 'File' && v [ Kind ] !== 'Files' )
1432
+ )
1433
+ continue
1434
+
1435
+ if ( validatorLength ) validateFile += ','
1436
+ validateFile += `validateFileExtension(c.body.${ k } ,${ JSON . stringify ( v . extension ) } ,'body.${ k } ')`
1437
+
1438
+ validatorLength ++
1439
+ }
1440
+
1441
+ if ( validateFile ) {
1442
+ if ( validatorLength === 1 )
1443
+ validator += `await ${ validateFile } \n`
1444
+ else if ( validatorLength > 1 )
1445
+ validator += `await Promise.all([${ validateFile } ])\n`
1446
+
1447
+ validator += '}'
1448
+
1449
+ fnLiteral += validator
1450
+ fileUnions . push ( candidate )
1451
+ }
1452
+ }
1453
+ }
1454
+ }
1455
+ } else if (
1456
+ hasNonUnionFileWithDefault ||
1457
+ ( ! hasUnion &&
1458
+ ( hasType ( 'File' , validator . body . schema ) ||
1459
+ hasType ( 'Files' , validator . body . schema ) ) )
1460
+ ) {
1381
1461
let validateFile = ''
1382
1462
1383
1463
let i = 0
1464
+ for ( const [ k , v ] of Object . entries (
1465
+ validator . body . schema . properties
1466
+ ) as [ string , TSchema ] [ ] ) {
1467
+ if (
1468
+ ! v . extension ||
1469
+ ( v [ Kind ] !== 'File' && v [ Kind ] !== 'Files' )
1470
+ )
1471
+ continue
1384
1472
1385
- if ( validator . body . schema . properties )
1386
- for ( const [ k , v ] of Object . entries (
1387
- validator . body . schema . properties
1388
- ) as [ string , TSchema ] [ ] ) {
1389
- if (
1390
- ! v . extension ||
1391
- ( v [ Kind ] !== 'File' && v [ Kind ] !== 'Files' )
1392
- )
1393
- continue
1394
-
1395
- if ( i ) validateFile += ','
1396
- validateFile += `validateFileExtension(c.body.${ k } ,${ JSON . stringify ( v . extension ) } ,'body.${ k } ')`
1473
+ if ( i ) validateFile += ','
1474
+ validateFile += `validateFileExtension(c.body.${ k } ,${ JSON . stringify ( v . extension ) } ,'body.${ k } ')`
1397
1475
1398
- i ++
1399
- }
1476
+ i ++
1477
+ }
1400
1478
1401
1479
if ( i ) fnLiteral += '\n'
1402
1480
@@ -1932,6 +2010,7 @@ export const composeHandler = ({
1932
2010
allocateIf ( `ELYSIA_REQUEST_ID,` , hasTrace ) +
1933
2011
allocateIf ( 'parser,' , hooks . parse ?. length ) +
1934
2012
allocateIf ( `getServer,` , inference . server ) +
2013
+ allocateIf ( `fileUnions,` , fileUnions . length ) +
1935
2014
adapterVariables +
1936
2015
allocateIf ( 'TypeBoxError' , hasValidation ) +
1937
2016
`}=hooks\n` +
@@ -1983,6 +2062,7 @@ export const composeHandler = ({
1983
2062
ELYSIA_REQUEST_ID : hasTrace ? ELYSIA_REQUEST_ID : undefined ,
1984
2063
// @ts -expect-error private property
1985
2064
getServer : ( ) => app . getServer ( ) ,
2065
+ fileUnions : fileUnions . length ? fileUnions : undefined ,
1986
2066
TypeBoxError : hasValidation ? TypeBoxError : undefined ,
1987
2067
parser : app [ '~parser' ] ,
1988
2068
...adapter . inject
@@ -2394,13 +2474,17 @@ export const composeErrorHandler = (app: AnyElysia) => {
2394
2474
}
2395
2475
2396
2476
fnLiteral +=
2397
- `if(error.constructor.name==="ValidationError"||error.constructor.name==="TransformDecodeError"){` +
2477
+ `if(error.constructor.name==="ValidationError"||error.constructor.name==="TransformDecodeError"){\n ` +
2398
2478
`if(error.error)error=error.error\n` +
2399
2479
`set.status=error.status??422\n` +
2400
2480
adapter . validationError +
2401
- `} `
2481
+ `\n}\n `
2402
2482
2403
- fnLiteral += `if(error instanceof Error){` + adapter . unknownError + `}`
2483
+ fnLiteral +=
2484
+ `if(error instanceof Error){` +
2485
+ `\nif(typeof error.toResponse==='function')return context.response=error.toResponse()\n` +
2486
+ adapter . unknownError +
2487
+ `\n}`
2404
2488
2405
2489
const mapResponseReporter = report ( 'mapResponse' , {
2406
2490
total : hooks . mapResponse ?. length ,
0 commit comments