[sub-spm-optional-products] LCFeatureControl review fixes: JWT log leak, platforms, request coalescing #13
@@ -1760,12 +1760,24 @@ digest.map { String(format: "%02x", $0) }.joined()
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>LCECryptoKitManager</b> — OTP / peppered-login bridge (needs the <code>LCECryptoKit</code> binary)</summary>
|
<summary><b>LCECryptoKitManager</b> — OTP / peppered-login bridge (separate <code>LCECryptoKit</code> product)</summary>
|
||||||
|
|
||||||
A thin facade over the optional `LCECryptoKit` binary product (enabled by the
|
Lives in its own SPM product, `LCECryptoKit` (target `LCECryptoKitManager`), not in
|
||||||
`LCE_ENABLE_CRYPTO_BINARY` build flag). **When the binary is not linked every
|
`LCEssentials` itself — an optional sub-package that target-depends on `LCEssentials`.
|
||||||
method is a no-op** returning `nil` / `""` / `false`, so calling code still
|
Add it explicitly alongside `LCEssentials` to a consumer's `Package.swift`:
|
||||||
compiles and runs.
|
|
||||||
|
```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:)`
|
### `init()` / `init(privateKey:)`
|
||||||
|
|
||||||
|
|||||||
44
Frameworks/LCECryptoKit.xcframework/Info.plist
Normal file
44
Frameworks/LCECryptoKit.xcframework/Info.plist
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>AvailableLibraries</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>BinaryPath</key>
|
||||||
|
<string>LCECryptoKit.framework/LCECryptoKit</string>
|
||||||
|
<key>LibraryIdentifier</key>
|
||||||
|
<string>ios-arm64_x86_64-simulator</string>
|
||||||
|
<key>LibraryPath</key>
|
||||||
|
<string>LCECryptoKit.framework</string>
|
||||||
|
<key>SupportedArchitectures</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
<string>x86_64</string>
|
||||||
|
</array>
|
||||||
|
<key>SupportedPlatform</key>
|
||||||
|
<string>ios</string>
|
||||||
|
<key>SupportedPlatformVariant</key>
|
||||||
|
<string>simulator</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>BinaryPath</key>
|
||||||
|
<string>LCECryptoKit.framework/LCECryptoKit</string>
|
||||||
|
<key>LibraryIdentifier</key>
|
||||||
|
<string>ios-arm64</string>
|
||||||
|
<key>LibraryPath</key>
|
||||||
|
<string>LCECryptoKit.framework</string>
|
||||||
|
<key>SupportedArchitectures</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
<key>SupportedPlatform</key>
|
||||||
|
<string>ios</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XFWK</string>
|
||||||
|
<key>XCFrameworkFormatVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -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(<swift/objc-prologue.h>)
|
||||||
|
# include <swift/objc-prologue.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma clang diagnostic ignored "-Wauto-import"
|
||||||
|
#if defined(__OBJC__)
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
#endif // defined(__OBJC__)
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdbool>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <new>
|
||||||
|
#include <type_traits>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
|
||||||
|
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
|
||||||
|
# include <ptrauth.h>
|
||||||
|
#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(<uchar.h>)
|
||||||
|
# include <uchar.h>
|
||||||
|
# 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
|
||||||
Binary file not shown.
Binary file not shown.
@@ -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": []
|
||||||
|
}
|
||||||
@@ -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?
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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?
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
framework module LCECryptoKit {
|
||||||
|
header "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(<swift/objc-prologue.h>)
|
||||||
|
# include <swift/objc-prologue.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma clang diagnostic ignored "-Wauto-import"
|
||||||
|
#if defined(__OBJC__)
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
#endif // defined(__OBJC__)
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdbool>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <new>
|
||||||
|
#include <type_traits>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
|
||||||
|
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
|
||||||
|
# include <ptrauth.h>
|
||||||
|
#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(<uchar.h>)
|
||||||
|
# include <uchar.h>
|
||||||
|
# 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(<swift/objc-prologue.h>)
|
||||||
|
# include <swift/objc-prologue.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma clang diagnostic ignored "-Wauto-import"
|
||||||
|
#if defined(__OBJC__)
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
#endif // defined(__OBJC__)
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdbool>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <new>
|
||||||
|
#include <type_traits>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
|
||||||
|
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
|
||||||
|
# include <ptrauth.h>
|
||||||
|
#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(<uchar.h>)
|
||||||
|
# include <uchar.h>
|
||||||
|
# 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
|
||||||
Binary file not shown.
Binary file not shown.
@@ -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": []
|
||||||
|
}
|
||||||
@@ -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?
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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?
|
||||||
|
}
|
||||||
@@ -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": []
|
||||||
|
}
|
||||||
@@ -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?
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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?
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
framework module LCECryptoKit {
|
||||||
|
header "LCECryptoKit-Swift.h"
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>Headers/LCECryptoKit-Swift.h</key>
|
||||||
|
<data>
|
||||||
|
uIM6/aOz59qnT/jGSBAiOinS2qo=
|
||||||
|
</data>
|
||||||
|
<key>Info.plist</key>
|
||||||
|
<data>
|
||||||
|
N0tkj+ldmO7dEKF+W1/6CjYNBrg=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json</key>
|
||||||
|
<data>
|
||||||
|
mIejxCLstZ77ufLnGayqr8wDScc=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
|
||||||
|
<data>
|
||||||
|
GJVZwAs/SRyyfGkg1t2Twb8grXE=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
|
||||||
|
<data>
|
||||||
|
vDvqI7FV3rThZRI4r/zPL877Z04=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface</key>
|
||||||
|
<data>
|
||||||
|
GJVZwAs/SRyyfGkg1t2Twb8grXE=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftmodule</key>
|
||||||
|
<data>
|
||||||
|
0n6YBQBivkPuELjPpXBIj0CQFIc=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
|
||||||
|
<data>
|
||||||
|
mIejxCLstZ77ufLnGayqr8wDScc=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
|
||||||
|
<data>
|
||||||
|
WheGrMD0QDYsEwZ2vOGY+CQwW1o=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
|
||||||
|
<data>
|
||||||
|
f52HXxbH78iRln4x78whSwyigbc=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
|
||||||
|
<data>
|
||||||
|
WheGrMD0QDYsEwZ2vOGY+CQwW1o=
|
||||||
|
</data>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftmodule</key>
|
||||||
|
<data>
|
||||||
|
FSzeAs9LdtELeWT3VL2PMjOFZcQ=
|
||||||
|
</data>
|
||||||
|
<key>Modules/module.modulemap</key>
|
||||||
|
<data>
|
||||||
|
OnB7ckFjsSU10c/y8kUFi69Cav4=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>files2</key>
|
||||||
|
<dict>
|
||||||
|
<key>Headers/LCECryptoKit-Swift.h</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
gHRFTmnGK28MYikS6gvnWErwc/oZ/scYeFssVTBw9MI=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.abi.json</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
/2qkU9Pje/wZPzf5sExK+0bliYzKjDZM7iqyIusKGGk=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
BsY3SInOh3u6xj+kXxauIrgLi4cTqE1mY6weF7QOk2g=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
Ldu7EDkaW9WinMjRHaBaSxpZoBNDXGvNn04410k9e5k=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
BsY3SInOh3u6xj+kXxauIrgLi4cTqE1mY6weF7QOk2g=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/arm64-apple-ios-simulator.swiftmodule</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
cuezjPqokn+YGP9z//TY4ZUxA1Dcmd6ApUafMMaQPAQ=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
/2qkU9Pje/wZPzf5sExK+0bliYzKjDZM7iqyIusKGGk=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
lJH2d+h2iS4AwqKh/vWgSYIGEelZHLrCNQVlcCFB3V4=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
5S2/W4WoD0L4bl+NBuPQI7sGxqpZ7y2s7tme6Eu2oIQ=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
lJH2d+h2iS4AwqKh/vWgSYIGEelZHLrCNQVlcCFB3V4=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/LCECryptoKit.swiftmodule/x86_64-apple-ios-simulator.swiftmodule</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
a5uCPhYWs0qR1YWQZb51vKm+4mYKUgc5ajZr2wEU6tU=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Modules/module.modulemap</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
X+gHfuxKBEgqt+p9I9kR0FYyzml9UPuJpKzZAbPNcc0=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>rules</key>
|
||||||
|
<dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version.plist$</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>rules2</key>
|
||||||
|
<dict>
|
||||||
|
<key>.*\.dSYM($|/)</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>11</real>
|
||||||
|
</dict>
|
||||||
|
<key>^(.*/)?\.DS_Store$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>2000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Info\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^PkgInfo$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^embedded\.provisionprofile$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,13 @@
|
|||||||
// swift-tools-version: 6.0
|
// swift-tools-version: 6.0
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
import Foundation
|
|
||||||
|
|
||||||
let isLocalDevelopment = false //FileManager.default.fileExists(atPath: "../LCECryptoKit/PrivateLib/LCECryptoKitBinary")
|
// LCECryptoKit ships as a prebuilt .xcframework (`Frameworks/LCECryptoKit.xcframework`) —
|
||||||
let enableCryptoBinary = ProcessInfo.processInfo.environment["LCE_ENABLE_CRYPTO_BINARY"] != "0"
|
// 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.
|
||||||
let cryptoPackageURL = isLocalDevelopment
|
//
|
||||||
? "../LCECryptoKit/PrivateLib/LCECryptoKitBinary"
|
// Sub-SPM rule: `LCEssentials` installs standalone (no sub required). Every sub
|
||||||
: "https://7876d8abdb7260a455259d01de49c4022f5da13c@git.loverde.com.br/Loverde-Company-LTDA/LCECryptoKitBinary.git"
|
// (`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 packageDependencies: [Package.Dependency] = enableCryptoBinary
|
|
||||||
? [
|
|
||||||
.package(url: cryptoPackageURL, exact: "1.0.4")
|
|
||||||
]
|
|
||||||
: []
|
|
||||||
|
|
||||||
let targetDependencies: [Target.Dependency] = enableCryptoBinary
|
|
||||||
? [
|
|
||||||
.product(name: "LCECryptoKit", package: "lcecryptokitbinary")
|
|
||||||
]
|
|
||||||
: []
|
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "LCEssentials",
|
name: "LCEssentials",
|
||||||
@@ -33,14 +21,33 @@ let package = Package(
|
|||||||
.library(
|
.library(
|
||||||
name: "LCEssentials",
|
name: "LCEssentials",
|
||||||
targets: ["LCEssentials"]),
|
targets: ["LCEssentials"]),
|
||||||
|
.library(
|
||||||
|
name: "LCECryptoKit",
|
||||||
|
targets: ["LCECryptoKitManager"]),
|
||||||
|
.library(
|
||||||
|
name: "LCFeatureControl",
|
||||||
|
targets: ["LCFeatureControl"]),
|
||||||
],
|
],
|
||||||
dependencies: packageDependencies,
|
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "LCEssentials",
|
name: "LCEssentials"),
|
||||||
dependencies: targetDependencies),
|
.binaryTarget(
|
||||||
|
name: "LCECryptoKit",
|
||||||
|
path: "Frameworks/LCECryptoKit.xcframework"),
|
||||||
|
.target(
|
||||||
|
name: "LCECryptoKitManager",
|
||||||
|
dependencies: [
|
||||||
|
"LCEssentials",
|
||||||
|
"LCECryptoKit"
|
||||||
|
]),
|
||||||
|
.target(
|
||||||
|
name: "LCFeatureControl",
|
||||||
|
dependencies: ["LCEssentials"]),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "LCEssentialsTests",
|
name: "LCEssentialsTests",
|
||||||
dependencies: ["LCEssentials"]),
|
dependencies: ["LCEssentials"]),
|
||||||
|
.testTarget(
|
||||||
|
name: "LCFeatureControlTests",
|
||||||
|
dependencies: ["LCFeatureControl", "LCEssentials"]),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,10 +19,8 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import LCEssentials
|
||||||
#if canImport(LCECryptoKit)
|
|
||||||
import LCECryptoKit
|
import LCECryptoKit
|
||||||
|
|
||||||
public final class LCECryptoKitManager {
|
public final class LCECryptoKitManager {
|
||||||
@@ -81,60 +79,3 @@ public final class LCECryptoKitManager {
|
|||||||
LCECryptoKit.otpDecode(otpEncoded)
|
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
|
|
||||||
39
Sources/LCFeatureControl/FeatureControlAuthorizing.swift
Normal file
39
Sources/LCFeatureControl/FeatureControlAuthorizing.swift
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Pluggable auth for outgoing Feature Control requests. No `Atomenta-Token` type
|
||||||
|
/// exists here on purpose — that module token must never be embedded in a
|
||||||
|
/// customer-facing app (see FC-060 §1 in Atomenta's `docs/feature-control/`).
|
||||||
|
/// A consumer who insists on it does so explicitly via `FeatureControlHeaderAuth`.
|
||||||
|
public protocol FeatureControlAuthorizing: Sendable {
|
||||||
|
func authorize(_ headers: inout [String: String]) async
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For internal/admin apps hitting Atomenta directly with a panel-role JWT.
|
||||||
|
public struct FeatureControlBearerAuth: FeatureControlAuthorizing {
|
||||||
|
private let tokenProvider: @Sendable () async -> String?
|
||||||
|
|
||||||
|
public init(tokenProvider: @escaping @Sendable () async -> String?) {
|
||||||
|
self.tokenProvider = tokenProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
public func authorize(_ headers: inout [String: String]) async {
|
||||||
|
guard let token = await tokenProvider() else { return }
|
||||||
|
headers["Authorization"] = "Bearer \(token)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For a customer app calling its own BFF, which enforces its own auth. Adds
|
||||||
|
/// exactly the given headers — never synthesizes an `Authorization` header.
|
||||||
|
public struct FeatureControlHeaderAuth: FeatureControlAuthorizing {
|
||||||
|
private let headers: [String: String]
|
||||||
|
|
||||||
|
public init(headers: [String: String]) {
|
||||||
|
self.headers = headers
|
||||||
|
}
|
||||||
|
|
||||||
|
public func authorize(_ headers: inout [String: String]) async {
|
||||||
|
for (key, value) in self.headers {
|
||||||
|
headers[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
Sources/LCFeatureControl/FeatureControlCache.swift
Normal file
46
Sources/LCFeatureControl/FeatureControlCache.swift
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Sources/LCFeatureControl/FeatureControlConfiguration.swift
Normal file
33
Sources/LCFeatureControl/FeatureControlConfiguration.swift
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Sources/LCFeatureControl/FeatureControlContext.swift
Normal file
30
Sources/LCFeatureControl/FeatureControlContext.swift
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Sources/LCFeatureControl/FeatureControlDateParsing.swift
Normal file
18
Sources/LCFeatureControl/FeatureControlDateParsing.swift
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
51
Sources/LCFeatureControl/FeatureControlError.swift
Normal file
51
Sources/LCFeatureControl/FeatureControlError.swift
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
28
Sources/LCFeatureControl/FeatureControlExposureEvent.swift
Normal file
28
Sources/LCFeatureControl/FeatureControlExposureEvent.swift
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// One exposure event for `POST /api/feature-control/telemetry/exposure`.
|
||||||
|
public struct FeatureControlExposureEvent: Encodable, Sendable, Equatable {
|
||||||
|
public let featureKey: String
|
||||||
|
public let variant: String?
|
||||||
|
public let subjectType: FeatureControlSubjectType
|
||||||
|
public let storeId: String?
|
||||||
|
|
||||||
|
public init(featureKey: String, variant: String?, subjectType: FeatureControlSubjectType, storeId: String?) {
|
||||||
|
self.featureKey = featureKey
|
||||||
|
self.variant = variant
|
||||||
|
self.subjectType = subjectType
|
||||||
|
self.storeId = storeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request body — server caps `events` at 100 (`maxItems: 100`); batching into that
|
||||||
|
/// limit is the caller's (`FeatureControlManager`'s) job, not this type's.
|
||||||
|
struct FeatureControlExposureBatchBody: Encodable, Sendable {
|
||||||
|
let events: [FeatureControlExposureEvent]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wire envelope for the telemetry response — `{error, code, result: {count}}`.
|
||||||
|
struct FeatureControlExposureBatchEnvelope: Decodable, Sendable {
|
||||||
|
let error: Bool
|
||||||
|
let code: String?
|
||||||
|
}
|
||||||
61
Sources/LCFeatureControl/FeatureControlFlag.swift
Normal file
61
Sources/LCFeatureControl/FeatureControlFlag.swift
Normal file
@@ -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
|
||||||
|
}
|
||||||
33
Sources/LCFeatureControl/FeatureControlJSON.swift
Normal file
33
Sources/LCFeatureControl/FeatureControlJSON.swift
Normal file
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
Sources/LCFeatureControl/FeatureControlManager.swift
Normal file
123
Sources/LCFeatureControl/FeatureControlManager.swift
Normal file
@@ -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..<Swift.min($0 + size, count)]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
65
Sources/LCFeatureControl/FeatureControlNotification.swift
Normal file
65
Sources/LCFeatureControl/FeatureControlNotification.swift
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// `GET /api/feature-control/notifications` `status` query filter.
|
||||||
|
public enum FeatureControlNotificationStatus: String, Sendable {
|
||||||
|
case all, unread
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One item from `NotificationListItem`.
|
||||||
|
public struct FeatureControlNotification: Sendable, Equatable, Identifiable {
|
||||||
|
public let id: String
|
||||||
|
public let title: String
|
||||||
|
public let body: String
|
||||||
|
public let severity: String
|
||||||
|
public let createdAt: Date
|
||||||
|
public let read: Bool
|
||||||
|
public let ctaLabel: String?
|
||||||
|
public let ctaUrl: String?
|
||||||
|
|
||||||
|
public init(id: String, title: String, body: String, severity: String,
|
||||||
|
createdAt: Date, read: Bool, ctaLabel: String?, ctaUrl: String?) {
|
||||||
|
self.id = id
|
||||||
|
self.title = title
|
||||||
|
self.body = body
|
||||||
|
self.severity = severity
|
||||||
|
self.createdAt = createdAt
|
||||||
|
self.read = read
|
||||||
|
self.ctaLabel = ctaLabel
|
||||||
|
self.ctaUrl = ctaUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FeatureControlNotification: Decodable {
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id, title, body, severity, createdAt, read, ctaLabel, ctaUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
let rawDate = try container.decode(String.self, forKey: .createdAt)
|
||||||
|
guard let date = FeatureControlDateParsing.parse(rawDate) else {
|
||||||
|
throw DecodingError.dataCorruptedError(forKey: .createdAt, in: container,
|
||||||
|
debugDescription: "Unrecognized date format: \(rawDate)")
|
||||||
|
}
|
||||||
|
self.id = try container.decode(String.self, forKey: .id)
|
||||||
|
self.title = try container.decode(String.self, forKey: .title)
|
||||||
|
self.body = try container.decode(String.self, forKey: .body)
|
||||||
|
self.severity = try container.decode(String.self, forKey: .severity)
|
||||||
|
self.createdAt = date
|
||||||
|
self.read = try container.decode(Bool.self, forKey: .read)
|
||||||
|
self.ctaLabel = try container.decodeIfPresent(String.self, forKey: .ctaLabel)
|
||||||
|
self.ctaUrl = try container.decodeIfPresent(String.self, forKey: .ctaUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `GET /api/feature-control/notifications` `result` shape.
|
||||||
|
struct FeatureControlNotificationListResult: Decodable, Sendable {
|
||||||
|
let items: [FeatureControlNotification]
|
||||||
|
let nextCursor: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wire envelope for the notifications list — `{error, result}`.
|
||||||
|
struct FeatureControlNotificationListEnvelope: Decodable, Sendable {
|
||||||
|
let error: Bool
|
||||||
|
let result: FeatureControlNotificationListResult
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import Foundation
|
||||||
|
import LCEssentials
|
||||||
|
|
||||||
|
/// Protocol seam for DI, mirroring `FeatureControlEvaluating`.
|
||||||
|
public protocol FeatureControlNotifying: Sendable {
|
||||||
|
func list(status: FeatureControlNotificationStatus, limit: Int) async throws
|
||||||
|
-> (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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Tests/LCFeatureControlTests/FeatureControlAuthTests.swift
Normal file
38
Tests/LCFeatureControlTests/FeatureControlAuthTests.swift
Normal file
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
70
Tests/LCFeatureControlTests/FeatureControlCacheTests.swift
Normal file
70
Tests/LCFeatureControlTests/FeatureControlCacheTests.swift
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
84
Tests/LCFeatureControlTests/FeatureControlErrorTests.swift
Normal file
84
Tests/LCFeatureControlTests/FeatureControlErrorTests.swift
Normal file
@@ -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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
Tests/LCFeatureControlTests/FeatureControlJSONTests.swift
Normal file
40
Tests/LCFeatureControlTests/FeatureControlJSONTests.swift
Normal file
@@ -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)]))
|
||||||
|
}
|
||||||
|
}
|
||||||
160
Tests/LCFeatureControlTests/FeatureControlManagerTests.swift
Normal file
160
Tests/LCFeatureControlTests/FeatureControlManagerTests.swift
Normal file
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
124
Tests/LCFeatureControlTests/FeatureControlModelsTests.swift
Normal file
124
Tests/LCFeatureControlTests/FeatureControlModelsTests.swift
Normal file
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
Tests/LCFeatureControlTests/Support/StubURLProtocol.swift
Normal file
116
Tests/LCFeatureControlTests/Support/StubURLProtocol.swift
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user