Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 68 additions & 84 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ export interface ElysiaTypeCheck<T extends AnySchema>
Check(value: unknown): T extends TSchema
? boolean
:
| {
value: UnwrapSchema<T>
}
| { issues: unknown[] }
| {
value: UnwrapSchema<T>
}
| { issues: unknown[] }
Clean?(v: unknown): UnwrapSchema<T>
parse(v: unknown): UnwrapSchema<T>
safeParse(v: unknown):
| { success: true; data: UnwrapSchema<T>; error: null }
| {
success: false
data: null
error: string | undefined
errors: MapValueError[]
}
success: false
data: null
error: string | undefined
errors: MapValueError[]
}
hasAdditionalProperties: boolean
'~hasAdditionalProperties'?: boolean
hasDefault: boolean
Expand Down Expand Up @@ -112,23 +112,14 @@ export const hasAdditionalProperties = (
if (schema.type === 'object') {
const properties = schema.properties as Record<string, TAnySchema>

if ('additionalProperties' in schema) return schema.additionalProperties
if ('patternProperties' in schema) return false

for (const key of Object.keys(properties)) {
const property = properties[key]
if (properties)
for (const key of Object.keys(properties)) {
const property = properties[key]

if (property.type === 'object') {
if (hasAdditionalProperties(property)) return true
} else if (property.anyOf) {
for (let i = 0; i < property.anyOf.length; i++)
if (hasAdditionalProperties(property.anyOf[i])) return true
}

return property.additionalProperties
}

return false
return schema.additionalProperties === true
}

if (schema.type === 'array' && schema.items && !Array.isArray(schema.items))
Expand Down Expand Up @@ -235,17 +226,10 @@ export const hasElysiaMeta = (meta: string, _schema: TAnySchema): boolean => {
for (const key of Object.keys(properties)) {
const property = properties[key]

if (property.type === 'object') {
if (hasElysiaMeta(meta, property)) return true
} else if (property.anyOf) {
for (let i = 0; i < property.anyOf.length; i++)
if (hasElysiaMeta(meta, property.anyOf[i])) return true
}

return schema.elysiaMeta === meta
if (hasElysiaMeta(meta, property)) return true
}

return false
return schema.elysiaMeta === meta
}

if (schema.type === 'array' && schema.items && !Array.isArray(schema.items))
Expand Down Expand Up @@ -401,7 +385,7 @@ const createCleaner = (schema: TAnySchema) => (value: unknown) => {
if (typeof value === 'object')
try {
return Value.Clean(schema, value)
} catch {}
} catch { }

return value
}
Expand Down Expand Up @@ -436,8 +420,8 @@ export const getSchemaValidator = <T extends AnySchema | string | undefined>(
): IsAny<T> extends true
? ElysiaTypeCheck<TAnySchema>
: undefined extends T
? undefined
: ElysiaTypeCheck<NonNullable<Exclude<T, string>>> => {
? undefined
: ElysiaTypeCheck<NonNullable<Exclude<T, string>>> => {
validators = validators?.filter((x) => x)

if (!s) {
Expand Down Expand Up @@ -674,7 +658,7 @@ export const getSchemaValidator = <T extends AnySchema | string | undefined>(
provider: 'standard',
schema,
references: '',
checkFunc: () => {},
checkFunc: () => { },
code: '',
// @ts-ignore
Check,
Expand Down Expand Up @@ -708,7 +692,7 @@ export const getSchemaValidator = <T extends AnySchema | string | undefined>(
error: null
}
} catch (error) {
const errors = [...compiled.Errors(v)].map(mapValueError)
const errors = [...validator.Errors(v)].map(mapValueError)

return {
success: false,
Expand Down Expand Up @@ -787,7 +771,7 @@ export const getSchemaValidator = <T extends AnySchema | string | undefined>(
schema,
// @ts-ignore
references: '',
checkFunc: () => {},
checkFunc: () => { },
code: '',
Check: (value: unknown) => Value.Check(schema, value),
Errors: (value: unknown) => Value.Errors(schema, value),
Expand Down Expand Up @@ -868,7 +852,7 @@ export const getSchemaValidator = <T extends AnySchema | string | undefined>(
error: null
}
} catch (error) {
const errors = [...compiled.Errors(v)].map(mapValueError)
const errors = [...validator.Errors(v)].map(mapValueError)

return {
success: false,
Expand All @@ -887,7 +871,7 @@ export const getSchemaValidator = <T extends AnySchema | string | undefined>(
provider: 'standard',
schema,
references: '',
checkFunc: () => {},
checkFunc: () => { },
code: '',
// @ts-ignore
Check: (v) => schema['~standard'].validate(v),
Expand Down Expand Up @@ -941,7 +925,7 @@ export const getSchemaValidator = <T extends AnySchema | string | undefined>(
error: null
}
} catch (error) {
const errors = [...compiled.Errors(v)].map(mapValueError)
const errors = [...validator.Errors(v)].map(mapValueError)

return {
success: false,
Expand Down Expand Up @@ -1150,13 +1134,13 @@ export const mergeObjectSchemas = (
if (schemas.length === 1)
return schemas[0].type === 'object'
? {
schema: schemas[0] as TObject,
notObjects: []
}
schema: schemas[0] as TObject,
notObjects: []
}
: {
schema: undefined,
notObjects: schemas
}
schema: undefined,
notObjects: schemas
}

let newSchema: TObject
const notObjects = <TSchema[]>[]
Expand Down Expand Up @@ -1293,16 +1277,16 @@ export const getResponseSchemaValidator = (
record[+status] =
Kind in schema || '~standard' in schema
? getSchemaValidator(schema as TSchema, {
modules,
models,
additionalProperties,
dynamic,
normalize,
coerce: false,
additionalCoerce: [],
validators: validators.map((x) => x![+status]),
sanitize
})!
modules,
models,
additionalProperties,
dynamic,
normalize,
coerce: false,
additionalCoerce: [],
validators: validators.map((x) => x![+status]),
sanitize
})!
: (schema as ElysiaTypeCheck<any>)
}

Expand All @@ -1313,16 +1297,16 @@ export const getResponseSchemaValidator = (
record[+status] =
Kind in maybeNameOrSchema || '~standard' in maybeNameOrSchema
? getSchemaValidator(maybeNameOrSchema as TSchema, {
modules,
models,
additionalProperties,
dynamic,
normalize,
coerce: false,
additionalCoerce: [],
validators: validators.map((x) => x![+status]),
sanitize
})
modules,
models,
additionalProperties,
dynamic,
normalize,
coerce: false,
additionalCoerce: [],
validators: validators.map((x) => x![+status]),
sanitize
})
: (maybeNameOrSchema as ElysiaTypeCheck<any>)
})

Expand All @@ -1341,11 +1325,11 @@ export const getCookieValidator = ({
sanitize
}: {
validator:
| TSchema
| StandardSchemaV1Like
| ElysiaTypeCheck<any>
| string
| undefined
| TSchema
| StandardSchemaV1Like
| ElysiaTypeCheck<any>
| string
| undefined
modules: TModule<any, any>
defaultConfig: CookieOptions | undefined
config: CookieOptions
Expand All @@ -1360,17 +1344,17 @@ export const getCookieValidator = ({
validator?.provider
? (validator as ElysiaTypeCheck<any>)
: // @ts-ignore
getSchemaValidator(validator, {
modules,
dynamic,
models,
normalize,
additionalProperties: true,
coerce: true,
additionalCoerce: stringToStructureCoercions(),
validators,
sanitize
})
getSchemaValidator(validator, {
modules,
dynamic,
models,
normalize,
additionalProperties: true,
coerce: true,
additionalCoerce: stringToStructureCoercions(),
validators,
sanitize
})

if (cookieValidator)
cookieValidator.config = mergeCookie(cookieValidator.config, config)
Expand Down Expand Up @@ -1431,7 +1415,7 @@ export const getCookieValidator = ({

export const unwrapImportSchema = (schema: TSchema): TSchema =>
schema &&
schema[Kind] === 'Import' &&
schema.$defs[schema.$ref][Kind] === 'Object'
schema[Kind] === 'Import' &&
schema.$defs[schema.$ref][Kind] === 'Object'
? schema.$defs[schema.$ref]
: schema
32 changes: 32 additions & 0 deletions test/schema/schema-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,35 @@ describe('hasProperty', () => {
expect(hasProperty('default', schema)).toBe(true)
})
})

describe('hasElysiaMeta', () => {
const { hasElysiaMeta } = require('../../src/schema')

it('finds elysiaMeta in Object properties even if placed after another property', () => {
const schema = t.Object({
a: t.String(),
b: t.ObjectString({ c: t.String() })
})

expect(hasElysiaMeta('ObjectString', schema)).toBe(true)
})

it('finds elysiaMeta in deeply nested objects', () => {
const schema = t.Object({
outer: t.Object({
inner: t.ObjectString({ c: t.String() })
})
})

expect(hasElysiaMeta('ObjectString', schema)).toBe(true)
})

it('returns false if elysiaMeta is not present', () => {
const schema = t.Object({
a: t.String(),
b: t.Object({ c: t.String() })
})

expect(hasElysiaMeta('ObjectString', schema)).toBe(false)
})
})
Loading