Files
hapi/ios/Packages/HapiKit/Sources/HapiClient/Endpoints/MediaEndpoints.swift
T
weishu 3510a0f4f1 fix(ios): Linux compile + full package test round — fixtures green locally (A-CI-local)
ios/scripts/linux-test.sh stages HapiKit + shared/fixtures at repo depth
into a persistent tmp dir and runs swift test in swift:6.1-noble; the
manifest drops HapiUI (SwiftUI/swift-markdown/Highlightr) under
#if os(Linux). 458 tests green, including all 48 chat and 11 pagination
golden fixtures.

Fixes that fell out of the first real compile of the blind-written port:
- ChatTypes.CodexReview.wireValue + SummaryPatching legacy id fallback:
  split expressions that exceeded the Swift 6 type-checker budget
- FileEndpointsTests: raw string containing "# terminated the literal
  early (never compiled anywhere) — now ##-delimited
- SSEClient.backoffSleep: Task { try? ... } inferred Task<()?, Never>
- Darwin gates: Security/Keychain behind canImport(Security) (tests use
  InMemoryCredentialStore via CredentialStoring), CryptoKit digest with
  FNV-1a filename fallback, FoundationNetworking imports for URLSession
  types, corelibs URLCache diskPath: initializer, get-only
  waitsForConnectivity, delegate-based SSE transport where
  URLSession.bytes(for:) does not exist
2026-08-18 11:51:57 +08:00

59 lines
2.2 KiB
Swift

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking // URLSession types live here on Linux
#endif
import HapiProtocol
/// Generated images and message-attachment uploads
/// (`docs/api/client-contract/rest.md`).
extension APIClient {
/// `GET /api/sessions/:id/generated-images/:imageId` — raw bytes.
///
/// The image id is a content fingerprint doubling as the `ETag`, and the
/// hub sends `Cache-Control: private, max-age=31536000, immutable`, so
/// the configured `URLCache` (see ``URLSessionHTTPPerformer``) serves
/// repeats locally and revalidates with `If-None-Match` transparently.
public func generatedImage(
sessionId: String,
imageId: String
) async throws -> (data: Data, mimeType: String?) {
let (data, response) = try await requestBytes(
"/api/sessions/\(encodePathComponent(sessionId))/generated-images/\(encodePathComponent(imageId))"
)
return (data, response.value(forHTTPHeaderField: "Content-Type"))
}
/// `POST /api/sessions/:id/upload` — JSON + base64, **not** multipart.
/// Decoded size limit 50 MB (413 above). Requires an active session.
/// Pass the returned `path` in the `attachments` metadata of a
/// subsequent send-message.
public func uploadFile(
sessionId: String,
filename: String,
data: Data,
mimeType: String
) async throws -> UploadFileResponse {
try await request(
.post,
"/api/sessions/\(encodePathComponent(sessionId))/upload",
body: UploadFileRequest(
filename: filename,
content: data.base64EncodedString(),
mimeType: mimeType
)
)
}
/// `POST /api/sessions/:id/upload/delete` — removes a pending upload.
public func deleteUpload(sessionId: String, path: String) async throws -> DeleteUploadResponse {
struct DeleteUploadRequest: Encodable {
let path: String
}
return try await request(
.post,
"/api/sessions/\(encodePathComponent(sessionId))/upload/delete",
body: DeleteUploadRequest(path: path)
)
}
}