Split UI-coupled types into portable data (LumaCore) and platform-specific editor profiles (Luma): - TracerConfig: data struct + template stubs to LumaCore, TracerEditorProfile stays with SwiftyMonaco in Luma - CodeShareConfig + CodeShareProjectRef to LumaCore, CodeShareEditorProfile stays in Luma - CodeShareService: drop ObservableObject, move as stateless enum with async methods to LumaCore - HookPack: data struct to LumaCore, discovery stays Add swift-crypto dependency for cross-platform SHA256. Add FoundationNetworking import for Linux URLSession.
91 lines
3.0 KiB
Swift
91 lines
3.0 KiB
Swift
import Foundation
|
|
#if canImport(FoundationNetworking)
|
|
import FoundationNetworking
|
|
#endif
|
|
|
|
public enum CodeShareService {
|
|
private static let baseURL = URL(string: "https://codeshare.frida.re")!
|
|
|
|
public enum Mode: Sendable {
|
|
case popular
|
|
case search(query: String)
|
|
}
|
|
|
|
public struct ProjectSummary: Identifiable, Decodable, Hashable, Sendable {
|
|
public let id: String
|
|
public let owner: String
|
|
public let slug: String
|
|
public let name: String
|
|
public let description: String
|
|
public let fridaVersion: String
|
|
public let likes: Int
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case owner
|
|
case slug
|
|
case name = "project_name"
|
|
case description
|
|
case fridaVersion = "frida_version"
|
|
case likes
|
|
}
|
|
}
|
|
|
|
public struct ProjectDetails: Decodable, Sendable {
|
|
public let id: String
|
|
public let owner: String
|
|
public let slug: String
|
|
public let name: String
|
|
public let description: String
|
|
public let source: String
|
|
public let fridaVersion: String
|
|
public let likes: Int
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case owner
|
|
case slug
|
|
case name = "project_name"
|
|
case description
|
|
case source
|
|
case fridaVersion = "frida_version"
|
|
case likes
|
|
}
|
|
}
|
|
|
|
public static func fetchPopular() async throws -> [ProjectSummary] {
|
|
let url = baseURL.appendingPathComponent("api/projects/popular")
|
|
do {
|
|
let (data, _) = try await URLSession.shared.data(from: url)
|
|
return try JSONDecoder().decode([ProjectSummary].self, from: data)
|
|
} catch let error as URLError where error.code == .cancelled {
|
|
throw CancellationError()
|
|
}
|
|
}
|
|
|
|
public static func searchProjects(query: String) async throws -> [ProjectSummary] {
|
|
guard !query.isEmpty else { return [] }
|
|
var comps = URLComponents(
|
|
url: baseURL.appendingPathComponent("api/projects/search"),
|
|
resolvingAgainstBaseURL: false
|
|
)!
|
|
comps.queryItems = [URLQueryItem(name: "query", value: query)]
|
|
do {
|
|
let (data, _) = try await URLSession.shared.data(from: comps.url!)
|
|
return try JSONDecoder().decode([ProjectSummary].self, from: data)
|
|
} catch let error as URLError where error.code == .cancelled {
|
|
throw CancellationError()
|
|
}
|
|
}
|
|
|
|
public static func fetchProjectDetails(owner: String, slug: String) async throws -> ProjectDetails {
|
|
let url = baseURL.appendingPathComponent("api/project/\(owner)/\(slug)")
|
|
do {
|
|
let (data, _) = try await URLSession.shared.data(from: url)
|
|
return try JSONDecoder().decode(ProjectDetails.self, from: data)
|
|
} catch let error as URLError where error.code == .cancelled {
|
|
throw CancellationError()
|
|
}
|
|
}
|
|
}
|