import Foundation public struct AttachmentRecord: Codable, Identifiable { public var id: String { hash } public let hash: String public let originalName: String public let contentType: String public let sizeBytes: Int public let createdAt: Date public let url: String public let kind: String? public let documentPath: String? enum CodingKeys: String, CodingKey { case hash case originalName case contentType case sizeBytes case createdAt case url case kind case documentPath } } public struct UploadResponse: Codable { public let hash: String public let contentType: String public let attachmentUrl: String public let url: String? public let kind: String? } public struct DocumentRecord: Codable, Identifiable { public let id: String public let path: String public let title: String public let hash: String public let createdAt: Date? public let updatedAt: Date? } public struct DocumentSaveRequest: Codable { public let content: String public let baseHash: String? } public struct DocumentSaveResponse: Codable { public let status: String public let path: String public let title: String public let hash: String } public struct SearchResult: Codable, Identifiable { public var id: String { path } public let path: String public let title: String } public struct SyncInitRequest: Codable { public let deviceId: String public let rootPath: String } public struct SyncInitResponse: Codable { public let snapshotId: String public let serverSnapshot: [FileEntry] enum CodingKeys: String, CodingKey { case snapshotId case serverSnapshot } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) snapshotId = try container.decode(String.self, forKey: .snapshotId) serverSnapshot = try container.decodeIfPresent([FileEntry].self, forKey: .serverSnapshot) ?? [] } } public struct FileEntry: Codable { public let path: String public let hash: String public let size: Int64 public let modified: Date } public struct DeltaRequest: Codable { public let snapshotId: String public let clientDelta: [Change] } public struct DeltaResponse: Codable { public let serverDelta: [Change] public let conflicts: [Conflict] public let newSnapshotId: String? enum CodingKeys: String, CodingKey { case serverDelta case conflicts case newSnapshotId } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) serverDelta = try container.decodeIfPresent([Change].self, forKey: .serverDelta) ?? [] conflicts = try container.decodeIfPresent([Conflict].self, forKey: .conflicts) ?? [] newSnapshotId = try container.decodeIfPresent(String.self, forKey: .newSnapshotId) } } public struct Change: Codable { public let type: String public let path: String public let oldPath: String? public let hash: String? public let content: String? public let size: Int64? public let modified: Date? public init( type: String, path: String, oldPath: String?, hash: String?, content: String? = nil, size: Int64?, modified: Date? ) { self.type = type self.path = path self.oldPath = oldPath self.hash = hash self.content = content self.size = size self.modified = modified } } public struct Conflict: Codable { public let path: String public let serverHash: String public let clientHash: String public let serverModified: Date public let clientModified: Date } public struct APIError: Error, Codable { public let error: String public let conflictType: String? public let existingHash: String? public let existingPath: String? public let existingUrl: String? public init( error: String, conflictType: String? = nil, existingHash: String? = nil, existingPath: String? = nil, existingUrl: String? = nil ) { self.error = error self.conflictType = conflictType self.existingHash = existingHash self.existingPath = existingPath self.existingUrl = existingUrl } public var isUploadConflict: Bool { conflictType == "document" || conflictType == "attachment" } } extension APIError: LocalizedError { public var errorDescription: String? { error } } public struct AttachmentsListResponse: Codable { public let attachments: [AttachmentRecord] } public struct DocumentsListResponse: Codable { public let documents: [DocumentRecord] } public struct SearchResponse: Codable { public let results: [SearchResult] } public enum UploadDuplicateAction: String { case overwrite case rename case reuse }