Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
da4e248
bump swift requirement to 6.0
mattpolzin Jan 27, 2026
dfcc859
Merge branch 'main' into release/6_0
mattpolzin Feb 4, 2026
26fe603
get rid of compiler and language directives that are no longer needed
mattpolzin Feb 10, 2026
c72fad4
add test coverage for the Either map helper. fix warning about XCTest…
mattpolzin Feb 10, 2026
b04716e
fix tests running against linux by just grabbing spec files to test a…
mattpolzin Feb 10, 2026
6ebf8ee
require yams 6.0 or greater and remove preconcurrency imports
mattpolzin Feb 10, 2026
d1ccf6e
update pinned dependency versions
mattpolzin Feb 10, 2026
a863d6d
Merge branch 'main' into release/6_0
mattpolzin Mar 9, 2026
a6a734b
Merge branch 'main' into release/6_0
mattpolzin Mar 9, 2026
5b40214
Merge branch 'main' into release/6_0
mattpolzin Mar 10, 2026
b29dc56
Merge branch 'main' into release/6_0
mattpolzin Mar 16, 2026
c464582
Merge branch 'main' into release/6_0
mattpolzin Mar 17, 2026
cb382c8
bump minimum swift version to 6.1. Add an as-yet-unimplemented trait …
mattpolzin Apr 23, 2026
d4b16cd
implement ExternalLoading trait
mattpolzin Apr 27, 2026
0269a00
build and test without default traits in CI
mattpolzin Apr 27, 2026
cb924b5
update Swift versions in CI test matrix
mattpolzin Apr 27, 2026
daa9780
replace unannotated warning/error with an annotated warning
mattpolzin Apr 27, 2026
08ceec3
update test matrix to remove old macos version with outdated swift
mattpolzin Apr 27, 2026
5c2a705
update tests around new trait
mattpolzin Apr 28, 2026
f21ab8c
Merge branch 'main' into release/6_0
mattpolzin Apr 28, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on: [pull_request]
jobs:
codecov:
container:
image: swift:5.10
image: swift:6.2
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
build:
runs-on: ubuntu-latest
container:
image: swift:5.10
image: swift:6.2

steps:
- uses: actions/checkout@v5
Expand Down
10 changes: 3 additions & 7 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,15 @@ jobs:
fail-fast: false
matrix:
image:
- swift:5.10-focal
- swift:5.10-jammy
- swift:6.0-focal
- swift:6.0-jammy
- swift:6.0-noble
- swift:6.1-focal
- swift:6.1-jammy
- swift:6.1-noble
- swift:6.2-jammy
- swift:6.2-noble
- swift:6.3-jammy
- swift:6.3-noble
- swiftlang/swift:nightly-focal
# - swiftlang/swift:nightly-jammy
- swiftlang/swift:nightly-jammy
container: ${{ matrix.image }}
steps:
- name: Checkout code
Expand All @@ -36,7 +33,6 @@ jobs:
fail-fast: false
matrix:
os:
- macos-14
- macos-15
runs-on: ${{ matrix.os }}
steps:
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/traits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Traits

on: [pull_request]

jobs:
without-default-traits:
container:
image: swift:6.3
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- run: swift test --enable-test-discovery --disable-default-traits
11 changes: 6 additions & 5 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.10
// swift-tools-version: 6.1

import PackageDescription

