diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Sources/JellyKit/Client.swift b/Sources/JellyKit/Client.swift
new file mode 100644
index 0000000..ebc5bf1
--- /dev/null
+++ b/Sources/JellyKit/Client.swift
@@ -0,0 +1,67 @@
+//
+// Client.swift
+// JellyKit
+//
+// Created by Alex Loren on 10/10/26.
+//
+
+import Foundation
+
+struct Client {
+ // MARK: - Properties
+ private let session: URLSession
+ private let defaultHeaders: [String:String]
+ private let server: String
+ private let decoder: JSONDecoder = .init()
+ private let encoder: JSONEncoder = .init()
+
+ // MARK: - Functions
+ init(server: String, session: URLSession = .shared, headers: [String:String] = [:]) {
+ self.server = server
+ self.session = session
+ self.defaultHeaders = headers
+ }
+
+ func statusRequest(_ request: any Request) async throws -> Int {
+ do {
+ let urlRequest = try createRequestObject(for: request)
+ let (_, response) = try await session.data(for: urlRequest)
+ guard let response = response as? HTTPURLResponse else {
+ throw RequestError.invalidResponse
+ }
+
+ return response.statusCode
+ } catch {
+ throw RequestError.unknown(message: error.localizedDescription)
+ }
+ }
+
+ func dataRequest(_ request: any Request) async throws -> T? where T: Codable {
+ do {
+ let urlRequest = try createRequestObject(for: request)
+ let (data, response) = try await session.data(for: urlRequest)
+ guard let response = response as? HTTPURLResponse else {
+ throw RequestError.invalidResponse
+ }
+
+ return try decoder.decode(T.self, from: data)
+ } catch {
+ throw RequestError.unknown(message: error.localizedDescription)
+ }
+ }
+
+ private func createRequestObject(for request: Request) throws -> URLRequest {
+ guard let url = URL(string: "\(server)\(request.endpoint)") else {
+ throw RequestError.badURL
+ }
+
+ let headers = defaultHeaders.merging(request.headers ?? [:]) { (current, new) in
+ return new
+ }
+
+ var urlRequest = URLRequest(url: url)
+ urlRequest.httpMethod = request.method
+ urlRequest.allHTTPHeaderFields = headers
+ return urlRequest
+ }
+}
diff --git a/Sources/JellyKit/Enums/RequestError.swift b/Sources/JellyKit/Enums/RequestError.swift
new file mode 100644
index 0000000..0ddff1f
--- /dev/null
+++ b/Sources/JellyKit/Enums/RequestError.swift
@@ -0,0 +1,14 @@
+//
+// RequestError.swift
+// JellyKit
+//
+// Created by Alex Loren on 10/13/26.
+//
+
+import Foundation
+
+enum RequestError: Error {
+ case badURL
+ case invalidResponse
+ case unknown(message: String?)
+}
diff --git a/Sources/JellyKit/JellyKit.swift b/Sources/JellyKit/JellyKit.swift
deleted file mode 100644
index 8d0c09a..0000000
--- a/Sources/JellyKit/JellyKit.swift
+++ /dev/null
@@ -1,5 +0,0 @@
-// The Swift Programming Language
-// https://docs.swift.org/swift-book
-public func hello() {
- print("Hello, world!")
-}
\ No newline at end of file
diff --git a/Sources/JellyKit/Models/User.swift b/Sources/JellyKit/Models/User.swift
new file mode 100644
index 0000000..e4520e7
--- /dev/null
+++ b/Sources/JellyKit/Models/User.swift
@@ -0,0 +1,22 @@
+//
+// User.swift
+// JellyKit
+//
+// Created by Alex Loren on 10/13/26.
+//
+
+struct User: Codable {
+ var id: String
+ var serverId: String
+ var name: String
+ var hasPassword: Bool
+ var primaryImageTag: String
+
+ enum CodingKeys: String, CodingKey {
+ case id = "Id"
+ case serverId = "ServerId"
+ case name = "Name"
+ case hasPassword = "HasPassword"
+ case primaryImageTag = "PrimaryImageTag"
+ }
+}
diff --git a/Sources/JellyKit/Protocols/Request.swift b/Sources/JellyKit/Protocols/Request.swift
new file mode 100644
index 0000000..3549b36
--- /dev/null
+++ b/Sources/JellyKit/Protocols/Request.swift
@@ -0,0 +1,15 @@
+//
+// Request.swift
+// JellyKit
+//
+// Created by Alex Loren on 10/13/26.
+//
+
+import Foundation
+
+protocol Request {
+ // MARK: - Properties
+ var endpoint: String { get set }
+ var method: String { get set }
+ var headers: [String:String]? { get set }
+}