···11+import Foundation
22+33+public protocol BrowserDriver: Sendable {
44+ /// Presents the given authorization URL to the user and returns the
55+ /// callback URL the OAuth server redirected to. The host app implements
66+ /// this via `ASWebAuthenticationSession`; tests use `StubBrowserDriver`.
77+ /// - Parameter redirectScheme: the custom URL scheme to listen for
88+ /// (e.g. `"pdfs"` for `pdfs://oauth/callback`).
99+ func authenticate(
1010+ authorizationURL: URL,
1111+ redirectScheme: String
1212+ ) async throws -> URL
1313+}
1414+1515+/// Test helper that runs a pre-configured handler when `authenticate` is called.
1616+/// Host code uses `ASWebAuthenticationSessionBrowserDriver` (to be added
1717+/// alongside the macOS host app). Tests pass a closure that inspects the
1818+/// authorization URL and returns a fabricated callback.
1919+public struct StubBrowserDriver: BrowserDriver {
2020+ public let handler: @Sendable (URL, String) async throws -> URL
2121+2222+ public init(handler: @escaping @Sendable (URL, String) async throws -> URL) {
2323+ self.handler = handler
2424+ }
2525+2626+ public func authenticate(authorizationURL: URL, redirectScheme: String) async throws -> URL {
2727+ try await handler(authorizationURL, redirectScheme)
2828+ }
2929+}