[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:
Daniel Arantes Loverde
2026-09-16 09:23:57 -03:00
parent d067791930
commit 2ba487a6e8
47 changed files with 4264 additions and 113 deletions

View File

@@ -0,0 +1,28 @@
import Foundation
/// One exposure event for `POST /api/feature-control/telemetry/exposure`.
public struct FeatureControlExposureEvent: Encodable, Sendable, Equatable {
public let featureKey: String
public let variant: String?
public let subjectType: FeatureControlSubjectType
public let storeId: String?
public init(featureKey: String, variant: String?, subjectType: FeatureControlSubjectType, storeId: String?) {
self.featureKey = featureKey
self.variant = variant
self.subjectType = subjectType
self.storeId = storeId
}
}
/// Request body server caps `events` at 100 (`maxItems: 100`); batching into that
/// limit is the caller's (`FeatureControlManager`'s) job, not this type's.
struct FeatureControlExposureBatchBody: Encodable, Sendable {
let events: [FeatureControlExposureEvent]
}
/// Wire envelope for the telemetry response `{error, code, result: {count}}`.
struct FeatureControlExposureBatchEnvelope: Decodable, Sendable {
let error: Bool
let code: String?
}