From 2ba487a6e83828c453c9992551596a527b0abf75 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Wed, 16 Sep 2026 09:23:57 -0300 Subject: [PATCH] [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. --- Documentation/Extensions.md | 22 +- .../LCECryptoKit.xcframework/Info.plist | 44 + .../Headers/LCECryptoKit-Swift.h | 382 +++++++++ .../LCECryptoKit.framework/Info.plist | Bin 0 -> 760 bytes .../LCECryptoKit.framework/LCECryptoKit | Bin 0 -> 99904 bytes .../arm64-apple-ios.abi.json | 417 ++++++++++ .../arm64-apple-ios.private.swiftinterface | 22 + .../arm64-apple-ios.swiftdoc | Bin 0 -> 3656 bytes .../arm64-apple-ios.swiftinterface | 22 + .../Modules/module.modulemap | 3 + .../Headers/LCECryptoKit-Swift.h | 760 ++++++++++++++++++ .../LCECryptoKit.framework/Info.plist | Bin 0 -> 740 bytes .../LCECryptoKit.framework/LCECryptoKit | Bin 0 -> 195728 bytes .../arm64-apple-ios-simulator.abi.json | 417 ++++++++++ ...apple-ios-simulator.private.swiftinterface | 22 + .../arm64-apple-ios-simulator.swiftdoc | Bin 0 -> 3668 bytes .../arm64-apple-ios-simulator.swiftinterface | 22 + .../x86_64-apple-ios-simulator.abi.json | 417 ++++++++++ ...apple-ios-simulator.private.swiftinterface | 22 + .../x86_64-apple-ios-simulator.swiftdoc | Bin 0 -> 3668 bytes .../x86_64-apple-ios-simulator.swiftinterface | 22 + .../Modules/module.modulemap | 3 + .../_CodeSignature/CodeResources | 234 ++++++ Package.resolved | 15 - Package.swift | 51 +- .../LCECryptoKitManager.swift} | 83 +- .../FeatureControlAuthorizing.swift | 39 + .../FeatureControlCache.swift | 46 ++ .../FeatureControlConfiguration.swift | 33 + .../FeatureControlContext.swift | 30 + .../FeatureControlDateParsing.swift | 18 + .../FeatureControlError.swift | 51 ++ .../FeatureControlExposureEvent.swift | 28 + .../LCFeatureControl/FeatureControlFlag.swift | 61 ++ .../LCFeatureControl/FeatureControlJSON.swift | 33 + .../FeatureControlManager.swift | 123 +++ .../FeatureControlNotification.swift | 65 ++ .../FeatureControlNotificationsClient.swift | 76 ++ .../FeatureControlAuthTests.swift | 38 + .../FeatureControlCacheTests.swift | 70 ++ .../FeatureControlErrorTests.swift | 84 ++ .../FeatureControlExposureTests.swift | 69 ++ .../FeatureControlJSONTests.swift | 40 + .../FeatureControlManagerTests.swift | 160 ++++ .../FeatureControlModelsTests.swift | 124 +++ ...atureControlNotificationsClientTests.swift | 93 +++ .../Support/StubURLProtocol.swift | 116 +++ 47 files changed, 4264 insertions(+), 113 deletions(-) create mode 100644 Frameworks/LCECryptoKit.xcframework/Info.plist create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Info.plist create mode 100755 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/LCECryptoKit create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.abi.json create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.private.swiftinterface create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.swiftdoc create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.swiftinterface create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/module.modulemap create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Info.plist create mode 100755 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/LCECryptoKit create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/module.modulemap create mode 100644 Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/_CodeSignature/CodeResources delete mode 100644 Package.resolved rename Sources/{LCEssentials/Classes/LCECrypto.swift => LCECryptoKitManager/LCECryptoKitManager.swift} (68%) create mode 100644 Sources/LCFeatureControl/FeatureControlAuthorizing.swift create mode 100644 Sources/LCFeatureControl/FeatureControlCache.swift create mode 100644 Sources/LCFeatureControl/FeatureControlConfiguration.swift create mode 100644 Sources/LCFeatureControl/FeatureControlContext.swift create mode 100644 Sources/LCFeatureControl/FeatureControlDateParsing.swift create mode 100644 Sources/LCFeatureControl/FeatureControlError.swift create mode 100644 Sources/LCFeatureControl/FeatureControlExposureEvent.swift create mode 100644 Sources/LCFeatureControl/FeatureControlFlag.swift create mode 100644 Sources/LCFeatureControl/FeatureControlJSON.swift create mode 100644 Sources/LCFeatureControl/FeatureControlManager.swift create mode 100644 Sources/LCFeatureControl/FeatureControlNotification.swift create mode 100644 Sources/LCFeatureControl/FeatureControlNotificationsClient.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlAuthTests.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlCacheTests.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlErrorTests.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlExposureTests.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlJSONTests.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlManagerTests.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlModelsTests.swift create mode 100644 Tests/LCFeatureControlTests/FeatureControlNotificationsClientTests.swift create mode 100644 Tests/LCFeatureControlTests/Support/StubURLProtocol.swift diff --git a/Documentation/Extensions.md b/Documentation/Extensions.md index 4a02881..5a23e7f 100644 --- a/Documentation/Extensions.md +++ b/Documentation/Extensions.md @@ -1760,12 +1760,24 @@ digest.map { String(format: "%02x", $0) }.joined()
-LCECryptoKitManager — OTP / peppered-login bridge (needs the LCECryptoKit binary) +LCECryptoKitManager — OTP / peppered-login bridge (separate LCECryptoKit product) -A thin facade over the optional `LCECryptoKit` binary product (enabled by the -`LCE_ENABLE_CRYPTO_BINARY` build flag). **When the binary is not linked every -method is a no-op** returning `nil` / `""` / `false`, so calling code still -compiles and runs. +Lives in its own SPM product, `LCECryptoKit` (target `LCECryptoKitManager`), not in +`LCEssentials` itself — an optional sub-package that target-depends on `LCEssentials`. +Add it explicitly alongside `LCEssentials` to a consumer's `Package.swift`: + +```swift +.product(name: "LCEssentials", package: "LCEssentials"), +.product(name: "LCECryptoKit", package: "LCEssentials"), +``` + +```swift +import LCECryptoKitManager +``` + +Wraps the vendored `LCECryptoKit.xcframework` (iOS device + simulator only — no +macOS/tvOS/watchOS slices). Consumers who don't link this product simply don't get +the symbol; there is no no-op fallback anymore. ### `init()` / `init(privateKey:)` diff --git a/Frameworks/LCECryptoKit.xcframework/Info.plist b/Frameworks/LCECryptoKit.xcframework/Info.plist new file mode 100644 index 0000000..c628319 --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/Info.plist @@ -0,0 +1,44 @@ + + + + + AvailableLibraries + + + BinaryPath + LCECryptoKit.framework/LCECryptoKit + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + LCECryptoKit.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + BinaryPath + LCECryptoKit.framework/LCECryptoKit + LibraryIdentifier + ios-arm64 + LibraryPath + LCECryptoKit.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h new file mode 100644 index 0000000..d6185ab --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h @@ -0,0 +1,382 @@ +#if 0 +#elif defined(__arm64__) && __arm64__ +// Generated by Apple Swift version 6.3.3 effective-5.10 (swiftlang-6.3.3.1.3 clang-2100.1.1.101) +#ifndef LCECRYPTOKIT_SWIFT_H +#define LCECRYPTOKIT_SWIFT_H +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgcc-compat" + +#if !defined(__has_include) +# define __has_include(x) 0 +#endif +#if !defined(__has_attribute) +# define __has_attribute(x) 0 +#endif +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif +#if !defined(__has_warning) +# define __has_warning(x) 0 +#endif + +#if __has_include() +# include +#endif + +#pragma clang diagnostic ignored "-Wauto-import" +#if defined(__OBJC__) +#include +#endif // defined(__OBJC__) +#if defined(__cplusplus) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#if defined(__cplusplus) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module" +#if defined(__arm64e__) && __has_include() +# include +#else +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +# ifndef __ptrauth_swift_value_witness_function_pointer +# define __ptrauth_swift_value_witness_function_pointer(x) +# endif +# ifndef __ptrauth_swift_class_method_pointer +# define __ptrauth_swift_class_method_pointer(x) +# endif +#pragma clang diagnostic pop +#endif +#pragma clang diagnostic pop +#endif + +#if !defined(SWIFT_TYPEDEFS) +# define SWIFT_TYPEDEFS 1 +# if __has_include() +# include +# elif !defined(__cplusplus) +typedef unsigned char char8_t; +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; +# endif +typedef float swift_float2 __attribute__((__ext_vector_type__(2))); +typedef float swift_float3 __attribute__((__ext_vector_type__(3))); +typedef float swift_float4 __attribute__((__ext_vector_type__(4))); +typedef double swift_double2 __attribute__((__ext_vector_type__(2))); +typedef double swift_double3 __attribute__((__ext_vector_type__(3))); +typedef double swift_double4 __attribute__((__ext_vector_type__(4))); +typedef int swift_int2 __attribute__((__ext_vector_type__(2))); +typedef int swift_int3 __attribute__((__ext_vector_type__(3))); +typedef int swift_int4 __attribute__((__ext_vector_type__(4))); +typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); +typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); +typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); +#endif + +#if !defined(SWIFT_PASTE) +# define SWIFT_PASTE_HELPER(x, y) x##y +# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) +#endif +#if !defined(SWIFT_METATYPE) +# define SWIFT_METATYPE(X) Class +#endif +#if !defined(SWIFT_CLASS_PROPERTY) +# if __has_feature(objc_class_property) +# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ +# else +# define SWIFT_CLASS_PROPERTY(...) +# endif +#endif +#if !defined(SWIFT_RUNTIME_NAME) +# if __has_attribute(objc_runtime_name) +# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) +# else +# define SWIFT_RUNTIME_NAME(X) +# endif +#endif +#if !defined(SWIFT_COMPILE_NAME) +# if __has_attribute(swift_name) +# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) +# else +# define SWIFT_COMPILE_NAME(X) +# endif +#endif +#if !defined(SWIFT_METHOD_FAMILY) +# if __has_attribute(objc_method_family) +# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) +# else +# define SWIFT_METHOD_FAMILY(X) +# endif +#endif +#if !defined(SWIFT_NOESCAPE) +# if __has_attribute(noescape) +# define SWIFT_NOESCAPE __attribute__((noescape)) +# else +# define SWIFT_NOESCAPE +# endif +#endif +#if !defined(SWIFT_RELEASES_ARGUMENT) +# if __has_attribute(ns_consumed) +# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) +# else +# define SWIFT_RELEASES_ARGUMENT +# endif +#endif +#if !defined(SWIFT_WARN_UNUSED_RESULT) +# if __has_attribute(warn_unused_result) +# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define SWIFT_WARN_UNUSED_RESULT +# endif +#endif +#if !defined(SWIFT_NORETURN) +# if __has_attribute(noreturn) +# define SWIFT_NORETURN __attribute__((noreturn)) +# else +# define SWIFT_NORETURN +# endif +#endif +#if !defined(SWIFT_CLASS_EXTRA) +# define SWIFT_CLASS_EXTRA +#endif +#if !defined(SWIFT_PROTOCOL_EXTRA) +# define SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_ENUM_EXTRA) +# define SWIFT_ENUM_EXTRA +#endif +#if !defined(SWIFT_CLASS) +# if __has_attribute(objc_subclassing_restricted) +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# else +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# endif +#endif +#if !defined(SWIFT_RESILIENT_CLASS) +# if __has_attribute(objc_class_stub) +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub)) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME) +# else +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME) +# endif +#endif +#if !defined(SWIFT_PROTOCOL) +# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_EXTENSION) +# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) +#endif +#if !defined(OBJC_DESIGNATED_INITIALIZER) +# if __has_attribute(objc_designated_initializer) +# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +# else +# define OBJC_DESIGNATED_INITIALIZER +# endif +#endif +#if !defined(SWIFT_ENUM_ATTR) +# if __has_attribute(enum_extensibility) +# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) +# else +# define SWIFT_ENUM_ATTR(_extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# if __has_feature(generalized_swift_name) +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# else +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +# else +# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM_TAG) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM_TAG enum +# else +# define SWIFT_ENUM_TAG +# endif +#endif +#if !defined(SWIFT_ENUM_FWD_DECL) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type; +# else +# define SWIFT_ENUM_FWD_DECL(_type, _name) typedef _type _name; +# endif +#endif +#if !defined(SWIFT_UNAVAILABLE) +# define SWIFT_UNAVAILABLE __attribute__((unavailable)) +#endif +#if !defined(SWIFT_UNAVAILABLE_MSG) +# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) +#endif +#if !defined(SWIFT_AVAILABILITY) +# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) +#endif +#if !defined(SWIFT_AVAILABILITY_DOMAIN) +# define SWIFT_AVAILABILITY_DOMAIN(dom, ...) __attribute__((availability(domain: dom, __VA_ARGS__))) +#endif +#if !defined(SWIFT_WEAK_IMPORT) +# define SWIFT_WEAK_IMPORT __attribute__((weak_import)) +#endif +#if !defined(SWIFT_DEPRECATED) +# define SWIFT_DEPRECATED __attribute__((deprecated)) +#endif +#if !defined(SWIFT_DEPRECATED_MSG) +# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) +#endif +#if !defined(SWIFT_DEPRECATED_OBJC) +# if __has_feature(attribute_diagnose_if_objc) +# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) +# else +# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) +# endif +#endif +#if defined(__OBJC__) +#if !defined(IBSegueAction) +# define IBSegueAction +#endif +#endif +#if !defined(SWIFT_EXTERN) +# if defined(__cplusplus) +# define SWIFT_EXTERN extern "C" +# else +# define SWIFT_EXTERN extern +# endif +#endif +#if !defined(SWIFT_CALL) +# define SWIFT_CALL __attribute__((swiftcall)) +#endif +#if !defined(SWIFT_INDIRECT_RESULT) +# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result)) +#endif +#if !defined(SWIFT_CONTEXT) +# define SWIFT_CONTEXT __attribute__((swift_context)) +#endif +#if !defined(SWIFT_ERROR_RESULT) +# define SWIFT_ERROR_RESULT __attribute__((swift_error_result)) +#endif +#if defined(__cplusplus) +# define SWIFT_NOEXCEPT noexcept +#else +# define SWIFT_NOEXCEPT +#endif +#if !defined(SWIFT_C_INLINE_THUNK) +# if __has_attribute(always_inline) +# if __has_attribute(nodebug) +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug)) +# else +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) +# endif +# else +# define SWIFT_C_INLINE_THUNK inline +# endif +#endif +#if defined(_WIN32) +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport) +#endif +#else +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL +#endif +#endif +#if !__has_feature(nullability) +# define _Nonnull +# define _Nullable +# define _Null_unspecified +#elif !defined(__OBJC__) +# pragma clang diagnostic ignored "-Wnullability-extension" +#endif +#if !__has_feature(nullability_nullable_result) +# define _Nullable_result _Nullable +#endif +#if __has_feature(objc_modules) +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +#endif + +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="LCECryptoKit",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(__cplusplus) +} // extern "C" +#endif +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__OBJC__) +#if __has_feature(objc_modules) +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +#endif + +#endif // defined(__OBJC__) +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="LCECryptoKit",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__OBJC__) + +#endif // defined(__OBJC__) +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__cplusplus) +#endif +#pragma clang diagnostic pop +#endif + +#else +#error unsupported Swift architecture +#endif diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Info.plist b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..359fa305c6323b0b719a9dde6fd3cdaf30dcb588 GIT binary patch literal 760 zcmYjO&vMd07|*U%YprElMB3VFTmM_lBp}#>UP{Ug16WD|MTa3S$pULP*%%V0%%~6G z%?I#M&t7~1U%-G6`~BPb_S?3@Xy22hUnl|Kxta49F3w(>yF7no zA%+&0;#U*blFKWr%h%V|Z*FXE-P*i;=kC4x+d7Kx`jnYvZ1kv2_SG@c(Mm4A>)R$H z3KS#T37VHjJsNn^9><;V1?FU^m zQeR*zxFH_F3O0^#muN#LU{@nn?Qz!&ld9)ZyL(pj9Vd{9Sz*}gaM$YVC>ABd&?`mD zam%4>G+9j1BJ23Yttp;Nvp6NDc~vQn-7gg_Xh+6FMQa$`B+amf>qBK~Q_HvZ)iB(u z6@pDtgXFPA*-%GowL+D=^{GqDiIBq%Zc|1*O8T`_Cch(9iQO#a_Hypf@pzGXI!YQ` zWPCtelZa;(H7iP5zI<4$$)Y4mCpQ|j(&MA=Jdky?WpGQxj>Cu;bmIl|MaBk}XfSMd zMOl~XsdRov#trI>V!W@VvSOwgp%Dm}tW=ku=4Cmp$!Ssg^57u?2%bEuVb{t&5oU#q zP!zhtJK>Y?P53UH3V*-?*Z|w$IVgeG;0XBOJ@^PdgRkHR_z6zIZ}1oVgEMdr&cg&u i!e=lCOYi`;paI`N4qfQM0X!DQ2ZBiz9)~q7@WX$34(N{n literal 0 HcmV?d00001 diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/LCECryptoKit b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/LCECryptoKit new file mode 100755 index 0000000000000000000000000000000000000000..2db08c2080a17bb521acc275eb4fb313ee831e04 GIT binary patch literal 99904 zcmeEv3wTu3wf{adlVlP=9+^Br6ToUftKpSQe3Y3CV0b73=7~6upG79AXTYK-5oJ<1R`~Cml zckll@^L;0Kt^HVg@3q%nd+oLNIeF)+zh4;4n8NUj#xoL6>mbJdjSV+ZTne5&M#h}Z zg6WG2C??|9IxrrEXnH`3|C~<0dzD`z2>Q{piL?(&Wgg%L3G%?iYPG1->GM|?`Ff%v z>3#4kk#raD1!IJ3>6s-x^qv0%J!hG_yvprGOe8()vm&2Lk8mwL)~|iVl@-3;4oA{! z+9~M0DFQ~gY^sdu8=!qFJxl#loc`uqr+w#TiZI!uf zGK|29ruxuWIi01Iy;6K}dL78if31DnMLOmCB8C5za@P|R*}jfekt72~xVE1nwfC>n zSy^;mu!IjuM2V8fAoE znqHC5*UP?s{aq9-=w-_GQC2NIO*E%Ma*~I~>6|n3w%OCC%@nhXF`LX;%d_?1wiyDa zR4uI`z==+BPrS%s^H#b1W!KMfOm}!|s{EC+J^t&sdRSTMUDhWW1@RlhSZyW&sGn#h zBP7{6o-1&(Lt*uoF}D78;D{%&qkQuU6!r{-MG8AUR;<_afYXoG5_%|a1)iJnT=eHu z*t-KHl08B7+}3uv=b7(YA81xGKlEIPy=)@l((w>&#=wQBRL&Q{^OL6Wh$a1_{;mZ; zbeIiKEgtzBjL?dQ(gdwf04ePHYM=M|a!(OGq#>@eUsqBC7>WGXzuX*{D}Br5*ZBPI z<=5qEOHw)Fk6NE&)qkDf|E^p2CGf0)wSoyBccNbTCJ^mWa_^DBh5j`3= zl^(lhGE`OHb`)Q8&2XSXFx|J}jE27DO)k^&bZ@Z!a#9!Q8`jcGA?ogXt+P#wp@WrpO2q_r&Py=ZpAexbV{ zmN83O6g$zed0Q&cKVnP>jsfizlJ?2&?xYT5e9$zEZ8~LS!P7=1Nch{R2W>fg#ApeA zj523Z&=#bh;pvtj(YX?3DDM$tV(|RxY3Q2+easDXIqd=Txv|yge6QPKJaq0x7XEsf zIS?+64V+yPhkS#;gTdf|v61MV1zpaIC|lU9*uu?qsa-dnyT9FhuCP6n#=_fT*osHd zznc^`rTxRIcD#pe**1u+a2Z*_G4LQ>iS7JPq^Gm=cSC7JBWc;|39KOghqjH2z~f|x zE$kb?TEb{^*CZBpU3l@4Q*{dGHSswcya?TI+ZY4;;8UlEy>^Qr*_2!QF+MADE$ZHQ>nAaY< zS_w=V##)H3&A3rE)=$(S*GN?4H}_JCFhtAovW3cFV>*^Y^BlSsj(JfOqgwO<7n_{bgQZH zg`sSd5?ed}V~oWR$lb^2Z%2IV&CFDLGnb1g7}HTK%;6ls=`3uiWjRfzhvwU&|J|$9 zo;&<~^pD0ib*w$G1arcPIf2)+3q}rNVRL+H1B)^QauDysyx4!Ty8y50@_7-Wc>!6# z{K6bF2fHATM~ox+9C>~S+jPX3jQL;-(-{99bLBMVV{_f&u9@h=ILPZb$m>+a+_@cN z(t*6^SI>cb&xV}Og1pZRe7!6$aPCITVTX~=2a?fm`_!OM-F2oz=PCyWzA7Dpd371) z*HF+OhWRr*aNc2RJc;(wyiLGd?K6jrsa+j0ra(uGG0=fLt)RvE&V~rS7e+M}fbU09 zHWz)#9cBx6V9p*nz#3n}IP6CLRs);NFA@g9`<3;5m*eZW3pb35t`&$&?d==33tW9T*N z+NegV@29A1B^ zOQx*bx(D<+VyLXr7@Ez(=_IG~u19|jji*5CIOuetKGKn4qahf&dU{~|`TN_QQEbIv z$lB-7PuKjls(lR`)MV{5RbK{+zh0qIUNVj2Z_Zg;rY5wQxc5}kE)OEnKMz?JZfN#ywB*)jKT=rE=PyA6q*cwxW5a&BJxHbZKs0qvP$?AU#pg?4ugwT0i0 z>3GRf=C@Kl>Q^#okWT9wYv$u&@rX3if2R!{yEFDxZJ_$cu_kYd65D|H{Vh}v%{f|g z?!^528|Ivf-v>D&nd9`awrTU;hP+{8T9nN`>5Im%b z??gP_-nS@2Y++h=MnlG?GGpg6QLKPybQq06tYe3b^zd>C!TktNEu4<|D%R@vvBuF_ z{R*YWOb<)}-fZlz$7oGe%bZ_*J9N!F=$yIGJ-0yz-5NMoIVS*})K8a*GV#GU&`(4= zlGugA*K*!a{qezT5I-EaR`=PdEg0u*2r0hXVSuhPq8=0Ki$cB8s6Pg}&J0_@09|Lq z+%xpC6+%hS+Yc*T$2|->r;1r)#~|2H>E{lAl7w~rFZMv_FO;4bT%)F=PHNK!NT>LF z5P$j|JMzSHnA$|?+b?u)R`rXW^weZq*aF)O>(cy>P}X7=dJONxKi7e|6}oVLJ8Z2L z<+QFxvnjMro+)O0ouoA~RLXLWEn`?e3)|nIb+(e_gnWcowZDlq_Sj0`tMSnKN^9#H zmUFg_VGYLmiZY>xQNDrF%aC5x{`q4pr*k9H(dW=+mSb1OH9*!HJAi*OmNkA4vXTb< zVTYXj>27=A6zqaRw3~ETEA$89Zs257%z(|2tRy$wX-w&S{W8cE(v~328pT@DSW2gP z=TEmPNXNVImOsN*LLWA!D5;&Zp;svHEadGluoj|C_*~$5p!+oJ4WdhNw;)cXWky_@ z5q7C@%U06mcn9CoeN^e{9tu9nJ{*z#pfoC`PWh!LCpmyALRbRNJDDT>;yi#vlgePlH#f~OPNrtg54J+_C%7H+9C9O^(C#&UiV zXw$r-`pta&lEAZsAXBEkr;dM_T*vE2vVt->?`V&Zfi<`gb~x!vF8koo8`vW->=9xB zQ{QR+nU%z4OHr0#ttFjGboZc5O%LaAT|x8T3wxkxhjNJQosWT&EfE8~Nox?-*@$}( zdG>r%6Tqh+CJQ-93Vt}4WPs!+K6oF})iytmderzOWNUD_N(!#vawYPAj5w0rznc5j z^9H9$a+nl^4Ue*x;AVuhrine9B{&Uy+w+k%z;#ujP1wtK+QQp+WC!*x%nCGt$AeLq zx!e};bl=}T6}svR=&Co&Y)b6;y!Pfgi#6{2y!Juo7qtgATCBkx7Hcf*lDP9_?a#bu zu?7whUe#_szp6dh4*aOa>gmpFzq@;0d-F+)^%Y}+wXAzl`?BuB_CS1sH8?f_>19Z- zYHyyDV134sU~N8XI26cFuqG(uu?C9%Od!4^zU3(KIw5!m7O?-UZNqq!p}9+XjqvM%*Gv7As-!kF8wrAU!$wkeZG$qIr81=@kD% z#D|t3-xA7~7`#vBQ}yun>7B zE38G;(Z7T}K|U09zAi#K=^oN6B2U6a{V12z)kXt!wXlt7%^=;0ebR9=_NQj)Poc5j zini1GJPJCyUh3>j& zeFgeAJ9dh#>q?4b=tgvM>5O2nu zqqHMqSwWLAp^Mglm-4f*ZlnZDQU7M_!D!v!`$bwe9JFqv@O5Jy($6A|d`k6v-4J^T zS~uF{dJ#&;8bQ3VV;^r!>Pj)dp2M1>AS7SVGq4$2j0-XUQo2a@lg}*~ z+W%*)Wlrhux&gc-pAxNQPVmqUev!Y6c&M&rA-@s6&a8QJ6;X}?*wuJhSU=tkICf3yfaM*4y94}h0J zmo7#6NF}8q0d>%rQu(!L)09|SnDV`Wd^X6tTJHD2n++&;ohXOzK3ZM@Kggd(GPJ9% zpo`i|FqPkj{AR36WJ7-ex_QxTatQLb3-;!|2kJZDz6N_w=)8YNKB@!z$QHs|WIew^ z8PYAZ?z^zA?Sg-d(s#)Gv^EglEc3rUjuoV!KhzG2--dYN{olbG;`eCmg-(OUyJ*`6 zq^au*;hQCmPa*%L+ldCn{}}N!Mll$p&rqJ)vlo3Py5;A)3%+7(%FDq26XObfVTnQC z35Nkp2pZyohx{J(z0mGnHY>EwWYg`9cnJderg8` zylO1yI%mXQh2$I$$>LGio+j*L{)V~pDRlcgpn)=twQ+1R_0xtq!kV~`?%?S3sKz(q z;G;7OAKjIZCE`Ek(LwTaGWhAt?A+mfW3vM!@9Cf$0KKNVgD$%XKMx(y6WtTX;0W(br;5W8OW9^0YP)JuhHdlLv!V^VKT@_-@_t zSqZlAc=(1ht~NlQWB#K~1NM9l%vTe_Exb%hFcoFgIe_`SVGPGfmnR4P=o8tTq{ByG z4R3-xlg&01u&U<6y2pcUC>19YK8!ZtwTB5ai2ia(e))a1|30}3KO-+cWv7x=t7x8S< z638ssXs&4JcCyi~o`kUw`CsL{9R=Qw4DJP7fcdo_Wk_z)qge~#uLBR%<#)km@4O0o zPMV9?fM#ru-4)wOGzVdisnUH~LznnObe;j74wKM3gzp6247ylTIv;``C;@aS{#S?( zp?;#Z3;6@a{H`?2Q}grdu(xFm7!%lD3DErs7&Fn2M^Fd#d%NuWuR-H-sqkIC& zUj_S)=s$+EJE8y5SORRjpMYjc1L-*;~r~RwpTWAy6b&=;?-+_I!7WQ#Rj1tJhemNHNG;}o!kUg{=?V@#^&kL;U z9Y*rAvBr;)_6cm8M?sTpCmNSH^rsAZ8G8Q2QRw{Y`3}gPB^bl|86Pyr{?giF0iVg{ z)t*nGZ9RM9OWIWqc8KgLTBB*~W}yvc+V74Q``wG#PLmY#TWhf26*6SuzPTinB|9$# zk7_&RL9Vl5J9S43+bP>5Z6}=9U5kE=MfEB?70%VF>=8gY}rX5cFRxbU+4t&X`jL3g+A})Ti2F zpTh6UZ86mK_POqYH({%&?Z1igU2PAH#<@qSJa-%Y5&0w&Iy7W=)wW4krZP!#;kw z2s-=&)G6jq65qQY22SHBd_SVzQz)y_KEdJS;CA>#A46Z&vYkAh=1xlRn{<8TJn62Z zy-k1LifXI7y1O?Yfq#wKPX2D%Lu6x)sAER+bT0by9(2{IC4J}2AJDE#&6ig6muz9$ z>wJ%RBInBi$juo!Uv5M^)scnpBSS)Gts(!bCdi%eQHXP2Y7;)YK7T*xiTP&A)lRfZ)Nmn`;Ni-}KFU7!TU{nIC{~2=9j5UT~e?xu?@{OWho0|Vw6QJ_V*bk&bzfgT^5uSoymuRc|ifGu# zyicG@_AA)|D((LOtnyRZNe9Pi&O}`q)D8pYj`17k&MBN`G z^fK%k-frZdh5Xd+y-C#p8kc6{qONB#E>A$7UqzW1oMmO993LClk$aP-1j1MYmY{yi zY1@Xc;8&gm8l>ZiN7QyJaN^}vxZ?qzUc)OpRtCO;ey04_A#EJSm-b(KVb9-!u_1mx zg?24jW83&L{Az3JwsyWf0((hGe`EyxbV+{(@(_LM&l1Q8wVlc&@buJWujBla_FL3f z>f=*&ckuC{JstIp@4*fzhe9}?Z8k3GIyIIBNJqjhlY21Oeb~2r3Olb7H2MC^%qG(q z*=TPi_Fp?`{{{QMXaDsY@{%7XgRz}`_Fp@40_UKM$)-#P-8|%L!g}^wgp}88A7BrL zGJ6js(;n=g+=CtDd$4xpkR9b{y(D_W0h68j48rDL)zBX7Ann28um>~qJy>$TJyUrmrVvU7(4R&~IyvA*xU(4Dm1cqeReOAr^CAOjYD z*MZtZ_RCRz|47)pWN-WlcvD?cm+DWUHT&ymqS;&blbX6uAx*9O1kWe*$YavBApQF& z;G>}ZB*yqZ(H@ee|IQPQda2ySYyynWLGnq3aQ>Buv7@shT3`9T7GV@@p_4dc?}jXO zz%C}glovG5VxF8tnSc2%80eF&I0m%;VG^{}yI^aGmIrwpKH2Erx5cvYaenDQkD5Ab*) z)1x_@9DIfHEJ2aefn;kCyl8Guw$k}`^H{^7Cg8uUGaS;M zf4@{D&cAV=j@z-VSW8Y}U40688+?=x!B&Y^;u{i;iJj4aDeY%So5at*6S1Fv9Q*lr ze*V1&Yd!j~F;PkGLRr-DKOQLSAScr@LRZLm#+_&TZM?+MBt*7whz2 z$d?U2*GtA^E8U$Sd1-^}G>@ea@-vCTvi4UT$<{X@GtK#gLvD(btsctnOSYCF|BH3W z*1M5Eu#w`iZttLY$lHsMp99HO7wkcj7i#xp^o?wv{kS9X7u=EXgU?B5FX;r55yD3S zCwt>l^oi~=IE^DaUx)2MX(NzE=gSK0jTwk@C3Nl1zfUCFfV zHI&(E9zr?l1JQUCFhhMuP+v3hP<&`*+Qe%3fngta$i^=Y3?SP z;N{2Q*9y=f{-;%mJ3^!fX+7L%u)lByJf4X%DlJ<3-agg6nPj;M{Na4mtatclY~O0V zi$gwI?`S;`^I5ESPs@IY^-j#Qx6l^q@2zO(Kzl3&;~>_Igm2tq{depZx;NHsBwwC_ zvt0x3hv5GD0r=*yCXg=ako&vkXN66$9eWIF|CxL`XSD>!h%xKAAF~GZwCAv?GF?6Y zT#0nr^A1Nm@qH+T3+bFS0s9tFhuHHa1kFfOeSGQ}>-p6Vv@;iH(71zc6uyTUXSDGeRCrA0WTT5cDF&_ltFJ$I_byVxDP|)_a*wZ+kiWz3d=r%z2B9vksh3e zyE2eL*u*%G!MzcK(36z!;|IuhA#AQ?C=1_RSlhQQ=WET{bM=AxW{d6GeG22l$3ymU z5%m%Ku)*jL?TZ7@M?tJ*@Ri`c`y*S=8rN-&p*~}e>q0+wN89;bQ0Nxi>8r&#HTe+8 zR|TIB{0Ldx-<`6m(@^dR+CzDG8Pu~1{HA;T zX+yzl;AYu|{h+rUzJVt2jP}0U(bguk8Ge_}Wk^%o@)pi)NFG%`An7fvHJk4F0fWr; z%7n@{>JQa_T;uaTH#D{D>tzb=Z5wcZJMumhJ9Gy9{tCW~uffxEka1e;p#yfZR(?*l zezYyBu^V+Z!AC$os#y4mGvpm8_=~PE%-{=^ctu;Oj{6H8q$38f157 zpbS5!t{d5jL?`Np9^(^MiML>XyI` z5cS-_b;?N8nHq!x4|;|6SZW(A!L{IJo|ZQD%|!cSNgMLoL%RrTLbIGzE=3~UPZn=;8POz{@C+1c1P!Rjv*O0CeEUF z0Ah_v4H@!`{#EqX4xe@`$(VdMhImVRP}&dSzx5h{Zjf#MEA%#vOW!u91dFt7 zfj%C%%_+e}ntZj8-QBXyBhlu2ai?Gn?iBnE;}tR_&PwHN9?9GM3EI)OovFN?wX&V3 zklu`avN@-A?>Mx7-LecT#(n2#$R+k#>vJKOvHmqdCv}bg5qLyufRp*QHSYO_eVtwVs6zi)8Qmnh%Q>@2!*aCs0Dc0tbDb^Nas`c|7_CPQ`)!IBZ z)k=3nn?#&mOLGKzWG(vpIBZdB<81Jj*5;W!6m8y)Hi!Qb z6@boK&+M%?7aD5uuk2{A(sg(%(oUi7e2mYT_UVCEtSRDrC>3i{Qg8>(JZPMz#ITlo zu+9~u&aJ$z)L^Ejj(U=<0q8$~c-mK|An%RPRTzVfwJ|Ka9(Fp;7sJm$k5KyMNI!)z z4eek!Q=#>O@GF6neNSh_7A3JE74`2sn!@h@sXW^bzENIkFEdVkuQ~?y{ln7(A3ruD zU`2ivz7#L%PT@V62iGDb{mkbE$*i<*e-HRmRras8g-I9DSq08t4*w0h>69@#_&Ls| zXf9BH>`4C`%6tWx*@nK(#vX>^k0MU!U|Lh)C%eWLR@S|9Vr_w~xS#7- zZ65gP`=p&&7^&Nh;2jG-ps*|tY_5#+_VzSI=^PK9$Fb1iG$rnt2jQd72(Q^d@#g(& zF|~141YT6b#z<2VIurN*WGm)y3%833rCrovYQ(wPrnZ0F*RJ6GqK_fB@fdfSe}4gg z%(d)9>@;>L<9XbNJwI)uIlQL*4`akTNLg6N-pj?iG_Z$VcqXH58N(79J~G628d%;} z&=mzwg6?6^S%bTHWc!RonPT|c$TmEOc(NIi`JABlRK*%}W}#Svh?fJ|&L{c4fX*WY zKh|A@AD>{(4aASfIX@CRllDKp^(@v&&W~Ej4;Ix}4SpO!e~1?c(MH-+ZXsTDt-)Co z_FJ<5IAq;<7X9x)-nDq9Q2$wCL$|@w(|3uJsjY+Xty!QoThbb*G;9qSAL&CY?@B={7PRON=2Xm)PSi0K zcfFrMm<}EH5JGKxX$+#=I$*;J6bh_Mo;LHH}czrQ^i*t|{??Lfz7tmOcty_jTGs9Ug)`f&v@|DB~ ze}Z;}!0WY!1nl=L{4UVh^WB@NjGJi0IC|E|GYhBntc|LS7edxZu3ZSV<3o2b9tUmF zU$O3|pR#ShI9f;!^T@VbZl`ms7Q)rF6Z3XG{@F9#PhC&vIQM}Dl$^;GL56)^QC-V5=A~CS>E4fMy|Iw?(_C{(AzxcD@a$|0KbPA!#MxP6o|5rMx?*l1n#y4GiToWi?KvK1&Ng8F*jU*vNP3*+yD0>pVOVSUn@6BsZfxe`D8yxT! ziu(xnaa7&%6#Ag*mV<~V*o-!;!M^+f&>{V`7W;C@CNKK|m6iJDagzUP5=LUZ5;%{hl}c$~yHJdVNVa17t^2;n;(_|eJH+ZGv&yKRqc=!VS8ZDy^-(r zMcb~^wCx&A+cGrpL)6Z9q5_byomjcc(+FyvrU7nlbS<6q+58A^ceICz~8vG1xCqG68{1}CUS#}6AU{<6rV>r!K@|UItk0BrJ>v89HQ=yr)(Ag^C z$AObSV=3+@S#W;|ju=b#(WtKO>+x>2>c4{iYlEMj#)5qGr0-~aT$oGhdFIuCNv>(_ zpmgZ(o;39y8=ZI1yR=tfd}$611}!y?-u6n9w}FUBs&IU&ihoQBhet@;%4x%B~hM}M_(Atnkw%5=5 z+hk<_6reoWKYT4Nu!R#rgYuh?4DMr>srPMXq7BrC1l;55+jp{|=!J9t}v z=mtq+|?10Z0-@CHVcS^8XTEFRcDUItcINQcW zvBq`x$F&8!yQhF}>&M;f2)_ZD$vXd0+qluJ@mLJ}g1}?3PEJ)4JGVn_)H-Mm`~mfF zxdCn3qtRJt^M&qRv^S>wP56GxY1|nbb2;6ytep>EP&gX0c5D#7sf4=E7zN$*QD{H* zYsUsb2M)(KV(zd7rWzAEkD%?R!ILy4zLR17&WVSA5b+FjGmN!+Q*k$SCc-yh`;mVp z59d;W3o%({tm$;mjrOusmcEgehq5z=+V+l1Fy_IGAWARTvXDLozO7~2-k`)a{^JAH!;`6+!S-m}b7%+E0BROqMqA>8H5 zFtS~T(H2U_d0@j-_$WHCpZgBvx)r?D(sJQG2*+1AV3$mNk9=peuQ(O6EtP1s!FJyV zT11<8O>t#syIam8#6V>6Z|2~fdOF&}>HM2)BiR`=XVy9(>+o%Zb`xw-I!bfc{L0)XwuXP-S&lfUoh_0RgGR{41v202QY^AjOSik~%IQ2ffBZ4`Hjn1x) z3+A?F%()DVV>8Ar0kk2j^PN+Tf5j^TZM?mG~lw7Xv5#aSHqqzWn&$Wb8Xhhm@gxq(5lh zs%bR$(GNcNk?%O^lluFeZRRL;sQyB1TL67Kzj`M0Rvz@%Ezn~#pwFh`I|10o*NJbH zSW$O7baXEIL-vQNYtzm}2e4n^-wtDpJ%>ApVb(Kduck`I&PMFmF6Se zhrrp-@#MOc#vFt>#VL(9AiNO|eg6sXNgaRUKuV*z`=d59<}-aGW&99(*GGmpE>E<_ z*avw^hx}(CPd&ze42?Hr&kS7>3xCK*hFG4L^z1&swU9$&cXxpqI`|C6CKh{fbN9Bk z4CKul%yyBk{0KC&mCai-P-b1X)a7f0E+2 zB|C7Qp7*fO5Bs4X%IZirBy!zAI_C{T?N;hj2sGkQ?^O8vXnw@;HpX#%toDb#XF>g; zwPK*Wk^P~*#16^rij8Kw+DPX}eQ^@?YxPAt^hG}Ogdd>=;dfzc!5=O31>*QvY4ioh zHM;Yjlij<>|H$w# zE9C8c$Z>jiUp>aB;2hov#&y1*98bwL)=v5w(Ptk*XdY5ze-9AwYNHafmVUQYX*ppA8~AMG@z@bBc2 z-tK^G?#C19!;NRQz3ryV>m122=j0@0%NmG!8#3n6O6sJb`N~)lgE?&0{h?? z^ex#hG)}ZWknfIcMbvZnbLgm<$RCURBm?AQxCZ51N?gMmC^s6uIGq1k)HKqWRQ?ZG zXE6sS*GIG4X^;0Y$p~m`{lRKIli^co{&DS|H}EhepE*I77A) z>{*geqP5)+|CZPz^uKF%sd>v~>0n$V=p4-bBChc$=Gb~_;~?RmqwkIKb@bzu#@aEg z@kOLcm-}D${ay0Uft{q8ZwLW)!LSuS5YaC_5_ZTU@Fh#`UY&V@58WYv{R+)cZ zBF{ez@pNBHwP`vr|J1#s8pr9eQIcK~FK6Iw6leMarQTL!JL2kok-onITV}pF9d^B9_Qs~x&W}rmZyw(fjm2FB z^ZAAF@%(VZP0&G)V?DURhVS^=)^4En4Es55n`R?C1AT(;lx6=Z+V*7==H}F)_|`7$ zhlNorn{*HH|7qBEe>B0Tg>@(mccG5Kw`j&U0P?P0)0TJj__jA;gTxy1vOR2G_O*)T z5jxMl_Jn_f=OLCe`HrorkLNvd!E5I$aTm#D0s{m7o2r|E_73T@M^xdF*N>>+qv<<Gi#`1g*(TVV48_`sJ-})B;^F5BliZNrSO=QiS3>(oS5~9>T_f)MsPCWH!k<7d z{tY^J3GL-fI0J*eNHen&t=I$8`f~amTi`ceJ2s#$ey&Hv! zTebb&f5S!ln@0UbKe6@~P`|NW6#U=n8~HZL#{4$)EZ__AWEOl#9njkY@gy>i?1?t; zrj2s7F(A7BqS9pRaAhw`jS3Xny+Iwzn~l zC||2#)>pNd-_*9pzn+^-b_e{tfti?}Rf!Hc(rBO>Lp|2=|HigC@o8Kpf47wdez_(`8sgQSKl^xr?ngPmsUt5_bN-%3t?4 z@b~)>{H48y%3p&2pYS*A-=Ok$=4t=M_{;f}k2Vl57f@Rw_&XbM#NToM0)O#NoJDK< zXJ90uJ4c5G@x4b9 z(hDQ@9+7(iqMe920~_byrUypxd69s={o%k4m*)3SlMhTAOg0b4o-!`?LX>(FGKjx9pN(QC8kV$5Bg#<uCfX9)H+o%4^0&Hak}iQ`ar z#{N;9mP)68KgE5$f%^A8XsZ1yGqUX3#J>HrV;vr-f0XY-l%@U=Og45s^>K_Xr;YZY zCfM5e_R<0L^@qquwjIfM2J~~;n1TBaKPK-xy~_|et{9V@qpgGae4#lVIbXPJbx7H= zS-5O{53=mz06GKq z?KROiY)bp}ZNU8a9}VQgr1?A`FVZg;dxM=~Z}2tv&v{fRW0@iCJr2Y7*aO3#*aJgP^uQxDFy>cJ ze5wY%LIaP{z*lMD@f!F#4Lnf;XKCOYH1PK{aIOZPp@C;<;M+9td=0!n125LVKhVHM z8hEJ&zFPxVXkf1fzDEP!tAX#=zz=BPhcxgb8u-T=c!LIhTmx^_z`xMIPio+&HE>V^ z|5^jTpn+c!@Gz1S*)Z5nvK240|n7i-`jXy76Zyi^0R4&*Ixk7l`#36@PSDl4?l9!2?^7gcDhl-zXN!+gx?1II|;+4O~QR8 zUY5>fm%xZ1`Ue0%DB)JXdnHUh6}*?v^Slf=8%}V_Pyc_VLc%S8pOEnLfPW|9UjzO~ z!Z_nf>XIOy|8nmvABAT@wC2-~$p~4EUgg7XtoF z!UcfONceWZad7xk8*T%PcSkrp8*qk%^8i~VOy3^8O~Sc=OC>xN@Vyeg3Gm|*#{Gb# z771qoeoMlW0DmUoOuz9lNn0h%0KYBa^EeOx zNWxzMJ}u$1fQR4%E!4+TfX7Jq1mGJa{13qN56>u1C*Uf;+Mg|B?6`UJ=iV~ke(Mc$ z9Mc`%nks+gY>z)Z9kH{fI|};5&hWaHyH{3vmwk5v#=7@+e5wjVZSWt|!9c&(#fZg} zViZDTnYK=#Y(5@pqMCjeV6c`I;i3E`cyPAF%JE=&@sQ$;EZvoU$68;VHktUyA=ZVKvBNXyEbZ@z+UrjDx~nR^eze|LKMt33Vza^S9QH_7QJJTZ8zVt{x^V}>-WHhlO@ z*h86+$Asm|1i3RY8)GDs^nCCu{K1c4?GJe|+A~?ELXyc$=TOZ}CT1KpoSCMLV8&Ca zEb57o%-D4~Gnvzvaqv~lRDLxxwqD1gYOiNX{q@Y$Jb{^Coy=nEr?9wm9J)Q5!v-z7 zff=^kz@nONWTq!>g6!YSl#Hp&@BwC@&Bmh2?2I+rnIVMm)m)~0;$ZB`Y0OYR4dHY) zI5eFN*))r>n`SdZ?QEvdMcj7*e*rjo4m0GOlkT)8@lrcOgZ=i7QNoZ*svlN^+^#^OvTtR zl`z8`Oj?_pDR;Rs-~Jn)Gcapq?n zMx4Y?xpzKOR$ZYC8=S^6fhXhH(tt^ZAI==buW<0?!+xRs+_2HO$@F;C=IAXkKQ(WS z{aM@-gBkp6Y!04buZl93Z&8MbUW?~JJYW1)lzW>Z={GN;E)mP?X#rfnPvqb7E$gB7 zeSyahV~BpwN_ylwQkc-V0Q7}mH0aiPyMGNFWII1H$YyQq6az$bPnho&^e%U zK<9wY0i6T?i#agQC=9;&GQ3@e_?{>KnPjNq7YofY-y$`?@SS7>z zWLPi5O)_kf;j=Q_Bg5BZcu63Psp%YhAlE|mEqemd|!s2$ndxf&&kmI@1ngH8K%qdS{YhpI8BE0WO$bh z%Vg-6VXX`wmEjf{{!)fJW%#NL+hq8z3_E1_g$%o7Xu^lo=r>G;qhy#N!^twV$#9Mg z7s;?hhE+1WPloj}6x9F!{hRmb|E#{X{z9~8=My4q`uYE?_UWzBIiPbu=YY-uodY@t zbPnho&^e%UK<9wY0i6Rn2XqeT9MCzSb3o^S&He6x+Q;h5AK^Vaj~arV!nGtwY#F&y}&xt@AkU9k`m!AhTwc4kw6~xdb88h*a{5+!minhJukzPuTS!Sf#aZO_ zl$5&X7TxVG_Rn{hyInqaZ=zR?7)kt^kU68Wx}wD8_f%F)n9|$z+#0{zC#vh4Y4S9e z-?d=E6z6<*mDlZaSNM6^xv~>ldLnBtlk@#{yE-)e^V#kF>bKif*7eV>4oCkK*^+!3 zd~Irf^0_`a0`qyg0QR~*!cAoH^R?3(B(b9r6GkfD6Hf5F6wE6Y4!T9vEV z?Xy>u+~!_c?x_IixTkb6NPErSMB!E!WgFH%+a#xxxUNd#lp-3Ota5inslRM;ab;D_ zgb5{XS9y75F$r^4<#0xp}#o4l>RQIj(U^-wQ4|Z0jH*x zPyfd9Sx(+gYQ;UJRAt6D7ZH6zWge$mQRX+6%65uk`5sY^^;?t>JW@;8zOjTDo4o${ zBInG!%n6w*J^r#gDtxY`ZbxR#iV~-jp8SQg7fqjCKBM4n;>Emvl;p(9oa6GAy7S9D z&`%R5xGQ|sUiTdp9%!uj?xk)o>8+ZY8AM{y#Yjvl!i3M7JiVg0vcz4IU0Fqw8*+%@ z&o6W4S5C~nEuZ8mmuH*iMmA`yxgy&F@ZCY=3ok}~GPHJSZ)+TaHDwpe-cM6ZnBsL; zm1CCVR~LDSlyfpMY)UZ{k-sLtq9)&Si<6`c!pb{Z^{q)~xvHw%6(tigi=o4zntXGY z*5ogA3Nov{HJKXliZ`R!!v(iixmeMhKCBk1Uu8I!q6k*=5z(+(Hmv?)fy^OKGvl&zzRIz&=B=y5k#*Ijurl z<)Tfps5&)50~F3yTZj4OEB1P-{MM>UpNA^}x!&dH!)if;7RWXnSIOkS#+s1DjjSnT zs;$!2Ez6+gYV}>RoNJ=hDc2u&ZuL^=DX6RpROu@B-0LoxFl9Ng<<-j_F076oe~m9Q z%gIfb+{^`JqCwlw;A>>9K?ti$3|YNI{gW%)tNg0;-hOX>X^qsQ>kWKr=GXKQR8j!T zt12r<1x%PA<=b5X2Ev}pzlGGX)1O~6A7MEkK-iMt7=CP7}K$i1lPldk{)<9{#w??&lZjtTYqZXYc$qUY?FNX?pFT^sCzr3>CQRc=D z!kOb2L{^Z_4q0)=DU5^Ma&fpJ1DD|QU&^cXRnT% zkclaXAtilRQ&Uw!I!F|G-+)bx!{PAORADc|yd45}=I7ZbPRXK}kOBLao^6W*jOAuB zB^{^C+P%+Gm#3T(x_AQL0eRh}9v^CVF0HO8rb+5_m6SL=%dxwloV7;CzO>+d{*rP} zk+ZUj_9w)E8WdczC#=K++tXiM=B$K`;9N?%w($6JUk!Fq z%-clRQm>oW)=WsL+wUU|SJDoETc0~AdO4rVRAf&t>hxhMQ(P;Lb1yGmj@=0Jwy9`U z4WS1my1eopp8I{{aGSHT=9NQ#V#ZRww+R@Br#skar)+C&)UK23EyDGlu)+nu3~J%nRUNZz(e6!tBp zwwWLyVvEO1h*?T02Q?|QX`P8S(`+IcrS$iE(+AufVni;M!|%f09wb7&8A@s@T+2Ph z4wsKJ=6G+4+v~0L_G~#ZKK+tZaxK+G@ z`aXHlFKnxP1upEZsbTATQ$0RmDwo&v7|SKjipq-Hs>{ornDQPWlUsUA&|D!RO}$YX z(QED(>*T!Hsf|~Aw5K=H2OV!$$F#L?{IuTDZquZQ2@iW14iRe5!6;0Gdn&7o%b+k^ zB{1R90b5mcVU49yJ9M%2RHw!h~K~ z2``Vs#2$T{s`%pfsKS`Z?w(lct|0a0&Ucmj$v%2H27t{ba{x1e&FyCSRF=l0Gd)4zvupZ=Uwvefoq z{{kl4r{@z3`1&JS7e-YNe#EQ7UuNJtBb7^N{f7o_-fCh#F__r94;#?9PmUT;0xHy4 z!5%l7*!nsQQq%>jF``mCVd%}1ddm70dN2pK}{xZXD)ys?E6Z;RNXbXGG z$PDngOq}DG&ex*K*&aAf7O-cH+8A{$I7Gt0h&kt!*61%{k4HZk+ici5yjl6cwBGRG zWgUihFKbmyrVo_Y44b0gR<^`EsywS)IjY`pPFc06MJf5Effn| zcgXZL67Q1u(-I%HRnWV3kf5*PBL)lHBGbDf@DGRdO@Hk&foI74!Jz`5C-KZ-0xy&J zOTe!Hf6FC4akv;y^*fJIBSii!@_UcDz-j!|?>}b7i}aoH`;e8uDP8?us#IUw;WiH{j6(wih+D)BuMe+D?U@1Vs0Ot`6*l|3)$ogqA`mfbD! zm{9`vO8iQR-zV`I5`RGA4@rE3#9xy56B7SY;z5azxm?h1k@(#be@)^)lla>b|2^SD zYFS6CXs_W4frli1gT#+Zyh7q#5`S9aY`-Y~zQoNEk4h8thDrQ;5>J+Rt;EwMzL)S} zwQQbI;RKIL{C0^CNf+t!C2j+L8RqyRiO;3<;fxhZ+(kI%bh1g%yPt5(;pQlTZw7uD z=H#Mif$x=ghs39k7U{<&zFOieM&$pq#D_`z<}o6Fy2J}5o+0u3C7vnqpGe#)@jpsD zPvXPI%JxZoro_u6{&R`1k~o$xvZh$aP5}6UeH?F+_zl3(as#E&&w)pa-zIP)rO~el z57LP&|J{whT6~4Td+0q7fp3byX_{#3dsE<12>94Y(XoC1Hi@<-#aU1@M-?|9=7ZLa_j`#NGKO*oyN8ta6zzyiBmLJIx z_;(}ltO)#j5%>&&_l(b-5x6%3|4{_~s|ft%2>kaE_>l-a8j_Cw^wfWO1fCax7ewI2 z5x74Be;@+i7=b?>fxp7>-tl}>q+=9%eyxE1S3DJHXeFL1Jft_gc>H+o!E-;JHF#?A zti|&Ho;p1LhUXzX596uFvkuQAcpC7KN&O={KgRPIp7nSd@od1e5fAAeGJ7A#vl-78 zJU_*=70=J`Jb~xucz%Ir8=fXS+wnY!X9u2N;&}?s(|Aa4HRB23@!_e)6U6f%9{gJW zm1iX~QyZub{8}&es64t@`v1gu?HK=W99!^|IS1Is%1!V+l+L%26758#M)mXl%7lU3z5^z4ccx6yI(lVP9}jQe zIBm&G`*dYFWa{J0mC@hEiz~Bo_pO9gzinR>4%?mxZ3BBz(R=}d7wn^fBD_A-y>wQN@U&ZknoIEdJT$^nI>F~Qr zR}jv6P{?f@Z@ew)P65?woSpXG>$C>n$koue_sJ1^ZLo14Q z%f9?9AkXi(*J3}jEBDJ*g6xSn9~u7YqMpUQOYRN1qp@d={a^dhgc{z5K%Mg{Ado(;c=vpLEmeCC>MT$Cj66K7HnxT)RK5?(aUYAp+ zE~pMaCMg4wYS_L`Ju?E@{TjJ@G!}t~T~5c5^(UTLrf5l<-QA6i4it>9%up2YGEL`8 zvc6PF>21XhU=^k*!3L{+36d5@$7GnqYiSHHn#qm%4@Z*U**TLrT16dx$#LR#r~x?8 zsiZ5DY?DDBUxBF=#)bvX=-5L4REH`|sNk-yxU!UvZ)J}^hYMY*ex!C!3v)4) zuxfNct`)oSy3JdZIU6nLlG*IFEqYQPjqKOAX*4{PwntRiF3!gdV@uc`e?r+6^^W>s zVfHDc=mVe5XXDYt`NXg1li_SMop9Wv^@p9Nhjfb`z3e{X@>+b9U~hMjbt>c)@g8s9 zzllpzGrae!%kE#nCAn?cCGviCX$dah`VN$`erqc9)7u}U`&BUW)9+~W_R*lcg%b@w z3ZGt=d|pGmSKnsGoij`L%sycSa22n2b0*zrE}oGKP3{F8EC)Yg4K@`4z&AhIhPs1l zUP}Ml&RqoYf4%`V=qV#YIML+PS81Y+-0_^7Ge~XV4IcYJ_TY?oJx+X*X^l-*B^v=r zx?P57cJbHn`P|HjD~nV+eO9lXJUBw@7!yj5e_k+iW=fqXwZcCot)3w&geSSEOd%># zZO+Qeh1EGCEX1gYY+#E>q^z8!uojz)Tn+`?q7f)rkbl@Fs%v1j*(e%}5>eFBoy!9v zjtlKrSSiM8vZJ+xt$8;02Hd;iOZ}J>;Oz1t@*fB3?RJHB0+iu4 z!LZAwq%Ol!`4Vv+0t~h#O<)v9pkIz?|0Wygsw`@k2)o3!gI-|4>Jjj<(G#7ZvZY-C zrbyIM8{$F*|5-qaVyT{MbIz2U1TYu@Z>N+bNLze3XHT zbt+bm4{s?2&=F>TV3lI4R;y%Rpc1(SC8u~Vc58zCO3lSC?hr}~{ToI|xuHcLU+-IH z$K~8~daoO7zX7wSvVu*>r*JJwOP_;i%iQ$|RLj<{P0HrXMCbJw(~4PoqkM}78pW16 zAmYR$Bf4pYoU8wg%`28s$uQAb(=PFslhWp`%~UpKUuQ&?Zakd7x!MhD_r4)**3 z8QsXCc5mlD_a^uSv8Z#Ogb$G%D&WWyi-84>355F?GmRH=00${~?Ex@3|9p`RrD!k3 zn~QoYY=JHy_Tpc@#u|s`w=eQJGq<_}%>*4ot-*`^j-njo1~Vr|5u5iKT#MY8WD1PeoUf7Ae59&DOQe%6(qRQDGr(B9{ zkC#syX;Iv{hYRzetB#A%&ohWJUyh~$`)-QHFkg^>Ta0o z+QIw&z&)xvb=g!^q|)pOm2I-Ss{RrI0Tq(87q!X_2K Swift.String + public static func encodeSeed(email: Swift.String, password: Swift.String) -> Swift.String? + public static func encodeSeed(email: Swift.String, password: Swift.String, hashKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String, hashKey: Swift.String) -> Swift.Bool + public static func generateSalt() -> Swift.String + public static func computeClientHash(email: Swift.String, password: Swift.String, salt: Swift.String) -> Swift.String + public static func computeLoginBearerToken(userId: Swift.String, clientHash: Swift.String) -> Swift.String? + public static func otpEncode(_ plainText: Swift.String) -> Swift.String? + public static func otpDecode(_ otpEncoded: Swift.String) -> Swift.String? +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.swiftdoc b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.swiftdoc new file mode 100644 index 0000000000000000000000000000000000000000..c14795912554e3541f22daf274fc521a7dee2c2c GIT binary patch literal 3656 zcmb_fO^g&p6z*Nb)qxl!8V{QIAW?gm>7Jkc0hTpkW`6)d*f_)kG1A3M%}hg2cc-dm zfAE3?6Av6n+=~e@MvZvDNDK!KB*-KtdNAQY;>pC|#RCU!UO4zwP0#KkqaeGp>DjKX zS6{t;^}X-CxpVQGp@C;CD{WcU(7>+j-f!=vzM9>1c{X)pZ|b{z>IS7Q-%EXYB{fv~ z{?_?U=Gvh1Qzvt!llyoubG>68 zX152k_s?d|;b|vpefX?p)izkxYnv@=2YlYT@#x7jKTv+l_Nl_`<`*{n`1#hMs{_^m zrh1<(n{Zc(J50P@=T+s;ac9&m!v@s}^?c-3$nnw*{#i?NCh`sy=`nP`oTI85*6_>n9OyLmjdtdE|ztWo$~e0s~~ zw=Qg6*lnI#aP;>N_MDl*=;l+FCC3ZJ)rh6iyM>+As1>&rpA38+s{KqhM|q3+!LFD| zIUkAIh-87P6f4JZshMN14q8@aYKl6ShQ~=n?XZ@oH>t9}R2&_%!$`DP;GgETX^lY{ z+J1aHzI#}>)oAG~sU{C;C$%Dhu8j zh)MF5q`+5Pu)r8fCH$~KiZ7@vg^{8fH!FcgCl(c#bmzb54ao(johZaRcR@_RjF&BcLC3ZU=J8Nc zVk&*aPupj)Kxj(jI+w;=MJBp$Ar zN(QZ*pgkt7yoY!N|3ZVbFG1>&>Oww4-yWbkD!}bti?m0qU}97 z_q?=gu%>$vS|bq+SSA5`JW{4$ZFGCk`udZf$6m+i56E~wNh~&a2vt$MqLr_*ICJb* zSPvITu&@@jx?=KxIGIGL8U=MsQfRiV%83z|_9d7#Bn-61WPZbkW_88JMlvp|Br=<8 zk*qQ#r(WRH_?(Yb_CdZ_Z9+XT8JZYM$Q7}0alQ!=gJe~Ctmp((Inc7hs;9J8X{{4n z@5V*WfMY1*D3shq(J;ySHva2lUa!|QM|3a&U7uGrkcSe;b$We63qV@$-~H_iO^O#$ z-};bZcckKJQ;8NUl}e*hnK||+tlwetbmZu)GcQ<7vG7~IF7;&B5#MHyFEOhbE>u~d ziyB{uBVEF3zJRJjY$5Yh52E>QfeQRn+?-e<}WC~;xGTdkqTjq(>Ald`6yVbUW2f1$f)VFCD&BFtd zz8>#wj+0S@Zs8eiicz9Ys7(CA>Y*z-HCOXcD$ndt`Ue;Qb-9097txUQjCx`U|GEiOgNVUB8DV4W-i61F1Ub>x!dECPx)iiX zqK(!Wt(c~JH{k`>^Q_mzXzn9+#7xsfYCh_viBh~3E9vUKVRi4&KZaiaAbMHSGye>u zp+&DouL`N>cFj$nu^d&eZ&lb7I|96xP#9KXxz9HAW!vBc5LEM`l lt|9&=Gmr|Fg`a_z))9U6`@Do>YNL1Do@#De{R2ze{tdfhnGgT~ literal 0 HcmV?d00001 diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.swiftinterface b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.swiftinterface new file mode 100644 index 0000000..d8cb5f9 --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios.swiftinterface @@ -0,0 +1,22 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.3.3 effective-5.10 (swiftlang-6.3.3.1.3 clang-2100.1.1.101) +// swift-module-flags: -target arm64-apple-ios13.0 -enable-objc-interop -enable-library-evolution -swift-version 5 -O -enable-experimental-feature DebugDescriptionMacro -enable-bare-slash-regex -module-name LCECryptoKit +// swift-module-flags-ignorable: -no-verify-emitted-module-interface -formal-cxx-interoperability-mode=off -interface-compiler-version 6.3.3 +import CommonCrypto +import Foundation +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public struct LCECryptoKit { + public static func generateRandomAESKeyString() -> Swift.String + public static func encodeSeed(email: Swift.String, password: Swift.String) -> Swift.String? + public static func encodeSeed(email: Swift.String, password: Swift.String, hashKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String, hashKey: Swift.String) -> Swift.Bool + public static func generateSalt() -> Swift.String + public static func computeClientHash(email: Swift.String, password: Swift.String, salt: Swift.String) -> Swift.String + public static func computeLoginBearerToken(userId: Swift.String, clientHash: Swift.String) -> Swift.String? + public static func otpEncode(_ plainText: Swift.String) -> Swift.String? + public static func otpDecode(_ otpEncoded: Swift.String) -> Swift.String? +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/module.modulemap b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/module.modulemap new file mode 100644 index 0000000..cccd3c0 --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64/LCECryptoKit.framework/Modules/module.modulemap @@ -0,0 +1,3 @@ +framework module LCECryptoKit { + header "LCECryptoKit-Swift.h" +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h new file mode 100644 index 0000000..1f9e2d6 --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Headers/LCECryptoKit-Swift.h @@ -0,0 +1,760 @@ +#if 0 +#elif defined(__arm64__) && __arm64__ +// Generated by Apple Swift version 6.3.3 effective-5.10 (swiftlang-6.3.3.1.3 clang-2100.1.1.101) +#ifndef LCECRYPTOKIT_SWIFT_H +#define LCECRYPTOKIT_SWIFT_H +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgcc-compat" + +#if !defined(__has_include) +# define __has_include(x) 0 +#endif +#if !defined(__has_attribute) +# define __has_attribute(x) 0 +#endif +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif +#if !defined(__has_warning) +# define __has_warning(x) 0 +#endif + +#if __has_include() +# include +#endif + +#pragma clang diagnostic ignored "-Wauto-import" +#if defined(__OBJC__) +#include +#endif // defined(__OBJC__) +#if defined(__cplusplus) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#if defined(__cplusplus) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module" +#if defined(__arm64e__) && __has_include() +# include +#else +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +# ifndef __ptrauth_swift_value_witness_function_pointer +# define __ptrauth_swift_value_witness_function_pointer(x) +# endif +# ifndef __ptrauth_swift_class_method_pointer +# define __ptrauth_swift_class_method_pointer(x) +# endif +#pragma clang diagnostic pop +#endif +#pragma clang diagnostic pop +#endif + +#if !defined(SWIFT_TYPEDEFS) +# define SWIFT_TYPEDEFS 1 +# if __has_include() +# include +# elif !defined(__cplusplus) +typedef unsigned char char8_t; +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; +# endif +typedef float swift_float2 __attribute__((__ext_vector_type__(2))); +typedef float swift_float3 __attribute__((__ext_vector_type__(3))); +typedef float swift_float4 __attribute__((__ext_vector_type__(4))); +typedef double swift_double2 __attribute__((__ext_vector_type__(2))); +typedef double swift_double3 __attribute__((__ext_vector_type__(3))); +typedef double swift_double4 __attribute__((__ext_vector_type__(4))); +typedef int swift_int2 __attribute__((__ext_vector_type__(2))); +typedef int swift_int3 __attribute__((__ext_vector_type__(3))); +typedef int swift_int4 __attribute__((__ext_vector_type__(4))); +typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); +typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); +typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); +#endif + +#if !defined(SWIFT_PASTE) +# define SWIFT_PASTE_HELPER(x, y) x##y +# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) +#endif +#if !defined(SWIFT_METATYPE) +# define SWIFT_METATYPE(X) Class +#endif +#if !defined(SWIFT_CLASS_PROPERTY) +# if __has_feature(objc_class_property) +# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ +# else +# define SWIFT_CLASS_PROPERTY(...) +# endif +#endif +#if !defined(SWIFT_RUNTIME_NAME) +# if __has_attribute(objc_runtime_name) +# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) +# else +# define SWIFT_RUNTIME_NAME(X) +# endif +#endif +#if !defined(SWIFT_COMPILE_NAME) +# if __has_attribute(swift_name) +# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) +# else +# define SWIFT_COMPILE_NAME(X) +# endif +#endif +#if !defined(SWIFT_METHOD_FAMILY) +# if __has_attribute(objc_method_family) +# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) +# else +# define SWIFT_METHOD_FAMILY(X) +# endif +#endif +#if !defined(SWIFT_NOESCAPE) +# if __has_attribute(noescape) +# define SWIFT_NOESCAPE __attribute__((noescape)) +# else +# define SWIFT_NOESCAPE +# endif +#endif +#if !defined(SWIFT_RELEASES_ARGUMENT) +# if __has_attribute(ns_consumed) +# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) +# else +# define SWIFT_RELEASES_ARGUMENT +# endif +#endif +#if !defined(SWIFT_WARN_UNUSED_RESULT) +# if __has_attribute(warn_unused_result) +# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define SWIFT_WARN_UNUSED_RESULT +# endif +#endif +#if !defined(SWIFT_NORETURN) +# if __has_attribute(noreturn) +# define SWIFT_NORETURN __attribute__((noreturn)) +# else +# define SWIFT_NORETURN +# endif +#endif +#if !defined(SWIFT_CLASS_EXTRA) +# define SWIFT_CLASS_EXTRA +#endif +#if !defined(SWIFT_PROTOCOL_EXTRA) +# define SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_ENUM_EXTRA) +# define SWIFT_ENUM_EXTRA +#endif +#if !defined(SWIFT_CLASS) +# if __has_attribute(objc_subclassing_restricted) +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# else +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# endif +#endif +#if !defined(SWIFT_RESILIENT_CLASS) +# if __has_attribute(objc_class_stub) +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub)) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME) +# else +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME) +# endif +#endif +#if !defined(SWIFT_PROTOCOL) +# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_EXTENSION) +# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) +#endif +#if !defined(OBJC_DESIGNATED_INITIALIZER) +# if __has_attribute(objc_designated_initializer) +# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +# else +# define OBJC_DESIGNATED_INITIALIZER +# endif +#endif +#if !defined(SWIFT_ENUM_ATTR) +# if __has_attribute(enum_extensibility) +# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) +# else +# define SWIFT_ENUM_ATTR(_extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# if __has_feature(generalized_swift_name) +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# else +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +# else +# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM_TAG) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM_TAG enum +# else +# define SWIFT_ENUM_TAG +# endif +#endif +#if !defined(SWIFT_ENUM_FWD_DECL) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type; +# else +# define SWIFT_ENUM_FWD_DECL(_type, _name) typedef _type _name; +# endif +#endif +#if !defined(SWIFT_UNAVAILABLE) +# define SWIFT_UNAVAILABLE __attribute__((unavailable)) +#endif +#if !defined(SWIFT_UNAVAILABLE_MSG) +# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) +#endif +#if !defined(SWIFT_AVAILABILITY) +# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) +#endif +#if !defined(SWIFT_AVAILABILITY_DOMAIN) +# define SWIFT_AVAILABILITY_DOMAIN(dom, ...) __attribute__((availability(domain: dom, __VA_ARGS__))) +#endif +#if !defined(SWIFT_WEAK_IMPORT) +# define SWIFT_WEAK_IMPORT __attribute__((weak_import)) +#endif +#if !defined(SWIFT_DEPRECATED) +# define SWIFT_DEPRECATED __attribute__((deprecated)) +#endif +#if !defined(SWIFT_DEPRECATED_MSG) +# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) +#endif +#if !defined(SWIFT_DEPRECATED_OBJC) +# if __has_feature(attribute_diagnose_if_objc) +# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) +# else +# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) +# endif +#endif +#if defined(__OBJC__) +#if !defined(IBSegueAction) +# define IBSegueAction +#endif +#endif +#if !defined(SWIFT_EXTERN) +# if defined(__cplusplus) +# define SWIFT_EXTERN extern "C" +# else +# define SWIFT_EXTERN extern +# endif +#endif +#if !defined(SWIFT_CALL) +# define SWIFT_CALL __attribute__((swiftcall)) +#endif +#if !defined(SWIFT_INDIRECT_RESULT) +# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result)) +#endif +#if !defined(SWIFT_CONTEXT) +# define SWIFT_CONTEXT __attribute__((swift_context)) +#endif +#if !defined(SWIFT_ERROR_RESULT) +# define SWIFT_ERROR_RESULT __attribute__((swift_error_result)) +#endif +#if defined(__cplusplus) +# define SWIFT_NOEXCEPT noexcept +#else +# define SWIFT_NOEXCEPT +#endif +#if !defined(SWIFT_C_INLINE_THUNK) +# if __has_attribute(always_inline) +# if __has_attribute(nodebug) +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug)) +# else +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) +# endif +# else +# define SWIFT_C_INLINE_THUNK inline +# endif +#endif +#if defined(_WIN32) +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport) +#endif +#else +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL +#endif +#endif +#if !__has_feature(nullability) +# define _Nonnull +# define _Nullable +# define _Null_unspecified +#elif !defined(__OBJC__) +# pragma clang diagnostic ignored "-Wnullability-extension" +#endif +#if !__has_feature(nullability_nullable_result) +# define _Nullable_result _Nullable +#endif +#if __has_feature(objc_modules) +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +#endif + +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="LCECryptoKit",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(__cplusplus) +} // extern "C" +#endif +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__OBJC__) +#if __has_feature(objc_modules) +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +#endif + +#endif // defined(__OBJC__) +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="LCECryptoKit",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__OBJC__) + +#endif // defined(__OBJC__) +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__cplusplus) +#endif +#pragma clang diagnostic pop +#endif + +#elif defined(__x86_64__) && __x86_64__ +// Generated by Apple Swift version 6.3.3 effective-5.10 (swiftlang-6.3.3.1.3 clang-2100.1.1.101) +#ifndef LCECRYPTOKIT_SWIFT_H +#define LCECRYPTOKIT_SWIFT_H +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgcc-compat" + +#if !defined(__has_include) +# define __has_include(x) 0 +#endif +#if !defined(__has_attribute) +# define __has_attribute(x) 0 +#endif +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif +#if !defined(__has_warning) +# define __has_warning(x) 0 +#endif + +#if __has_include() +# include +#endif + +#pragma clang diagnostic ignored "-Wauto-import" +#if defined(__OBJC__) +#include +#endif // defined(__OBJC__) +#if defined(__cplusplus) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#if defined(__cplusplus) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module" +#if defined(__arm64e__) && __has_include() +# include +#else +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +# ifndef __ptrauth_swift_value_witness_function_pointer +# define __ptrauth_swift_value_witness_function_pointer(x) +# endif +# ifndef __ptrauth_swift_class_method_pointer +# define __ptrauth_swift_class_method_pointer(x) +# endif +#pragma clang diagnostic pop +#endif +#pragma clang diagnostic pop +#endif + +#if !defined(SWIFT_TYPEDEFS) +# define SWIFT_TYPEDEFS 1 +# if __has_include() +# include +# elif !defined(__cplusplus) +typedef unsigned char char8_t; +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; +# endif +typedef float swift_float2 __attribute__((__ext_vector_type__(2))); +typedef float swift_float3 __attribute__((__ext_vector_type__(3))); +typedef float swift_float4 __attribute__((__ext_vector_type__(4))); +typedef double swift_double2 __attribute__((__ext_vector_type__(2))); +typedef double swift_double3 __attribute__((__ext_vector_type__(3))); +typedef double swift_double4 __attribute__((__ext_vector_type__(4))); +typedef int swift_int2 __attribute__((__ext_vector_type__(2))); +typedef int swift_int3 __attribute__((__ext_vector_type__(3))); +typedef int swift_int4 __attribute__((__ext_vector_type__(4))); +typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); +typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); +typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); +#endif + +#if !defined(SWIFT_PASTE) +# define SWIFT_PASTE_HELPER(x, y) x##y +# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) +#endif +#if !defined(SWIFT_METATYPE) +# define SWIFT_METATYPE(X) Class +#endif +#if !defined(SWIFT_CLASS_PROPERTY) +# if __has_feature(objc_class_property) +# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ +# else +# define SWIFT_CLASS_PROPERTY(...) +# endif +#endif +#if !defined(SWIFT_RUNTIME_NAME) +# if __has_attribute(objc_runtime_name) +# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) +# else +# define SWIFT_RUNTIME_NAME(X) +# endif +#endif +#if !defined(SWIFT_COMPILE_NAME) +# if __has_attribute(swift_name) +# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) +# else +# define SWIFT_COMPILE_NAME(X) +# endif +#endif +#if !defined(SWIFT_METHOD_FAMILY) +# if __has_attribute(objc_method_family) +# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) +# else +# define SWIFT_METHOD_FAMILY(X) +# endif +#endif +#if !defined(SWIFT_NOESCAPE) +# if __has_attribute(noescape) +# define SWIFT_NOESCAPE __attribute__((noescape)) +# else +# define SWIFT_NOESCAPE +# endif +#endif +#if !defined(SWIFT_RELEASES_ARGUMENT) +# if __has_attribute(ns_consumed) +# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) +# else +# define SWIFT_RELEASES_ARGUMENT +# endif +#endif +#if !defined(SWIFT_WARN_UNUSED_RESULT) +# if __has_attribute(warn_unused_result) +# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define SWIFT_WARN_UNUSED_RESULT +# endif +#endif +#if !defined(SWIFT_NORETURN) +# if __has_attribute(noreturn) +# define SWIFT_NORETURN __attribute__((noreturn)) +# else +# define SWIFT_NORETURN +# endif +#endif +#if !defined(SWIFT_CLASS_EXTRA) +# define SWIFT_CLASS_EXTRA +#endif +#if !defined(SWIFT_PROTOCOL_EXTRA) +# define SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_ENUM_EXTRA) +# define SWIFT_ENUM_EXTRA +#endif +#if !defined(SWIFT_CLASS) +# if __has_attribute(objc_subclassing_restricted) +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# else +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# endif +#endif +#if !defined(SWIFT_RESILIENT_CLASS) +# if __has_attribute(objc_class_stub) +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub)) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME) +# else +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME) +# endif +#endif +#if !defined(SWIFT_PROTOCOL) +# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_EXTENSION) +# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) +#endif +#if !defined(OBJC_DESIGNATED_INITIALIZER) +# if __has_attribute(objc_designated_initializer) +# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +# else +# define OBJC_DESIGNATED_INITIALIZER +# endif +#endif +#if !defined(SWIFT_ENUM_ATTR) +# if __has_attribute(enum_extensibility) +# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) +# else +# define SWIFT_ENUM_ATTR(_extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# if __has_feature(generalized_swift_name) +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# else +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +# else +# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM_TAG) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM_TAG enum +# else +# define SWIFT_ENUM_TAG +# endif +#endif +#if !defined(SWIFT_ENUM_FWD_DECL) +# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum) +# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type; +# else +# define SWIFT_ENUM_FWD_DECL(_type, _name) typedef _type _name; +# endif +#endif +#if !defined(SWIFT_UNAVAILABLE) +# define SWIFT_UNAVAILABLE __attribute__((unavailable)) +#endif +#if !defined(SWIFT_UNAVAILABLE_MSG) +# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) +#endif +#if !defined(SWIFT_AVAILABILITY) +# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) +#endif +#if !defined(SWIFT_AVAILABILITY_DOMAIN) +# define SWIFT_AVAILABILITY_DOMAIN(dom, ...) __attribute__((availability(domain: dom, __VA_ARGS__))) +#endif +#if !defined(SWIFT_WEAK_IMPORT) +# define SWIFT_WEAK_IMPORT __attribute__((weak_import)) +#endif +#if !defined(SWIFT_DEPRECATED) +# define SWIFT_DEPRECATED __attribute__((deprecated)) +#endif +#if !defined(SWIFT_DEPRECATED_MSG) +# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) +#endif +#if !defined(SWIFT_DEPRECATED_OBJC) +# if __has_feature(attribute_diagnose_if_objc) +# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) +# else +# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) +# endif +#endif +#if defined(__OBJC__) +#if !defined(IBSegueAction) +# define IBSegueAction +#endif +#endif +#if !defined(SWIFT_EXTERN) +# if defined(__cplusplus) +# define SWIFT_EXTERN extern "C" +# else +# define SWIFT_EXTERN extern +# endif +#endif +#if !defined(SWIFT_CALL) +# define SWIFT_CALL __attribute__((swiftcall)) +#endif +#if !defined(SWIFT_INDIRECT_RESULT) +# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result)) +#endif +#if !defined(SWIFT_CONTEXT) +# define SWIFT_CONTEXT __attribute__((swift_context)) +#endif +#if !defined(SWIFT_ERROR_RESULT) +# define SWIFT_ERROR_RESULT __attribute__((swift_error_result)) +#endif +#if defined(__cplusplus) +# define SWIFT_NOEXCEPT noexcept +#else +# define SWIFT_NOEXCEPT +#endif +#if !defined(SWIFT_C_INLINE_THUNK) +# if __has_attribute(always_inline) +# if __has_attribute(nodebug) +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug)) +# else +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) +# endif +# else +# define SWIFT_C_INLINE_THUNK inline +# endif +#endif +#if defined(_WIN32) +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport) +#endif +#else +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL +#endif +#endif +#if !__has_feature(nullability) +# define _Nonnull +# define _Nullable +# define _Null_unspecified +#elif !defined(__OBJC__) +# pragma clang diagnostic ignored "-Wnullability-extension" +#endif +#if !__has_feature(nullability_nullable_result) +# define _Nullable_result _Nullable +#endif +#if __has_feature(objc_modules) +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +#endif + +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="LCECryptoKit",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(__cplusplus) +} // extern "C" +#endif +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__OBJC__) +#if __has_feature(objc_modules) +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +#endif + +#endif // defined(__OBJC__) +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="LCECryptoKit",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__OBJC__) + +#endif // defined(__OBJC__) +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__cplusplus) +#endif +#pragma clang diagnostic pop +#endif + +#else +#error unsupported Swift architecture +#endif diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Info.plist b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..4782fa86c6da9fc848b2dd5e66b61956dd4c180c GIT binary patch literal 740 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 literal 0 HcmV?d00001 diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json new file mode 100644 index 0000000..32ec755 --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json @@ -0,0 +1,417 @@ +{ + "ABIRoot": { + "kind": "Root", + "name": "LCECryptoKit", + "printedName": "LCECryptoKit", + "children": [ + { + "kind": "Import", + "name": "Foundation", + "printedName": "Foundation", + "declKind": "Import", + "moduleName": "LCECryptoKit" + }, + { + "kind": "Import", + "name": "CommonCrypto", + "printedName": "CommonCrypto", + "declKind": "Import", + "moduleName": "LCECryptoKit" + }, + { + "kind": "TypeDecl", + "name": "LCECryptoKit", + "printedName": "LCECryptoKit", + "children": [ + { + "kind": "Function", + "name": "generateRandomAESKeyString", + "printedName": "generateRandomAESKeyString()", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV26generateRandomAESKeyStringSSyFZ", + "mangledName": "$s12LCECryptoKitAAV26generateRandomAESKeyStringSSyFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "encodeSeed", + "printedName": "encodeSeed(email:password:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10encodeSeed5email8passwordSSSgSS_SStFZ", + "mangledName": "$s12LCECryptoKitAAV10encodeSeed5email8passwordSSSgSS_SStFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "encodeSeed", + "printedName": "encodeSeed(email:password:hashKey:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10encodeSeed5email8password7hashKeySSSgSS_S2StFZ", + "mangledName": "$s12LCECryptoKitAAV10encodeSeed5email8password7hashKeySSSgSS_S2StFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "decodeSeed", + "printedName": "decodeSeed(otpKey:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10decodeSeed6otpKeySSSgSS_tFZ", + "mangledName": "$s12LCECryptoKitAAV10decodeSeed6otpKeySSSgSS_tFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "decodeSeed", + "printedName": "decodeSeed(otpKey:hashKey:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Bool", + "printedName": "Swift.Bool", + "usr": "s:Sb" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10decodeSeed6otpKey04hashF0SbSS_SStFZ", + "mangledName": "$s12LCECryptoKitAAV10decodeSeed6otpKey04hashF0SbSS_SStFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "generateSalt", + "printedName": "generateSalt()", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV12generateSaltSSyFZ", + "mangledName": "$s12LCECryptoKitAAV12generateSaltSSyFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "computeClientHash", + "printedName": "computeClientHash(email:password:salt:)", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV17computeClientHash5email8password4saltS2S_S2StFZ", + "mangledName": "$s12LCECryptoKitAAV17computeClientHash5email8password4saltS2S_S2StFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "computeLoginBearerToken", + "printedName": "computeLoginBearerToken(userId:clientHash:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV23computeLoginBearerToken6userId10clientHashSSSgSS_SStFZ", + "mangledName": "$s12LCECryptoKitAAV23computeLoginBearerToken6userId10clientHashSSSgSS_SStFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "otpEncode", + "printedName": "otpEncode(_:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV9otpEncodeySSSgSSFZ", + "mangledName": "$s12LCECryptoKitAAV9otpEncodeySSSgSSFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "otpDecode", + "printedName": "otpDecode(_:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV9otpDecodeySSSgSSFZ", + "mangledName": "$s12LCECryptoKitAAV9otpDecodeySSSgSSFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + } + ], + "declKind": "Struct", + "usr": "s:12LCECryptoKitAAV", + "mangledName": "$s12LCECryptoKitAAV", + "moduleName": "LCECryptoKit", + "declAttributes": [ + "AccessControl" + ], + "conformances": [ + { + "kind": "Conformance", + "name": "Copyable", + "printedName": "Copyable", + "usr": "s:s8CopyableP", + "mangledName": "$ss8CopyableP" + }, + { + "kind": "Conformance", + "name": "Escapable", + "printedName": "Escapable", + "usr": "s:s9EscapableP", + "mangledName": "$ss9EscapableP" + } + ] + } + ], + "json_format_version": 8 + }, + "ConstValues": [] +} \ No newline at end of file diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface new file mode 100644 index 0000000..f75100d --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface @@ -0,0 +1,22 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.3.3 effective-5.10 (swiftlang-6.3.3.1.3 clang-2100.1.1.101) +// swift-module-flags: -target arm64-apple-ios13.0-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -O -enable-experimental-feature DebugDescriptionMacro -enable-bare-slash-regex -module-name LCECryptoKit +// swift-module-flags-ignorable: -no-verify-emitted-module-interface -formal-cxx-interoperability-mode=off -interface-compiler-version 6.3.3 +import CommonCrypto +import Foundation +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public struct LCECryptoKit { + public static func generateRandomAESKeyString() -> Swift.String + public static func encodeSeed(email: Swift.String, password: Swift.String) -> Swift.String? + public static func encodeSeed(email: Swift.String, password: Swift.String, hashKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String, hashKey: Swift.String) -> Swift.Bool + public static func generateSalt() -> Swift.String + public static func computeClientHash(email: Swift.String, password: Swift.String, salt: Swift.String) -> Swift.String + public static func computeLoginBearerToken(userId: Swift.String, clientHash: Swift.String) -> Swift.String? + public static func otpEncode(_ plainText: Swift.String) -> Swift.String? + public static func otpDecode(_ otpEncoded: Swift.String) -> Swift.String? +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc new file mode 100644 index 0000000000000000000000000000000000000000..9fa6aa29752793ad7c6e9e14fa524ff07a54671a GIT binary patch literal 3668 zcmb_fO^6&t6z<)q(a9n)C>|7i2GkyBy60!Jn{{0wGfCEHqFJ2aA&BY9OwCLsJ>5-J z&Hm^mE(#uU2$+lDK~&U3#2`835YP+=dJu9DJP8sn9z=5Ul7nAW_v~)Ws4>}DdbYdk z)mN`xeeZj3u72?O*0FmXC+|4U*0Gu5o-eNDK3UjyZXx&ep4^w^+}D&lcP;nv`P|mp zS65CC3Kz${?*@hQgVMX>g-ZkX++gpH3-$b!x_hSX;(M-KFI*bW|2SSaJ1AYOyBM2$ zbc^@)YViYD4Qku(-}Zxhsb0W)AYET|&kqWx2T#wxD6A z3`4`u_HDSUB}vGsu_W4>7P(X+j%eDS^e2h8+q|j8BKM~JN|{_`Y(f@wJUi}JVAHhE z)k?XHNBos5CSEOEhxYE@D_0U79~AlzJTuvw-sLf~nkQmane@w^61{%NbSyD$KJPr> zI8)d|^`7k$FP@oLekOelr+<0#*;D)QbonmFQL~lmM)X?k)ynQ>+)MhJ?+pcy^Z};2 zQ@qDSIFm4?mSWl3rC6vN)y6Sgdj9BRt=_#R^6)}ZB!q`N$#-PDUELU^F&VRnk9$eke_YZk()%a=VKHRBd< z`F17=MSaCv#1sjwC={AY7Ft84rHDGD`LZrj6l-d6I}>Phd_{9bS5KX#9@EV(?%@Ev z3}BMH%_SD$Bzg#O4^d#+oF!joO&zX~dsBo+L{!tUHNAT3eYN(mc5c2_a38D?Z2@o1 z@?JZZp1C2`VIoo*E-<~myakGvB5KN5DKEkCVnz@9f$1Xn6idX$JgHJ!EbG4HsssvH zFTAE@%t@iWgeM7?o-y>YY5oZnM|q!K=J1HJ?I?;RLepQ1hT;;#UL0Z1DToc2^|B|H zO>DbhA&(Sgrb-}w#=eLN(omu__-xuoJ~?;edI49dRH8My1h>nVpqSeTcpKyi4DW&s z^s#{RnTK~wC5u+i&~A}d!A-n^f00FcK0_Lk>?uXT@O~r5g8w9$g`3LYZX_H)|ATIJ zvJnq+if(ZG+zax)#o9lN&>a%df)z5bTO(x-)>gyEoloEUe)>r~{R&ARC5hDzkDw}= zH;nQ%R_2fX1nbEvNfx!@UMeOxh?7l}rd3c#Gm^rvZ9`7%^6B{uvxS6#*4WJN2xwL+ zHZGEJO(l`p+=yh8Avw(iuf-PyX1NFXO0x^~z+hx!s32FwqLrmCL=2MEKvRr7g&=fVkoW!PtwSJUuAxP!A~$73vD zDw)mZhyw`)&N{#_ERPFtO0G~=4#6)_!x%@@iEV-GLWVnCVrx862E=$kZdz>{cu`8r zr@77hXdZ5m^v&3}J+5bT#T!zchhb5HYx?Vl0f+Q+V7ckP|H~ ze1*cIOF@ey`)H%liW$162`{-BXTv7Oa38TFW}7C`OYtyGwC24;E8p}D8+(V*F$_ir zF({H5`Fj{GErvCELr4QZHMf1nT2zCPVGY*fYT=%RpN3_@s2~IQ-=l0HUK~P<47uqQ t{zuF$;+_og4&rY!1F7IR_(|v(9WmFa&%-#TK6=NUx$ff|e{31szW|q(opS&H literal 0 HcmV?d00001 diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface new file mode 100644 index 0000000..f75100d --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface @@ -0,0 +1,22 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.3.3 effective-5.10 (swiftlang-6.3.3.1.3 clang-2100.1.1.101) +// swift-module-flags: -target arm64-apple-ios13.0-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -O -enable-experimental-feature DebugDescriptionMacro -enable-bare-slash-regex -module-name LCECryptoKit +// swift-module-flags-ignorable: -no-verify-emitted-module-interface -formal-cxx-interoperability-mode=off -interface-compiler-version 6.3.3 +import CommonCrypto +import Foundation +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public struct LCECryptoKit { + public static func generateRandomAESKeyString() -> Swift.String + public static func encodeSeed(email: Swift.String, password: Swift.String) -> Swift.String? + public static func encodeSeed(email: Swift.String, password: Swift.String, hashKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String, hashKey: Swift.String) -> Swift.Bool + public static func generateSalt() -> Swift.String + public static func computeClientHash(email: Swift.String, password: Swift.String, salt: Swift.String) -> Swift.String + public static func computeLoginBearerToken(userId: Swift.String, clientHash: Swift.String) -> Swift.String? + public static func otpEncode(_ plainText: Swift.String) -> Swift.String? + public static func otpDecode(_ otpEncoded: Swift.String) -> Swift.String? +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json new file mode 100644 index 0000000..32ec755 --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json @@ -0,0 +1,417 @@ +{ + "ABIRoot": { + "kind": "Root", + "name": "LCECryptoKit", + "printedName": "LCECryptoKit", + "children": [ + { + "kind": "Import", + "name": "Foundation", + "printedName": "Foundation", + "declKind": "Import", + "moduleName": "LCECryptoKit" + }, + { + "kind": "Import", + "name": "CommonCrypto", + "printedName": "CommonCrypto", + "declKind": "Import", + "moduleName": "LCECryptoKit" + }, + { + "kind": "TypeDecl", + "name": "LCECryptoKit", + "printedName": "LCECryptoKit", + "children": [ + { + "kind": "Function", + "name": "generateRandomAESKeyString", + "printedName": "generateRandomAESKeyString()", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV26generateRandomAESKeyStringSSyFZ", + "mangledName": "$s12LCECryptoKitAAV26generateRandomAESKeyStringSSyFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "encodeSeed", + "printedName": "encodeSeed(email:password:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10encodeSeed5email8passwordSSSgSS_SStFZ", + "mangledName": "$s12LCECryptoKitAAV10encodeSeed5email8passwordSSSgSS_SStFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "encodeSeed", + "printedName": "encodeSeed(email:password:hashKey:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10encodeSeed5email8password7hashKeySSSgSS_S2StFZ", + "mangledName": "$s12LCECryptoKitAAV10encodeSeed5email8password7hashKeySSSgSS_S2StFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "decodeSeed", + "printedName": "decodeSeed(otpKey:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10decodeSeed6otpKeySSSgSS_tFZ", + "mangledName": "$s12LCECryptoKitAAV10decodeSeed6otpKeySSSgSS_tFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "decodeSeed", + "printedName": "decodeSeed(otpKey:hashKey:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Bool", + "printedName": "Swift.Bool", + "usr": "s:Sb" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV10decodeSeed6otpKey04hashF0SbSS_SStFZ", + "mangledName": "$s12LCECryptoKitAAV10decodeSeed6otpKey04hashF0SbSS_SStFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "generateSalt", + "printedName": "generateSalt()", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV12generateSaltSSyFZ", + "mangledName": "$s12LCECryptoKitAAV12generateSaltSSyFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "computeClientHash", + "printedName": "computeClientHash(email:password:salt:)", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV17computeClientHash5email8password4saltS2S_S2StFZ", + "mangledName": "$s12LCECryptoKitAAV17computeClientHash5email8password4saltS2S_S2StFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "computeLoginBearerToken", + "printedName": "computeLoginBearerToken(userId:clientHash:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV23computeLoginBearerToken6userId10clientHashSSSgSS_SStFZ", + "mangledName": "$s12LCECryptoKitAAV23computeLoginBearerToken6userId10clientHashSSSgSS_SStFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "otpEncode", + "printedName": "otpEncode(_:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV9otpEncodeySSSgSSFZ", + "mangledName": "$s12LCECryptoKitAAV9otpEncodeySSSgSSFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl", + "RawDocComment" + ], + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "otpDecode", + "printedName": "otpDecode(_:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:12LCECryptoKitAAV9otpDecodeySSSgSSFZ", + "mangledName": "$s12LCECryptoKitAAV9otpDecodeySSSgSSFZ", + "moduleName": "LCECryptoKit", + "static": true, + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + } + ], + "declKind": "Struct", + "usr": "s:12LCECryptoKitAAV", + "mangledName": "$s12LCECryptoKitAAV", + "moduleName": "LCECryptoKit", + "declAttributes": [ + "AccessControl" + ], + "conformances": [ + { + "kind": "Conformance", + "name": "Copyable", + "printedName": "Copyable", + "usr": "s:s8CopyableP", + "mangledName": "$ss8CopyableP" + }, + { + "kind": "Conformance", + "name": "Escapable", + "printedName": "Escapable", + "usr": "s:s9EscapableP", + "mangledName": "$ss9EscapableP" + } + ] + } + ], + "json_format_version": 8 + }, + "ConstValues": [] +} \ No newline at end of file diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface new file mode 100644 index 0000000..81da07e --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface @@ -0,0 +1,22 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.3.3 effective-5.10 (swiftlang-6.3.3.1.3 clang-2100.1.1.101) +// swift-module-flags: -target x86_64-apple-ios13.0-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -O -enable-experimental-feature DebugDescriptionMacro -enable-bare-slash-regex -module-name LCECryptoKit +// swift-module-flags-ignorable: -no-verify-emitted-module-interface -formal-cxx-interoperability-mode=off -interface-compiler-version 6.3.3 +import CommonCrypto +import Foundation +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public struct LCECryptoKit { + public static func generateRandomAESKeyString() -> Swift.String + public static func encodeSeed(email: Swift.String, password: Swift.String) -> Swift.String? + public static func encodeSeed(email: Swift.String, password: Swift.String, hashKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String, hashKey: Swift.String) -> Swift.Bool + public static func generateSalt() -> Swift.String + public static func computeClientHash(email: Swift.String, password: Swift.String, salt: Swift.String) -> Swift.String + public static func computeLoginBearerToken(userId: Swift.String, clientHash: Swift.String) -> Swift.String? + public static func otpEncode(_ plainText: Swift.String) -> Swift.String? + public static func otpDecode(_ otpEncoded: Swift.String) -> Swift.String? +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc new file mode 100644 index 0000000000000000000000000000000000000000..f2ae82529e151a48e0532ae477eca7b391017b76 GIT binary patch literal 3668 zcmb_fO^g&p6z*MAbRY(a#)Bq4Nz@)^y60!vWmzy`23SxKHxBVYjCAcx%}ha0cc-dm zfAoSx6AvCttR%&13gFPTp~x&12KWeP7(jeLA=0!d&j^zTB7P+*Qh5xRLwhVs3No zt84EL3YW*d?*@g7gVHMkb|KoV!+@N&1?&96v z$2WOzoh^O{t3hqsgIj)Zuha{84y2oB-HU_5yMt%Np;J-hS`#Q)j-W^0plZDsx*O-t_H9+c#et zbH?y$_}R7vceNx5IW-nVThjuUO2iRO`4j#G@phXxwOHWZq+cnMtBg&^qK;?Z`xV$U z?YLSgmoda&xnkng!gX~2!ToYM(eV+X|G;=@dg|oVPLG-8JQ1tPgkScQ==DRUV+jL% z-hRk&Cb5U=ecQIa@ZQ#?-RWaE{mUDB&K$t#n|mBb%~Yyu(QCC=D!ZC-FX?N(KNLLD zhneb5@*Wf6bi$NcjAd)5VxewS8z*t;*%MEUJ5J-k0UDg2m?0VWqgI)oqsHM{b#ls$ zV%cM%c%8Qn8VoAXj*~m^J;cJTLCY6OcX>p+s259w@Nmt;>=aFrJ54&)EQD2;FL^v_ z#x36R?MxDi`ii%RDH2*yC^VNWw1!Gc5p_uOC0(Q_*3{y5CeUbpS#w3#&zz$k)6FjK z;Q$>2FiGC#5{qyWJw&*NC@^i#k}t8Q4wuQjEkYzBs_EF8UO)4JS{baIo39nz2P;Hd zz+1Dt*N&xUZisc5h?Is4Os_6)f#Su8nle_(OK`fF(ZhaVx&S`K60tEORcebR-IrXI zzyRxo*R+f|Ewq>LB;nFChF&(!Kc(U*@6$03k0{%YqF5p{{gr4aF7eunBkVZ^u>rGQ z_QaBjZ5Jfuk)q603B=FX7coH^N|XlYr+nm-b1$yvag|CXTA@pDw|ohTxwU|IL7u?y zE?7e!3pks3xNa(0w0eeihqMZA;}!giEYhJ2X-KkX6a~ZkwHyonlVlcdGK0I7Z~*-e zy4lG_Jj^M&!QFE&$om%S;4nh#B%%c?WMFqj${eh%hL1a+z5o5x(-{2!cPy7Vyi7H7Jwc=hXCbx)_O_ZipP$)B!!mw>kPVDsQP=?t;!a!?m=63`% zD-|0T$+)7D$ZT#zvdNH~W`ft^3j(v;BYe5pg?iv+WMilxSHz;_#V$k)lGWsirg^Ax zXk>>qPaCZ=S|_<#jmwe+$5174q_~fwVUzVm{5QwEQQNdfbR+}am@Qk#qZ#A|ZQs%Y zkj`tr|NOBb#UrS1BS^73*2zIziIy9UMyJu3J@Ff?UuKK+((yTOQL=;*DSE<`eRA7~ zZ+FO-gtaUenk+O$jV~p!DPb)kq3RG@$UL>fcrh(dp*YR$iDg2L`h+?JlQ8b!EZKaF z1xzKg*&J~op}<)O7+%YH0Zz$f%E}@51!@@Mh&r(?kX^`dr%P;wC(3{r56DfcZ37QV zY56p_c^}QgEt0+w`?kk9q}@ivz=95Ceckb}XisAAsQ|79CnyE0;W6q48^@&5nDghW zzdvSp|IKJ3RVOmpKWdJ%&Y9%$czl{iQ|N2t^Q}rb)e+N)rA#~7Fn!lfh*efd2?rc0 zq3xm15SUp(3kDjxt5d-ugOOnkR^w{n_P|fWvS3t@0sQY#wh%82Ax4JW u^a}qY<`!{ZhPaOS+sr^JI1YXiIz~s#HR|&yj;W8{aYwHE Swift.String + public static func encodeSeed(email: Swift.String, password: Swift.String) -> Swift.String? + public static func encodeSeed(email: Swift.String, password: Swift.String, hashKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String) -> Swift.String? + public static func decodeSeed(otpKey: Swift.String, hashKey: Swift.String) -> Swift.Bool + public static func generateSalt() -> Swift.String + public static func computeClientHash(email: Swift.String, password: Swift.String, salt: Swift.String) -> Swift.String + public static func computeLoginBearerToken(userId: Swift.String, clientHash: Swift.String) -> Swift.String? + public static func otpEncode(_ plainText: Swift.String) -> Swift.String? + public static func otpDecode(_ otpEncoded: Swift.String) -> Swift.String? +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/module.modulemap b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/module.modulemap new file mode 100644 index 0000000..cccd3c0 --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/Modules/module.modulemap @@ -0,0 +1,3 @@ +framework module LCECryptoKit { + header "LCECryptoKit-Swift.h" +} diff --git a/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/_CodeSignature/CodeResources b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/_CodeSignature/CodeResources new file mode 100644 index 0000000..0e9463b --- /dev/null +++ b/Frameworks/LCECryptoKit.xcframework/ios-arm64_x86_64-simulator/LCECryptoKit.framework/_CodeSignature/CodeResources @@ -0,0 +1,234 @@ + + + + + files + + Headers/LCECryptoKit-Swift.h + + uIM6/aOz59qnT/jGSBAiOinS2qo= + + Info.plist + + N0tkj+ldmO7dEKF+W1/6CjYNBrg= + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json + + mIejxCLstZ77ufLnGayqr8wDScc= + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface + + GJVZwAs/SRyyfGkg1t2Twb8grXE= + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc + + vDvqI7FV3rThZRI4r/zPL877Z04= + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface + + GJVZwAs/SRyyfGkg1t2Twb8grXE= + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftmodule + + 0n6YBQBivkPuELjPpXBIj0CQFIc= + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json + + mIejxCLstZ77ufLnGayqr8wDScc= + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface + + WheGrMD0QDYsEwZ2vOGY+CQwW1o= + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc + + f52HXxbH78iRln4x78whSwyigbc= + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface + + WheGrMD0QDYsEwZ2vOGY+CQwW1o= + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftmodule + + FSzeAs9LdtELeWT3VL2PMjOFZcQ= + + Modules/module.modulemap + + OnB7ckFjsSU10c/y8kUFi69Cav4= + + + files2 + + Headers/LCECryptoKit-Swift.h + + hash2 + + gHRFTmnGK28MYikS6gvnWErwc/oZ/scYeFssVTBw9MI= + + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json + + hash2 + + /2qkU9Pje/wZPzf5sExK+0bliYzKjDZM7iqyIusKGGk= + + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface + + hash2 + + BsY3SInOh3u6xj+kXxauIrgLi4cTqE1mY6weF7QOk2g= + + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc + + hash2 + + Ldu7EDkaW9WinMjRHaBaSxpZoBNDXGvNn04410k9e5k= + + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface + + hash2 + + BsY3SInOh3u6xj+kXxauIrgLi4cTqE1mY6weF7QOk2g= + + + Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftmodule + + hash2 + + cuezjPqokn+YGP9z//TY4ZUxA1Dcmd6ApUafMMaQPAQ= + + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json + + hash2 + + /2qkU9Pje/wZPzf5sExK+0bliYzKjDZM7iqyIusKGGk= + + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface + + hash2 + + lJH2d+h2iS4AwqKh/vWgSYIGEelZHLrCNQVlcCFB3V4= + + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc + + hash2 + + 5S2/W4WoD0L4bl+NBuPQI7sGxqpZ7y2s7tme6Eu2oIQ= + + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface + + hash2 + + lJH2d+h2iS4AwqKh/vWgSYIGEelZHLrCNQVlcCFB3V4= + + + Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftmodule + + hash2 + + a5uCPhYWs0qR1YWQZb51vKm+4mYKUgc5ajZr2wEU6tU= + + + Modules/module.modulemap + + hash2 + + X+gHfuxKBEgqt+p9I9kR0FYyzml9UPuJpKzZAbPNcc0= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 563f2c1..0000000 --- a/Package.resolved +++ /dev/null @@ -1,15 +0,0 @@ -{ - "originHash" : "6e3fcf8724d6b7d2d0bbffc25b4ec3b9d8b65a6ef53d157498d5447fd26cf5db", - "pins" : [ - { - "identity" : "lcecryptokitbinary", - "kind" : "remoteSourceControl", - "location" : "https://7876d8abdb7260a455259d01de49c4022f5da13c@git.loverde.com.br/Loverde-Company-LTDA/LCECryptoKitBinary.git", - "state" : { - "revision" : "11f66b1b9785b4965e027c2fa4742f61e8453403", - "version" : "1.0.4" - } - } - ], - "version" : 3 -} diff --git a/Package.swift b/Package.swift index d5c1b1b..ac9ab4f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,25 +1,13 @@ // swift-tools-version: 6.0 import PackageDescription -import Foundation -let isLocalDevelopment = false //FileManager.default.fileExists(atPath: "../LCECryptoKit/PrivateLib/LCECryptoKitBinary") -let enableCryptoBinary = ProcessInfo.processInfo.environment["LCE_ENABLE_CRYPTO_BINARY"] != "0" - -let cryptoPackageURL = isLocalDevelopment - ? "../LCECryptoKit/PrivateLib/LCECryptoKitBinary" - : "https://7876d8abdb7260a455259d01de49c4022f5da13c@git.loverde.com.br/Loverde-Company-LTDA/LCECryptoKitBinary.git" - -let packageDependencies: [Package.Dependency] = enableCryptoBinary - ? [ - .package(url: cryptoPackageURL, exact: "1.0.4") - ] - : [] - -let targetDependencies: [Target.Dependency] = enableCryptoBinary - ? [ - .product(name: "LCECryptoKit", package: "lcecryptokitbinary") - ] - : [] +// LCECryptoKit ships as a prebuilt .xcframework (`Frameworks/LCECryptoKit.xcframework`) — +// vendored locally in this repo, no remote package dependency. iOS device + simulator slices +// only; consumers who need macOS/tvOS/watchOS simply don't link the `LCECryptoKit` product. +// +// Sub-SPM rule: `LCEssentials` installs standalone (no sub required). Every sub +// (`LCECryptoKit` here) target-depends on `LCEssentials`, so linking the sub's product always +// pulls `LCEssentials` in too — a consumer never has to declare it separately. let package = Package( name: "LCEssentials", @@ -33,14 +21,33 @@ let package = Package( .library( name: "LCEssentials", targets: ["LCEssentials"]), + .library( + name: "LCECryptoKit", + targets: ["LCECryptoKitManager"]), + .library( + name: "LCFeatureControl", + targets: ["LCFeatureControl"]), ], - dependencies: packageDependencies, targets: [ .target( - name: "LCEssentials", - dependencies: targetDependencies), + name: "LCEssentials"), + .binaryTarget( + name: "LCECryptoKit", + path: "Frameworks/LCECryptoKit.xcframework"), + .target( + name: "LCECryptoKitManager", + dependencies: [ + "LCEssentials", + "LCECryptoKit" + ]), + .target( + name: "LCFeatureControl", + dependencies: ["LCEssentials"]), .testTarget( name: "LCEssentialsTests", dependencies: ["LCEssentials"]), + .testTarget( + name: "LCFeatureControlTests", + dependencies: ["LCFeatureControl", "LCEssentials"]), ] ) diff --git a/Sources/LCEssentials/Classes/LCECrypto.swift b/Sources/LCECryptoKitManager/LCECryptoKitManager.swift similarity index 68% rename from Sources/LCEssentials/Classes/LCECrypto.swift rename to Sources/LCECryptoKitManager/LCECryptoKitManager.swift index 731d61f..e5428cf 100644 --- a/Sources/LCEssentials/Classes/LCECrypto.swift +++ b/Sources/LCECryptoKitManager/LCECryptoKitManager.swift @@ -1,4 +1,4 @@ -// +// // Copyright (c) 2025 Loverde Co. // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,43 +18,41 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation - -#if canImport(LCECryptoKit) +import LCEssentials import LCECryptoKit public final class LCECryptoKitManager { - + private let hashKey: String - + public init() { self.hashKey = "" } - - public init(privateKey: String){ + + public init(privateKey: String) { self.hashKey = privateKey } - + public static func generateKey() -> String { LCECryptoKit.generateRandomAESKeyString() } - + public func encodeTP(email: String, password: String) -> String? { return LCECryptoKit.encodeSeed(email: email, password: password) } - + public func decodeOTP(_ otpHash: String) -> String? { return LCECryptoKit.decodeSeed(otpKey: otpHash) } - + // MARK: Need hashKey to decode - + public func encodeOTPWithKey(email: String, password: String) -> String? { return LCECryptoKit.encodeSeed(email: email, password: password, hashKey: self.hashKey) } - + public func decodeOTPWithKey(_ otpHash: String) -> Bool { LCECryptoKit.decodeSeed(otpKey: otpHash, hashKey: self.hashKey) } @@ -81,60 +79,3 @@ public final class LCECryptoKitManager { LCECryptoKit.otpDecode(otpEncoded) } } -#else - -public final class LCECryptoKitManager { - - private let hashKey: String - - public init() { - self.hashKey = "" - } - - public init(privateKey: String){ - self.hashKey = privateKey - } - - public static func generateKey() -> String { - "" - } - - public func encodeTP(email: String, password: String) -> String? { - nil - } - - public func decodeOTP(_ otpHash: String) -> String? { - nil - } - - public func encodeOTPWithKey(email: String, password: String) -> String? { - nil - } - - public func decodeOTPWithKey(_ otpHash: String) -> Bool { - false - } - - // MARK: - Salted/Iterated/Peppered Login (atomenta-cryptokit-pepper-refactor-sdd.md) - - public static func generateSalt() -> String { - "" - } - - public static func computeClientHash(email: String, password: String, salt: String) -> String { - "" - } - - public static func computeLoginBearerToken(userId: String, clientHash: String) -> String? { - nil - } - - public static func otpEncode(_ plainText: String) -> String? { - nil - } - - public static func otpDecode(_ otpEncoded: String) -> String? { - nil - } -} -#endif diff --git a/Sources/LCFeatureControl/FeatureControlAuthorizing.swift b/Sources/LCFeatureControl/FeatureControlAuthorizing.swift new file mode 100644 index 0000000..8fd3124 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlAuthorizing.swift @@ -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 + } + } +} diff --git a/Sources/LCFeatureControl/FeatureControlCache.swift b/Sources/LCFeatureControl/FeatureControlCache.swift new file mode 100644 index 0000000..a519854 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlCache.swift @@ -0,0 +1,46 @@ +import Foundation + +/// Identifies a cached evaluation — FC-060 §5: "environment + subjectId + keys(sorted) + +/// platform + appVersion". `sortedKeys` means caller `keys` order never affects hits. +struct FeatureControlCacheKey: Hashable, Sendable { + let environment: String + let subjectId: String + let sortedKeys: [String] + let platform: String? + let appVersion: String? +} + +/// In-memory-only TTL cache (no disk persistence — see SDD §2 Non-Goals). The clock is +/// injectable so tests can force expiry deterministically instead of sleeping. +actor FeatureControlCache { + private struct Entry { + let snapshot: FeatureControlSnapshot + let storedAt: Date + let ttl: TimeInterval + } + + private var entries: [FeatureControlCacheKey: Entry] = [:] + private let now: @Sendable () -> Date + + init(now: @escaping @Sendable () -> Date = { Date() }) { + self.now = now + } + + /// `allowStale: true` returns an expired entry rather than `nil` — used by the + /// safe-degrade fallback path (SDD §4.4/§4.5). + func get(_ key: FeatureControlCacheKey, allowStale: Bool) -> FeatureControlSnapshot? { + guard let entry = entries[key] else { return nil } + let age = now().timeIntervalSince(entry.storedAt) + if age <= entry.ttl { return entry.snapshot } + return allowStale ? entry.snapshot : nil + } + + /// Always overwrites — a fresh fetch's `configVersion` simply replaces whatever was there. + func set(_ key: FeatureControlCacheKey, snapshot: FeatureControlSnapshot, ttl: TimeInterval) { + entries[key] = Entry(snapshot: snapshot, storedAt: now(), ttl: ttl) + } + + func invalidateAll() { + entries.removeAll() + } +} diff --git a/Sources/LCFeatureControl/FeatureControlConfiguration.swift b/Sources/LCFeatureControl/FeatureControlConfiguration.swift new file mode 100644 index 0000000..10ecc45 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlConfiguration.swift @@ -0,0 +1,33 @@ +import Foundation + +/// Wiring for one Atomenta Feature Control module deployment. +public struct FeatureControlConfiguration: Sendable { + public var baseURL: String + public var evaluatePath: String + public var notificationsPath: String + public var telemetryPath: String + public var environment: String + public var auth: any FeatureControlAuthorizing + /// In-memory cache TTL. FC-060 §5 recommends 30–60s client-side. + public var cacheTTL: TimeInterval + /// FC-060 §5 recommends 1.5–3s at the BFF; same budget applies here. + public var requestTimeout: TimeInterval + + public init(baseURL: String, + environment: String, + auth: any FeatureControlAuthorizing, + evaluatePath: String = "/api/feature-control/evaluate", + notificationsPath: String = "/api/feature-control/notifications", + telemetryPath: String = "/api/feature-control/telemetry/exposure", + cacheTTL: TimeInterval = 45, + requestTimeout: TimeInterval = 3) { + self.baseURL = baseURL + self.environment = environment + self.auth = auth + self.evaluatePath = evaluatePath + self.notificationsPath = notificationsPath + self.telemetryPath = telemetryPath + self.cacheTTL = cacheTTL + self.requestTimeout = requestTimeout + } +} diff --git a/Sources/LCFeatureControl/FeatureControlContext.swift b/Sources/LCFeatureControl/FeatureControlContext.swift new file mode 100644 index 0000000..89787d0 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlContext.swift @@ -0,0 +1,30 @@ +import Foundation + +/// Who/what a flag evaluation is for — mirrors Atomenta's `context.subjectType` enum. +public enum FeatureControlSubjectType: String, Codable, Sendable { + case user, customer, store, anonymous +} + +/// Evaluation context sent as `context` in `POST /api/feature-control/evaluate`. +public struct FeatureControlContext: Encodable, Sendable, Equatable { + public var subjectType: FeatureControlSubjectType + public var subjectId: String + public var storeId: String? + public var platform: String? + public var appVersion: String? + public var attributes: [String: String]? + + public init(subjectType: FeatureControlSubjectType, + subjectId: String, + storeId: String? = nil, + platform: String? = nil, + appVersion: String? = nil, + attributes: [String: String]? = nil) { + self.subjectType = subjectType + self.subjectId = subjectId + self.storeId = storeId + self.platform = platform + self.appVersion = appVersion + self.attributes = attributes + } +} diff --git a/Sources/LCFeatureControl/FeatureControlDateParsing.swift b/Sources/LCFeatureControl/FeatureControlDateParsing.swift new file mode 100644 index 0000000..f66095e --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlDateParsing.swift @@ -0,0 +1,18 @@ +import Foundation + +/// Parses the ISO-8601 timestamps Atomenta sends (`2026-04-16T12:00:00.000Z`, with +/// milliseconds). `LCEssentials.API`'s internal `JSONDecoder` uses the default +/// (`.deferredToDate`, numeric epoch) strategy, so `Date` fields on wire models +/// decode the raw string manually instead of relying on `Decodable`'s default +/// date handling. +enum FeatureControlDateParsing { + static func parse(_ string: String) -> Date? { + let withFractional = ISO8601DateFormatter() + withFractional.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + if let date = withFractional.date(from: string) { return date } + + let plain = ISO8601DateFormatter() + plain.formatOptions = [.withInternetDateTime] + return plain.date(from: string) + } +} diff --git a/Sources/LCFeatureControl/FeatureControlError.swift b/Sources/LCFeatureControl/FeatureControlError.swift new file mode 100644 index 0000000..2bf0876 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlError.swift @@ -0,0 +1,51 @@ +import Foundation +import LCEssentials + +/// Explicit failure for callers that want to see it (`evaluateOrThrow`, the +/// notifications client). The safe `evaluate(...)` path never surfaces this — +/// it degrades to cache/defaults instead (FC-060 §4). +public enum FeatureControlError: Error, Sendable, Equatable { + /// 400 `FEATURE_CONTROL_CONTEXT_INVALID` + case invalidContext(code: String) + /// 401 `…MISSING_AUTH` / `…INVALID_MODULE_TOKEN` + case unauthorized(code: String) + /// 403 `…PANEL_ROLE_REQUIRED` / `…INSUFFICIENT_PERMISSIONS` + case forbidden(code: String) + /// 429 `FEATURE_CONTROL_RATE_LIMIT` + case rateLimited(code: String) + /// Any other non-2xx status. + case server(code: String, status: Int) + /// Decoding failure, or any error not raised by `LCEssentials.API`'s HTTP path. + case transport(message: String) +} + +enum FeatureControlErrorMapper { + /// `LCEssentials.API` throws an `NSError` (domain `LCEssentials.DEFAULT_ERROR_DOMAIN`, + /// `code` = HTTP status, `localizedFailureReason` = pretty-printed body) for non-2xx + /// responses, or a bridged `DecodingError`/`URLError` for anything else. Only the + /// former carries a real HTTP status to map. + static func map(_ error: Error) -> FeatureControlError { + let nsError = error as NSError + guard nsError.domain == LCEssentials.DEFAULT_ERROR_DOMAIN else { + return .transport(message: nsError.localizedDescription) + } + + let status = nsError.code + let bodyCode = extractCode(from: nsError.localizedFailureReason) ?? "UNKNOWN" + switch status { + case 400: return .invalidContext(code: bodyCode) + case 401: return .unauthorized(code: bodyCode) + case 403: return .forbidden(code: bodyCode) + case 429: return .rateLimited(code: bodyCode) + default: return .server(code: bodyCode, status: status) + } + } + + private static func extractCode(from prettyJSON: String?) -> String? { + guard let prettyJSON, + let data = prettyJSON.data(using: .utf8), + let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let code = object["code"] as? String else { return nil } + return code + } +} diff --git a/Sources/LCFeatureControl/FeatureControlExposureEvent.swift b/Sources/LCFeatureControl/FeatureControlExposureEvent.swift new file mode 100644 index 0000000..d8f6523 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlExposureEvent.swift @@ -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? +} diff --git a/Sources/LCFeatureControl/FeatureControlFlag.swift b/Sources/LCFeatureControl/FeatureControlFlag.swift new file mode 100644 index 0000000..8e19f65 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlFlag.swift @@ -0,0 +1,61 @@ +import Foundation + +/// One evaluated flag, as returned inside `EvaluateResponse.result.flags[key]`. +public struct FeatureControlFlag: Decodable, Sendable, Equatable { + public let enabled: Bool + public let variant: String? + public let payload: FeatureControlJSON? + public let reason: String? + + public init(enabled: Bool, variant: String?, payload: FeatureControlJSON?, reason: String?) { + self.enabled = enabled + self.variant = variant + self.payload = payload + self.reason = reason + } +} + +/// `EvaluateResponse.result` — the batch evaluation result for a set of keys. +public struct FeatureControlSnapshot: Sendable, Equatable { + public let evaluatedAt: Date + public let configVersion: Int + public let flags: [String: FeatureControlFlag] + + public init(evaluatedAt: Date, configVersion: Int, flags: [String: FeatureControlFlag]) { + self.evaluatedAt = evaluatedAt + self.configVersion = configVersion + self.flags = flags + } +} + +extension FeatureControlSnapshot: Decodable { + private enum CodingKeys: String, CodingKey { + case evaluatedAt, configVersion, flags + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let rawDate = try container.decode(String.self, forKey: .evaluatedAt) + guard let date = FeatureControlDateParsing.parse(rawDate) else { + throw DecodingError.dataCorruptedError(forKey: .evaluatedAt, in: container, + debugDescription: "Unrecognized date format: \(rawDate)") + } + self.evaluatedAt = date + self.configVersion = try container.decode(Int.self, forKey: .configVersion) + self.flags = try container.decode([String: FeatureControlFlag].self, forKey: .flags) + } +} + +/// Wire envelope for `POST /api/feature-control/evaluate` — `{error, code, result}`. +struct FeatureControlEvaluateEnvelope: Decodable, Sendable { + let error: Bool + let code: String? + let result: FeatureControlSnapshot +} + +/// Request body for `POST /api/feature-control/evaluate`. +struct FeatureControlEvaluateRequestBody: Encodable, Sendable { + let environment: String + let keys: [String] + let context: FeatureControlContext +} diff --git a/Sources/LCFeatureControl/FeatureControlJSON.swift b/Sources/LCFeatureControl/FeatureControlJSON.swift new file mode 100644 index 0000000..3e33f71 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlJSON.swift @@ -0,0 +1,33 @@ +import Foundation + +/// Minimal "any JSON" box for a flag's `payload`, which the backend declares as +/// `additionalProperties: true` (arbitrary shape). No force operations — an +/// unrecognized shape throws a `DecodingError`, it never crashes. +public indirect enum FeatureControlJSON: Decodable, Sendable, Equatable { + case null + case bool(Bool) + case number(Double) + case string(String) + case array([FeatureControlJSON]) + case object([String: FeatureControlJSON]) + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if container.decodeNil() { + self = .null + } else if let value = try? container.decode(Bool.self) { + self = .bool(value) + } else if let value = try? container.decode(Double.self) { + self = .number(value) + } else if let value = try? container.decode(String.self) { + self = .string(value) + } else if let value = try? container.decode([FeatureControlJSON].self) { + self = .array(value) + } else if let value = try? container.decode([String: FeatureControlJSON].self) { + self = .object(value) + } else { + throw DecodingError.dataCorruptedError(in: container, + debugDescription: "Unsupported JSON value for FeatureControlJSON") + } + } +} diff --git a/Sources/LCFeatureControl/FeatureControlManager.swift b/Sources/LCFeatureControl/FeatureControlManager.swift new file mode 100644 index 0000000..bdd0fa7 --- /dev/null +++ b/Sources/LCFeatureControl/FeatureControlManager.swift @@ -0,0 +1,123 @@ +import Foundation +import LCEssentials + +/// Protocol seam for DI — ViewModels/Interactors depend on this, never on the +/// concrete actor, so a mock can stand in for tests. +public protocol FeatureControlEvaluating: Sendable { + /// Never throws. Fresh fetch → stale cache → app-supplied `defaults`, in that + /// order (FC-060 §4 "nunca bloquear fluxo crítico"). Use this everywhere a + /// flag gates real product behaviour. + func evaluate(keys: [String], context: FeatureControlContext) async -> FeatureControlSnapshot + /// Same fetch, but surfaces the real failure. For admin/debug tooling only. + func evaluateOrThrow(keys: [String], context: FeatureControlContext) async throws -> FeatureControlSnapshot + /// Drops every cached entry. Call on user/store change, or app foreground if desired — + /// this type does not observe app lifecycle itself (SDD §2 Non-Goals). + func invalidateCache() async +} + +public actor FeatureControlManager: FeatureControlEvaluating { + private let configuration: FeatureControlConfiguration + private let api: API + private let defaults: [String: FeatureControlFlag] + private let cache: FeatureControlCache + private var exposureBuffer: [FeatureControlExposureEvent] = [] + + /// `configVersion` sentinel returned when no network response and no cache exist — + /// distinguishes "never evaluated" from any real server value (server versions are ≥ 0). + public static let unresolvedConfigVersion = -1 + + public init(configuration: FeatureControlConfiguration, + api: API = .shared, + defaults: [String: FeatureControlFlag] = [:], + now: @escaping @Sendable () -> Date = { Date() }) { + self.configuration = configuration + self.api = api + self.defaults = defaults + self.cache = FeatureControlCache(now: now) + } + + public func evaluate(keys: [String], context: FeatureControlContext) async -> FeatureControlSnapshot { + do { + return try await evaluateOrThrow(keys: keys, context: context) + } catch { + let key = cacheKey(keys: keys, context: context) + if let stale = await cache.get(key, allowStale: true) { return stale } + return FeatureControlSnapshot(evaluatedAt: Date(), + configVersion: Self.unresolvedConfigVersion, + flags: defaults) + } + } + + public func evaluateOrThrow(keys: [String], context: FeatureControlContext) async throws -> FeatureControlSnapshot { + let key = cacheKey(keys: keys, context: context) + if let fresh = await cache.get(key, allowStale: false) { return fresh } + + var headers: [String: String] = [:] + await configuration.auth.authorize(&headers) + + let body = jsonBody(FeatureControlEvaluateRequestBody( + environment: configuration.environment, keys: keys, context: context)) + + do { + let envelope: FeatureControlEvaluateEnvelope = try await api.request( + url: configuration.baseURL + configuration.evaluatePath, + method: .post, + body: body, + headers: headers, + timeoutInterval: configuration.requestTimeout + ) + await cache.set(key, snapshot: envelope.result, ttl: configuration.cacheTTL) + return envelope.result + } catch { + throw FeatureControlErrorMapper.map(error) + } + } + + public func invalidateCache() async { + await cache.invalidateAll() + } + + private func cacheKey(keys: [String], context: FeatureControlContext) -> FeatureControlCacheKey { + FeatureControlCacheKey(environment: configuration.environment, + subjectId: context.subjectId, + sortedKeys: keys.sorted(), + platform: context.platform, + appVersion: context.appVersion) + } +} + +// MARK: - Exposure telemetry + +extension FeatureControlManager { + /// Buffers locally. Nothing is sent until `flushExposures()` is called. + public func recordExposure(_ event: FeatureControlExposureEvent) { + exposureBuffer.append(event) + } + + /// Best-effort — batches of ≤100 (server `maxItems: 100`); a failed batch is + /// dropped, never retried indefinitely (not a critical-path operation). + public func flushExposures() async { + guard !exposureBuffer.isEmpty else { return } + let events = exposureBuffer + exposureBuffer.removeAll() + + for batch in events.lce_featureControlChunked(into: 100) { + var headers: [String: String] = [:] + await configuration.auth.authorize(&headers) + let body = jsonBody(FeatureControlExposureBatchBody(events: batch)) + _ = try? await api.request( + url: configuration.baseURL + configuration.telemetryPath, + method: .post, + body: body, + headers: headers, + timeoutInterval: configuration.requestTimeout + ) as FeatureControlExposureBatchEnvelope + } + } +} + +private extension Array { + func lce_featureControlChunked(into size: Int) -> [[Element]] { + stride(from: 0, to: count, by: size).map { Array(self[$0.. (items: [FeatureControlNotification], nextCursor: String?) + func markRead(id: String) async throws + func markAllRead() async throws +} + +/// JWT-only — the OpenAPI fragment declares `security: [bearerAuth]` for every +/// notifications route, no module-token alternative. Configure `configuration.auth` +/// with `FeatureControlBearerAuth`; anything else gets a `401` from the server, +/// surfaced as `FeatureControlError.unauthorized`. +public actor FeatureControlNotificationsClient: FeatureControlNotifying { + private let configuration: FeatureControlConfiguration + private let api: API + + public init(configuration: FeatureControlConfiguration, api: API = .shared) { + self.configuration = configuration + self.api = api + } + + public func list(status: FeatureControlNotificationStatus = .all, limit: Int = 20) async throws + -> (items: [FeatureControlNotification], nextCursor: String?) { + var headers: [String: String] = [:] + await configuration.auth.authorize(&headers) + + let query = "?status=\(status.rawValue)&limit=\(limit)" + do { + let envelope: FeatureControlNotificationListEnvelope = try await api.request( + url: configuration.baseURL + configuration.notificationsPath + query, + method: .get, + headers: headers, + timeoutInterval: configuration.requestTimeout + ) + return (envelope.result.items, envelope.result.nextCursor) + } catch { + throw FeatureControlErrorMapper.map(error) + } + } + + public func markRead(id: String) async throws { + var headers: [String: String] = [:] + await configuration.auth.authorize(&headers) + + let encodedId = id.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? id + do { + let _: String = try await api.request( + url: configuration.baseURL + configuration.notificationsPath + "/\(encodedId)/read", + method: .post, + headers: headers, + timeoutInterval: configuration.requestTimeout + ) + } catch { + throw FeatureControlErrorMapper.map(error) + } + } + + public func markAllRead() async throws { + var headers: [String: String] = [:] + await configuration.auth.authorize(&headers) + + do { + let _: String = try await api.request( + url: configuration.baseURL + configuration.notificationsPath + "/read-all", + method: .post, + headers: headers, + timeoutInterval: configuration.requestTimeout + ) + } catch { + throw FeatureControlErrorMapper.map(error) + } + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlAuthTests.swift b/Tests/LCFeatureControlTests/FeatureControlAuthTests.swift new file mode 100644 index 0000000..adeaf0c --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlAuthTests.swift @@ -0,0 +1,38 @@ +import XCTest +@testable import LCFeatureControl + +final class FeatureControlAuthTests: XCTestCase { + + func testBearerAuthAddsAuthorizationHeader() async { + let auth = FeatureControlBearerAuth { "jwt-123" } + var headers: [String: String] = [:] + await auth.authorize(&headers) + XCTAssertEqual(headers["Authorization"], "Bearer jwt-123") + } + + func testBearerAuthAddsNoHeaderWhenTokenIsNil() async { + let auth = FeatureControlBearerAuth { nil } + var headers: [String: String] = [:] + await auth.authorize(&headers) + XCTAssertNil(headers["Authorization"]) + } + + func testHeaderAuthAddsExactHeadersOnly() async { + let auth = FeatureControlHeaderAuth(headers: ["X-BFF-Session": "abc"]) + var headers: [String: String] = [:] + await auth.authorize(&headers) + XCTAssertEqual(headers, ["X-BFF-Session": "abc"]) + XCTAssertNil(headers["Authorization"]) + } + + func testConfigurationDefaults() { + let config = FeatureControlConfiguration(baseURL: "https://api.example.com", + environment: "production", + auth: FeatureControlHeaderAuth(headers: [:])) + XCTAssertEqual(config.cacheTTL, 45) + XCTAssertEqual(config.requestTimeout, 3) + XCTAssertEqual(config.evaluatePath, "/api/feature-control/evaluate") + XCTAssertEqual(config.notificationsPath, "/api/feature-control/notifications") + XCTAssertEqual(config.telemetryPath, "/api/feature-control/telemetry/exposure") + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlCacheTests.swift b/Tests/LCFeatureControlTests/FeatureControlCacheTests.swift new file mode 100644 index 0000000..2751859 --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlCacheTests.swift @@ -0,0 +1,70 @@ +import XCTest +@testable import LCFeatureControl + +/// Test-only mutable clock box — a `var` captured directly by the cache's +/// escaping `@Sendable` closure would trip strict-concurrency capture checks; +/// this mirrors the `@unchecked Sendable` pattern `StubURLProtocol` already uses. +private final class MutableClock: @unchecked Sendable { + var value: Date + init(_ value: Date) { self.value = value } +} + +final class FeatureControlCacheTests: XCTestCase { + + private func snapshot(configVersion: Int) -> FeatureControlSnapshot { + FeatureControlSnapshot(evaluatedAt: Date(), configVersion: configVersion, flags: [:]) + } + + private func key(_ keys: [String] = ["a", "b"]) -> FeatureControlCacheKey { + FeatureControlCacheKey(environment: "production", subjectId: "cust_1", + sortedKeys: keys.sorted(), platform: "ios", appVersion: "1.0") + } + + func testHitWithinTTLReturnsSameSnapshot() async { + let cache = FeatureControlCache(now: { Date() }) + await cache.set(key(), snapshot: snapshot(configVersion: 1), ttl: 45) + let hit = await cache.get(key(), allowStale: false) + XCTAssertEqual(hit?.configVersion, 1) + } + + func testMissAfterTTLExpiryReturnsNilWhenStaleNotAllowed() async { + let clock = MutableClock(Date()) + let cache = FeatureControlCache(now: { clock.value }) + await cache.set(key(), snapshot: snapshot(configVersion: 1), ttl: 1) + clock.value = clock.value.addingTimeInterval(2) + let hit = await cache.get(key(), allowStale: false) + XCTAssertNil(hit) + } + + func testStaleAllowedReturnsExpiredEntry() async { + let clock = MutableClock(Date()) + let cache = FeatureControlCache(now: { clock.value }) + await cache.set(key(), snapshot: snapshot(configVersion: 1), ttl: 1) + clock.value = clock.value.addingTimeInterval(2) + let hit = await cache.get(key(), allowStale: true) + XCTAssertEqual(hit?.configVersion, 1) + } + + func testKeyDiffersBySortedKeysNotInputOrder() async { + let cache = FeatureControlCache(now: { Date() }) + await cache.set(key(["a", "b"]), snapshot: snapshot(configVersion: 1), ttl: 45) + let hit = await cache.get(key(["b", "a"]), allowStale: false) + XCTAssertEqual(hit?.configVersion, 1) + } + + func testSetOverwritesPriorConfigVersion() async { + let cache = FeatureControlCache(now: { Date() }) + await cache.set(key(), snapshot: snapshot(configVersion: 7), ttl: 45) + await cache.set(key(), snapshot: snapshot(configVersion: 8), ttl: 45) + let hit = await cache.get(key(), allowStale: false) + XCTAssertEqual(hit?.configVersion, 8) + } + + func testInvalidateAllClearsEntries() async { + let cache = FeatureControlCache(now: { Date() }) + await cache.set(key(), snapshot: snapshot(configVersion: 1), ttl: 45) + await cache.invalidateAll() + let hit = await cache.get(key(), allowStale: true) + XCTAssertNil(hit) + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlErrorTests.swift b/Tests/LCFeatureControlTests/FeatureControlErrorTests.swift new file mode 100644 index 0000000..95864ac --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlErrorTests.swift @@ -0,0 +1,84 @@ +import XCTest +@testable import LCFeatureControl +@testable import LCEssentials + +final class FeatureControlErrorTests: XCTestCase { + + private var api: API! + + override func setUp() { + super.setUp() + let cfg = URLSessionConfiguration.ephemeral + cfg.protocolClasses = [StubURLProtocol.self] + api = API(testConfiguration: cfg) + } + + override func tearDown() { + StubURLProtocol.reset() + api = nil + super.tearDown() + } + + private func makeManager() -> FeatureControlManager { + let config = FeatureControlConfiguration(baseURL: "https://api.example.com", + environment: "production", + auth: FeatureControlHeaderAuth(headers: [:])) + return FeatureControlManager(configuration: config, api: api) + } + + private func context() -> FeatureControlContext { + FeatureControlContext(subjectType: .customer, subjectId: "cust_1") + } + + private func expectMappedError(status: Int, bodyCode: String) async throws -> FeatureControlError { + StubURLProtocol.setStub(.init(statusCode: status, body: Data(#"{"code":"\#(bodyCode)"}"#.utf8))) + let manager = makeManager() + do { + _ = try await manager.evaluateOrThrow(keys: ["x"], context: context()) + XCTFail("expected FeatureControlError") + throw FeatureControlError.transport(message: "unreachable") + } catch let error as FeatureControlError { + return error + } + } + + func testMaps400ToInvalidContext() async throws { + let error = try await expectMappedError(status: 400, bodyCode: "FEATURE_CONTROL_CONTEXT_INVALID") + XCTAssertEqual(error, .invalidContext(code: "FEATURE_CONTROL_CONTEXT_INVALID")) + } + + func testMaps401ToUnauthorized() async throws { + let error = try await expectMappedError(status: 401, bodyCode: "FEATURE_CONTROL_MISSING_AUTH") + XCTAssertEqual(error, .unauthorized(code: "FEATURE_CONTROL_MISSING_AUTH")) + } + + func testMaps403ToForbidden() async throws { + let error = try await expectMappedError(status: 403, bodyCode: "FEATURE_CONTROL_PANEL_ROLE_REQUIRED") + XCTAssertEqual(error, .forbidden(code: "FEATURE_CONTROL_PANEL_ROLE_REQUIRED")) + } + + func testMaps429ToRateLimited() async throws { + let error = try await expectMappedError(status: 429, bodyCode: "FEATURE_CONTROL_RATE_LIMIT") + XCTAssertEqual(error, .rateLimited(code: "FEATURE_CONTROL_RATE_LIMIT")) + } + + func testMaps500ToServerWithStatusCode() async throws { + let error = try await expectMappedError(status: 500, bodyCode: "FEATURE_CONTROL_ERROR") + XCTAssertEqual(error, .server(code: "FEATURE_CONTROL_ERROR", status: 500)) + } + + func testDecodingFailureMapsToTransport() async { + StubURLProtocol.setStub(.init(statusCode: 200, body: Data("not json at all".utf8))) + let manager = makeManager() + do { + _ = try await manager.evaluateOrThrow(keys: ["x"], context: context()) + XCTFail("expected throw") + } catch let error as FeatureControlError { + guard case .transport = error else { + return XCTFail("expected .transport, got \(error)") + } + } catch { + XCTFail("expected FeatureControlError, got \(error)") + } + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlExposureTests.swift b/Tests/LCFeatureControlTests/FeatureControlExposureTests.swift new file mode 100644 index 0000000..eef011f --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlExposureTests.swift @@ -0,0 +1,69 @@ +import XCTest +@testable import LCFeatureControl +@testable import LCEssentials + +final class FeatureControlExposureTests: XCTestCase { + + private var api: API! + + override func setUp() { + super.setUp() + let cfg = URLSessionConfiguration.ephemeral + cfg.protocolClasses = [StubURLProtocol.self] + api = API(testConfiguration: cfg) + } + + override func tearDown() { + StubURLProtocol.reset() + api = nil + super.tearDown() + } + + private func makeManager() -> FeatureControlManager { + let config = FeatureControlConfiguration(baseURL: "https://api.example.com", + environment: "production", + auth: FeatureControlHeaderAuth(headers: [:])) + return FeatureControlManager(configuration: config, api: api) + } + + private func event(_ index: Int) -> FeatureControlExposureEvent { + FeatureControlExposureEvent(featureKey: "fc.checkout_v2", variant: "on", + subjectType: .customer, storeId: "store_\(index)") + } + + func testFlushSendsSingleBatchUnderLimit() async { + StubURLProtocol.setStub(.init(statusCode: 200, body: Data(#"{"error":false,"code":"OK"}"#.utf8))) + let manager = makeManager() + for i in 0..<10 { await manager.recordExposure(event(i)) } + + await manager.flushExposures() + + XCTAssertEqual(StubURLProtocol.requestCount, 1) + } + + func testFlushSplitsIntoMultipleBatchesOverLimit() async { + StubURLProtocol.setStub(.init(statusCode: 200, body: Data(#"{"error":false,"code":"OK"}"#.utf8))) + let manager = makeManager() + for i in 0..<130 { await manager.recordExposure(event(i)) } + + await manager.flushExposures() + + XCTAssertEqual(StubURLProtocol.requestCount, 2) + } + + func testFailedBatchIsDroppedNotRetriedIndefinitely() async { + StubURLProtocol.setStub(.init(statusCode: 500, body: Data())) + let manager = makeManager() + for i in 0..<10 { await manager.recordExposure(event(i)) } + + await manager.flushExposures() // must not throw, must not hang + + XCTAssertEqual(StubURLProtocol.requestCount, 1) + + // buffer was cleared even though the batch failed — a second flush sends nothing new + StubURLProtocol.reset() + StubURLProtocol.setStub(.init(statusCode: 200, body: Data(#"{"error":false,"code":"OK"}"#.utf8))) + await manager.flushExposures() + XCTAssertEqual(StubURLProtocol.requestCount, 0) + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlJSONTests.swift b/Tests/LCFeatureControlTests/FeatureControlJSONTests.swift new file mode 100644 index 0000000..5da1eb9 --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlJSONTests.swift @@ -0,0 +1,40 @@ +import XCTest +@testable import LCFeatureControl + +final class FeatureControlJSONTests: XCTestCase { + + private func decode(_ json: String) throws -> FeatureControlJSON { + try JSONDecoder().decode(FeatureControlJSON.self, from: Data(json.utf8)) + } + + func testDecodesNull() throws { + XCTAssertEqual(try decode("null"), .null) + } + + func testDecodesBool() throws { + XCTAssertEqual(try decode("true"), .bool(true)) + } + + func testDecodesNumber() throws { + XCTAssertEqual(try decode("5"), .number(5)) + } + + func testDecodesString() throws { + XCTAssertEqual(try decode("\"hello\""), .string("hello")) + } + + func testDecodesArray() throws { + XCTAssertEqual(try decode("[\"a\",\"b\"]"), .array([.string("a"), .string("b")])) + } + + func testDecodesNestedObject() throws { + let json = #"{"limit": 5, "tags": ["a","b"], "nested": {"x": true}}"# + let value = try decode(json) + guard case let .object(object) = value else { + return XCTFail("expected .object, got \(value)") + } + XCTAssertEqual(object["limit"], .number(5)) + XCTAssertEqual(object["tags"], .array([.string("a"), .string("b")])) + XCTAssertEqual(object["nested"], .object(["x": .bool(true)])) + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlManagerTests.swift b/Tests/LCFeatureControlTests/FeatureControlManagerTests.swift new file mode 100644 index 0000000..ca8d547 --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlManagerTests.swift @@ -0,0 +1,160 @@ +import XCTest +@testable import LCFeatureControl +@testable import LCEssentials + +private final class MutableClock: @unchecked Sendable { + var value: Date + init(_ value: Date) { self.value = value } +} + +final class FeatureControlManagerTests: XCTestCase { + + private var api: API! + + override func setUp() { + super.setUp() + let cfg = URLSessionConfiguration.ephemeral + cfg.protocolClasses = [StubURLProtocol.self] + api = API(testConfiguration: cfg) + } + + override func tearDown() { + StubURLProtocol.reset() + api = nil + super.tearDown() + } + + private func makeManager(defaults: [String: FeatureControlFlag] = [:], + now: @escaping @Sendable () -> Date = { Date() }) -> FeatureControlManager { + let config = FeatureControlConfiguration(baseURL: "https://api.example.com", + environment: "production", + auth: FeatureControlHeaderAuth(headers: [:])) + return FeatureControlManager(configuration: config, api: api, defaults: defaults, now: now) + } + + private func evaluateResponseBody(configVersion: Int) -> Data { + Data(""" + {"error": false, "code": "FEATURE_CONTROL_EVALUATED", "result": { + "evaluatedAt": "2026-04-16T12:00:00.000Z", + "configVersion": \(configVersion), + "flags": {"fc.checkout_v2": {"enabled": true, "variant": "on", "payload": null, "reason": "rollout"}} + }} + """.utf8) + } + + private func context() -> FeatureControlContext { + FeatureControlContext(subjectType: .customer, subjectId: "cust_1") + } + + // MARK: - Happy path (T4) + + func testEvaluateFetchesAndCachesOnMiss() async throws { + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 7))) + let manager = makeManager() + + let snapshot = try await manager.evaluateOrThrow(keys: ["fc.checkout_v2"], context: context()) + + XCTAssertEqual(snapshot.configVersion, 7) + XCTAssertEqual(StubURLProtocol.requestCount, 1) + let sent = StubURLProtocol.capturedRequests.first + XCTAssertEqual(sent?.httpMethod, "POST") + XCTAssertEqual(sent?.url?.absoluteString, "https://api.example.com/api/feature-control/evaluate") + } + + func testEvaluateReturnsCacheWithoutNetworkCallOnHit() async { + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 7))) + let manager = makeManager() + + _ = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + _ = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + + XCTAssertEqual(StubURLProtocol.requestCount, 1) + } + + func testConfigVersionBumpReplacesCacheAfterExpiry() async { + let clock = MutableClock(Date()) + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 7))) + let manager = makeManager(now: { clock.value }) + + let first = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + XCTAssertEqual(first.configVersion, 7) + + clock.value = clock.value.addingTimeInterval(1000) // past default 45s TTL + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 8))) + + let second = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + XCTAssertEqual(second.configVersion, 8) + XCTAssertEqual(StubURLProtocol.requestCount, 1) + } + + func testInvalidateCacheForcesFreshFetch() async { + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 7))) + let manager = makeManager() + _ = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + + await manager.invalidateCache() + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 9))) + let second = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + + XCTAssertEqual(second.configVersion, 9) + XCTAssertEqual(StubURLProtocol.requestCount, 1) + } + + func testEvaluateOrThrowPropagatesDecodingFailure() async { + StubURLProtocol.setStub(.init(statusCode: 200, body: Data("not json".utf8))) + let manager = makeManager() + + do { + _ = try await manager.evaluateOrThrow(keys: ["x"], context: context()) + XCTFail("expected throw") + } catch { + // any throw is correct — evaluateOrThrow must not silently degrade + } + } + + // MARK: - Safe-degrade fallback chain (T5) + + func testEvaluate429FallsBackToStaleCache() async { + let clock = MutableClock(Date()) + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 7))) + let manager = makeManager(now: { clock.value }) + _ = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + + clock.value = clock.value.addingTimeInterval(1000) + StubURLProtocol.setStub(.init(statusCode: 429, body: Data(#"{"code":"FEATURE_CONTROL_RATE_LIMIT"}"#.utf8))) + + let result = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + XCTAssertEqual(result.configVersion, 7) + } + + func testEvaluate500FallsBackToStaleCache() async { + let clock = MutableClock(Date()) + StubURLProtocol.setStub(.init(statusCode: 200, body: evaluateResponseBody(configVersion: 7))) + let manager = makeManager(now: { clock.value }) + _ = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + + clock.value = clock.value.addingTimeInterval(1000) + StubURLProtocol.setStub(.init(statusCode: 500, body: Data())) + + let result = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + XCTAssertEqual(result.configVersion, 7) + } + + func testEvaluateNoCacheAndServerDownReturnsDefaults() async { + StubURLProtocol.setStub(.init(statusCode: 500, body: Data())) + let defaultFlag = FeatureControlFlag(enabled: false, variant: nil, payload: nil, reason: "default") + let manager = makeManager(defaults: ["fc.checkout_v2": defaultFlag]) + + let result = await manager.evaluate(keys: ["fc.checkout_v2"], context: context()) + + XCTAssertEqual(result.configVersion, FeatureControlManager.unresolvedConfigVersion) + XCTAssertEqual(result.flags["fc.checkout_v2"]?.enabled, false) + } + + func testEvaluateNeverThrowsEvenWhenServerAlwaysErrors() async { + StubURLProtocol.setStub(.init(statusCode: 500, body: Data())) + let manager = makeManager() + // No `try` above this line compiles only because `evaluate` truly never throws. + _ = await manager.evaluate(keys: ["x"], context: context()) + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlModelsTests.swift b/Tests/LCFeatureControlTests/FeatureControlModelsTests.swift new file mode 100644 index 0000000..2f15fa1 --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlModelsTests.swift @@ -0,0 +1,124 @@ +import XCTest +@testable import LCFeatureControl + +/// Exact evaluate-response shape from Atomenta's `docs/feature-control/EXTERNAL_CLIENTS.md` §3. +private let evaluateResponseFixture = """ +{ + "error": false, + "code": "FEATURE_CONTROL_EVALUATED", + "result": { + "evaluatedAt": "2026-04-16T12:00:00.000Z", + "configVersion": 7, + "flags": { + "fc.checkout_v2": { + "enabled": true, + "variant": "on", + "payload": null, + "reason": "rollout" + }, + "fc.search_ranking_v3": { + "enabled": false, + "variant": "off", + "payload": null, + "reason": "default" + } + } + } +} +""" + +final class FeatureControlModelsTests: XCTestCase { + + func testFlagDecodesFromEvaluateResponseFixture() throws { + let data = Data(evaluateResponseFixture.utf8) + let envelope = try JSONDecoder().decode(FeatureControlEvaluateEnvelope.self, from: data) + + XCTAssertFalse(envelope.error) + XCTAssertEqual(envelope.result.configVersion, 7) + + let checkout = try XCTUnwrap(envelope.result.flags["fc.checkout_v2"]) + XCTAssertTrue(checkout.enabled) + XCTAssertEqual(checkout.variant, "on") + XCTAssertNil(checkout.payload) + XCTAssertEqual(checkout.reason, "rollout") + + let ranking = try XCTUnwrap(envelope.result.flags["fc.search_ranking_v3"]) + XCTAssertFalse(ranking.enabled) + } + + func testEvaluatedAtParsesFractionalSecondsISO8601() throws { + let data = Data(evaluateResponseFixture.utf8) + let envelope = try JSONDecoder().decode(FeatureControlEvaluateEnvelope.self, from: data) + + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(identifier: "UTC")! + let components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], + from: envelope.result.evaluatedAt) + XCTAssertEqual(components.year, 2026) + XCTAssertEqual(components.month, 4) + XCTAssertEqual(components.day, 16) + XCTAssertEqual(components.hour, 12) + } + + func testContextEncodesExactOpenAPIShape() throws { + let context = FeatureControlContext(subjectType: .customer, subjectId: "cust_123", + storeId: "store_001", platform: "ios", + appVersion: "2.3.1", + attributes: ["city": "Belo Horizonte"]) + let data = try JSONEncoder().encode(context) + let object = try XCTUnwrap(try JSONSerialization.jsonObject(with: data) as? [String: Any]) + + XCTAssertEqual(object["subjectType"] as? String, "customer") + XCTAssertEqual(object["subjectId"] as? String, "cust_123") + XCTAssertEqual(object["storeId"] as? String, "store_001") + XCTAssertEqual(object["platform"] as? String, "ios") + XCTAssertEqual(object["appVersion"] as? String, "2.3.1") + let attributes = try XCTUnwrap(object["attributes"] as? [String: String]) + XCTAssertEqual(attributes["city"], "Belo Horizonte") + } + + func testNotificationDecodesFromOpenAPIShape() throws { + let json = """ + { + "id": "n1", + "title": "Manutenção", + "body": "Janela de manutenção às 22h", + "severity": "warning", + "createdAt": "2026-04-16T12:00:00.000Z", + "read": false, + "ctaLabel": "Ver detalhes", + "ctaUrl": "https://example.com" + } + """ + let notification = try JSONDecoder().decode(FeatureControlNotification.self, from: Data(json.utf8)) + XCTAssertEqual(notification.id, "n1") + XCTAssertEqual(notification.severity, "warning") + XCTAssertFalse(notification.read) + XCTAssertEqual(notification.ctaLabel, "Ver detalhes") + } + + func testNotificationDecodesWithoutOptionalCTAFields() throws { + let json = """ + { + "id": "n2", + "title": "Info", + "body": "Just FYI", + "severity": "info", + "createdAt": "2026-04-16T12:00:00Z", + "read": true + } + """ + let notification = try JSONDecoder().decode(FeatureControlNotification.self, from: Data(json.utf8)) + XCTAssertNil(notification.ctaLabel) + XCTAssertNil(notification.ctaUrl) + } + + func testExposureEventEncodesExpectedShape() throws { + let event = FeatureControlExposureEvent(featureKey: "fc.checkout_v2", variant: "on", + subjectType: .customer, storeId: "store_001") + let data = try JSONEncoder().encode(event) + let object = try XCTUnwrap(try JSONSerialization.jsonObject(with: data) as? [String: Any]) + XCTAssertEqual(object["featureKey"] as? String, "fc.checkout_v2") + XCTAssertEqual(object["subjectType"] as? String, "customer") + } +} diff --git a/Tests/LCFeatureControlTests/FeatureControlNotificationsClientTests.swift b/Tests/LCFeatureControlTests/FeatureControlNotificationsClientTests.swift new file mode 100644 index 0000000..217eb02 --- /dev/null +++ b/Tests/LCFeatureControlTests/FeatureControlNotificationsClientTests.swift @@ -0,0 +1,93 @@ +import XCTest +@testable import LCFeatureControl +@testable import LCEssentials + +final class FeatureControlNotificationsClientTests: XCTestCase { + + private var api: API! + + override func setUp() { + super.setUp() + let cfg = URLSessionConfiguration.ephemeral + cfg.protocolClasses = [StubURLProtocol.self] + api = API(testConfiguration: cfg) + } + + override func tearDown() { + StubURLProtocol.reset() + api = nil + super.tearDown() + } + + private func makeClient() -> FeatureControlNotificationsClient { + let config = FeatureControlConfiguration(baseURL: "https://api.example.com", + environment: "production", + auth: FeatureControlBearerAuth { "jwt-123" }) + return FeatureControlNotificationsClient(configuration: config, api: api) + } + + func testListDecodesItemsAndNextCursor() async throws { + let body = """ + {"error": false, "result": {"items": [ + {"id": "n1", "title": "T", "body": "B", "severity": "info", + "createdAt": "2026-04-16T12:00:00.000Z", "read": false} + ], "nextCursor": "cursor-2"}} + """ + StubURLProtocol.setStub(.init(statusCode: 200, body: Data(body.utf8))) + let client = makeClient() + + let (items, nextCursor) = try await client.list(status: .unread, limit: 20) + + XCTAssertEqual(items.count, 1) + XCTAssertEqual(items.first?.id, "n1") + XCTAssertEqual(nextCursor, "cursor-2") + } + + func testListPassesStatusAndLimitAsQueryParams() async throws { + let body = #"{"error": false, "result": {"items": [], "nextCursor": null}}"# + StubURLProtocol.setStub(.init(statusCode: 200, body: Data(body.utf8))) + let client = makeClient() + + _ = try await client.list(status: .unread, limit: 5) + + let url = StubURLProtocol.capturedRequests.first?.url?.absoluteString + XCTAssertEqual(url, "https://api.example.com/api/feature-control/notifications?status=unread&limit=5") + XCTAssertEqual(StubURLProtocol.capturedRequests.first?.value(forHTTPHeaderField: "Authorization"), + "Bearer jwt-123") + } + + func testMarkReadPostsToCorrectPathAndSucceedsOn200() async throws { + StubURLProtocol.setStub(.init(statusCode: 200, headers: ["Content-Type": "text/plain"], body: Data("ok".utf8))) + let client = makeClient() + + try await client.markRead(id: "n1") + + let sent = StubURLProtocol.capturedRequests.first + XCTAssertEqual(sent?.httpMethod, "POST") + XCTAssertEqual(sent?.url?.absoluteString, "https://api.example.com/api/feature-control/notifications/n1/read") + } + + func testMarkAllReadSucceedsOn200() async throws { + StubURLProtocol.setStub(.init(statusCode: 200, headers: ["Content-Type": "text/plain"], body: Data("ok".utf8))) + let client = makeClient() + + try await client.markAllRead() + + XCTAssertEqual(StubURLProtocol.capturedRequests.first?.url?.absoluteString, + "https://api.example.com/api/feature-control/notifications/read-all") + } + + func testUnauthorizedMapsToFeatureControlErrorNotCrash() async { + StubURLProtocol.setStub(.init(statusCode: 401, body: Data(#"{"code":"FEATURE_CONTROL_MISSING_AUTH"}"#.utf8))) + let client = makeClient() + + do { + try await client.markAllRead() + XCTFail("expected throw") + } catch let error as FeatureControlError { + XCTAssertEqual(error, .unauthorized(code: "FEATURE_CONTROL_MISSING_AUTH")) + } catch { + XCTFail("expected FeatureControlError, got \(error)") + } + } +} diff --git a/Tests/LCFeatureControlTests/Support/StubURLProtocol.swift b/Tests/LCFeatureControlTests/Support/StubURLProtocol.swift new file mode 100644 index 0000000..bd3c94e --- /dev/null +++ b/Tests/LCFeatureControlTests/Support/StubURLProtocol.swift @@ -0,0 +1,116 @@ +import Foundation + +/// Test double for `URLProtocol`. Intercepts every request on a `URLSession` +/// configured with it, records the outgoing `URLRequest`, and replays a canned +/// response or error supplied by the test. +/// +/// Register via: +/// ``` +/// let cfg = URLSessionConfiguration.ephemeral +/// cfg.protocolClasses = [StubURLProtocol.self] +/// let session = URLSession(configuration: cfg) +/// ``` +final class StubURLProtocol: URLProtocol, @unchecked Sendable { + + struct Stub { + var statusCode: Int = 200 + var headers: [String: String] = ["Content-Type": "application/json"] + var body: Data = Data() + var error: Error? + /// Bytes reported through `URLSession`'s upload progress, in order. + var uploadProgressChunks: [Int] = [] + } + + // MARK: - Test-facing state (guarded) + + private static let lock = NSLock() + // Access is serialised through `lock`; the unsafe opt-out is the documented + // pattern for lock-guarded mutable statics under strict concurrency. + nonisolated(unsafe) private static var _stub = Stub() + nonisolated(unsafe) private static var _capturedRequests: [URLRequest] = [] + nonisolated(unsafe) private static var _capturedBodies: [Data] = [] + + static func setStub(_ stub: Stub) { + lock.lock(); defer { lock.unlock() } + _stub = stub + _capturedRequests = [] + _capturedBodies = [] + } + + static func reset() { setStub(Stub()) } + + static var capturedRequests: [URLRequest] { + lock.lock(); defer { lock.unlock() } + return _capturedRequests + } + + /// Body of the last intercepted request. `URLProtocol` strips `httpBody` for + /// stream bodies, so this reads `httpBodyStream` when needed. + static var lastCapturedBody: Data? { + lock.lock(); defer { lock.unlock() } + return _capturedBodies.last + } + + static var requestCount: Int { + lock.lock(); defer { lock.unlock() } + return _capturedRequests.count + } + + private static func currentStub() -> Stub { + lock.lock(); defer { lock.unlock() } + return _stub + } + + private static func record(_ request: URLRequest, body: Data) { + lock.lock(); defer { lock.unlock() } + _capturedRequests.append(request) + _capturedBodies.append(body) + } + + // MARK: - URLProtocol + + override class func canInit(with request: URLRequest) -> Bool { true } + + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + + override func startLoading() { + let stub = Self.currentStub() + Self.record(request, body: Self.bodyData(from: request)) + + guard let client = client else { return } + + if let error = stub.error { + client.urlProtocol(self, didFailWithError: error) + return + } + + let url = request.url ?? URL(string: "https://stub.invalid")! + let response = HTTPURLResponse(url: url, + statusCode: stub.statusCode, + httpVersion: "HTTP/1.1", + headerFields: stub.headers)! + client.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + client.urlProtocol(self, didLoad: stub.body) + client.urlProtocolDidFinishLoading(self) + } + + override func stopLoading() {} + + // MARK: - Body extraction + + private static func bodyData(from request: URLRequest) -> Data { + if let body = request.httpBody { return body } + guard let stream = request.httpBodyStream else { return Data() } + stream.open() + defer { stream.close() } + var data = Data() + let bufferSize = 64 * 1024 + var buffer = [UInt8](repeating: 0, count: bufferSize) + while stream.hasBytesAvailable { + let read = stream.read(&buffer, maxLength: bufferSize) + if read <= 0 { break } + data.append(buffer, count: read) + } + return data + } +}