feat(protocol): define native session credentials
Verify Protocol / module (push) Successful in 1m13s
Verify Protocol / verify (push) Successful in 35s

This commit is contained in:
sechmachine
2026-08-11 18:38:19 +07:00
parent 79d9e49497
commit afcd5d99db
32 changed files with 1565 additions and 149 deletions
+227 -23
View File
@@ -1,10 +1,10 @@
// Code generated by tools/generate.py; DO NOT EDIT.
import Foundation
public typealias JSONObject = [String: String]
public let schemaSHA256 = "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c"
public let currentWireVersion = "1"
public let nMinus1WireVersion = "0"
public let nMinus2WireVersion = "-1"
public let schemaSHA256 = "dea3dd210c53d5a2d37050dd6afd8b0ac5bb8edcb7ab25a02e4026489ce8a00f"
public let currentWireVersion = "2"
public let nMinus1WireVersion = "1"
public let nMinus2WireVersion = "0"
public struct ContractValidationError: Error, Equatable { public let field: String; public let code: String }
private struct AnyCodingKey: CodingKey { let stringValue: String; let intValue: Int?; init?(stringValue: String) { self.stringValue = stringValue; self.intValue = nil }; init?(intValue: Int) { self.stringValue = String(intValue); self.intValue = intValue } }
private func validBase64URL(_ value: String) -> Bool {
@@ -16,6 +16,23 @@ private func validBase64URL(_ value: String) -> Bool {
guard let decoded = Data(base64Encoded: standard) else { return false }
return decoded.base64EncodedString().replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "=", with: "") == value
}
private func validRFC3339UTC(_ value: String) -> Bool {
let bytes = Array(value.utf8)
guard (20...30).contains(bytes.count), bytes[4] == 45, bytes[7] == 45, bytes[10] == 84, bytes[13] == 58, bytes[16] == 58, bytes.last == 90 else { return false }
func digits(_ range: Range<Int>) -> Int? {
var result = 0
for index in range { guard bytes[index] >= 48 && bytes[index] <= 57 else { return nil }; result = result * 10 + Int(bytes[index] - 48) }
return result
}
guard let year = digits(0..<4), let month = digits(5..<7), let day = digits(8..<10), let hour = digits(11..<13), let minute = digits(14..<16), let second = digits(17..<19), hour <= 23, minute <= 59, second <= 59 else { return false }
let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
let days: Int
switch month { case 1, 3, 5, 7, 8, 10, 12: days = 31; case 4, 6, 9, 11: days = 30; case 2: days = leap ? 29 : 28; default: return false }
guard day > 0 && day <= days else { return false }
if bytes.count == 20 { return true }
let fraction = bytes[20..<(bytes.count - 1)]
return bytes[19] == 46 && !fraction.isEmpty && fraction.count <= 9 && fraction.allSatisfy { $0 >= 48 && $0 <= 57 } && fraction.last != 48
}
public struct AllocationPolicy: Codable, Equatable {
public let minimumKbps: Int64
@@ -217,7 +234,7 @@ public struct BrokerSession: Codable, Equatable {
try self.policySnapshot.validate()
if let value = self.reconnectDeadline {
if value.utf8.count > 64 { throw ContractValidationError(field: "reconnect_deadline", code: "max_length") }
if ISO8601DateFormatter().date(from: value) == nil { throw ContractValidationError(field: "reconnect_deadline", code: "invalid_time") }
if !validRFC3339UTC(value) { throw ContractValidationError(field: "reconnect_deadline", code: "invalid_time") }
}
if let value = self.outcome {
if value.utf8.count > 64 { throw ContractValidationError(field: "outcome", code: "max_length") }
@@ -235,10 +252,10 @@ public struct BrokerSession: Codable, Equatable {
if !self.correlationId.isEmpty && self.correlationId.utf8.count < 1 { throw ContractValidationError(field: "correlation_id", code: "min_length") }
if self.correlationId.utf8.count > 128 { throw ContractValidationError(field: "correlation_id", code: "max_length") }
if self.requestedAt.utf8.count > 64 { throw ContractValidationError(field: "requested_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.requestedAt) == nil { throw ContractValidationError(field: "requested_at", code: "invalid_time") }
if !validRFC3339UTC(self.requestedAt) { throw ContractValidationError(field: "requested_at", code: "invalid_time") }
if let value = self.endedAt {
if value.utf8.count > 64 { throw ContractValidationError(field: "ended_at", code: "max_length") }
if ISO8601DateFormatter().date(from: value) == nil { throw ContractValidationError(field: "ended_at", code: "invalid_time") }
if !validRFC3339UTC(value) { throw ContractValidationError(field: "ended_at", code: "invalid_time") }
}
if self.version < 1 { throw ContractValidationError(field: "version", code: "minimum") }
if let value = self.requestedDisplayMode {
@@ -253,6 +270,51 @@ public struct BrokerSession: Codable, Equatable {
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public struct BrowserAuthenticatedSession: Codable, Equatable {
public let username: String
public let provider: String
public let roles: [String]
public let role: String
enum CodingKeys: String, CodingKey {
case username = "username"
case provider = "provider"
case roles = "roles"
case role = "role"
}
public init(username: String, provider: String, roles: [String], role: String) throws {
self.username = username
self.provider = provider
self.roles = roles
self.role = role
try validate()
}
public init(from decoder: Decoder) throws {
let all = try decoder.container(keyedBy: AnyCodingKey.self)
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
let c = try decoder.container(keyedBy: CodingKeys.self)
try self.init(username: try c.decode(String.self, forKey: .username), provider: try c.decode(String.self, forKey: .provider), roles: try c.decode([String].self, forKey: .roles), role: try c.decode(String.self, forKey: .role))
}
public func validate() throws {
if self.username.isEmpty { throw ContractValidationError(field: "username", code: "required") }
if !self.username.isEmpty && self.username.utf8.count < 1 { throw ContractValidationError(field: "username", code: "min_length") }
if self.username.utf8.count > 256 { throw ContractValidationError(field: "username", code: "max_length") }
if self.provider.isEmpty { throw ContractValidationError(field: "provider", code: "required") }
if !self.provider.isEmpty && self.provider.utf8.count < 1 { throw ContractValidationError(field: "provider", code: "min_length") }
if self.provider.utf8.count > 64 { throw ContractValidationError(field: "provider", code: "max_length") }
if self.roles.count > 16 { throw ContractValidationError(field: "roles", code: "max_items") }
for item in self.roles where item.utf8.count < 1 { throw ContractValidationError(field: "roles", code: "min_item_length") }
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_length") }
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_bytes") }
if !["user", "admin"].contains(self.role) { throw ContractValidationError(field: "role", code: "invalid_value") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public struct CapabilityProfile: Codable, Equatable {
public let transport: String
public let framing: String
@@ -551,7 +613,7 @@ public struct DeviceChallenge: Codable, Equatable {
if !self.challenge.isEmpty && self.challenge.utf8.count < 1 { throw ContractValidationError(field: "challenge", code: "min_length") }
if self.challenge.utf8.count > 256 { throw ContractValidationError(field: "challenge", code: "max_length") }
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if self.algorithm != "ed25519" { throw ContractValidationError(field: "algorithm", code: "invalid_value") }
if self.signatureFormat != "ed25519-domain-separated-v1" { throw ContractValidationError(field: "signature_format", code: "invalid_value") }
}
@@ -831,7 +893,7 @@ public struct EventEnvelope: Codable, Equatable {
if self.version < 1 { throw ContractValidationError(field: "version", code: "minimum") }
try self.resource.validate()
if self.occurredAt.utf8.count > 64 { throw ContractValidationError(field: "occurred_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.occurredAt) == nil { throw ContractValidationError(field: "occurred_at", code: "invalid_time") }
if !validRFC3339UTC(self.occurredAt) { throw ContractValidationError(field: "occurred_at", code: "invalid_time") }
if self.correlationId.isEmpty { throw ContractValidationError(field: "correlation_id", code: "required") }
if !self.correlationId.isEmpty && self.correlationId.utf8.count < 1 { throw ContractValidationError(field: "correlation_id", code: "min_length") }
if self.correlationId.utf8.count > 128 { throw ContractValidationError(field: "correlation_id", code: "max_length") }
@@ -1036,7 +1098,7 @@ public struct GatewayDrain: Codable, Equatable {
if !self.reason.isEmpty && self.reason.utf8.count < 1 { throw ContractValidationError(field: "reason", code: "min_length") }
if self.reason.utf8.count > 256 { throw ContractValidationError(field: "reason", code: "max_length") }
if self.deadline.utf8.count > 64 { throw ContractValidationError(field: "deadline", code: "max_length") }
if ISO8601DateFormatter().date(from: self.deadline) == nil { throw ContractValidationError(field: "deadline", code: "invalid_time") }
if !validRFC3339UTC(self.deadline) { throw ContractValidationError(field: "deadline", code: "invalid_time") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
@@ -1089,7 +1151,7 @@ public struct GatewayHeartbeat: Codable, Equatable {
if self.gatewayId.utf8.count > 128 { throw ContractValidationError(field: "gateway_id", code: "max_length") }
if self.sequence < 1 { throw ContractValidationError(field: "sequence", code: "minimum") }
if self.observedAt.utf8.count > 64 { throw ContractValidationError(field: "observed_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.observedAt) == nil { throw ContractValidationError(field: "observed_at", code: "invalid_time") }
if !validRFC3339UTC(self.observedAt) { throw ContractValidationError(field: "observed_at", code: "invalid_time") }
if self.activeConnections < 0 { throw ContractValidationError(field: "active_connections", code: "minimum") }
if self.activeConnections > 1000000 { throw ContractValidationError(field: "active_connections", code: "maximum") }
if self.egressKbps < 0 { throw ContractValidationError(field: "egress_kbps", code: "minimum") }
@@ -1185,6 +1247,8 @@ public struct GatewayRegistration: Codable, Equatable {
if self.bandwidthCapacityKbps < 1 { throw ContractValidationError(field: "bandwidth_capacity_kbps", code: "minimum") }
if self.bandwidthCapacityKbps > 1000000000 { throw ContractValidationError(field: "bandwidth_capacity_kbps", code: "maximum") }
if self.features.count > 64 { throw ContractValidationError(field: "features", code: "max_items") }
for item in self.features where item.utf8.count < 1 { throw ContractValidationError(field: "features", code: "min_item_length") }
for item in self.features where item.utf8.count > 64 { throw ContractValidationError(field: "features", code: "max_item_length") }
try self.capabilities.validate()
if protocolMinVersion > protocolMaxVersion { throw ContractValidationError(field: "protocol_version", code: "invalid_order") }
}
@@ -1333,7 +1397,7 @@ public struct GrantReference: Codable, Equatable {
if !self.opaqueValue.isEmpty && self.opaqueValue.utf8.count < 43 { throw ContractValidationError(field: "opaque_value", code: "min_length") }
if self.opaqueValue.utf8.count > 256 { throw ContractValidationError(field: "opaque_value", code: "max_length") }
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if self.audience.isEmpty { throw ContractValidationError(field: "audience", code: "required") }
if !self.audience.isEmpty && self.audience.utf8.count < 1 { throw ContractValidationError(field: "audience", code: "min_length") }
if self.audience.utf8.count > 128 { throw ContractValidationError(field: "audience", code: "max_length") }
@@ -1451,6 +1515,8 @@ public struct ManifestGateway: Codable, Equatable {
if self.id.utf8.count > 128 { throw ContractValidationError(field: "id", code: "max_length") }
if self.addresses.count < 1 { throw ContractValidationError(field: "addresses", code: "min_items") }
if self.addresses.count > 4 { throw ContractValidationError(field: "addresses", code: "max_items") }
for item in self.addresses where item.utf8.count < 1 { throw ContractValidationError(field: "addresses", code: "min_item_length") }
for item in self.addresses where item.utf8.count > 256 { throw ContractValidationError(field: "addresses", code: "max_item_length") }
if self.publicIdentity.isEmpty { throw ContractValidationError(field: "public_identity", code: "required") }
if !self.publicIdentity.isEmpty && self.publicIdentity.utf8.count < 1 { throw ContractValidationError(field: "public_identity", code: "min_length") }
if self.publicIdentity.utf8.count > 256 { throw ContractValidationError(field: "public_identity", code: "max_length") }
@@ -1522,7 +1588,60 @@ public struct ManifestTunnel: Codable, Equatable {
public func validate() throws {
if self.versions.count < 1 { throw ContractValidationError(field: "versions", code: "min_items") }
if self.versions.count > 4 { throw ContractValidationError(field: "versions", code: "max_items") }
for item in self.versions where item.utf8.count < 1 { throw ContractValidationError(field: "versions", code: "min_item_length") }
for item in self.versions where item.utf8.count > 64 { throw ContractValidationError(field: "versions", code: "max_item_length") }
if self.features.count > 32 { throw ContractValidationError(field: "features", code: "max_items") }
for item in self.features where item.utf8.count < 1 { throw ContractValidationError(field: "features", code: "min_item_length") }
for item in self.features where item.utf8.count > 64 { throw ContractValidationError(field: "features", code: "max_item_length") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public struct NativeAuthenticatedSession: Codable, Equatable {
public let username: String
public let provider: String
public let roles: [String]
public let role: String
public let nativeIdentity: NativeSessionIdentity
enum CodingKeys: String, CodingKey {
case username = "username"
case provider = "provider"
case roles = "roles"
case role = "role"
case nativeIdentity = "native_identity"
}
public init(username: String, provider: String, roles: [String], role: String, nativeIdentity: NativeSessionIdentity) throws {
self.username = username
self.provider = provider
self.roles = roles
self.role = role
self.nativeIdentity = nativeIdentity
try validate()
}
public init(from decoder: Decoder) throws {
let all = try decoder.container(keyedBy: AnyCodingKey.self)
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
let c = try decoder.container(keyedBy: CodingKeys.self)
try self.init(username: try c.decode(String.self, forKey: .username), provider: try c.decode(String.self, forKey: .provider), roles: try c.decode([String].self, forKey: .roles), role: try c.decode(String.self, forKey: .role), nativeIdentity: try c.decode(NativeSessionIdentity.self, forKey: .nativeIdentity))
}
public func validate() throws {
if self.username.isEmpty { throw ContractValidationError(field: "username", code: "required") }
if !self.username.isEmpty && self.username.utf8.count < 1 { throw ContractValidationError(field: "username", code: "min_length") }
if self.username.utf8.count > 256 { throw ContractValidationError(field: "username", code: "max_length") }
if self.provider.isEmpty { throw ContractValidationError(field: "provider", code: "required") }
if !self.provider.isEmpty && self.provider.utf8.count < 1 { throw ContractValidationError(field: "provider", code: "min_length") }
if self.provider.utf8.count > 64 { throw ContractValidationError(field: "provider", code: "max_length") }
if self.roles.count > 16 { throw ContractValidationError(field: "roles", code: "max_items") }
for item in self.roles where item.utf8.count < 1 { throw ContractValidationError(field: "roles", code: "min_item_length") }
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_length") }
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_bytes") }
if !["user", "admin"].contains(self.role) { throw ContractValidationError(field: "role", code: "invalid_value") }
try self.nativeIdentity.validate()
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
@@ -1576,10 +1695,10 @@ public struct NativeCredential: Codable, Equatable {
if !self.refreshToken.isEmpty && self.refreshToken.utf8.count < 1 { throw ContractValidationError(field: "refresh_token", code: "min_length") }
if self.refreshToken.utf8.count > 256 { throw ContractValidationError(field: "refresh_token", code: "max_length") }
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if let value = self.refreshExpiresAt {
if value.utf8.count > 64 { throw ContractValidationError(field: "refresh_expires_at", code: "max_length") }
if ISO8601DateFormatter().date(from: value) == nil { throw ContractValidationError(field: "refresh_expires_at", code: "invalid_time") }
if !validRFC3339UTC(value) { throw ContractValidationError(field: "refresh_expires_at", code: "invalid_time") }
}
}
@@ -1587,6 +1706,91 @@ public struct NativeCredential: Codable, Equatable {
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public struct NativeSessionIdentity: Codable, Equatable {
public let clientDeviceId: String
public let deviceKeyId: String
enum CodingKeys: String, CodingKey {
case clientDeviceId = "client_device_id"
case deviceKeyId = "device_key_id"
}
public init(clientDeviceId: String, deviceKeyId: String) throws {
self.clientDeviceId = clientDeviceId
self.deviceKeyId = deviceKeyId
try validate()
}
public init(from decoder: Decoder) throws {
let all = try decoder.container(keyedBy: AnyCodingKey.self)
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
let c = try decoder.container(keyedBy: CodingKeys.self)
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId))
}
public func validate() throws {
if self.clientDeviceId.isEmpty { throw ContractValidationError(field: "client_device_id", code: "required") }
if !self.clientDeviceId.isEmpty && self.clientDeviceId.utf8.count < 1 { throw ContractValidationError(field: "client_device_id", code: "min_length") }
if self.clientDeviceId.utf8.count > 128 { throw ContractValidationError(field: "client_device_id", code: "max_length") }
if self.deviceKeyId.isEmpty { throw ContractValidationError(field: "device_key_id", code: "required") }
if !self.deviceKeyId.isEmpty && self.deviceKeyId.utf8.count < 1 { throw ContractValidationError(field: "device_key_id", code: "min_length") }
if self.deviceKeyId.utf8.count > 128 { throw ContractValidationError(field: "device_key_id", code: "max_length") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public struct NativeTunnelCredential: Codable, Equatable {
public let clientDeviceId: String
public let deviceKeyId: String
public let certificateChainPem: String
public let trustBundlePem: String
public let expiresAt: String
enum CodingKeys: String, CodingKey {
case clientDeviceId = "client_device_id"
case deviceKeyId = "device_key_id"
case certificateChainPem = "certificate_chain_pem"
case trustBundlePem = "trust_bundle_pem"
case expiresAt = "expires_at"
}
public init(clientDeviceId: String, deviceKeyId: String, certificateChainPem: String, trustBundlePem: String, expiresAt: String) throws {
self.clientDeviceId = clientDeviceId
self.deviceKeyId = deviceKeyId
self.certificateChainPem = certificateChainPem
self.trustBundlePem = trustBundlePem
self.expiresAt = expiresAt
try validate()
}
public init(from decoder: Decoder) throws {
let all = try decoder.container(keyedBy: AnyCodingKey.self)
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
let c = try decoder.container(keyedBy: CodingKeys.self)
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId), certificateChainPem: try c.decode(String.self, forKey: .certificateChainPem), trustBundlePem: try c.decode(String.self, forKey: .trustBundlePem), expiresAt: try c.decode(String.self, forKey: .expiresAt))
}
public func validate() throws {
if self.clientDeviceId.isEmpty { throw ContractValidationError(field: "client_device_id", code: "required") }
if !self.clientDeviceId.isEmpty && self.clientDeviceId.utf8.count < 1 { throw ContractValidationError(field: "client_device_id", code: "min_length") }
if self.clientDeviceId.utf8.count > 128 { throw ContractValidationError(field: "client_device_id", code: "max_length") }
if self.deviceKeyId.isEmpty { throw ContractValidationError(field: "device_key_id", code: "required") }
if !self.deviceKeyId.isEmpty && self.deviceKeyId.utf8.count < 1 { throw ContractValidationError(field: "device_key_id", code: "min_length") }
if self.deviceKeyId.utf8.count > 128 { throw ContractValidationError(field: "device_key_id", code: "max_length") }
if self.certificateChainPem.isEmpty { throw ContractValidationError(field: "certificate_chain_pem", code: "required") }
if !self.certificateChainPem.isEmpty && self.certificateChainPem.utf8.count < 1 { throw ContractValidationError(field: "certificate_chain_pem", code: "min_length") }
if self.certificateChainPem.utf8.count > 65536 { throw ContractValidationError(field: "certificate_chain_pem", code: "max_length") }
if self.trustBundlePem.isEmpty { throw ContractValidationError(field: "trust_bundle_pem", code: "required") }
if !self.trustBundlePem.isEmpty && self.trustBundlePem.utf8.count < 1 { throw ContractValidationError(field: "trust_bundle_pem", code: "min_length") }
if self.trustBundlePem.utf8.count > 65536 { throw ContractValidationError(field: "trust_bundle_pem", code: "max_length") }
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public struct PageInfo: Codable, Equatable {
public let limit: Int64
public let nextCursor: String
@@ -1703,7 +1907,7 @@ public struct ProviderSessionWork: Codable, Equatable {
if self.gatewayId.utf8.count > 128 { throw ContractValidationError(field: "gateway_id", code: "max_length") }
if self.reconnectSequence < 0 { throw ContractValidationError(field: "reconnect_sequence", code: "minimum") }
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if self.providerProfile != "apollo" { throw ContractValidationError(field: "provider_profile", code: "invalid_value") }
if self.providerIdentity.isEmpty { throw ContractValidationError(field: "provider_identity", code: "required") }
if !self.providerIdentity.isEmpty && self.providerIdentity.utf8.count < 1 { throw ContractValidationError(field: "provider_identity", code: "min_length") }
@@ -1781,6 +1985,8 @@ public struct ProviderState: Codable, Equatable {
if self.sessionId.utf8.count > 128 { throw ContractValidationError(field: "session_id", code: "max_length") }
if !["starting", "ready", "disconnected", "terminating", "terminated", "cleanup_pending", "failed"].contains(self.state) { throw ContractValidationError(field: "state", code: "invalid_value") }
if self.channels.count > 8 { throw ContractValidationError(field: "channels", code: "max_items") }
for item in self.channels where item.utf8.count < 1 { throw ContractValidationError(field: "channels", code: "min_item_length") }
for item in self.channels where item.utf8.count > 64 { throw ContractValidationError(field: "channels", code: "max_item_length") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
@@ -1868,7 +2074,7 @@ public struct ReauthGrant: Codable, Equatable {
if !self.purpose.isEmpty && self.purpose.utf8.count < 1 { throw ContractValidationError(field: "purpose", code: "min_length") }
if self.purpose.utf8.count > 64 { throw ContractValidationError(field: "purpose", code: "max_length") }
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
@@ -2169,7 +2375,7 @@ public struct SessionAuthority: Codable, Equatable {
if self.audience.utf8.count > 256 { throw ContractValidationError(field: "audience", code: "max_length") }
if self.reconnectSequence < 0 { throw ContractValidationError(field: "reconnect_sequence", code: "minimum") }
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
try self.capabilities.validate()
if !["apollo"].contains(self.providerProfile) { throw ContractValidationError(field: "provider_profile", code: "invalid_value") }
if self.providerIdentity.isEmpty { throw ContractValidationError(field: "provider_identity", code: "required") }
@@ -2186,23 +2392,20 @@ public struct SessionRequest: Codable, Equatable {
public let deviceKeyId: String
public let poolId: String
public let idempotencyKey: String
public let policySnapshot: AllocationPolicy
public let requestedDisplayMode: DisplayMode?
enum CodingKeys: String, CodingKey {
case clientDeviceId = "client_device_id"
case deviceKeyId = "device_key_id"
case poolId = "pool_id"
case idempotencyKey = "idempotency_key"
case policySnapshot = "policy_snapshot"
case requestedDisplayMode = "requested_display_mode"
}
public init(clientDeviceId: String, deviceKeyId: String, poolId: String, idempotencyKey: String, policySnapshot: AllocationPolicy, requestedDisplayMode: DisplayMode?) throws {
public init(clientDeviceId: String, deviceKeyId: String, poolId: String, idempotencyKey: String, requestedDisplayMode: DisplayMode?) throws {
self.clientDeviceId = clientDeviceId
self.deviceKeyId = deviceKeyId
self.poolId = poolId
self.idempotencyKey = idempotencyKey
self.policySnapshot = policySnapshot
self.requestedDisplayMode = requestedDisplayMode
try validate()
}
@@ -2211,7 +2414,7 @@ public struct SessionRequest: Codable, Equatable {
let all = try decoder.container(keyedBy: AnyCodingKey.self)
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
let c = try decoder.container(keyedBy: CodingKeys.self)
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId), poolId: try c.decode(String.self, forKey: .poolId), idempotencyKey: try c.decode(String.self, forKey: .idempotencyKey), policySnapshot: try c.decode(AllocationPolicy.self, forKey: .policySnapshot), requestedDisplayMode: try c.contains(.requestedDisplayMode) ? c.decode(DisplayMode.self, forKey: .requestedDisplayMode) : nil)
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId), poolId: try c.decode(String.self, forKey: .poolId), idempotencyKey: try c.decode(String.self, forKey: .idempotencyKey), requestedDisplayMode: try c.contains(.requestedDisplayMode) ? c.decode(DisplayMode.self, forKey: .requestedDisplayMode) : nil)
}
public func validate() throws {
@@ -2227,7 +2430,6 @@ public struct SessionRequest: Codable, Equatable {
if self.idempotencyKey.isEmpty { throw ContractValidationError(field: "idempotency_key", code: "required") }
if !self.idempotencyKey.isEmpty && self.idempotencyKey.utf8.count < 1 { throw ContractValidationError(field: "idempotency_key", code: "min_length") }
if self.idempotencyKey.utf8.count > 256 { throw ContractValidationError(field: "idempotency_key", code: "max_length") }
try self.policySnapshot.validate()
if let value = self.requestedDisplayMode {
try value.validate()
}
@@ -2372,7 +2574,9 @@ public struct VersionNegotiation: Codable, Equatable {
public func validate() throws {
if self.supportedVersions.count < 1 { throw ContractValidationError(field: "supported_versions", code: "min_items") }
if self.supportedVersions.count > 3 { throw ContractValidationError(field: "supported_versions", code: "max_items") }
for item in self.supportedVersions where item.utf8.count > 16 { throw ContractValidationError(field: "supported_versions", code: "max_item_length") }
if self.features.count > 64 { throw ContractValidationError(field: "features", code: "max_items") }
for item in self.features where item.utf8.count > 64 { throw ContractValidationError(field: "features", code: "max_item_length") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }