mirror of
https://github.com/zoriya/typebox.git
synced 2025-12-06 06:46:10 +00:00
Revision 0.31.19 (#644)
* Encode Error Path for RFC9601 Escape Sequences * Record Tests + Version
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.31.18",
|
||||
"version": "0.31.19",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.31.18",
|
||||
"version": "0.31.19",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@sinclair/hammer": "^0.18.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.31.18",
|
||||
"version": "0.31.19",
|
||||
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
|
||||
"keywords": [
|
||||
"typescript",
|
||||
|
||||
@@ -119,6 +119,12 @@ export class ValueErrorsUnknownTypeError extends Types.TypeBoxError {
|
||||
}
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// EscapeKey
|
||||
// --------------------------------------------------------------------------
|
||||
export function EscapeKey(key: string): string {
|
||||
return key.replace(/~/g, '~0').replace(/\//g, '~1') // RFC6901 Path
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// Guards
|
||||
// --------------------------------------------------------------------------
|
||||
function IsDefined<T>(value: unknown): value is T {
|
||||
@@ -319,31 +325,31 @@ function* TObject(schema: Types.TObject, references: Types.TSchema[], path: stri
|
||||
const unknownKeys = Object.getOwnPropertyNames(value)
|
||||
for (const requiredKey of requiredKeys) {
|
||||
if (unknownKeys.includes(requiredKey)) continue
|
||||
yield Create(ValueErrorType.ObjectRequiredProperty, schema.properties[requiredKey], `${path}/${requiredKey}`, undefined)
|
||||
yield Create(ValueErrorType.ObjectRequiredProperty, schema.properties[requiredKey], `${path}/${EscapeKey(requiredKey)}`, undefined)
|
||||
}
|
||||
if (schema.additionalProperties === false) {
|
||||
for (const valueKey of unknownKeys) {
|
||||
if (!knownKeys.includes(valueKey)) {
|
||||
yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${valueKey}`, value[valueKey])
|
||||
yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${EscapeKey(valueKey)}`, value[valueKey])
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof schema.additionalProperties === 'object') {
|
||||
for (const valueKey of unknownKeys) {
|
||||
if (knownKeys.includes(valueKey)) continue
|
||||
yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${valueKey}`, value[valueKey])
|
||||
yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${EscapeKey(valueKey)}`, value[valueKey])
|
||||
}
|
||||
}
|
||||
for (const knownKey of knownKeys) {
|
||||
const property = schema.properties[knownKey]
|
||||
if (schema.required && schema.required.includes(knownKey)) {
|
||||
yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey])
|
||||
yield* Visit(property, references, `${path}/${EscapeKey(knownKey)}`, value[knownKey])
|
||||
if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) {
|
||||
yield Create(ValueErrorType.ObjectRequiredProperty, property, `${path}/${knownKey}`, undefined)
|
||||
yield Create(ValueErrorType.ObjectRequiredProperty, property, `${path}/${EscapeKey(knownKey)}`, undefined)
|
||||
}
|
||||
} else {
|
||||
if (TypeSystemPolicy.IsExactOptionalProperty(value, knownKey)) {
|
||||
yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey])
|
||||
yield* Visit(property, references, `${path}/${EscapeKey(knownKey)}`, value[knownKey])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,17 +368,17 @@ function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: stri
|
||||
const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0]
|
||||
const regex = new RegExp(patternKey)
|
||||
for (const [propertyKey, propertyValue] of Object.entries(value)) {
|
||||
if (regex.test(propertyKey)) yield* Visit(patternSchema, references, `${path}/${propertyKey}`, propertyValue)
|
||||
if (regex.test(propertyKey)) yield* Visit(patternSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue)
|
||||
}
|
||||
if (typeof schema.additionalProperties === 'object') {
|
||||
for (const [propertyKey, propertyValue] of Object.entries(value)) {
|
||||
if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${propertyKey}`, propertyValue)
|
||||
if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue)
|
||||
}
|
||||
}
|
||||
if (schema.additionalProperties === false) {
|
||||
for (const [propertyKey, propertyValue] of Object.entries(value)) {
|
||||
if (regex.test(propertyKey)) continue
|
||||
return yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${propertyKey}`, propertyValue)
|
||||
return yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${EscapeKey(propertyKey)}`, propertyValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +43,11 @@ import './number-multiple-of'
|
||||
import './object-additional-properties'
|
||||
import './object-max-properties'
|
||||
import './object-min-properties'
|
||||
import './object-pointer-property'
|
||||
import './object-required-property'
|
||||
import './object'
|
||||
import './promise'
|
||||
import './record-pointer-property'
|
||||
import './string-format-unknown'
|
||||
import './string-format'
|
||||
import './string-max-length'
|
||||
|
||||
139
test/runtime/errors/types/object-pointer-property.ts
Normal file
139
test/runtime/errors/types/object-pointer-property.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/ObjectPointerProperty', () => {
|
||||
// ----------------------------------------------------------------
|
||||
// Known
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce known pointer property path 1', () => {
|
||||
const T = Type.Object({ 'a/b': Type.String() })
|
||||
const R = Resolve(T, { 'a/b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~1b')
|
||||
})
|
||||
it('Should produce known pointer property path 2', () => {
|
||||
const T = Type.Object({ 'a~b': Type.String() })
|
||||
const R = Resolve(T, { 'a~b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b')
|
||||
})
|
||||
it('Should produce known pointer property path 3', () => {
|
||||
const T = Type.Object({ 'a/b~c': Type.String() })
|
||||
const R = Resolve(T, { 'a/b~c': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~1b~0c')
|
||||
})
|
||||
it('Should produce known pointer property path 4', () => {
|
||||
const T = Type.Object({ 'a~b/c': Type.String() })
|
||||
const R = Resolve(T, { 'a~b/c': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b~1c')
|
||||
})
|
||||
it('Should produce known pointer property path 5', () => {
|
||||
const T = Type.Object({ 'a~b/c/d': Type.String() })
|
||||
const R = Resolve(T, { 'a~b/c/d': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b~1c~1d')
|
||||
})
|
||||
it('Should produce known pointer property path 6', () => {
|
||||
const T = Type.Object({ 'a~b/c/d~e': Type.String() })
|
||||
const R = Resolve(T, { 'a~b/c/d~e': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b~1c~1d~0e')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Unknown Additional
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce unknown pointer property path 1', () => {
|
||||
const T = Type.Object({}, { additionalProperties: false })
|
||||
const R = Resolve(T, { 'a/b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~1b')
|
||||
})
|
||||
it('Should produce unknown pointer property path 2', () => {
|
||||
const T = Type.Object({}, { additionalProperties: false })
|
||||
const R = Resolve(T, { 'a~b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Unknown Constrained
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce unknown constrained pointer property path 1', () => {
|
||||
const T = Type.Object({}, { additionalProperties: Type.String() })
|
||||
const R = Resolve(T, { 'a/b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~1b')
|
||||
})
|
||||
it('Should produce unknown constrained pointer property path 2', () => {
|
||||
const T = Type.Object({}, { additionalProperties: Type.String() })
|
||||
const R = Resolve(T, { 'a~b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Nested
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce nested pointer 1', () => {
|
||||
const T = Type.Object({
|
||||
'x/y': Type.Object({
|
||||
z: Type.Object({
|
||||
w: Type.String(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
const R = Resolve(T, { 'x/y': { z: { w: 1 } } })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/x~1y/z/w')
|
||||
})
|
||||
it('Should produce nested pointer 2', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Object({
|
||||
'y/z': Type.Object({
|
||||
w: Type.String(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
const R = Resolve(T, { x: { 'y/z': { w: 1 } } })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/x/y~1z/w')
|
||||
})
|
||||
it('Should produce nested pointer 3', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Object({
|
||||
y: Type.Object({
|
||||
'z/w': Type.String(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
const R = Resolve(T, { x: { y: { 'z/w': 1 } } })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/x/y/z~1w')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Nested Array
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce nested array pointer property path 1', () => {
|
||||
const T = Type.Object({
|
||||
'x/y': Type.Object({
|
||||
z: Type.Array(Type.String()),
|
||||
}),
|
||||
})
|
||||
const R = Resolve(T, { 'x/y': { z: ['a', 'b', 1] } })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/x~1y/z/2')
|
||||
})
|
||||
it('Should produce nested array pointer property path 2', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Array(
|
||||
Type.Object({
|
||||
'y/z': Type.String(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
const R = Resolve(T, { x: [{ 'y/z': 'a' }, { 'y/z': 'b' }, { 'y/z': 1 }] })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/x/2/y~1z')
|
||||
})
|
||||
})
|
||||
70
test/runtime/errors/types/record-pointer-property.ts
Normal file
70
test/runtime/errors/types/record-pointer-property.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Resolve } from './resolve'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('errors/type/RecordPointerProperty', () => {
|
||||
// ----------------------------------------------------------------
|
||||
// Known
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce known pointer property path 1', () => {
|
||||
const T = Type.Record(Type.String(), Type.String())
|
||||
const R = Resolve(T, { 'a/b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~1b')
|
||||
})
|
||||
it('Should produce known pointer property path 2', () => {
|
||||
const T = Type.Record(Type.String(), Type.String())
|
||||
const R = Resolve(T, { 'a~b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Unknown
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce unknown pointer property path 1', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
const R = Resolve(T, { 'a/b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~1b')
|
||||
})
|
||||
it('Should produce unknown pointer property path 1', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
const R = Resolve(T, { 'a~b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Unknown Constrained
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce unknown constrained pointer property path 1', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), {
|
||||
additionalProperties: Type.String(),
|
||||
})
|
||||
const R = Resolve(T, { 'a/b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~1b')
|
||||
})
|
||||
it('Should produce unknown constrained pointer property path 1', () => {
|
||||
const T = Type.Record(Type.Number(), Type.String(), {
|
||||
additionalProperties: Type.String(),
|
||||
})
|
||||
const R = Resolve(T, { 'a~b': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/a~0b')
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// PatternProperties
|
||||
// ----------------------------------------------------------------
|
||||
it('Should produce pattern pointer property path 1', () => {
|
||||
const T = Type.Record(Type.TemplateLiteral('${string}/${string}/c'), Type.String(), {
|
||||
additionalProperties: false,
|
||||
})
|
||||
const R = Resolve(T, { 'x/y/z': 1 })
|
||||
Assert.IsEqual(R.length, 1)
|
||||
Assert.IsEqual(R[0].path, '/x~1y~1z')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user