Expand All @@ -19,9 +19,16 @@ let package = Package(
name: "OpenAPIKitCompat",
targets: ["OpenAPIKitCompat"]),
],
traits: [
.init(
name: "ExternalLoading",
description: "A framework that allows using OpenAPIKit for loading external JSON references into the Components Object. Building OpenAPKit with this capability is notably slower given its use of Swift Concurrency."
),
.default(enabledTraits: ["ExternalLoading"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
.package(url: "https://github.com/jpsim/Yams.git", "5.1.0"..<"7.0.0") // just for tests
.package(url: "https://github.com/jpsim/Yams.git", "6.0.0"..<"7.0.0") // just for tests
],
targets: [
.target(
Expand Down Expand Up @@ -74,5 +81,5 @@ let package = Package(
name: "OpenAPIKitCompatTests",
dependencies: ["OpenAPIKitCompat"])
],
swiftLanguageVersions: [ .v5 ]
swiftLanguageModes: [ .v5, .v6 ]
)
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![sswg:sandbox|94x20](https://img.shields.io/badge/sswg-sandbox-lightgrey.svg)](https://github.com/swift-server/sswg/blob/master/process/incubation.md#sandbox-level) [![Swift 5.10+](http://img.shields.io/badge/Swift-5.10+-blue.svg)](https://swift.org)
[![sswg:sandbox|94x20](https://img.shields.io/badge/sswg-sandbox-lightgrey.svg)](https://github.com/swift-server/sswg/blob/master/process/incubation.md#sandbox-level) [![Swift 6.1+](http://img.shields.io/badge/Swift-6.1+-blue.svg)](https://swift.org)

[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) ![Tests](https://github.com/mattpolzin/OpenAPIKit/actions/workflows/tests.yml/badge.svg?branch=main)

Expand All @@ -16,9 +16,9 @@ versions and key features are supported by which OpenAPIKit versions.

| OpenAPIKit | Swift | OpenAPI v3.0, v3.1 | External Dereferencing & Sendable | OpenAPI v3.2 |
|------------|-------|--------------------|-----------------------------------|--------------|
| v3.x | 5.1+ | ✅ | | |
| v4.x | 5.8+ | ✅ | ✅ | |
| v5.x | 5.10+ | ✅ | ✅ | ✅ |
| v6.x | 6.1+ | ✅ | ✅ | ✅ |

- [Usage](#usage)
- [Migration](#migration)
Expand Down Expand Up @@ -215,6 +215,25 @@ You could also call `oldDoc?.convert(to: .v3_1_4)` if you specifically wanted to
work against a 3.1.x document for whatever reason (possibly because you wanted
to reserialize the document at a 3.1.x version).

### Traits
The OpenAPIKit package offers an `"ExternalLoading"` trait that is enabled by
default. External Loading (i.e. loading multiple OpenAPI files in order to
resolve references between them) uses Swift Concurrency to parallelize loading
of each file. Swift Concurrency adds enough overhead to the compilation time of
OpenAPIKit that it might be worth disabling this trait if you do not need the
External Loading capability. Note that it is _compile time_ that takes a hit
from enabling External Loading, not _runtime speed_.

To disable External Loading features, depend on OpenAPIKit with an empty
`traits` argument:
```swift
.package(
url: "https://github/com/mattpolzin/openapikit.git",
from: "6.0.0",
traits: []
)
```

### A note on dictionary ordering
The **Foundation** library's `JSONEncoder` and `JSONDecoder` do not make any
guarantees about the ordering of keyed containers. This means decoding a JSON
Expand Down
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Components Object/Components.swift
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ extension OpenAPI.Components {
}

extension OpenAPI.Components {
#if ExternalLoading
internal mutating func externallyDereference<Loader: ExternalLoader>(with loader: Loader.Type, depth: ExternalDereferenceDepth = .iterations(1), context: [Loader.Message] = []) async throws -> [Loader.Message] {
if case let .iterations(number) = depth,
number <= 0 {
Expand Down Expand Up @@ -542,6 +543,7 @@ extension OpenAPI.Components {
return try await externallyDereference(with: loader, depth: .full, context: newMessages)
}
}
#endif
}

extension OpenAPI.Components: Validatable {}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Content/DereferencedContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ extension OpenAPI.Content: LocallyDereferenceable {
}

extension OpenAPI.Content: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
let oldSchema = schema
let oldItemSchema = itemSchema
Expand Down Expand Up @@ -140,4 +141,5 @@ extension OpenAPI.Content: ExternallyDereferenceable {

return (newContent, newComponents, newMessages)
}
#endif
}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Content/DereferencedContentEncoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ extension OpenAPI.Content.Encoding: LocallyDereferenceable {
}

extension OpenAPI.Content.Encoding: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
let newHeaders: OpenAPI.Header.Map?
let newComponents: OpenAPI.Components
Expand All @@ -81,4 +82,5 @@ extension OpenAPI.Content.Encoding: ExternallyDereferenceable {

return (newEncoding, newComponents, newMessages)
}
#endif
}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ extension OpenAPI.Document {
return try DereferencedDocument(self)
}

#if ExternalLoading
/// Load all remote references into the document. A remote reference is one
/// that points to another file rather than a location within the
/// same file.
Expand Down Expand Up @@ -437,6 +438,7 @@ extension OpenAPI.Document {

return try await context + m1 + m2 + m3
}
#endif
}

extension OpenAPI {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import OpenAPIKitCore
// MARK: - ExternallyDereferenceable
extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable, B: ExternallyDereferenceable {

#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
switch self {
case .a(let a):
Expand All @@ -20,4 +21,5 @@ extension Either: ExternallyDereferenceable where A: ExternallyDereferenceable,
return (.b(newB), components, messages)
}
}
#endif
}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Example/Example.swift
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,11 @@ extension OpenAPI.Example: LocallyDereferenceable {
}

extension OpenAPI.Example: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
return (self, .init(), [])
}
#endif
}

extension OpenAPI.Example: Validatable {}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/ExternalLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ public protocol ExternalLoader: _ExternalLoaderMetatype where Message: Sendable
}

public protocol ExternallyDereferenceable {
#if ExternalLoading
func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message])
#endif
}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Header/DereferencedHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ extension OpenAPI.Header: LocallyDereferenceable {
}

extension OpenAPI.Header: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {

// if not for a Swift bug, this whole next bit would just be the
Expand Down Expand Up @@ -117,4 +118,5 @@ extension OpenAPI.Header: ExternallyDereferenceable {

return (newHeader, newComponents, newMessages)
}
#endif
}
4 changes: 4 additions & 0 deletions Sources/OpenAPIKit/JSONReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ extension JSONReference: LocallyDereferenceable where ReferenceType: LocallyDere
}

