Skip to content

Commit 188a5bd

Browse files
authored
Implement AuthenticationMiddleware (#171)
1 parent 9484314 commit 188a5bd

29 files changed

Lines changed: 1428 additions & 238 deletions

Package.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,5 +204,10 @@ let package = Package(
204204
dependencies: ["SyntaxKit", "DocumentationHarness"],
205205
swiftSettings: swiftSettings
206206
),
207+
.testTarget(
208+
name: "AiSTKitTests",
209+
dependencies: ["AiSTKit"],
210+
swiftSettings: swiftSettings
211+
),
207212
]
208213
)
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//
2+
// AuthenticationMiddleware.swift
3+
// SyntaxKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2026 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
public import HTTPTypes
31+
public import OpenAPIRuntime
32+
33+
#if os(Linux)
34+
@preconcurrency public import struct Foundation.URL
35+
#else
36+
public import struct Foundation.URL
37+
#endif
38+
39+
/// OpenAPI client middleware that adds Anthropic API authentication headers.
40+
///
41+
/// Injects the `x-api-key` and `anthropic-version` headers into every
42+
/// outgoing request before forwarding it to the next handler in the chain.
43+
public struct AuthenticationMiddleware: ClientMiddleware {
44+
/// The Anthropic API version sent with every request.
45+
private static let anthropicVersion = "2023-06-01"
46+
47+
/// The `x-api-key` header name, constructed once. `"x-api-key"` is a valid
48+
/// RFC 9110 token, so this is always non-`nil`.
49+
private static let apiKeyFieldName = HTTPField.Name("x-api-key")
50+
51+
/// The `anthropic-version` header name, constructed once. `"anthropic-version"`
52+
/// is a valid RFC 9110 token, so this is always non-`nil`.
53+
private static let anthropicVersionFieldName = HTTPField.Name("anthropic-version")
54+
55+
/// The Anthropic API key used to authenticate requests.
56+
private let apiKey: String
57+
58+
/// Creates a middleware that authenticates requests with the given API key.
59+
/// - Parameter apiKey: The Anthropic API key to send in the `x-api-key` header.
60+
public init(apiKey: String) {
61+
self.apiKey = apiKey
62+
}
63+
64+
/// Adds the Anthropic authentication headers, then forwards the request.
65+
///
66+
/// Any `x-api-key` or `anthropic-version` header already present on the
67+
/// request is silently overwritten with this middleware's values.
68+
/// - Parameters:
69+
/// - request: The outgoing HTTP request.
70+
/// - body: The outgoing HTTP request body, if any.
71+
/// - baseURL: The base URL of the server.
72+
/// - operationID: The OpenAPI operation identifier for the request.
73+
/// - next: The next handler in the middleware chain.
74+
/// - Returns: The HTTP response and body from the next handler.
75+
/// - Throws: Any error thrown by the `next` handler.
76+
public func intercept(
77+
_ request: HTTPRequest,
78+
body: HTTPBody?,
79+
baseURL: URL,
80+
operationID: String,
81+
next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
82+
) async throws -> (HTTPResponse, HTTPBody?) {
83+
var request = request
84+
if let apiKeyFieldName = Self.apiKeyFieldName {
85+
request.headerFields[apiKeyFieldName] = apiKey
86+
} else {
87+
assertionFailure("\"x-api-key\" is a valid header name and must never be nil.")
88+
}
89+
if let anthropicVersionFieldName = Self.anthropicVersionFieldName {
90+
request.headerFields[anthropicVersionFieldName] = Self.anthropicVersion
91+
} else {
92+
assertionFailure("\"anthropic-version\" is a valid header name and must never be nil.")
93+
}
94+
return try await next(request, body, baseURL)
95+
}
96+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Architecture.swift
3+
// SyntaxKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2026 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
/// CPU-architecture identifiers used in `arch(...)` checks.
31+
public enum Architecture: String, Sendable {
32+
/// `arch(arm64)`
33+
case arm64
34+
/// `arch(x86_64)`
35+
case x86 = "x86_64"
36+
/// `arch(i386)`
37+
case i386
38+
/// `arch(arm)`
39+
case arm
40+
/// `arch(wasm32)`
41+
case wasm32
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// Comparison.swift
3+
// SyntaxKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2026 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
/// The comparison operator used between a keyword and a version in a
31+
/// `swift(...)` / `compiler(...)` version check.
32+
public enum Comparison: String, Sendable {
33+
/// `>=`
34+
case greaterThanOrEqual = ">="
35+
/// `>`
36+
case greaterThan = ">"
37+
/// `<=`
38+
case lessThanOrEqual = "<="
39+
/// `<`
40+
case lessThan = "<"
41+
/// `==`
42+
case equal = "=="
43+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// OperatingSystem.swift
3+
// SyntaxKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2026 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
/// Operating-system identifiers used in `os(...)` checks.
31+
public enum OperatingSystem: String, Sendable {
32+
/// `os(iOS)`
33+
case iOS
34+
/// `os(macOS)`
35+
case macOS
36+
/// `os(tvOS)`
37+
case tvOS
38+
/// `os(watchOS)`
39+
case watchOS
40+
/// `os(visionOS)`
41+
case visionOS
42+
/// `os(anyAppleOS)` — matches any Apple operating system (Swift 6.4+).
43+
case anyAppleOS
44+
/// `os(Linux)`
45+
case linux = "Linux"
46+
/// `os(Windows)`
47+
case windows = "Windows"
48+
/// `os(FreeBSD)`
49+
case freeBSD = "FreeBSD"
50+
/// `os(Android)`
51+
case android = "Android"
52+
/// `os(WASI)`
53+
case wasi = "WASI"
54+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// TargetEnvironment.swift
3+
// SyntaxKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2026 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
/// Target-environment identifiers used in `targetEnvironment(...)` checks.
31+
public enum TargetEnvironment: String, Sendable {
32+
/// `targetEnvironment(simulator)`
33+
case simulator
34+
/// `targetEnvironment(macCatalyst)`
35+
case macCatalyst
36+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// Version.swift
3+
// SyntaxKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2026 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
/// A `major.minor.patch` version, with optional minor and patch components.
31+
public struct Version: Sendable, CustomStringConvertible {
32+
/// The major version component.
33+
public let major: Int
34+
/// The optional minor version component.
35+
public let minor: Int?
36+
/// The optional patch version component.
37+
public let patch: Int?
38+
39+
/// The dotted string form, e.g. `5`, `5.9`, or `5.9.1`.
40+
public var description: String {
41+
var result = String(major)
42+
if let minor = minor {
43+
result += ".\(minor)"
44+
if let patch = patch {
45+
result += ".\(patch)"
46+
}
47+
}
48+
return result
49+
}
50+
51+
/// The dotted string form, e.g. `5`, `5.9`, or `5.9.1`.
52+
internal var versionString: String { description }
53+
54+
/// Create a version from its components.
55+
/// - Parameters:
56+
/// - major: The major version component.
57+
/// - minor: The optional minor version component.
58+
/// - patch: The optional patch version component.
59+
public init(_ major: Int, _ minor: Int? = nil, _ patch: Int? = nil) {
60+
self.major = major
61+
self.minor = minor
62+
self.patch = patch
63+
}
64+
}

0 commit comments

Comments
 (0)