diff --git a/src/schema.ts b/src/schema.ts index fd5cdc551..fe8de451a 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -50,20 +50,20 @@ export interface ElysiaTypeCheck Check(value: unknown): T extends TSchema ? boolean : - | { - value: UnwrapSchema - } - | { issues: unknown[] } + | { + value: UnwrapSchema + } + | { issues: unknown[] } Clean?(v: unknown): UnwrapSchema parse(v: unknown): UnwrapSchema safeParse(v: unknown): | { success: true; data: UnwrapSchema; 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 @@ -112,23 +112,14 @@ export const hasAdditionalProperties = ( if (schema.type === 'object') { const properties = schema.properties as Record - 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)) @@ -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)) @@ -401,7 +385,7 @@ const createCleaner = (schema: TAnySchema) => (value: unknown) => { if (typeof value === 'object') try { return Value.Clean(schema, value) - } catch {} + } catch { } return value } @@ -436,8 +420,8 @@ export const getSchemaValidator = ( ): IsAny extends true ? ElysiaTypeCheck : undefined extends T - ? undefined - : ElysiaTypeCheck>> => { + ? undefined + : ElysiaTypeCheck>> => { validators = validators?.filter((x) => x) if (!s) { @@ -674,7 +658,7 @@ export const getSchemaValidator = ( provider: 'standard', schema, references: '', - checkFunc: () => {}, + checkFunc: () => { }, code: '', // @ts-ignore Check, @@ -708,7 +692,7 @@ export const getSchemaValidator = ( error: null } } catch (error) { - const errors = [...compiled.Errors(v)].map(mapValueError) + const errors = [...validator.Errors(v)].map(mapValueError) return { success: false, @@ -787,7 +771,7 @@ export const getSchemaValidator = ( schema, // @ts-ignore references: '', - checkFunc: () => {}, + checkFunc: () => { }, code: '', Check: (value: unknown) => Value.Check(schema, value), Errors: (value: unknown) => Value.Errors(schema, value), @@ -868,7 +852,7 @@ export const getSchemaValidator = ( error: null } } catch (error) { - const errors = [...compiled.Errors(v)].map(mapValueError) + const errors = [...validator.Errors(v)].map(mapValueError) return { success: false, @@ -887,7 +871,7 @@ export const getSchemaValidator = ( provider: 'standard', schema, references: '', - checkFunc: () => {}, + checkFunc: () => { }, code: '', // @ts-ignore Check: (v) => schema['~standard'].validate(v), @@ -941,7 +925,7 @@ export const getSchemaValidator = ( error: null } } catch (error) { - const errors = [...compiled.Errors(v)].map(mapValueError) + const errors = [...validator.Errors(v)].map(mapValueError) return { success: false, @@ -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 = [] @@ -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) } @@ -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) }) @@ -1341,11 +1325,11 @@ export const getCookieValidator = ({ sanitize }: { validator: - | TSchema - | StandardSchemaV1Like - | ElysiaTypeCheck - | string - | undefined + | TSchema + | StandardSchemaV1Like + | ElysiaTypeCheck + | string + | undefined modules: TModule defaultConfig: CookieOptions | undefined config: CookieOptions @@ -1360,17 +1344,17 @@ export const getCookieValidator = ({ validator?.provider ? (validator as ElysiaTypeCheck) : // @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) @@ -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 diff --git a/test/schema/schema-utils.test.ts b/test/schema/schema-utils.test.ts index b6a9924b6..2f4f3c39a 100644 --- a/test/schema/schema-utils.test.ts +++ b/test/schema/schema-utils.test.ts @@ -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) + }) +})