// The saved-host model + its on-disk JSON wire format — the widget/extension depends on BOTH, so // they live in the dependency-free shared module. The `ObservableObject` store that wraps them // (`HostStore`, with add/remove/pin/reachability) stays in the app target; discovery-join helpers // (`matches`, `advertises`) stay there too because they reference PunktfunkKit's `DiscoveredHost`. // // Wire-format stability: the JSON encoding of `StoredHost` is now a shared contract between the app // (writer) and the widget (reader). The `PunktfunkSharedTests` codec round-trip pins it — do not // rename the coding keys or make a stored `Optional` non-optional (older saved JSON must still // decode; synthesized Decodable treats a missing Optional as nil). import Foundation /// The management-API port default (distinct from the data-plane `port`). Lives here (not in /// PunktfunkKit's LibraryClient, which re-exports it) so `StoredHost.effectiveMgmtPort` can resolve /// it without the shared module taking a dependency on the kit. public let punktfunkDefaultMgmtPort: UInt16 = 47990 public struct StoredHost: Identifiable, Codable, Hashable { public var id = UUID() public var name: String public var address: String public var port: UInt16 = 9777 /// SHA-256 of the host's certificate, set after the user explicitly trusted it. public var pinnedSHA256: Data? /// Last time a streaming session actually started (nil until the first one). public var lastConnected: Date? /// Management-API port for the library browser (distinct from the data-plane `port`). Optional /// (NOT a defaulted non-optional) so older saved hosts — whose JSON lacks this key — still /// decode: synthesized Decodable ignores property defaults but treats a missing Optional as /// nil. Resolve via `effectiveMgmtPort`. (Auth is mTLS by the pinned identity — no token.) public var mgmtPort: UInt16? /// Wake-on-LAN MAC address(es) of the host's wake-capable NIC(s), each `aa:bb:cc:dd:ee:ff`. /// Learned from the host's mDNS `mac` TXT record while it's awake and persisted here, so the /// client can send a magic packet to wake the host later (when it's asleep and no longer /// advertising). Optional (same forward-compat reason as `mgmtPort`); nil until first learned. public var macAddresses: [String]? public init( id: UUID = UUID(), name: String, address: String, port: UInt16 = 9777, pinnedSHA256: Data? = nil, lastConnected: Date? = nil, mgmtPort: UInt16? = nil, macAddresses: [String]? = nil ) { self.id = id self.name = name self.address = address self.port = port self.pinnedSHA256 = pinnedSHA256 self.lastConnected = lastConnected self.mgmtPort = mgmtPort self.macAddresses = macAddresses } public var displayName: String { name.isEmpty ? address : name } public var effectiveMgmtPort: UInt16 { mgmtPort ?? punktfunkDefaultMgmtPort } /// Wake-capable, in a form the wake helper accepts (empty when none learned yet). public var wakeMacs: [String] { macAddresses ?? [] } }