@rs-native-kit/version-check 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +256 -0
- package/VersionCheck.podspec +20 -0
- package/android/build.gradle +56 -0
- package/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/java/com/rsnativekit/versioncheck/SecureStorageCipher.kt +57 -0
- package/android/src/main/java/com/rsnativekit/versioncheck/VersionCheckHeadlessTaskService.kt +31 -0
- package/android/src/main/java/com/rsnativekit/versioncheck/VersionCheckModule.kt +215 -0
- package/android/src/main/java/com/rsnativekit/versioncheck/VersionCheckPackage.kt +31 -0
- package/android/src/main/java/com/rsnativekit/versioncheck/VersionCheckWorker.kt +29 -0
- package/ios/PlatformBridge/Ed25519Verifier.swift +19 -0
- package/ios/VersionCheck.h +5 -0
- package/ios/VersionCheck.mm +278 -0
- package/lib/module/data/cache/MemoryCacheStore.js +30 -0
- package/lib/module/data/cache/MemoryCacheStore.js.map +1 -0
- package/lib/module/data/cache/PersistentCacheStore.js +56 -0
- package/lib/module/data/cache/PersistentCacheStore.js.map +1 -0
- package/lib/module/data/config/ConfigCache.js +29 -0
- package/lib/module/data/config/ConfigCache.js.map +1 -0
- package/lib/module/data/config/ConfigDocumentValidator.js +185 -0
- package/lib/module/data/config/ConfigDocumentValidator.js.map +1 -0
- package/lib/module/data/config/ConfigLoader.js +82 -0
- package/lib/module/data/config/ConfigLoader.js.map +1 -0
- package/lib/module/data/config/ConfigProvider.js +58 -0
- package/lib/module/data/config/ConfigProvider.js.map +1 -0
- package/lib/module/data/config/ConfigResolutionPipeline.js +268 -0
- package/lib/module/data/config/ConfigResolutionPipeline.js.map +1 -0
- package/lib/module/data/config/ConfigValidator.js +69 -0
- package/lib/module/data/config/ConfigValidator.js.map +1 -0
- package/lib/module/data/config/SignatureVerifier.js +80 -0
- package/lib/module/data/config/SignatureVerifier.js.map +1 -0
- package/lib/module/data/config/canonicalize.js +88 -0
- package/lib/module/data/config/canonicalize.js.map +1 -0
- package/lib/module/data/config/sources/EnvironmentOverrideSource.js +20 -0
- package/lib/module/data/config/sources/EnvironmentOverrideSource.js.map +1 -0
- package/lib/module/data/config/sources/LocalConfigSource.js +22 -0
- package/lib/module/data/config/sources/LocalConfigSource.js.map +1 -0
- package/lib/module/data/config/sources/RemoteConfigSource.js +38 -0
- package/lib/module/data/config/sources/RemoteConfigSource.js.map +1 -0
- package/lib/module/data/providers/amazon/AmazonAppstoreProvider.js +13 -0
- package/lib/module/data/providers/amazon/AmazonAppstoreProvider.js.map +1 -0
- package/lib/module/data/providers/apple/AppleStoreProvider.js +67 -0
- package/lib/module/data/providers/apple/AppleStoreProvider.js.map +1 -0
- package/lib/module/data/providers/custom-api/CustomApiProvider.js +51 -0
- package/lib/module/data/providers/custom-api/CustomApiProvider.js.map +1 -0
- package/lib/module/data/providers/firebase-remote-config/FirebaseRemoteConfigProvider.js +13 -0
- package/lib/module/data/providers/firebase-remote-config/FirebaseRemoteConfigProvider.js.map +1 -0
- package/lib/module/data/providers/google-play/GooglePlayProvider.js +62 -0
- package/lib/module/data/providers/google-play/GooglePlayProvider.js.map +1 -0
- package/lib/module/data/providers/huawei/HuaweiAppGalleryProvider.js +18 -0
- package/lib/module/data/providers/huawei/HuaweiAppGalleryProvider.js.map +1 -0
- package/lib/module/data/repositories/VersionRepositoryImpl.js +69 -0
- package/lib/module/data/repositories/VersionRepositoryImpl.js.map +1 -0
- package/lib/module/di/container.js +80 -0
- package/lib/module/di/container.js.map +1 -0
- package/lib/module/domain/IVersionManagerCore.js +4 -0
- package/lib/module/domain/IVersionManagerCore.js.map +1 -0
- package/lib/module/domain/VersionManagerCore.js +276 -0
- package/lib/module/domain/VersionManagerCore.js.map +1 -0
- package/lib/module/domain/engines/decision/DecisionEngine.js +40 -0
- package/lib/module/domain/engines/decision/DecisionEngine.js.map +1 -0
- package/lib/module/domain/engines/policy/IUpdatePolicyEngine.js +4 -0
- package/lib/module/domain/engines/policy/IUpdatePolicyEngine.js.map +1 -0
- package/lib/module/domain/engines/policy/PolicyEngine.js +42 -0
- package/lib/module/domain/engines/policy/PolicyEngine.js.map +1 -0
- package/lib/module/domain/engines/semver/IVersionComparator.js +4 -0
- package/lib/module/domain/engines/semver/IVersionComparator.js.map +1 -0
- package/lib/module/domain/engines/semver/SemVerEngine.js +176 -0
- package/lib/module/domain/engines/semver/SemVerEngine.js.map +1 -0
- package/lib/module/domain/errors/VersionManagerException.js +104 -0
- package/lib/module/domain/errors/VersionManagerException.js.map +1 -0
- package/lib/module/domain/models/ActionPlan.js +9 -0
- package/lib/module/domain/models/ActionPlan.js.map +1 -0
- package/lib/module/domain/models/ConfigDocument.js +4 -0
- package/lib/module/domain/models/ConfigDocument.js.map +1 -0
- package/lib/module/domain/models/Events.js +4 -0
- package/lib/module/domain/models/Events.js.map +1 -0
- package/lib/module/domain/models/LifecycleState.js +29 -0
- package/lib/module/domain/models/LifecycleState.js.map +1 -0
- package/lib/module/domain/models/ParsedVersion.js +2 -0
- package/lib/module/domain/models/ParsedVersion.js.map +1 -0
- package/lib/module/domain/models/PlatformId.js +2 -0
- package/lib/module/domain/models/PlatformId.js.map +1 -0
- package/lib/module/domain/models/PublicApiOptions.js +2 -0
- package/lib/module/domain/models/PublicApiOptions.js.map +1 -0
- package/lib/module/domain/models/RemoteVersionInfo.js +4 -0
- package/lib/module/domain/models/RemoteVersionInfo.js.map +1 -0
- package/lib/module/domain/models/StoreLookup.js +2 -0
- package/lib/module/domain/models/StoreLookup.js.map +1 -0
- package/lib/module/domain/models/Unsubscribe.js +2 -0
- package/lib/module/domain/models/Unsubscribe.js.map +1 -0
- package/lib/module/domain/models/UpdateInfo.js +4 -0
- package/lib/module/domain/models/UpdateInfo.js.map +1 -0
- package/lib/module/domain/models/VersionManagerOptions.js +4 -0
- package/lib/module/domain/models/VersionManagerOptions.js.map +1 -0
- package/lib/module/domain/ports/ICacheStore.js +2 -0
- package/lib/module/domain/ports/ICacheStore.js.map +1 -0
- package/lib/module/domain/ports/IClock.js +2 -0
- package/lib/module/domain/ports/IClock.js.map +1 -0
- package/lib/module/domain/ports/IConfigProvider.js +4 -0
- package/lib/module/domain/ports/IConfigProvider.js.map +1 -0
- package/lib/module/domain/ports/IConfigSources.js +4 -0
- package/lib/module/domain/ports/IConfigSources.js.map +1 -0
- package/lib/module/domain/ports/IPlatformBridge.js +4 -0
- package/lib/module/domain/ports/IPlatformBridge.js.map +1 -0
- package/lib/module/domain/ports/IStoreProvider.js +4 -0
- package/lib/module/domain/ports/IStoreProvider.js.map +1 -0
- package/lib/module/domain/ports/IVersionRepository.js +4 -0
- package/lib/module/domain/ports/IVersionRepository.js.map +1 -0
- package/lib/module/domain/statemachine/LifecycleStateMachine.js +55 -0
- package/lib/module/domain/statemachine/LifecycleStateMachine.js.map +1 -0
- package/lib/module/domain/usecases/CheckForUpdatesUseCase.js +31 -0
- package/lib/module/domain/usecases/CheckForUpdatesUseCase.js.map +1 -0
- package/lib/module/domain/usecases/ForceTriggerUpdateUseCase.js +20 -0
- package/lib/module/domain/usecases/ForceTriggerUpdateUseCase.js.map +1 -0
- package/lib/module/domain/usecases/ResetIgnoredVersionsUseCase.js +24 -0
- package/lib/module/domain/usecases/ResetIgnoredVersionsUseCase.js.map +1 -0
- package/lib/module/index.js +10 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/platform/contracts/IPlatformBridge.js +2 -0
- package/lib/module/platform/contracts/IPlatformBridge.js.map +1 -0
- package/lib/module/platform/createPlatformBridge.js +17 -0
- package/lib/module/platform/createPlatformBridge.js.map +1 -0
- package/lib/module/platform/createPlatformBridge.native.js +9 -0
- package/lib/module/platform/createPlatformBridge.native.js.map +1 -0
- package/lib/module/platform/native/NativePlatformBridge.js +143 -0
- package/lib/module/platform/native/NativePlatformBridge.js.map +1 -0
- package/lib/module/platform/native/NativeVersionCheck.js +33 -0
- package/lib/module/platform/native/NativeVersionCheck.js.map +1 -0
- package/lib/module/platform/web/WebPlatformBridge.js +228 -0
- package/lib/module/platform/web/WebPlatformBridge.js.map +1 -0
- package/lib/module/presentation/components/ForceUpdateScreen.js +68 -0
- package/lib/module/presentation/components/ForceUpdateScreen.js.map +1 -0
- package/lib/module/presentation/components/OptionalUpdateBanner.js +69 -0
- package/lib/module/presentation/components/OptionalUpdateBanner.js.map +1 -0
- package/lib/module/presentation/components/SoftUpdateDialog.js +101 -0
- package/lib/module/presentation/components/SoftUpdateDialog.js.map +1 -0
- package/lib/module/presentation/hooks/VersionManagerProvider.js +18 -0
- package/lib/module/presentation/hooks/VersionManagerProvider.js.map +1 -0
- package/lib/module/presentation/hooks/context.js +5 -0
- package/lib/module/presentation/hooks/context.js.map +1 -0
- package/lib/module/presentation/hooks/useUpdateState.js +26 -0
- package/lib/module/presentation/hooks/useUpdateState.js.map +1 -0
- package/lib/module/presentation/hooks/useVersionManager.js +33 -0
- package/lib/module/presentation/hooks/useVersionManager.js.map +1 -0
- package/lib/module/presentation/state/VersionManagerStore.js +47 -0
- package/lib/module/presentation/state/VersionManagerStore.js.map +1 -0
- package/lib/module/publicApi.js +114 -0
- package/lib/module/publicApi.js.map +1 -0
- package/lib/module/shared/eventbus/EventBus.js +34 -0
- package/lib/module/shared/eventbus/EventBus.js.map +1 -0
- package/lib/module/shared/logging/Logger.js +56 -0
- package/lib/module/shared/logging/Logger.js.map +1 -0
- package/lib/module/ui.js +13 -0
- package/lib/module/ui.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/data/cache/MemoryCacheStore.d.ts +12 -0
- package/lib/typescript/src/data/cache/MemoryCacheStore.d.ts.map +1 -0
- package/lib/typescript/src/data/cache/PersistentCacheStore.d.ts +14 -0
- package/lib/typescript/src/data/cache/PersistentCacheStore.d.ts.map +1 -0
- package/lib/typescript/src/data/config/ConfigCache.d.ts +15 -0
- package/lib/typescript/src/data/config/ConfigCache.d.ts.map +1 -0
- package/lib/typescript/src/data/config/ConfigDocumentValidator.d.ts +20 -0
- package/lib/typescript/src/data/config/ConfigDocumentValidator.d.ts.map +1 -0
- package/lib/typescript/src/data/config/ConfigLoader.d.ts +28 -0
- package/lib/typescript/src/data/config/ConfigLoader.d.ts.map +1 -0
- package/lib/typescript/src/data/config/ConfigProvider.d.ts +30 -0
- package/lib/typescript/src/data/config/ConfigProvider.d.ts.map +1 -0
- package/lib/typescript/src/data/config/ConfigResolutionPipeline.d.ts +49 -0
- package/lib/typescript/src/data/config/ConfigResolutionPipeline.d.ts.map +1 -0
- package/lib/typescript/src/data/config/ConfigValidator.d.ts +10 -0
- package/lib/typescript/src/data/config/ConfigValidator.d.ts.map +1 -0
- package/lib/typescript/src/data/config/SignatureVerifier.d.ts +16 -0
- package/lib/typescript/src/data/config/SignatureVerifier.d.ts.map +1 -0
- package/lib/typescript/src/data/config/canonicalize.d.ts +13 -0
- package/lib/typescript/src/data/config/canonicalize.d.ts.map +1 -0
- package/lib/typescript/src/data/config/sources/EnvironmentOverrideSource.d.ts +17 -0
- package/lib/typescript/src/data/config/sources/EnvironmentOverrideSource.d.ts.map +1 -0
- package/lib/typescript/src/data/config/sources/LocalConfigSource.d.ts +19 -0
- package/lib/typescript/src/data/config/sources/LocalConfigSource.d.ts.map +1 -0
- package/lib/typescript/src/data/config/sources/RemoteConfigSource.d.ts +10 -0
- package/lib/typescript/src/data/config/sources/RemoteConfigSource.d.ts.map +1 -0
- package/lib/typescript/src/data/providers/amazon/AmazonAppstoreProvider.d.ts +9 -0
- package/lib/typescript/src/data/providers/amazon/AmazonAppstoreProvider.d.ts.map +1 -0
- package/lib/typescript/src/data/providers/apple/AppleStoreProvider.d.ts +19 -0
- package/lib/typescript/src/data/providers/apple/AppleStoreProvider.d.ts.map +1 -0
- package/lib/typescript/src/data/providers/custom-api/CustomApiProvider.d.ts +14 -0
- package/lib/typescript/src/data/providers/custom-api/CustomApiProvider.d.ts.map +1 -0
- package/lib/typescript/src/data/providers/firebase-remote-config/FirebaseRemoteConfigProvider.d.ts +9 -0
- package/lib/typescript/src/data/providers/firebase-remote-config/FirebaseRemoteConfigProvider.d.ts.map +1 -0
- package/lib/typescript/src/data/providers/google-play/GooglePlayProvider.d.ts +22 -0
- package/lib/typescript/src/data/providers/google-play/GooglePlayProvider.d.ts.map +1 -0
- package/lib/typescript/src/data/providers/huawei/HuaweiAppGalleryProvider.d.ts +14 -0
- package/lib/typescript/src/data/providers/huawei/HuaweiAppGalleryProvider.d.ts.map +1 -0
- package/lib/typescript/src/data/repositories/VersionRepositoryImpl.d.ts +23 -0
- package/lib/typescript/src/data/repositories/VersionRepositoryImpl.d.ts.map +1 -0
- package/lib/typescript/src/di/container.d.ts +12 -0
- package/lib/typescript/src/di/container.d.ts.map +1 -0
- package/lib/typescript/src/domain/IVersionManagerCore.d.ts +30 -0
- package/lib/typescript/src/domain/IVersionManagerCore.d.ts.map +1 -0
- package/lib/typescript/src/domain/VersionManagerCore.d.ts +94 -0
- package/lib/typescript/src/domain/VersionManagerCore.d.ts.map +1 -0
- package/lib/typescript/src/domain/engines/decision/DecisionEngine.d.ts +16 -0
- package/lib/typescript/src/domain/engines/decision/DecisionEngine.d.ts.map +1 -0
- package/lib/typescript/src/domain/engines/policy/IUpdatePolicyEngine.d.ts +17 -0
- package/lib/typescript/src/domain/engines/policy/IUpdatePolicyEngine.d.ts.map +1 -0
- package/lib/typescript/src/domain/engines/policy/PolicyEngine.d.ts +16 -0
- package/lib/typescript/src/domain/engines/policy/PolicyEngine.d.ts.map +1 -0
- package/lib/typescript/src/domain/engines/semver/IVersionComparator.d.ts +7 -0
- package/lib/typescript/src/domain/engines/semver/IVersionComparator.d.ts.map +1 -0
- package/lib/typescript/src/domain/engines/semver/SemVerEngine.d.ts +11 -0
- package/lib/typescript/src/domain/engines/semver/SemVerEngine.d.ts.map +1 -0
- package/lib/typescript/src/domain/errors/VersionManagerException.d.ts +97 -0
- package/lib/typescript/src/domain/errors/VersionManagerException.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/ActionPlan.d.ts +15 -0
- package/lib/typescript/src/domain/models/ActionPlan.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/ConfigDocument.d.ts +86 -0
- package/lib/typescript/src/domain/models/ConfigDocument.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/Events.d.ts +39 -0
- package/lib/typescript/src/domain/models/Events.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/LifecycleState.d.ts +18 -0
- package/lib/typescript/src/domain/models/LifecycleState.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/ParsedVersion.d.ts +9 -0
- package/lib/typescript/src/domain/models/ParsedVersion.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/PlatformId.d.ts +3 -0
- package/lib/typescript/src/domain/models/PlatformId.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/PublicApiOptions.d.ts +13 -0
- package/lib/typescript/src/domain/models/PublicApiOptions.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/RemoteVersionInfo.d.ts +21 -0
- package/lib/typescript/src/domain/models/RemoteVersionInfo.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/StoreLookup.d.ts +12 -0
- package/lib/typescript/src/domain/models/StoreLookup.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/Unsubscribe.d.ts +2 -0
- package/lib/typescript/src/domain/models/Unsubscribe.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/UpdateInfo.d.ts +11 -0
- package/lib/typescript/src/domain/models/UpdateInfo.d.ts.map +1 -0
- package/lib/typescript/src/domain/models/VersionManagerOptions.d.ts +149 -0
- package/lib/typescript/src/domain/models/VersionManagerOptions.d.ts.map +1 -0
- package/lib/typescript/src/domain/ports/ICacheStore.d.ts +6 -0
- package/lib/typescript/src/domain/ports/ICacheStore.d.ts.map +1 -0
- package/lib/typescript/src/domain/ports/IClock.d.ts +4 -0
- package/lib/typescript/src/domain/ports/IClock.d.ts.map +1 -0
- package/lib/typescript/src/domain/ports/IConfigProvider.d.ts +11 -0
- package/lib/typescript/src/domain/ports/IConfigProvider.d.ts.map +1 -0
- package/lib/typescript/src/domain/ports/IConfigSources.d.ts +25 -0
- package/lib/typescript/src/domain/ports/IConfigSources.d.ts.map +1 -0
- package/lib/typescript/src/domain/ports/IPlatformBridge.d.ts +77 -0
- package/lib/typescript/src/domain/ports/IPlatformBridge.d.ts.map +1 -0
- package/lib/typescript/src/domain/ports/IStoreProvider.d.ts +7 -0
- package/lib/typescript/src/domain/ports/IStoreProvider.d.ts.map +1 -0
- package/lib/typescript/src/domain/ports/IVersionRepository.d.ts +8 -0
- package/lib/typescript/src/domain/ports/IVersionRepository.d.ts.map +1 -0
- package/lib/typescript/src/domain/statemachine/LifecycleStateMachine.d.ts +25 -0
- package/lib/typescript/src/domain/statemachine/LifecycleStateMachine.d.ts.map +1 -0
- package/lib/typescript/src/domain/usecases/CheckForUpdatesUseCase.d.ts +30 -0
- package/lib/typescript/src/domain/usecases/CheckForUpdatesUseCase.d.ts.map +1 -0
- package/lib/typescript/src/domain/usecases/ForceTriggerUpdateUseCase.d.ts +17 -0
- package/lib/typescript/src/domain/usecases/ForceTriggerUpdateUseCase.d.ts.map +1 -0
- package/lib/typescript/src/domain/usecases/ResetIgnoredVersionsUseCase.d.ts +7 -0
- package/lib/typescript/src/domain/usecases/ResetIgnoredVersionsUseCase.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +17 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/platform/contracts/IPlatformBridge.d.ts +2 -0
- package/lib/typescript/src/platform/contracts/IPlatformBridge.d.ts.map +1 -0
- package/lib/typescript/src/platform/createPlatformBridge.d.ts +12 -0
- package/lib/typescript/src/platform/createPlatformBridge.d.ts.map +1 -0
- package/lib/typescript/src/platform/createPlatformBridge.native.d.ts +4 -0
- package/lib/typescript/src/platform/createPlatformBridge.native.d.ts.map +1 -0
- package/lib/typescript/src/platform/native/NativePlatformBridge.d.ts +17 -0
- package/lib/typescript/src/platform/native/NativePlatformBridge.d.ts.map +1 -0
- package/lib/typescript/src/platform/native/NativeVersionCheck.d.ts +45 -0
- package/lib/typescript/src/platform/native/NativeVersionCheck.d.ts.map +1 -0
- package/lib/typescript/src/platform/web/WebPlatformBridge.d.ts +17 -0
- package/lib/typescript/src/platform/web/WebPlatformBridge.d.ts.map +1 -0
- package/lib/typescript/src/presentation/components/ForceUpdateScreen.d.ts +12 -0
- package/lib/typescript/src/presentation/components/ForceUpdateScreen.d.ts.map +1 -0
- package/lib/typescript/src/presentation/components/OptionalUpdateBanner.d.ts +12 -0
- package/lib/typescript/src/presentation/components/OptionalUpdateBanner.d.ts.map +1 -0
- package/lib/typescript/src/presentation/components/SoftUpdateDialog.d.ts +14 -0
- package/lib/typescript/src/presentation/components/SoftUpdateDialog.d.ts.map +1 -0
- package/lib/typescript/src/presentation/hooks/VersionManagerProvider.d.ts +9 -0
- package/lib/typescript/src/presentation/hooks/VersionManagerProvider.d.ts.map +1 -0
- package/lib/typescript/src/presentation/hooks/context.d.ts +3 -0
- package/lib/typescript/src/presentation/hooks/context.d.ts.map +1 -0
- package/lib/typescript/src/presentation/hooks/useUpdateState.d.ts +17 -0
- package/lib/typescript/src/presentation/hooks/useUpdateState.d.ts.map +1 -0
- package/lib/typescript/src/presentation/hooks/useVersionManager.d.ts +22 -0
- package/lib/typescript/src/presentation/hooks/useVersionManager.d.ts.map +1 -0
- package/lib/typescript/src/presentation/state/VersionManagerStore.d.ts +28 -0
- package/lib/typescript/src/presentation/state/VersionManagerStore.d.ts.map +1 -0
- package/lib/typescript/src/publicApi.d.ts +24 -0
- package/lib/typescript/src/publicApi.d.ts.map +1 -0
- package/lib/typescript/src/shared/eventbus/EventBus.d.ts +21 -0
- package/lib/typescript/src/shared/eventbus/EventBus.d.ts.map +1 -0
- package/lib/typescript/src/shared/logging/Logger.d.ts +17 -0
- package/lib/typescript/src/shared/logging/Logger.d.ts.map +1 -0
- package/lib/typescript/src/ui.d.ts +15 -0
- package/lib/typescript/src/ui.d.ts.map +1 -0
- package/package.json +192 -0
- package/src/data/cache/MemoryCacheStore.ts +36 -0
- package/src/data/cache/PersistentCacheStore.ts +63 -0
- package/src/data/config/ConfigCache.ts +30 -0
- package/src/data/config/ConfigDocumentValidator.ts +309 -0
- package/src/data/config/ConfigLoader.ts +105 -0
- package/src/data/config/ConfigProvider.ts +84 -0
- package/src/data/config/ConfigResolutionPipeline.ts +424 -0
- package/src/data/config/ConfigValidator.ts +79 -0
- package/src/data/config/SignatureVerifier.ts +90 -0
- package/src/data/config/canonicalize.ts +111 -0
- package/src/data/config/sources/EnvironmentOverrideSource.ts +22 -0
- package/src/data/config/sources/LocalConfigSource.ts +24 -0
- package/src/data/config/sources/RemoteConfigSource.ts +41 -0
- package/src/data/providers/amazon/AmazonAppstoreProvider.ts +16 -0
- package/src/data/providers/apple/AppleStoreProvider.ts +94 -0
- package/src/data/providers/custom-api/CustomApiProvider.ts +75 -0
- package/src/data/providers/firebase-remote-config/FirebaseRemoteConfigProvider.ts +16 -0
- package/src/data/providers/google-play/GooglePlayProvider.ts +75 -0
- package/src/data/providers/huawei/HuaweiAppGalleryProvider.ts +21 -0
- package/src/data/repositories/VersionRepositoryImpl.ts +90 -0
- package/src/di/container.ts +128 -0
- package/src/domain/IVersionManagerCore.ts +54 -0
- package/src/domain/VersionManagerCore.ts +425 -0
- package/src/domain/engines/decision/DecisionEngine.ts +47 -0
- package/src/domain/engines/policy/IUpdatePolicyEngine.ts +18 -0
- package/src/domain/engines/policy/PolicyEngine.ts +60 -0
- package/src/domain/engines/semver/IVersionComparator.ts +7 -0
- package/src/domain/engines/semver/SemVerEngine.ts +200 -0
- package/src/domain/errors/VersionManagerException.ts +166 -0
- package/src/domain/models/ActionPlan.ts +17 -0
- package/src/domain/models/ConfigDocument.ts +97 -0
- package/src/domain/models/Events.ts +48 -0
- package/src/domain/models/LifecycleState.ts +50 -0
- package/src/domain/models/ParsedVersion.ts +8 -0
- package/src/domain/models/PlatformId.ts +9 -0
- package/src/domain/models/PublicApiOptions.ts +14 -0
- package/src/domain/models/RemoteVersionInfo.ts +23 -0
- package/src/domain/models/StoreLookup.ts +12 -0
- package/src/domain/models/Unsubscribe.ts +1 -0
- package/src/domain/models/UpdateInfo.ts +11 -0
- package/src/domain/models/VersionManagerOptions.ts +150 -0
- package/src/domain/ports/ICacheStore.ts +5 -0
- package/src/domain/ports/IClock.ts +3 -0
- package/src/domain/ports/IConfigProvider.ts +18 -0
- package/src/domain/ports/IConfigSources.ts +44 -0
- package/src/domain/ports/IPlatformBridge.ts +97 -0
- package/src/domain/ports/IStoreProvider.ts +12 -0
- package/src/domain/ports/IVersionRepository.ts +14 -0
- package/src/domain/statemachine/LifecycleStateMachine.ts +76 -0
- package/src/domain/usecases/CheckForUpdatesUseCase.ts +63 -0
- package/src/domain/usecases/ForceTriggerUpdateUseCase.ts +35 -0
- package/src/domain/usecases/ResetIgnoredVersionsUseCase.ts +26 -0
- package/src/index.tsx +84 -0
- package/src/platform/contracts/IPlatformBridge.ts +14 -0
- package/src/platform/createPlatformBridge.native.ts +7 -0
- package/src/platform/createPlatformBridge.ts +15 -0
- package/src/platform/native/NativePlatformBridge.ts +179 -0
- package/src/platform/native/NativeVersionCheck.ts +64 -0
- package/src/platform/web/WebPlatformBridge.ts +347 -0
- package/src/presentation/components/ForceUpdateScreen.tsx +64 -0
- package/src/presentation/components/OptionalUpdateBanner.tsx +53 -0
- package/src/presentation/components/SoftUpdateDialog.tsx +84 -0
- package/src/presentation/hooks/VersionManagerProvider.tsx +23 -0
- package/src/presentation/hooks/context.ts +6 -0
- package/src/presentation/hooks/useUpdateState.ts +50 -0
- package/src/presentation/hooks/useVersionManager.ts +68 -0
- package/src/presentation/state/VersionManagerStore.ts +72 -0
- package/src/publicApi.ts +126 -0
- package/src/shared/eventbus/EventBus.ts +56 -0
- package/src/shared/logging/Logger.ts +70 -0
- package/src/ui.tsx +19 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { IPlatformBridge } from '../ports/IPlatformBridge';
|
|
2
|
+
import type {
|
|
3
|
+
SignatureAlgorithm,
|
|
4
|
+
VersionManagerConfigDocument,
|
|
5
|
+
} from './ConfigDocument';
|
|
6
|
+
import type { RemoteVersionInfo } from './RemoteVersionInfo';
|
|
7
|
+
|
|
8
|
+
export type LogLevel =
|
|
9
|
+
'silent' | 'error' | 'warn' | 'info' | 'debug' | 'verbose';
|
|
10
|
+
|
|
11
|
+
export interface LogEntry {
|
|
12
|
+
readonly level: LogLevel;
|
|
13
|
+
readonly message: string;
|
|
14
|
+
readonly timestamp: number;
|
|
15
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ILogSink {
|
|
19
|
+
write(entry: LogEntry): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface LoggingOptions {
|
|
23
|
+
level?: LogLevel;
|
|
24
|
+
sink?: ILogSink;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CacheOptions {
|
|
28
|
+
ttlMs?: number;
|
|
29
|
+
storage?: 'memory' | 'persistent';
|
|
30
|
+
bustOnManualCheck?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RetryOptions {
|
|
34
|
+
maxAttempts?: number;
|
|
35
|
+
backoff?: 'fixed' | 'exponential-jitter';
|
|
36
|
+
baseDelayMs?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FallbackOptions {
|
|
40
|
+
onNetworkError?: 'useCache' | 'useDefaultConfig' | 'noAction';
|
|
41
|
+
defaultConfig?: RemoteVersionInfo;
|
|
42
|
+
requestTimeoutMs?: number;
|
|
43
|
+
retry?: RetryOptions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface StoreLinksOptions {
|
|
47
|
+
/**
|
|
48
|
+
* appStoreId is optional — when omitted, the App Store lookup falls back to the
|
|
49
|
+
* running app's bundle id (IAppInfoProvider.getBundleId(), read at check time)
|
|
50
|
+
* instead of the numeric App Store Connect id.
|
|
51
|
+
*/
|
|
52
|
+
ios?: { appStoreId?: string; region?: string };
|
|
53
|
+
/**
|
|
54
|
+
* packageName is optional — when omitted, the Google Play lookup falls back to the
|
|
55
|
+
* running app's bundle id (which *is* the Android package name) at check time.
|
|
56
|
+
*/
|
|
57
|
+
android?: { packageName?: string };
|
|
58
|
+
huawei?: { appId: string };
|
|
59
|
+
amazon?: { asin: string };
|
|
60
|
+
custom?: { url: string; headers?: Record<string, string> };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface PolicyOptions {
|
|
64
|
+
forceUpdateBelow?: string;
|
|
65
|
+
reminderIntervalMs?: number;
|
|
66
|
+
rolloutPercentage?: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Doc 03 §1.1 Security field group — fixed by the app developer, tier-4 only. */
|
|
70
|
+
export interface SecurityOptions {
|
|
71
|
+
signatureAlgorithm?: SignatureAlgorithm;
|
|
72
|
+
trustedKeyIds?: string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Doc 03 §0/§4 — optional loadable-config-document extension points layered on top of
|
|
77
|
+
* the VersionManagerOptions "default fallback" tier. All optional; an app that supplies
|
|
78
|
+
* none of these still works, driven entirely by configure() options (tier 4 only).
|
|
79
|
+
*/
|
|
80
|
+
export interface ConfigSourcesOptions {
|
|
81
|
+
/**
|
|
82
|
+
* Bundled local config envelope. Metro/webpack natively supports JSON imports
|
|
83
|
+
* (`import localConfig from './vm-config.json'`), which already avoids the
|
|
84
|
+
* double-buffer-copy §5.1 is concerned about for a <=64KB document without requiring
|
|
85
|
+
* a dedicated native mmap reader — pass the imported envelope object directly.
|
|
86
|
+
*/
|
|
87
|
+
local?: import('./ConfigDocument').SignedConfigEnvelope;
|
|
88
|
+
/** Extra headers/timeout for the remote fetch beyond FallbackOptions.requestTimeoutMs. */
|
|
89
|
+
remoteFetchHeaders?: Record<string, string>;
|
|
90
|
+
/** Explicit runtime overrides — highest-precedence tier (§4.2 tier 1). */
|
|
91
|
+
envOverrides?: Partial<VersionManagerConfigDocument>;
|
|
92
|
+
/** Registered customUiHookIds -> handler ids that a config document is allowed to activate (§1.2, §7.1 VM-1009). */
|
|
93
|
+
registeredUiHookIds?: string[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface VersionManagerOptions {
|
|
97
|
+
appVersion?: string;
|
|
98
|
+
logging?: LoggingOptions;
|
|
99
|
+
cache?: CacheOptions;
|
|
100
|
+
fallback?: FallbackOptions;
|
|
101
|
+
stores: StoreLinksOptions;
|
|
102
|
+
policy?: PolicyOptions;
|
|
103
|
+
/** DI override, advanced/testing use only (Prompt 2 §1.1). */
|
|
104
|
+
platformBridge?: IPlatformBridge;
|
|
105
|
+
strictReadiness?: boolean;
|
|
106
|
+
/** Doc 03 §1.1 — if absent, the SDK operates in local-file/default-only mode. */
|
|
107
|
+
remoteConfigUrl?: string;
|
|
108
|
+
security?: SecurityOptions;
|
|
109
|
+
configSources?: ConfigSourcesOptions;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Fully-defaulted, immutable configuration actually consumed at runtime, produced by
|
|
114
|
+
* IConfigProvider.resolve() from a VersionManagerOptions "default fallback" tier
|
|
115
|
+
* (Prompt 3 §4.2). Local-file/remote/env overlay tiers are extension points on
|
|
116
|
+
* IConfigProvider, not yet wired into a signed-envelope pipeline in this pass —
|
|
117
|
+
* see docs/architecture/03-configuration-system.md §3 for that follow-up scope.
|
|
118
|
+
*/
|
|
119
|
+
export interface ResolvedVersionManagerConfig {
|
|
120
|
+
readonly appVersion: string | null;
|
|
121
|
+
readonly logging: { readonly level: LogLevel; readonly sink: ILogSink };
|
|
122
|
+
readonly cache: {
|
|
123
|
+
readonly ttlMs: number;
|
|
124
|
+
readonly storage: 'memory' | 'persistent';
|
|
125
|
+
readonly bustOnManualCheck: boolean;
|
|
126
|
+
};
|
|
127
|
+
readonly fallback: {
|
|
128
|
+
readonly onNetworkError: 'useCache' | 'useDefaultConfig' | 'noAction';
|
|
129
|
+
readonly defaultConfig: RemoteVersionInfo | null;
|
|
130
|
+
readonly requestTimeoutMs: number;
|
|
131
|
+
readonly retry: {
|
|
132
|
+
readonly maxAttempts: number;
|
|
133
|
+
readonly backoff: 'fixed' | 'exponential-jitter';
|
|
134
|
+
readonly baseDelayMs: number;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
readonly stores: StoreLinksOptions;
|
|
138
|
+
readonly policy: {
|
|
139
|
+
readonly forceUpdateBelow: string | null;
|
|
140
|
+
readonly reminderIntervalMs: number;
|
|
141
|
+
readonly rolloutPercentage: number;
|
|
142
|
+
};
|
|
143
|
+
readonly strictReadiness: boolean;
|
|
144
|
+
readonly remoteConfigUrl: string | null;
|
|
145
|
+
readonly security: {
|
|
146
|
+
readonly signatureAlgorithm: SignatureAlgorithm;
|
|
147
|
+
readonly trustedKeyIds: readonly string[];
|
|
148
|
+
};
|
|
149
|
+
readonly schemaVersion: string;
|
|
150
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ConfigLoadRequest } from '../models/ConfigDocument';
|
|
2
|
+
import type {
|
|
3
|
+
ResolvedVersionManagerConfig,
|
|
4
|
+
VersionManagerOptions,
|
|
5
|
+
} from '../models/VersionManagerOptions';
|
|
6
|
+
import type { Unsubscribe } from './IPlatformBridge';
|
|
7
|
+
|
|
8
|
+
export interface IConfigProvider {
|
|
9
|
+
resolve(
|
|
10
|
+
defaults: VersionManagerOptions
|
|
11
|
+
): Promise<ResolvedVersionManagerConfig>;
|
|
12
|
+
/** Doc 03 §0's named surface (`load(request)`) — delegates to resolve(request.defaults). */
|
|
13
|
+
load(request: ConfigLoadRequest): Promise<ResolvedVersionManagerConfig>;
|
|
14
|
+
/** Fires when a later resolution (e.g. a remote refresh) produces a changed config. */
|
|
15
|
+
subscribe(
|
|
16
|
+
listener: (config: ResolvedVersionManagerConfig) => void
|
|
17
|
+
): Unsubscribe;
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BoundaryValidationResult,
|
|
3
|
+
FetchOptions,
|
|
4
|
+
RawConfigDocument,
|
|
5
|
+
SchemaValidationResult,
|
|
6
|
+
SignedConfigEnvelope,
|
|
7
|
+
SignatureVerificationResult,
|
|
8
|
+
TrustedKeySet,
|
|
9
|
+
} from '../models/ConfigDocument';
|
|
10
|
+
import type { ResolvedVersionManagerConfig } from '../models/VersionManagerOptions';
|
|
11
|
+
|
|
12
|
+
/** Doc 03 §0 — small pipeline of ports living under src/data/config/. */
|
|
13
|
+
|
|
14
|
+
export interface ILocalConfigSource {
|
|
15
|
+
/** null if no bundled config asset/envelope was supplied. */
|
|
16
|
+
read(): Promise<RawConfigDocument | null>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IRemoteConfigSource {
|
|
20
|
+
fetch(url: string, options: FetchOptions): Promise<RawConfigDocument>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface IEnvironmentOverrideSource {
|
|
24
|
+
read(): Promise<
|
|
25
|
+
Partial<import('../models/ConfigDocument').VersionManagerConfigDocument>
|
|
26
|
+
>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IConfigCache {
|
|
30
|
+
getLastValid(): Promise<ResolvedVersionManagerConfig | null>;
|
|
31
|
+
setLastValid(config: ResolvedVersionManagerConfig): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface IConfigValidator {
|
|
35
|
+
validateSchema(doc: RawConfigDocument): SchemaValidationResult;
|
|
36
|
+
validateBoundaries(doc: RawConfigDocument): BoundaryValidationResult;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ISignatureVerifier {
|
|
40
|
+
verify(
|
|
41
|
+
envelope: SignedConfigEnvelope,
|
|
42
|
+
trustedKeys: TrustedKeySet
|
|
43
|
+
): Promise<SignatureVerificationResult>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { PlatformId } from '../models/PlatformId';
|
|
2
|
+
import type { Unsubscribe } from '../models/Unsubscribe';
|
|
3
|
+
import type { IClock } from './IClock';
|
|
4
|
+
|
|
5
|
+
export type { Unsubscribe };
|
|
6
|
+
|
|
7
|
+
export interface HttpRequest {
|
|
8
|
+
readonly url: string;
|
|
9
|
+
readonly method: 'GET' | 'POST';
|
|
10
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
11
|
+
readonly body?: string;
|
|
12
|
+
readonly timeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface HttpResponse {
|
|
16
|
+
readonly status: number;
|
|
17
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
18
|
+
readonly body: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IHttpClient {
|
|
22
|
+
request(req: HttpRequest): Promise<HttpResponse>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface IKeyValueStorage {
|
|
26
|
+
get(key: string): Promise<string | null>;
|
|
27
|
+
set(key: string, value: string): Promise<void>;
|
|
28
|
+
remove(key: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ISecureStorage {
|
|
32
|
+
getItem(key: string): Promise<string | null>;
|
|
33
|
+
setItem(key: string, value: string): Promise<void>;
|
|
34
|
+
removeItem(key: string): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface IAppInfoProvider {
|
|
38
|
+
getCurrentVersion(): Promise<string>;
|
|
39
|
+
getBuildNumber(): Promise<string>;
|
|
40
|
+
getBundleId(): Promise<string>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface IDeviceInfoProvider {
|
|
44
|
+
getOsVersion(): Promise<string>;
|
|
45
|
+
getDeviceModel(): Promise<string>;
|
|
46
|
+
getLocale(): Promise<string>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface BackgroundTaskDescriptor {
|
|
50
|
+
readonly taskId: string;
|
|
51
|
+
readonly minIntervalMs: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface IBackgroundScheduler {
|
|
55
|
+
schedule(task: BackgroundTaskDescriptor): Promise<void>;
|
|
56
|
+
cancel(taskId: string): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface IAppLifecycleObserver {
|
|
60
|
+
onForeground(cb: () => void): Unsubscribe;
|
|
61
|
+
onBackground(cb: () => void): Unsubscribe;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Additive port (not in doc 01 §4's original table) backing ISignatureVerifier
|
|
66
|
+
* (doc 03 §3.4): "OS-provided cryptography exclusively." Optional because it is only
|
|
67
|
+
* required when a consumer configures remote/local signed config documents with
|
|
68
|
+
* algorithm !== 'none'. `keyId` (not raw key material) crosses the JS/native boundary —
|
|
69
|
+
* per doc 03 §3.4 the actual public key / HMAC secret is embedded at build time on the
|
|
70
|
+
* native side (iOS Info.plist/Secrets.xcconfig, Android BuildConfig) and resolved from
|
|
71
|
+
* `keyId` inside the native implementation; JS never sees key material.
|
|
72
|
+
*/
|
|
73
|
+
export interface ICryptoProvider {
|
|
74
|
+
verifyEd25519(
|
|
75
|
+
keyId: string,
|
|
76
|
+
messageBase64: string,
|
|
77
|
+
signatureBase64: string
|
|
78
|
+
): Promise<boolean>;
|
|
79
|
+
verifyHmacSha256(
|
|
80
|
+
keyId: string,
|
|
81
|
+
messageBase64: string,
|
|
82
|
+
macBase64: string
|
|
83
|
+
): Promise<boolean>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface IPlatformBridge {
|
|
87
|
+
readonly platform: PlatformId;
|
|
88
|
+
readonly http: IHttpClient;
|
|
89
|
+
readonly storage: IKeyValueStorage;
|
|
90
|
+
readonly secureStorage: ISecureStorage;
|
|
91
|
+
readonly appInfo: IAppInfoProvider;
|
|
92
|
+
readonly deviceInfo: IDeviceInfoProvider;
|
|
93
|
+
readonly scheduler: IBackgroundScheduler;
|
|
94
|
+
readonly clock: IClock;
|
|
95
|
+
readonly lifecycle: IAppLifecycleObserver;
|
|
96
|
+
readonly crypto?: ICryptoProvider;
|
|
97
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StoreProviderId } from '../models/PlatformId';
|
|
2
|
+
import type {
|
|
3
|
+
StoreLookupRequest,
|
|
4
|
+
StoreLookupResult,
|
|
5
|
+
} from '../models/StoreLookup';
|
|
6
|
+
|
|
7
|
+
export interface IStoreProvider {
|
|
8
|
+
readonly id: StoreProviderId;
|
|
9
|
+
fetchLatestVersionInfo(
|
|
10
|
+
request: StoreLookupRequest
|
|
11
|
+
): Promise<StoreLookupResult>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RemoteVersionInfo,
|
|
3
|
+
UserVersionDecision,
|
|
4
|
+
VersionCheckRequest,
|
|
5
|
+
} from '../models/RemoteVersionInfo';
|
|
6
|
+
|
|
7
|
+
export interface IVersionRepository {
|
|
8
|
+
getRemoteVersionInfo(
|
|
9
|
+
request: VersionCheckRequest
|
|
10
|
+
): Promise<RemoteVersionInfo>;
|
|
11
|
+
getCachedVersionInfo(): Promise<RemoteVersionInfo | null>;
|
|
12
|
+
persistUserDecision(decision: UserVersionDecision): Promise<void>;
|
|
13
|
+
getUserDecision(): Promise<UserVersionDecision | null>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LIFECYCLE_TRANSITIONS,
|
|
3
|
+
LifecycleState,
|
|
4
|
+
} from '../models/LifecycleState';
|
|
5
|
+
import type { Unsubscribe } from '../models/Unsubscribe';
|
|
6
|
+
|
|
7
|
+
/** Internal invariant violation — not part of the public VersionManagerException hierarchy. */
|
|
8
|
+
export class InvalidTransitionError extends Error {
|
|
9
|
+
constructor(from: LifecycleState, to: LifecycleState) {
|
|
10
|
+
super(`Invalid lifecycle transition: ${from} -> ${to}`);
|
|
11
|
+
this.name = 'InvalidTransitionError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ILifecycleStateMachine {
|
|
16
|
+
readonly current: LifecycleState;
|
|
17
|
+
transition(to: LifecycleState): void;
|
|
18
|
+
onEnter(state: LifecycleState, handler: () => void): Unsubscribe;
|
|
19
|
+
onExit(state: LifecycleState, handler: () => void): Unsubscribe;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type HandlerMap = Map<LifecycleState, Set<() => void>>;
|
|
23
|
+
|
|
24
|
+
export class LifecycleStateMachine implements ILifecycleStateMachine {
|
|
25
|
+
private state: LifecycleState = LifecycleState.UNINITIALIZED;
|
|
26
|
+
private readonly enterHandlers: HandlerMap = new Map();
|
|
27
|
+
private readonly exitHandlers: HandlerMap = new Map();
|
|
28
|
+
|
|
29
|
+
get current(): LifecycleState {
|
|
30
|
+
return this.state;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
transition(to: LifecycleState): void {
|
|
34
|
+
const allowed = LIFECYCLE_TRANSITIONS[this.state];
|
|
35
|
+
if (!allowed.includes(to)) {
|
|
36
|
+
throw new InvalidTransitionError(this.state, to);
|
|
37
|
+
}
|
|
38
|
+
const from = this.state;
|
|
39
|
+
this.notify(this.exitHandlers, from);
|
|
40
|
+
this.state = to;
|
|
41
|
+
this.notify(this.enterHandlers, to);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
onEnter(state: LifecycleState, handler: () => void): Unsubscribe {
|
|
45
|
+
return this.register(this.enterHandlers, state, handler);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onExit(state: LifecycleState, handler: () => void): Unsubscribe {
|
|
49
|
+
return this.register(this.exitHandlers, state, handler);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private register(
|
|
53
|
+
map: HandlerMap,
|
|
54
|
+
state: LifecycleState,
|
|
55
|
+
handler: () => void
|
|
56
|
+
): Unsubscribe {
|
|
57
|
+
let set = map.get(state);
|
|
58
|
+
if (!set) {
|
|
59
|
+
set = new Set();
|
|
60
|
+
map.set(state, set);
|
|
61
|
+
}
|
|
62
|
+
set.add(handler);
|
|
63
|
+
return () => {
|
|
64
|
+
set?.delete(handler);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Copy-on-write snapshot dispatch (Prompt 1 §9.2) — safe if a handler unsubscribes mid-dispatch. */
|
|
69
|
+
private notify(map: HandlerMap, state: LifecycleState): void {
|
|
70
|
+
const set = map.get(state);
|
|
71
|
+
if (!set) return;
|
|
72
|
+
for (const handler of Array.from(set)) {
|
|
73
|
+
handler();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { DecisionEngine } from '../engines/decision/DecisionEngine';
|
|
2
|
+
import type { IUpdatePolicyEngine } from '../engines/policy/IUpdatePolicyEngine';
|
|
3
|
+
import type { IVersionComparator } from '../engines/semver/IVersionComparator';
|
|
4
|
+
import type { ActionPlan } from '../models/ActionPlan';
|
|
5
|
+
import type { PlatformId } from '../models/PlatformId';
|
|
6
|
+
import type { IClock } from '../ports/IClock';
|
|
7
|
+
import type { IVersionRepository } from '../ports/IVersionRepository';
|
|
8
|
+
|
|
9
|
+
export interface CheckForUpdatesUseCaseDeps {
|
|
10
|
+
readonly repository: IVersionRepository;
|
|
11
|
+
readonly comparator: IVersionComparator;
|
|
12
|
+
readonly policyEngine: IUpdatePolicyEngine;
|
|
13
|
+
readonly decisionEngine: DecisionEngine;
|
|
14
|
+
readonly clock: IClock;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CheckForUpdatesInput {
|
|
18
|
+
readonly currentVersion: string;
|
|
19
|
+
readonly bundleId: string;
|
|
20
|
+
readonly platform: PlatformId;
|
|
21
|
+
readonly bypassCache: boolean;
|
|
22
|
+
readonly timeoutMs: number;
|
|
23
|
+
readonly forceUpdateBelow: string | null;
|
|
24
|
+
readonly rolloutPercentage: number;
|
|
25
|
+
readonly rolloutBucket: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class CheckForUpdatesUseCase {
|
|
29
|
+
constructor(private readonly deps: CheckForUpdatesUseCaseDeps) {}
|
|
30
|
+
|
|
31
|
+
async execute(input: CheckForUpdatesInput): Promise<ActionPlan> {
|
|
32
|
+
const remote = await this.deps.repository.getRemoteVersionInfo({
|
|
33
|
+
currentVersion: input.currentVersion,
|
|
34
|
+
bundleId: input.bundleId,
|
|
35
|
+
platform: input.platform,
|
|
36
|
+
bypassCache: input.bypassCache,
|
|
37
|
+
timeoutMs: input.timeoutMs,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const currentParsed = this.deps.comparator.parse(input.currentVersion);
|
|
41
|
+
const remoteParsed = this.deps.comparator.parse(remote.latestVersion);
|
|
42
|
+
const forceUpdateBelowParsed = input.forceUpdateBelow
|
|
43
|
+
? this.deps.comparator.parse(input.forceUpdateBelow)
|
|
44
|
+
: null;
|
|
45
|
+
const userDecision = await this.deps.repository.getUserDecision();
|
|
46
|
+
|
|
47
|
+
const actionType = this.deps.policyEngine.evaluate({
|
|
48
|
+
currentVersion: currentParsed,
|
|
49
|
+
remoteVersion: remoteParsed,
|
|
50
|
+
forceUpdateBelow: forceUpdateBelowParsed,
|
|
51
|
+
userDecision,
|
|
52
|
+
now: this.deps.clock.now(),
|
|
53
|
+
rolloutPercentage: input.rolloutPercentage,
|
|
54
|
+
rolloutBucket: input.rolloutBucket,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return this.deps.decisionEngine.buildActionPlan(
|
|
58
|
+
actionType,
|
|
59
|
+
input.currentVersion,
|
|
60
|
+
remote
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ActionType } from '../models/ActionPlan';
|
|
2
|
+
import type { ActionPlan } from '../models/ActionPlan';
|
|
3
|
+
import type { DecisionEngine } from '../engines/decision/DecisionEngine';
|
|
4
|
+
import type { PlatformId } from '../models/PlatformId';
|
|
5
|
+
import type { IVersionRepository } from '../ports/IVersionRepository';
|
|
6
|
+
|
|
7
|
+
export interface ForceTriggerUpdateInput {
|
|
8
|
+
readonly currentVersion: string;
|
|
9
|
+
readonly bundleId: string;
|
|
10
|
+
readonly platform: PlatformId;
|
|
11
|
+
readonly timeoutMs: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ForceTriggerUpdateUseCase {
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly repository: IVersionRepository,
|
|
17
|
+
private readonly decisionEngine: DecisionEngine
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
async execute(input: ForceTriggerUpdateInput): Promise<ActionPlan> {
|
|
21
|
+
const remote = await this.repository.getRemoteVersionInfo({
|
|
22
|
+
currentVersion: input.currentVersion,
|
|
23
|
+
bundleId: input.bundleId,
|
|
24
|
+
platform: input.platform,
|
|
25
|
+
bypassCache: true,
|
|
26
|
+
timeoutMs: input.timeoutMs,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return this.decisionEngine.buildActionPlan(
|
|
30
|
+
ActionType.FORCE_UPDATE,
|
|
31
|
+
input.currentVersion,
|
|
32
|
+
remote
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IVersionRepository } from '../ports/IVersionRepository';
|
|
2
|
+
|
|
3
|
+
export class ResetIgnoredVersionsUseCase {
|
|
4
|
+
constructor(private readonly repository: IVersionRepository) {}
|
|
5
|
+
|
|
6
|
+
async execute(versions?: readonly string[]): Promise<void> {
|
|
7
|
+
const current = await this.repository.getUserDecision();
|
|
8
|
+
const remindMeLaterUntil = current?.remindMeLaterUntil ?? null;
|
|
9
|
+
|
|
10
|
+
if (!versions || versions.length === 0) {
|
|
11
|
+
await this.repository.persistUserDecision({
|
|
12
|
+
ignoredVersions: [],
|
|
13
|
+
remindMeLaterUntil,
|
|
14
|
+
});
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const remaining = (current?.ignoredVersions ?? []).filter(
|
|
19
|
+
(v) => !versions.includes(v)
|
|
20
|
+
);
|
|
21
|
+
await this.repository.persistUserDecision({
|
|
22
|
+
ignoredVersions: remaining,
|
|
23
|
+
remindMeLaterUntil,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Public API entry point (core only — no React/UI code reachable from this path,
|
|
2
|
+
// see src/ui.tsx for the Hooks adapter). Prompt 2.
|
|
3
|
+
|
|
4
|
+
export { VersionManager, VersionManagerBuilder } from './publicApi';
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
IVersionManagerCore,
|
|
8
|
+
IVersionManagerEvents,
|
|
9
|
+
} from './domain/IVersionManagerCore';
|
|
10
|
+
|
|
11
|
+
export type { ActionPlan } from './domain/models/ActionPlan';
|
|
12
|
+
export { ActionType } from './domain/models/ActionPlan';
|
|
13
|
+
export type { ParsedVersion } from './domain/models/ParsedVersion';
|
|
14
|
+
export type { UpdateInfo } from './domain/models/UpdateInfo';
|
|
15
|
+
export { LifecycleState } from './domain/models/LifecycleState';
|
|
16
|
+
export type { PlatformId, StoreProviderId } from './domain/models/PlatformId';
|
|
17
|
+
export type {
|
|
18
|
+
RemoteVersionInfo,
|
|
19
|
+
UserVersionDecision,
|
|
20
|
+
VersionCheckRequest,
|
|
21
|
+
} from './domain/models/RemoteVersionInfo';
|
|
22
|
+
export type { Unsubscribe } from './domain/models/Unsubscribe';
|
|
23
|
+
export type {
|
|
24
|
+
CheckForUpdatesOptions,
|
|
25
|
+
ForceTriggerOptions,
|
|
26
|
+
ResetIgnoredVersionsOptions,
|
|
27
|
+
} from './domain/models/PublicApiOptions';
|
|
28
|
+
export type {
|
|
29
|
+
CacheOptions,
|
|
30
|
+
FallbackOptions,
|
|
31
|
+
ILogSink,
|
|
32
|
+
LogEntry,
|
|
33
|
+
LoggingOptions,
|
|
34
|
+
LogLevel,
|
|
35
|
+
PolicyOptions,
|
|
36
|
+
ResolvedVersionManagerConfig,
|
|
37
|
+
RetryOptions,
|
|
38
|
+
StoreLinksOptions,
|
|
39
|
+
VersionManagerOptions,
|
|
40
|
+
} from './domain/models/VersionManagerOptions';
|
|
41
|
+
export type {
|
|
42
|
+
ErrorPhase,
|
|
43
|
+
StateChangedEvent,
|
|
44
|
+
UpdateDetectedEvent,
|
|
45
|
+
UpdateNotAvailableEvent,
|
|
46
|
+
UserActionEvent,
|
|
47
|
+
UserActionType,
|
|
48
|
+
VersionManagerErrorEvent,
|
|
49
|
+
VersionManagerEventMap,
|
|
50
|
+
VersionManagerEventType,
|
|
51
|
+
} from './domain/models/Events';
|
|
52
|
+
|
|
53
|
+
export type { IPlatformBridge } from './domain/ports/IPlatformBridge';
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
AppNotFoundException,
|
|
57
|
+
BoundaryValidationException,
|
|
58
|
+
CacheCorruptionException,
|
|
59
|
+
CacheException,
|
|
60
|
+
CacheWriteException,
|
|
61
|
+
ConfigSizeLimitExceededException,
|
|
62
|
+
EnvironmentOverrideConflictException,
|
|
63
|
+
InvalidConfigException,
|
|
64
|
+
InvalidVersionFormatException,
|
|
65
|
+
NetworkException,
|
|
66
|
+
NotInitializedException,
|
|
67
|
+
NotReadyException,
|
|
68
|
+
OfflineException,
|
|
69
|
+
PlatformBridgeException,
|
|
70
|
+
PolicyEvaluationException,
|
|
71
|
+
RateLimitException,
|
|
72
|
+
RequestTimeoutException,
|
|
73
|
+
SchemaValidationException,
|
|
74
|
+
SignatureVerificationException,
|
|
75
|
+
StorePermissionException,
|
|
76
|
+
StoreProviderException,
|
|
77
|
+
StoreResponseParseException,
|
|
78
|
+
TlsHandshakeException,
|
|
79
|
+
UnknownVersionManagerException,
|
|
80
|
+
UnsupportedStoreException,
|
|
81
|
+
UntrustedKeyIdException,
|
|
82
|
+
VersionManagerException,
|
|
83
|
+
} from './domain/errors/VersionManagerException';
|
|
84
|
+
export type { VersionManagerErrorCode } from './domain/errors/VersionManagerException';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
IPlatformBridge,
|
|
3
|
+
IHttpClient,
|
|
4
|
+
IKeyValueStorage,
|
|
5
|
+
ISecureStorage,
|
|
6
|
+
IAppInfoProvider,
|
|
7
|
+
IDeviceInfoProvider,
|
|
8
|
+
IBackgroundScheduler,
|
|
9
|
+
IAppLifecycleObserver,
|
|
10
|
+
ICryptoProvider,
|
|
11
|
+
BackgroundTaskDescriptor,
|
|
12
|
+
HttpRequest,
|
|
13
|
+
HttpResponse,
|
|
14
|
+
} from '../../domain/ports/IPlatformBridge';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IPlatformBridge } from '../domain/ports/IPlatformBridge';
|
|
2
|
+
import { NativePlatformBridge } from './native/NativePlatformBridge';
|
|
3
|
+
|
|
4
|
+
/** iOS/Android implementation — see createPlatformBridge.ts for why this is a separate file. */
|
|
5
|
+
export function createPlatformBridge(): IPlatformBridge {
|
|
6
|
+
return new NativePlatformBridge();
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IPlatformBridge } from '../domain/ports/IPlatformBridge';
|
|
2
|
+
import { WebPlatformBridge } from './web/WebPlatformBridge';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Web/default implementation. Metro (React Native's bundler) prefers
|
|
6
|
+
* createPlatformBridge.native.ts for iOS/Android builds; Vite (the web build, see
|
|
7
|
+
* example/vite.config.mjs) has no such resolution rule and always resolves this file
|
|
8
|
+
* — the same convention already used by src/multiply.tsx / multiply.native.tsx. This
|
|
9
|
+
* indirection matters: NativePlatformBridge.ts imports NativeVersionCheck, whose
|
|
10
|
+
* top-level TurboModuleRegistry.getEnforcing() call would throw if ever evaluated on
|
|
11
|
+
* Web, so the native module must never even be imported into a web bundle.
|
|
12
|
+
*/
|
|
13
|
+
export function createPlatformBridge(): IPlatformBridge {
|
|
14
|
+
return new WebPlatformBridge();
|
|
15
|
+
}
|