extension JSONReference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
switch self {
case .internal(let ref):
Expand All @@ -619,6 +620,7 @@ extension JSONReference: ExternallyDereferenceable where ReferenceType: External
return (try components.reference(named: componentKey.rawValue, ofType: ReferenceType.self).jsonReference, components, messages)
}
}
#endif
}

extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: LocallyDereferenceable {
Expand Down Expand Up @@ -649,10 +651,12 @@ extension OpenAPI.Reference: LocallyDereferenceable where ReferenceType: Locally
}

extension OpenAPI.Reference: ExternallyDereferenceable where ReferenceType: ExternallyDereferenceable & Decodable & Equatable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
let (newRef, components, messages) = try await jsonReference.externallyDereferenced(with: loader)
return (.init(newRef), components, messages)
}
#endif
}

extension OpenAPI.Reference: Validatable where ReferenceType: Validatable {}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Link.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ extension OpenAPI.Link: LocallyDereferenceable {
}

extension OpenAPI.Link: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
let (newServer, newComponents, newMessages) = try await server.externallyDereferenced(with: loader)

Expand All @@ -302,6 +303,7 @@ extension OpenAPI.Link: ExternallyDereferenceable {

return (newLink, newComponents, newMessages)
}
#endif
}

extension OpenAPI.Link: Validatable {}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Operation/DereferencedOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ extension OpenAPI.Operation: LocallyDereferenceable {
}

extension OpenAPI.Operation: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
let oldParameters = parameters
let oldRequestBody = requestBody
Expand Down Expand Up @@ -162,4 +163,5 @@ extension OpenAPI.Operation: ExternallyDereferenceable {

return (newOperation, newComponents, newMessages)
}
#endif
}
4 changes: 4 additions & 0 deletions Sources/OpenAPIKit/Parameter/DereferencedParameter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ extension OpenAPI.Parameter: LocallyDereferenceable {
}

extension OpenAPI.Parameter: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {

// if not for a Swift bug, this whole function would just be the
Expand Down Expand Up @@ -131,8 +132,10 @@ extension OpenAPI.Parameter: ExternallyDereferenceable {

return (newParameter, newComponents, newMessages)
}
#endif
}

#if ExternalLoading
fileprivate func externallyDereference<Loader: ExternalLoader>(
schemaOrContent: Either<OpenAPI.Parameter.SchemaContext, OpenAPI.Content.Map>,
with loader: Loader.Type
Expand All @@ -146,3 +149,4 @@ fileprivate func externallyDereference<Loader: ExternalLoader>(
return (.b(map), components, messages)
}
}
#endif
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Parameter/DereferencedSchemaContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ extension OpenAPI.Parameter.SchemaContext: LocallyDereferenceable {
}

extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
let oldSchema = schema

Expand All @@ -91,4 +92,5 @@ extension OpenAPI.Parameter.SchemaContext: ExternallyDereferenceable {

return (newSchemaContext, newComponents, newMessages)
}
#endif
}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Path Item/DereferencedPathItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ extension OpenAPI.PathItem: LocallyDereferenceable {
}

extension OpenAPI.PathItem: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
let oldParameters = parameters
let oldServers = servers
Expand Down Expand Up @@ -231,4 +232,5 @@ extension OpenAPI.PathItem: ExternallyDereferenceable {

return (pathItem, newComponents, newMessages)
}
#endif
}
2 changes: 2 additions & 0 deletions Sources/OpenAPIKit/Request/DereferencedRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ extension OpenAPI.Request: LocallyDereferenceable {
}

extension OpenAPI.Request: ExternallyDereferenceable {
#if ExternalLoading
public func externallyDereferenced<Loader: ExternalLoader>(with loader: Loader.Type) async throws -> (Self, OpenAPI.Components, [Loader.Message]) {
var newRequest = self

Expand All @@ -71,4 +72,5 @@ extension OpenAPI.Request: ExternallyDereferenceable {
newRequest.content = newContent
return (newRequest, components, messages)
}
#endif
}
Loading