[sub-spm-optional-products] Add LCECryptoKit and LCFeatureControl as optional sub-SPM products
LCEssentials installs standalone; each sub target-depends on it so linking a sub's product always pulls LCEssentials in too, without the consumer having to declare it separately. - LCECryptoKit: internalized from the remote LCECryptoKitBinary git dependency (embedded token URL removed) into a local binaryTarget vendoring Frameworks/LCECryptoKit.xcframework. LCECryptoKitManager moved out of LCEssentials core into its own LCECryptoKitManager target/product; the no-op fallback for when the binary wasn't linked is gone (breaking change for existing consumers, see decisions/2026-09-15-sub-spm-optional-products.md). - LCFeatureControl: new product wrapping Atomenta's Feature Control API (flag evaluation with TTL cache + safe-degrade fallback to defaults, notifications inbox, batched exposure telemetry). 45 new tests.
This commit is contained in:
39
Sources/LCFeatureControl/FeatureControlAuthorizing.swift
Normal file
39
Sources/LCFeatureControl/FeatureControlAuthorizing.swift
Normal file
@@ -0,0 +1,39 @@
|
||||
import Foundation
|
||||
|
||||
/// Pluggable auth for outgoing Feature Control requests. No `Atomenta-Token` type
|
||||
/// exists here on purpose — that module token must never be embedded in a
|
||||
/// customer-facing app (see FC-060 §1 in Atomenta's `docs/feature-control/`).
|
||||
/// A consumer who insists on it does so explicitly via `FeatureControlHeaderAuth`.
|
||||
public protocol FeatureControlAuthorizing: Sendable {
|
||||
func authorize(_ headers: inout [String: String]) async
|
||||
}
|
||||
|
||||
/// For internal/admin apps hitting Atomenta directly with a panel-role JWT.
|
||||
public struct FeatureControlBearerAuth: FeatureControlAuthorizing {
|
||||
private let tokenProvider: @Sendable () async -> String?
|
||||
|
||||
public init(tokenProvider: @escaping @Sendable () async -> String?) {
|
||||
self.tokenProvider = tokenProvider
|
||||
}
|
||||
|
||||
public func authorize(_ headers: inout [String: String]) async {
|
||||
guard let token = await tokenProvider() else { return }
|
||||
headers["Authorization"] = "Bearer \(token)"
|
||||
}
|
||||
}
|
||||
|
||||
/// For a customer app calling its own BFF, which enforces its own auth. Adds
|
||||
/// exactly the given headers — never synthesizes an `Authorization` header.
|
||||
public struct FeatureControlHeaderAuth: FeatureControlAuthorizing {
|
||||
private let headers: [String: String]
|
||||
|
||||
public init(headers: [String: String]) {
|
||||
self.headers = headers
|
||||
}
|
||||
|
||||
public func authorize(_ headers: inout [String: String]) async {
|
||||
for (key, value) in self.headers {
|
||||
headers[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user