From 65a4e9771bd0ac53ce42f78902b0e8dced6c8b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20B=C3=B6hler?= Date: Thu, 10 Sep 2026 12:22:50 +0200 Subject: [PATCH 1/3] add validations for simple bounds checks --- .../Validator/Validation+Builtins.swift | 149 +++++ .../Validator/BuiltinValidationTests.swift | 536 ++++++++++++++++++ 2 files changed, 685 insertions(+) diff --git a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift index 437b31b8a..f61a0df9d 100644 --- a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift +++ b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift @@ -691,6 +691,155 @@ public enum BuiltinValidation { ) ) } + + /// Ensure minimum and maximum are compatible for integers + /// + /// - Important: This is not an included validation by default. + public static var jsonSchemaIntegerBoundIsValid: Validation { + .init( + description: "Integer minimum and maximum are compatible", + check: { context in + guard + case .integer(_, let integerContext) = context.subject.value, + let minimum = integerContext.minimum, + let maximum = integerContext.maximum + else { + return true + } + guard minimum.value <= maximum.value else { + return false + } + + if minimum.value == maximum.value, minimum.exclusive || maximum.exclusive { + return false + } + + return true + } + ) + } + + /// Ensure minimum and maximum are compatible for numbers + /// + /// - Important: This is not an included validation by default. + public static var jsonSchemaNumberBoundIsValid: Validation { + .init( + description: "Number minimum and maximum are compatible", + check: { context in + guard + case .number(_, let numberContext) = context.subject.value, + let minimum = numberContext.minimum, + let maximum = numberContext.maximum + else { + return true + } + + guard minimum.value <= maximum.value else { + return false + } + + if minimum.value == maximum.value, minimum.exclusive || maximum.exclusive { + return false + } + + return true + } + ) + } + + /// Ensure array length definitions are compatible + /// + /// - Important: This is not an included validation by default. + public static var jsonSchemaArrayLengthBoundIsValid: Validation { + .init( + description: "Array minItem, maxItem and prefixItems count are compatible", + check: { context in + guard + case .array(_, let arrayContext) = context.subject.value + else { + return true + } + guard arrayContext.minItems >= 0 else { + return false + } + + let maxItems = arrayContext.maxItems ?? Int.max + + guard arrayContext.minItems <= maxItems else { + return false + } + if let prefixItems = arrayContext.prefixItems?.count { + // defining more a bigger prefix than maxItems is weird + guard prefixItems <= maxItems else { + return false + } + + // NOTE: minItems doesn't affect prefixItems (it's possible to have less items than prefix suggests) + } + + return true + } + ) + } + + /// Ensure object property count definitions are compatible + /// + /// - Important: This is not an included validation by default. + public static var jsonSchemaObjectPropertyCountIsValid: Validation { + .init( + description: "Object minProperties, maxProperties and required properties count are compatible", + check: { context in + guard + case .object(_, let objectContext) = context.subject.value + else { + return true + } + // NOTE: using `minProperties` a `max(_, 0)` would hide a negative value + let minProperties = objectContext._minProperties ?? 0 + + guard minProperties >= 0 else { + return false + } + let maxProperties = objectContext.maxProperties ?? Int.max + + guard minProperties <= maxProperties else { + return false + } + + let minRequired = objectContext.properties.count { $0.value.required } + + // NOTE: checking `minProperties <= minRequired` would be wrong + guard + minRequired <= maxProperties + else { + return false + } + + return true + } + ) + } + + /// Ensure string length definitions are compatible + /// + /// - Important: This is not an included validation by default. + public static var jsonSchemaStringLengthBoundIsValid: Validation { + .init( + description: "String minLength and maxLength are compatible", + check: { context in + guard + case .string(_, let stringContext) = context.subject.value + else { + return true + } + guard stringContext.minLength >= 0 else { + return false + } + let maxLength = stringContext.maxLength ?? Int.max + return stringContext.minLength <= maxLength + } + ) + } } // For backwards compatibilty, expose builtin validations on the Validation diff --git a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift index 0c0b9da4d..36b71ffee 100644 --- a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift +++ b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift @@ -1619,4 +1619,540 @@ final class BuiltinValidationTests: XCTestCase { let validator = Validator.blank.validating(.querystringParametersAreCompatible) try document.validate(using: validator) } + + func test_partialIntegerBound_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .integer(maximum: (1, true)))) + ], + responses: [ + 200: .response( + description: "Test", + content: [ + .json: .content(.init( + schema: .integer(minimum: (0, false)) + )) + ] + ) + ] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + try document.validate(using: validator) + } + + func test_singleIntegerInBound_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .integer(maximum: (1, false), minimum: (1, false)))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + try document.validate(using: validator) + } + + func test_invalidIntegerBound_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .integer(maximum: (1, true), minimum: (2, false)))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_emptyIntegerBound_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .integer(maximum: (1, false), minimum: (1, true)))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_partialNumberBound_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .number(maximum: (1, true)))) + ], + responses: [ + 200: .response( + description: "Test", + content: [ + .json: .content(.init( + schema: .number(minimum: (0, false)) + )) + ] + ) + ] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + try document.validate(using: validator) + } + + func test_singleNumberInBound_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .number(maximum: (1, false), minimum: (1, false)))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + try document.validate(using: validator) + } + + func test_invalidNumberBound_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .number(maximum: (1, true), minimum: (2, false)))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_emptyNumberBound_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .number(maximum: (1, false), minimum: (1, true)))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_partialStringLength_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .string(minLength: 1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaStringLengthBoundIsValid) + try document.validate(using: validator) + } + + func test_singleCharStringLength_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .string(minLength: 1, maxLength: 1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaStringLengthBoundIsValid) + try document.validate(using: validator) + } + + func test_negativeStringLength_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .string(minLength: -1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaStringLengthBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_invalidStringLength_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .string(minLength: 2, maxLength: 1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaStringLengthBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_partialArrayCount_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .array(minItems: 2))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaArrayLengthBoundIsValid) + try document.validate(using: validator) + } + + func test_fixedArrayCount_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .array(minItems: 2, maxItems: 2))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaArrayLengthBoundIsValid) + try document.validate(using: validator) + } + + func test_negativeArrayCount_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .array(minItems: -1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaArrayLengthBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_invalidArrayCount_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .array(minItems: 2, maxItems: 1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaArrayLengthBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_partialTupleArrayCount_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .array(minItems: 1, maxItems: 3, prefixItems: [.integer, .integer]))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaArrayLengthBoundIsValid) + try document.validate(using: validator) + } + + func test_excessiveTupleArrayCount_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .array(minItems: 1, maxItems: 2, prefixItems: [.integer, .integer, .integer]))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaArrayLengthBoundIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_partialPropertyCount_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .object(minProperties: 2))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaObjectPropertyCountIsValid) + try document.validate(using: validator) + } + + func test_validPropertyCount_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .object(minProperties: 2, maxProperties: 4))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaObjectPropertyCountIsValid) + try document.validate(using: validator) + } + + func test_negativePropertyCount_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .object(minProperties: -1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaObjectPropertyCountIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_invalidPropertyCount_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + .parameter(.query(name: "test", schema: .object(minProperties: 3, maxProperties: 1))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaObjectPropertyCountIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_requiredPropertyCountMismatch_fails() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + // NOTE: the schema requires all 3 properties, but it should be at most 2 + .parameter(.query(name: "test", schema: .object(minProperties: 1, maxProperties: 2, properties: ["0": .integer, "1": .integer, "2": .integer]))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaObjectPropertyCountIsValid) + XCTAssertThrowsError(try document.validate(using: validator)) { error in + } + } + + func test_requiredPropertyCountMatch_succeeds() throws { + let document = OpenAPI.Document( + info: .init(title: "test", version: "1.0"), + servers: [], + paths: [ + "/hello/world": .init( + get: .init( + parameters: [ + // NOTE: the schema requires "0", "1" & "2" be present, saying `minProperties: 1` is technically not wrong + .parameter(.query(name: "test", schema: .object(minProperties: 1, maxProperties: 5, properties: ["0": .integer, "1": .integer, "2": .integer], additionalProperties: .boolean(true)))) + ], + responses: [:] + ) + ) + ], + components: .noComponents + ) + + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaObjectPropertyCountIsValid) + try document.validate(using: validator) + } } From 0a0cb1dc0cebbb57b7fbdb5439156b282b0e21a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20B=C3=B6hler?= Date: Mon, 14 Sep 2026 08:16:38 +0200 Subject: [PATCH 2/3] pull request feedback --- .../Validator/Validation+Builtins.swift | 60 +++++++------------ .../Validator/BuiltinValidationTests.swift | 16 ++--- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift index f61a0df9d..9afcf51d2 100644 --- a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift +++ b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift @@ -692,56 +692,42 @@ public enum BuiltinValidation { ) } - /// Ensure minimum and maximum are compatible for integers + /// Ensure minimum and maximum are compatible for integers / numbers /// /// - Important: This is not an included validation by default. - public static var jsonSchemaIntegerBoundIsValid: Validation { + public static var jsonSchemaNumericBoundIsValid: Validation { .init( - description: "Integer minimum and maximum are compatible", + description: "Number and Integer minimum and maximum are compatible", check: { context in - guard - case .integer(_, let integerContext) = context.subject.value, - let minimum = integerContext.minimum, - let maximum = integerContext.maximum - else { - return true - } - guard minimum.value <= maximum.value else { - return false - } - - if minimum.value == maximum.value, minimum.exclusive || maximum.exclusive { - return false - } + func isValidBound(minimum: (value: T, exclusive: Bool), maximum: (value: T, exclusive: Bool)) -> Bool { + guard minimum.value <= maximum.value else { + return false + } - return true - } - ) - } + if minimum.value == maximum.value, minimum.exclusive || maximum.exclusive { + return false + } - /// Ensure minimum and maximum are compatible for numbers - /// - /// - Important: This is not an included validation by default. - public static var jsonSchemaNumberBoundIsValid: Validation { - .init( - description: "Number minimum and maximum are compatible", - check: { context in - guard - case .number(_, let numberContext) = context.subject.value, - let minimum = numberContext.minimum, - let maximum = numberContext.maximum - else { return true } - guard minimum.value <= maximum.value else { - return false + if + case let .number(_, numberContext) = context.subject.value, + let minimum = numberContext.minimum, + let maximum = numberContext.maximum + { + return isValidBound(minimum: (minimum.value, minimum.exclusive), maximum: (maximum.value, maximum.exclusive)) } - if minimum.value == maximum.value, minimum.exclusive || maximum.exclusive { - return false + if + case let .integer(_, integerContext) = context.subject.value, + let minimum = integerContext.minimum, + let maximum = integerContext.maximum + { + return isValidBound(minimum: (minimum.value, minimum.exclusive), maximum: (maximum.value, maximum.exclusive)) } + // NOTE: base case return true } ) diff --git a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift index 36b71ffee..4f4ed4427 100644 --- a/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift +++ b/Tests/OpenAPIKitTests/Validator/BuiltinValidationTests.swift @@ -1646,7 +1646,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) try document.validate(using: validator) } @@ -1667,7 +1667,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) try document.validate(using: validator) } @@ -1688,7 +1688,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) XCTAssertThrowsError(try document.validate(using: validator)) { error in } } @@ -1710,7 +1710,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaIntegerBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) XCTAssertThrowsError(try document.validate(using: validator)) { error in } } @@ -1741,7 +1741,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) try document.validate(using: validator) } @@ -1762,7 +1762,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) try document.validate(using: validator) } @@ -1783,7 +1783,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) XCTAssertThrowsError(try document.validate(using: validator)) { error in } } @@ -1805,7 +1805,7 @@ final class BuiltinValidationTests: XCTestCase { components: .noComponents ) - let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumberBoundIsValid) + let validator = Validator.blank.validating(BuiltinValidation.jsonSchemaNumericBoundIsValid) XCTAssertThrowsError(try document.validate(using: validator)) { error in } } From 649ee4473552b7cd106cc4b38a5bf593f38bb27f Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 14 Sep 2026 12:35:43 +0200 Subject: [PATCH 3/3] Update Sources/OpenAPIKit/Validator/Validation+Builtins.swift Co-authored-by: Mathew Polzin --- Sources/OpenAPIKit/Validator/Validation+Builtins.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift index 9afcf51d2..dacd18c47 100644 --- a/Sources/OpenAPIKit/Validator/Validation+Builtins.swift +++ b/Sources/OpenAPIKit/Validator/Validation+Builtins.swift @@ -755,7 +755,7 @@ public enum BuiltinValidation { return false } if let prefixItems = arrayContext.prefixItems?.count { - // defining more a bigger prefix than maxItems is weird + // defining a bigger prefix than maxItems is weird guard prefixItems <= maxItems else { return false }