import Foundation enum ConfigPathSegment: Hashable { case key(String) case index(Int) } typealias ConfigPath = [ConfigPathSegment] struct ConfigUiHint { let label: String? let help: String? let order: Double? let advanced: Bool? let sensitive: Bool? let placeholder: String? init(raw: [String: Any]) { self.label = raw["label"] as? String self.help = raw["help"] as? String if let order = raw["order"] as? Double { self.order = order } else if let orderInt = raw["order"] as? Int { self.order = Double(orderInt) } else { self.order = nil } self.advanced = raw["advanced"] as? Bool self.sensitive = raw["sensitive"] as? Bool self.placeholder = raw["placeholder"] as? String } } struct ConfigSchemaNode { let raw: [String: Any] init?(raw: Any) { guard let dict = raw as? [String: Any] else { return nil } self.raw = dict } var title: String? { self.raw["title"] as? String } var description: String? { self.raw["description"] as? String } var enumValues: [Any]? { self.raw["enum"] as? [Any] } var constValue: Any? { self.raw["const"] } var explicitDefault: Any? { self.raw["default"] } var requiredKeys: Set { Set((self.raw["required"] as? [String]) ?? []) } var typeList: [String] { if let type = self.raw["type"] as? String { return [type] } if let types = self.raw["type"] as? [String] { return types } return [] } var schemaType: String? { let filtered = self.typeList.filter { $0 != "null" } if let first = filtered.first { return first } return self.typeList.first } var isNullSchema: Bool { let types = self.typeList return types.count == 1 && types.first == "null" } var properties: [String: ConfigSchemaNode] { guard let props = self.raw["properties"] as? [String: Any] else { return [:] } return props.compactMapValues { ConfigSchemaNode(raw: $0) } } var anyOf: [ConfigSchemaNode] { guard let raw = self.raw["anyOf"] as? [Any] else { return [] } return raw.compactMap { ConfigSchemaNode(raw: $0) } } var oneOf: [ConfigSchemaNode] { guard let raw = self.raw["oneOf"] as? [Any] else { return [] } return raw.compactMap { ConfigSchemaNode(raw: $0) } } var literalValue: Any? { if let constValue { return constValue } if let enumValues, enumValues.count == 1 { return enumValues[0] } return nil } var items: ConfigSchemaNode? { if let items = self.raw["items"] as? [Any], let first = items.first { return ConfigSchemaNode(raw: first) } if let items = self.raw["items"] { return ConfigSchemaNode(raw: items) } return nil } var additionalProperties: ConfigSchemaNode? { if let additional = self.raw["additionalProperties"] as? [String: Any] { return ConfigSchemaNode(raw: additional) } return nil } var allowsAdditionalProperties: Bool { if let allow = self.raw["additionalProperties"] as? Bool { return allow } return self.additionalProperties != nil } var defaultValue: Any { if let value = self.raw["default"] { return value } switch self.schemaType { case "object": return [String: Any]() case "array": return [Any]() case "boolean": return false case "integer": return 0 case "number": return 0.0 case "string": return "" default: return "" } } func node(at path: ConfigPath) -> ConfigSchemaNode? { var current: ConfigSchemaNode? = self for segment in path { guard let node = current else { return nil } switch segment { case .key(let key): if node.schemaType == "object" { if let next = node.properties[key] { current = next continue } if let additional = node.additionalProperties { current = additional continue } return nil } return nil case .index: guard node.schemaType == "array" else { return nil } current = node.items } } return current } } func decodeUiHints(_ raw: [String: Any]) -> [String: ConfigUiHint] { raw.reduce(into: [:]) { result, entry in if let hint = entry.value as? [String: Any] { result[entry.key] = ConfigUiHint(raw: hint) } } } func hintForPath(_ path: ConfigPath, hints: [String: ConfigUiHint]) -> ConfigUiHint? { let key = pathKey(path) if let direct = hints[key] { return direct } let segments = key.split(separator: ".").map(String.init) for (hintKey, hint) in hints { guard hintKey.contains("*") else { continue } let hintSegments = hintKey.split(separator: ".").map(String.init) guard hintSegments.count == segments.count else { continue } var match = true for (index, seg) in segments.enumerated() { let hintSegment = hintSegments[index] if hintSegment != "*" && hintSegment != seg { match = false break } } if match { return hint } } return nil } func isSensitivePath(_ path: ConfigPath) -> Bool { let key = pathKey(path).lowercased() return key.contains("token") || key.contains("password") || key.contains("secret") || key.contains("apikey") || key.hasSuffix("key") } func pathKey(_ path: ConfigPath) -> String { path.compactMap { segment -> String? in switch segment { case .key(let key): return key case .index: return nil } } .joined(separator: ".") }