@_nazmiforreal/flutter-ota 0.1.26 → 0.1.28
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/README.md +112 -18
- package/bin/flutter-ota-linux-x64 +0 -0
- package/bin/flutter-ota.js +12 -11
- package/dart-src/packages/cli-tools/README.md +146 -2
- package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +46 -13
- package/dart-src/packages/cli-tools/lib/src/backend.dart +6 -6
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +2 -0
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +7 -7
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +4 -1
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +36 -8
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/config.dart +5 -5
- package/dart-src/packages/cli-tools/lib/src/operations.dart +2 -2
- package/dart-src/packages/cli-tools/pubspec.yaml +8 -21
- package/dart-src/packages/cli-tools/test/backends_test.dart +3 -3
- package/dart-src/packages/cli-tools/test/cli_test.dart +1 -1
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +4 -1
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +1 -1
- package/dart-src/root/README.md +334 -0
- package/dart-src/root/analysis_options.yaml +21 -0
- package/dart-src/root/android/build.gradle +116 -0
- package/dart-src/root/android/consumer-rules.pro +23 -0
- package/dart-src/root/android/settings.gradle +1 -0
- package/dart-src/root/android/src/main/AndroidManifest.xml +21 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/ApplyResult.kt +52 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BlacklistStore.kt +154 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BootDiagnosticStore.kt +150 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/CrashGuard.kt +271 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherApplication.kt +201 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherAutoInitProvider.kt +93 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherPlugin.kt +245 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/LoaderHook.kt +298 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatchManager.kt +1579 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfig.kt +173 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/SignatureVerifier.kt +177 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/CrashGuardTest.kt +61 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/LoaderHookTest.kt +25 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatchManagerTest.kt +401 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfigTest.kt +30 -0
- package/dart-src/root/bin/pack.dart +182 -0
- package/dart-src/root/lib/flutter_ota_kit.dart +1170 -0
- package/dart-src/root/lib/src/app_version_resolver.dart +36 -0
- package/dart-src/root/lib/src/aws_update_source.dart +97 -0
- package/dart-src/root/lib/src/blacklist.dart +54 -0
- package/dart-src/root/lib/src/boot_diagnostic.dart +138 -0
- package/dart-src/root/lib/src/cloudflare_update_source.dart +97 -0
- package/dart-src/root/lib/src/flutter_ota_app.dart +69 -0
- package/dart-src/root/lib/src/io.dart +48 -0
- package/dart-src/root/lib/src/io_stub.dart +19 -0
- package/dart-src/root/lib/src/ota_progress_overlay.dart +1186 -0
- package/dart-src/root/lib/src/pack/pack.dart +434 -0
- package/dart-src/root/lib/src/patch_info.dart +12 -0
- package/dart-src/root/lib/src/patcher_channel.dart +160 -0
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_database.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_s3_client.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage_profile.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart +1 -1
- package/dart-src/root/lib/src/pkg/client/flutter_ota_kit_client.dart +1 -0
- package/dart-src/root/lib/src/pkg/client/src/server_update_source.dart +46 -0
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/cloudflare_worker_database.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_build_where.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_bundle_mapper.dart +3 -3
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database_plugin.dart +2 -2
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_s3_client.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage_profile.dart +1 -1
- package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/status.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/flutter_ota_kit_plugin_core.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_blob_database_plugin.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/filter_compatible_app_versions.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/get_update_info.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/request_update_bundle_state.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/resolve_update_info_from_bundles.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/types.dart +2 -2
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_database.dart +26 -15
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_storage.dart +1 -1
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_database.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_get_update_info.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_storage.dart +1 -1
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_types.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_database.dart +2 -2
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_database.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_storage.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_storage.dart +1 -1
- package/dart-src/root/lib/src/pocketbase_update_source.dart +107 -0
- package/dart-src/root/lib/src/postgres_update_source.dart +95 -0
- package/dart-src/root/lib/src/shared_update_check.dart +131 -0
- package/dart-src/root/lib/src/supabase_update_source.dart +93 -0
- package/dart-src/root/pubspec.yaml +54 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +17 -13
- package/scripts/vendor.sh +22 -7
- package/dart-src/packages/core/CHANGELOG.md +0 -21
- package/dart-src/packages/core/README.md +0 -23
- package/dart-src/packages/core/example/lib/main.dart +0 -69
- package/dart-src/packages/core/pubspec.yaml +0 -13
- package/dart-src/packages/core/test/rollout_test.dart +0 -135
- package/dart-src/packages/core/test/semver_test.dart +0 -55
- package/dart-src/packages/core/test/semver_vectors.json +0 -1
- package/dart-src/packages/core/test/types_test.dart +0 -131
- package/dart-src/packages/core/test/uuid_test.dart +0 -40
- package/dart-src/packages/core/tool/dbg.dart +0 -7
- package/dart-src/plugins/aws/.gitkeep +0 -0
- package/dart-src/plugins/aws/CHANGELOG.md +0 -23
- package/dart-src/plugins/aws/LICENSE +0 -21
- package/dart-src/plugins/aws/README.md +0 -28
- package/dart-src/plugins/aws/example/lib/main.dart +0 -60
- package/dart-src/plugins/aws/pubspec.yaml +0 -30
- package/dart-src/plugins/aws/test/aws_database_test.dart +0 -161
- package/dart-src/plugins/aws/test/aws_storage_test.dart +0 -73
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +0 -78
- package/dart-src/plugins/cloudflare/.gitkeep +0 -0
- package/dart-src/plugins/cloudflare/CHANGELOG.md +0 -23
- package/dart-src/plugins/cloudflare/LICENSE +0 -21
- package/dart-src/plugins/cloudflare/README.md +0 -25
- package/dart-src/plugins/cloudflare/example/lib/main.dart +0 -62
- package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +0 -16
- package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +0 -8
- package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +0 -38
- package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +0 -11
- package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +0 -24
- package/dart-src/plugins/cloudflare/pubspec.yaml +0 -28
- package/dart-src/plugins/cloudflare/sql/bundles.sql +0 -42
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +0 -126
- package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +0 -106
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +0 -200
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +0 -261
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +0 -78
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +0 -74
- package/dart-src/plugins/plugin-core/CHANGELOG.md +0 -20
- package/dart-src/plugins/plugin-core/LICENSE +0 -21
- package/dart-src/plugins/plugin-core/README.md +0 -21
- package/dart-src/plugins/plugin-core/example/lib/main.dart +0 -61
- package/dart-src/plugins/plugin-core/pubspec.yaml +0 -23
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +0 -170
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +0 -74
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +0 -345
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +0 -843
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +0 -298
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +0 -195
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +0 -56
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +0 -1589
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +0 -283
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +0 -202
- package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +0 -86
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +0 -88
- package/dart-src/plugins/pocketbase/CHANGELOG.md +0 -23
- package/dart-src/plugins/pocketbase/LICENSE +0 -21
- package/dart-src/plugins/pocketbase/README.md +0 -54
- package/dart-src/plugins/pocketbase/example/lib/main.dart +0 -62
- package/dart-src/plugins/pocketbase/pubspec.yaml +0 -26
- package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +0 -12
- package/dart-src/plugins/postgres/.gitkeep +0 -0
- package/dart-src/plugins/postgres/CHANGELOG.md +0 -23
- package/dart-src/plugins/postgres/LICENSE +0 -21
- package/dart-src/plugins/postgres/README.md +0 -29
- package/dart-src/plugins/postgres/example/lib/main.dart +0 -67
- package/dart-src/plugins/postgres/pubspec.yaml +0 -26
- package/dart-src/plugins/postgres/sql/bundles.sql +0 -45
- package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +0 -21
- package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +0 -171
- package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +0 -126
- package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +0 -125
- package/dart-src/plugins/postgres/sql/hash_user_id.sql +0 -30
- package/dart-src/plugins/postgres/sql/is_device_eligible.sql +0 -237
- package/dart-src/plugins/postgres/sql/prepareSql.ts +0 -11
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +0 -320
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +0 -139
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +0 -210
- package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +0 -60
- package/dart-src/plugins/supabase/CHANGELOG.md +0 -24
- package/dart-src/plugins/supabase/LICENSE +0 -21
- package/dart-src/plugins/supabase/README.md +0 -53
- package/dart-src/plugins/supabase/example/lib/main.dart +0 -63
- package/dart-src/plugins/supabase/pubspec.yaml +0 -27
- package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +0 -44
- package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +0 -968
- package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +0 -232
- package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -137
- package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -206
- package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -186
- package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -505
- package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
- package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -75
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +0 -654
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +0 -115
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +0 -198
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +0 -168
- /package/dart-src/{packages/core → root}/LICENSE +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/flutter_ota_kit_aws.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_client.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_signer.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_config.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_ssm_client.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/flutter_ota_kit_cloudflare.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_client.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_config.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_config.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/flutter_ota_kit_core.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/app_update_info.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_artifacts.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_patch_artifact.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/changed_asset.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/get_bundles_args.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/metadata.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/patch_info.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/platform.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/rollout.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_range.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_version.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/strategy.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/update_bundle_params.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/uuid.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/asset_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_helpers.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work_store.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/calculate_pagination.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/compression_format.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/content_addressed_assets.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_database_plugin.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_key_builder.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_plugin.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/generate_min_bundle_id.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/is_object.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/legacy_asset_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/paginate_bundles.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/parse_storage_uri.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/query_bundles.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_helpers.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_profile.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/uuidv7.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/flutter_ota_kit_pocketbase.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_client.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_config.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/flutter_ota_kit_postgres.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_client.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_config.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/edge.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/flutter_ota_kit_supabase.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/error_message.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_adapter.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_http.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_config.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_signed_url_batcher.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/types.dart +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import 'package:package_info_plus/package_info_plus.dart';
|
|
2
|
+
|
|
3
|
+
/// Resolves the app version used for `appVersion`-strategy targeting.
|
|
4
|
+
///
|
|
5
|
+
/// If [configured] is non-empty it is honored (explicit override via
|
|
6
|
+
/// `XxxUpdateConfig.appVersion` / `--dart-define=APP_VERSION`). Otherwise the
|
|
7
|
+
/// real version is detected at runtime from the host app's `versionName` via
|
|
8
|
+
/// `package_info_plus`. This prevents the silent failure where a missing or
|
|
9
|
+
/// incorrect `APP_VERSION` made the client report a wrong version and the
|
|
10
|
+
/// backend return no compatible bundle (`filterCompatibleAppVersions` then drops
|
|
11
|
+
/// it).
|
|
12
|
+
///
|
|
13
|
+
/// Cached after the first successful detection so repeated update checks don't
|
|
14
|
+
/// re-read `PackageInfo`.
|
|
15
|
+
Future<String> resolveAppVersion(String? configured) async {
|
|
16
|
+
if (configured != null && configured.trim().isNotEmpty) return configured;
|
|
17
|
+
if (_cachedAppVersion != null) return _cachedAppVersion!;
|
|
18
|
+
var detected = '';
|
|
19
|
+
try {
|
|
20
|
+
final info = await PackageInfo.fromPlatform();
|
|
21
|
+
detected = info.version;
|
|
22
|
+
} catch (_) {
|
|
23
|
+
detected = '';
|
|
24
|
+
}
|
|
25
|
+
// Only cache a successful, non-empty detection. Caching '' would permanently
|
|
26
|
+
// pin the version to empty after a transient failure and silently break
|
|
27
|
+
// `appVersion`-strategy targeting forever. Callers retry on the next check.
|
|
28
|
+
if (detected.isNotEmpty) _cachedAppVersion = detected;
|
|
29
|
+
return detected;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
String? _cachedAppVersion;
|
|
33
|
+
|
|
34
|
+
/// Resets the cached detected app version. Intended for tests that need to
|
|
35
|
+
/// re-run detection against a different [PackageInfo] (or simulate a failure).
|
|
36
|
+
void resetAppVersionCache() => _cachedAppVersion = null;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import 'package:flutter_ota_kit/src/pkg/core/flutter_ota_kit_core.dart'
|
|
2
|
+
show nilUuid, Platform, UpdateStrategy;
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/client/flutter_ota_kit_client.dart'
|
|
4
|
+
show ServerUpdateResult;
|
|
5
|
+
import 'package:flutter_ota_kit/src/pkg/aws/flutter_ota_kit_aws.dart'
|
|
6
|
+
show s3Database, s3Storage, S3DatabaseConfig, AwsS3StorageConfig;
|
|
7
|
+
|
|
8
|
+
import 'shared_update_check.dart';
|
|
9
|
+
|
|
10
|
+
/// Configuration for an **AWS S3** update source.
|
|
11
|
+
///
|
|
12
|
+
/// Talks to AWS directly — an S3-backed blob database for bundle metadata and
|
|
13
|
+
/// S3 (presigned URLs) for artifact storage — with no intermediate server.
|
|
14
|
+
/// Optionally fronts downloads with CloudFront for a custom domain.
|
|
15
|
+
class AwsUpdateConfig {
|
|
16
|
+
const AwsUpdateConfig({
|
|
17
|
+
required this.bucketName,
|
|
18
|
+
required this.region,
|
|
19
|
+
required this.accessKeyId,
|
|
20
|
+
required this.secretAccessKey,
|
|
21
|
+
this.basePath,
|
|
22
|
+
this.endpoint,
|
|
23
|
+
this.sessionToken,
|
|
24
|
+
this.cloudfrontDistributionId,
|
|
25
|
+
required this.channel,
|
|
26
|
+
required this.platform,
|
|
27
|
+
required this.updateStrategy,
|
|
28
|
+
this.appVersion,
|
|
29
|
+
this.fingerprintHash,
|
|
30
|
+
this.sdkVersion = '1.0.0',
|
|
31
|
+
this.cohort,
|
|
32
|
+
this.minBundleId = nilUuid,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
final String bucketName;
|
|
36
|
+
final String region;
|
|
37
|
+
final String accessKeyId;
|
|
38
|
+
final String secretAccessKey;
|
|
39
|
+
final String? basePath;
|
|
40
|
+
final String? endpoint;
|
|
41
|
+
final String? sessionToken;
|
|
42
|
+
final String? cloudfrontDistributionId;
|
|
43
|
+
final String channel;
|
|
44
|
+
final Platform platform;
|
|
45
|
+
final UpdateStrategy updateStrategy;
|
|
46
|
+
final String? appVersion;
|
|
47
|
+
final String? fingerprintHash;
|
|
48
|
+
final String sdkVersion;
|
|
49
|
+
final String? cohort;
|
|
50
|
+
final String minBundleId;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class AwsUpdateSource {
|
|
54
|
+
const AwsUpdateSource();
|
|
55
|
+
|
|
56
|
+
Future<ServerUpdateResult> check(
|
|
57
|
+
AwsUpdateConfig config, {
|
|
58
|
+
String? currentBundleId,
|
|
59
|
+
Duration timeout = const Duration(seconds: 10),
|
|
60
|
+
}) async {
|
|
61
|
+
final dbConfig = S3DatabaseConfig(
|
|
62
|
+
bucketName: config.bucketName,
|
|
63
|
+
region: config.region,
|
|
64
|
+
accessKeyId: config.accessKeyId,
|
|
65
|
+
secretAccessKey: config.secretAccessKey,
|
|
66
|
+
basePath: config.basePath,
|
|
67
|
+
cloudfrontDistributionId: config.cloudfrontDistributionId,
|
|
68
|
+
endpoint: config.endpoint,
|
|
69
|
+
sessionToken: config.sessionToken,
|
|
70
|
+
);
|
|
71
|
+
final storageConfig = AwsS3StorageConfig(
|
|
72
|
+
bucketName: config.bucketName,
|
|
73
|
+
region: config.region,
|
|
74
|
+
accessKeyId: config.accessKeyId,
|
|
75
|
+
secretAccessKey: config.secretAccessKey,
|
|
76
|
+
basePath: config.basePath,
|
|
77
|
+
endpoint: config.endpoint,
|
|
78
|
+
sessionToken: config.sessionToken,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
final db = s3Database(dbConfig)();
|
|
82
|
+
final storage = s3Storage(storageConfig);
|
|
83
|
+
|
|
84
|
+
return performSharedUpdateCheck(
|
|
85
|
+
db: db,
|
|
86
|
+
storage: storage,
|
|
87
|
+
channel: config.channel,
|
|
88
|
+
platform: config.platform,
|
|
89
|
+
updateStrategy: config.updateStrategy,
|
|
90
|
+
appVersion: config.appVersion,
|
|
91
|
+
fingerprintHash: config.fingerprintHash,
|
|
92
|
+
minBundleId: config.minBundleId,
|
|
93
|
+
currentBundleId: currentBundleId,
|
|
94
|
+
timeout: timeout,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import 'package:flutter/foundation.dart';
|
|
2
|
+
|
|
3
|
+
/// A local patch blacklist entry. Mirrors the native `BlacklistStore` JSON shape.
|
|
4
|
+
///
|
|
5
|
+
/// Typical usage on the app side:
|
|
6
|
+
/// ```dart
|
|
7
|
+
/// final entries = await FlutterPatcher.blacklist;
|
|
8
|
+
/// for (final e in entries) {
|
|
9
|
+
/// debugPrint('blacklisted ${e.version} reason=${e.reason}');
|
|
10
|
+
/// }
|
|
11
|
+
/// ```
|
|
12
|
+
@immutable
|
|
13
|
+
class BlacklistEntry {
|
|
14
|
+
/// The blacklisted patch version (matches [PatchInfo.version]).
|
|
15
|
+
final String version;
|
|
16
|
+
|
|
17
|
+
/// The blacklisted patch md5 (lowercase hex).
|
|
18
|
+
final String md5;
|
|
19
|
+
|
|
20
|
+
/// The reason the patch was blacklisted (a native constant; passed as a string
|
|
21
|
+
/// across the platform boundary instead of an enum for forward compatibility).
|
|
22
|
+
/// Possible values:
|
|
23
|
+
/// - `BOOT_CRASH`: circuit breaker tripped after repeated boot failures
|
|
24
|
+
/// - `MD5_MISMATCH`: local file md5 verification failed
|
|
25
|
+
/// - `SIGNATURE_INVALID`: Ed25519 signature verification failed
|
|
26
|
+
/// - `APPLY_FAILED`: patch apply failed (download/install error)
|
|
27
|
+
final String reason;
|
|
28
|
+
|
|
29
|
+
/// When the entry was blacklisted.
|
|
30
|
+
final DateTime blacklistedAt;
|
|
31
|
+
|
|
32
|
+
const BlacklistEntry({
|
|
33
|
+
required this.version,
|
|
34
|
+
required this.md5,
|
|
35
|
+
required this.reason,
|
|
36
|
+
required this.blacklistedAt,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
factory BlacklistEntry.fromNative(Map<dynamic, dynamic> raw) {
|
|
40
|
+
final map = Map<String, dynamic>.from(raw);
|
|
41
|
+
return BlacklistEntry(
|
|
42
|
+
version: (map['version'] as String?) ?? '',
|
|
43
|
+
md5: (map['md5'] as String?) ?? '',
|
|
44
|
+
reason: (map['reason'] as String?) ?? '',
|
|
45
|
+
blacklistedAt: DateTime.fromMillisecondsSinceEpoch(
|
|
46
|
+
((map['blacklistedAt'] as num?) ?? 0).toInt(),
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@override
|
|
52
|
+
String toString() =>
|
|
53
|
+
'BlacklistEntry(version=$version, md5=$md5, reason=$reason, at=$blacklistedAt)';
|
|
54
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import 'package:flutter/foundation.dart';
|
|
2
|
+
|
|
3
|
+
/// Last cold-start patch loading decision.
|
|
4
|
+
///
|
|
5
|
+
/// Apply-time failures are reported by `PatchApplyError`. After a patch is
|
|
6
|
+
/// installed, the next cold start can still drop it because of version mismatch,
|
|
7
|
+
/// md5/signature failure, crash rollback, or loader hook failure. This enum
|
|
8
|
+
/// exposes those native decisions to Dart for monitoring.
|
|
9
|
+
enum PatchBootStatus {
|
|
10
|
+
/// No local patch is installed or loaded.
|
|
11
|
+
noPatch,
|
|
12
|
+
|
|
13
|
+
/// This cold start loaded the patch.
|
|
14
|
+
patched,
|
|
15
|
+
|
|
16
|
+
/// The patch target versionCode does not match the installed APK.
|
|
17
|
+
droppedVersionCodeMismatch,
|
|
18
|
+
|
|
19
|
+
/// Cold-start validation found that local `libapp_patch.so` no longer
|
|
20
|
+
/// matches metadata.
|
|
21
|
+
droppedMd5Mismatch,
|
|
22
|
+
|
|
23
|
+
/// Cold-start signature validation failed.
|
|
24
|
+
droppedSignatureInvalid,
|
|
25
|
+
|
|
26
|
+
/// Metadata or required patch artifacts are missing or corrupt on disk.
|
|
27
|
+
///
|
|
28
|
+
/// For asset patches, the on-disk asset bundle the install step produced
|
|
29
|
+
/// is missing or unreadable — typically caused by the patch directory
|
|
30
|
+
/// being partially wiped between install and boot.
|
|
31
|
+
droppedMetaCorrupted,
|
|
32
|
+
|
|
33
|
+
/// Crash protection tripped after consecutive early boot failures.
|
|
34
|
+
droppedCircuitBreaker,
|
|
35
|
+
|
|
36
|
+
/// Loader injection failed; this launch used the APK built-in version.
|
|
37
|
+
hookInstallFailed,
|
|
38
|
+
|
|
39
|
+
/// Unclassified startup failure.
|
|
40
|
+
unknown,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
PatchBootStatus _parseStatus(String? raw) {
|
|
44
|
+
switch (raw) {
|
|
45
|
+
case 'NO_PATCH':
|
|
46
|
+
return PatchBootStatus.noPatch;
|
|
47
|
+
case 'PATCHED':
|
|
48
|
+
return PatchBootStatus.patched;
|
|
49
|
+
case 'DROPPED_VERSION_CODE_MISMATCH':
|
|
50
|
+
return PatchBootStatus.droppedVersionCodeMismatch;
|
|
51
|
+
case 'DROPPED_MD5_MISMATCH':
|
|
52
|
+
return PatchBootStatus.droppedMd5Mismatch;
|
|
53
|
+
case 'DROPPED_SIGNATURE_INVALID':
|
|
54
|
+
return PatchBootStatus.droppedSignatureInvalid;
|
|
55
|
+
case 'DROPPED_META_CORRUPTED':
|
|
56
|
+
return PatchBootStatus.droppedMetaCorrupted;
|
|
57
|
+
case 'DROPPED_CIRCUIT_BREAKER':
|
|
58
|
+
return PatchBootStatus.droppedCircuitBreaker;
|
|
59
|
+
case 'HOOK_INSTALL_FAILED':
|
|
60
|
+
return PatchBootStatus.hookInstallFailed;
|
|
61
|
+
default:
|
|
62
|
+
return PatchBootStatus.unknown;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Structured diagnostic for the last cold-start patch decision.
|
|
67
|
+
@immutable
|
|
68
|
+
class PatchBootDiagnostic {
|
|
69
|
+
/// Cold-start patch loading result.
|
|
70
|
+
final PatchBootStatus status;
|
|
71
|
+
|
|
72
|
+
/// Patch version involved in the decision.
|
|
73
|
+
///
|
|
74
|
+
/// For dropped patches this is the dropped version when metadata is readable.
|
|
75
|
+
final String? patchVersion;
|
|
76
|
+
|
|
77
|
+
/// Patch target versionCode, mainly used for version mismatch diagnostics.
|
|
78
|
+
final int? patchTargetVersionCode;
|
|
79
|
+
|
|
80
|
+
/// Current host APK versionCode.
|
|
81
|
+
final int? appVersionCode;
|
|
82
|
+
|
|
83
|
+
/// Crash count when the circuit breaker tripped.
|
|
84
|
+
final int? crashCount;
|
|
85
|
+
|
|
86
|
+
/// FlutterInjector loader field candidates attempted when hook install failed.
|
|
87
|
+
final List<String>? attemptedLoaderFields;
|
|
88
|
+
|
|
89
|
+
/// Time when this diagnostic was recorded.
|
|
90
|
+
final DateTime recordedAt;
|
|
91
|
+
|
|
92
|
+
/// Developer-facing diagnostic message. Do not show directly to end users.
|
|
93
|
+
final String? message;
|
|
94
|
+
|
|
95
|
+
const PatchBootDiagnostic({
|
|
96
|
+
required this.status,
|
|
97
|
+
required this.recordedAt,
|
|
98
|
+
this.patchVersion,
|
|
99
|
+
this.patchTargetVersionCode,
|
|
100
|
+
this.appVersionCode,
|
|
101
|
+
this.crashCount,
|
|
102
|
+
this.attemptedLoaderFields,
|
|
103
|
+
this.message,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/// Whether the last boot state is normal for business monitoring.
|
|
107
|
+
bool get isHealthy =>
|
|
108
|
+
status == PatchBootStatus.patched || status == PatchBootStatus.noPatch;
|
|
109
|
+
|
|
110
|
+
/// Deserializes the native MethodChannel map.
|
|
111
|
+
factory PatchBootDiagnostic.fromNative(Map<dynamic, dynamic> raw) {
|
|
112
|
+
final map = Map<String, dynamic>.from(raw);
|
|
113
|
+
final fields = map['attemptedLoaderFields'];
|
|
114
|
+
return PatchBootDiagnostic(
|
|
115
|
+
status: _parseStatus(map['status'] as String?),
|
|
116
|
+
patchVersion: map['patchVersion'] as String?,
|
|
117
|
+
patchTargetVersionCode: (map['patchTargetVersionCode'] as num?)?.toInt(),
|
|
118
|
+
appVersionCode: (map['appVersionCode'] as num?)?.toInt(),
|
|
119
|
+
crashCount: (map['crashCount'] as num?)?.toInt(),
|
|
120
|
+
attemptedLoaderFields: fields is List
|
|
121
|
+
? List<String>.from(fields.map((e) => e?.toString() ?? ''))
|
|
122
|
+
: null,
|
|
123
|
+
recordedAt: DateTime.fromMillisecondsSinceEpoch(
|
|
124
|
+
((map['recordedAt'] as num?) ?? 0).toInt(),
|
|
125
|
+
),
|
|
126
|
+
message: map['message'] as String?,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@override
|
|
131
|
+
String toString() =>
|
|
132
|
+
'PatchBootDiagnostic(${status.name}'
|
|
133
|
+
'${patchVersion != null ? ', v=$patchVersion' : ''}'
|
|
134
|
+
'${patchTargetVersionCode != null ? ', patchVc=$patchTargetVersionCode' : ''}'
|
|
135
|
+
'${appVersionCode != null ? ', appVc=$appVersionCode' : ''}'
|
|
136
|
+
'${crashCount != null ? ', crashes=$crashCount' : ''}'
|
|
137
|
+
'${message != null ? ', msg=$message' : ''})';
|
|
138
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import 'package:flutter_ota_kit/src/pkg/core/flutter_ota_kit_core.dart'
|
|
2
|
+
show nilUuid, Platform, UpdateStrategy;
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/client/flutter_ota_kit_client.dart'
|
|
4
|
+
show ServerUpdateResult;
|
|
5
|
+
import 'package:flutter_ota_kit/src/pkg/cloudflare/flutter_ota_kit_cloudflare.dart'
|
|
6
|
+
show d1Database, r2Storage, D1DatabaseConfig, R2S3StorageConfig;
|
|
7
|
+
|
|
8
|
+
import 'shared_update_check.dart';
|
|
9
|
+
|
|
10
|
+
/// Configuration for a **Cloudflare** update source.
|
|
11
|
+
///
|
|
12
|
+
/// Talks to Cloudflare directly — D1 for bundle metadata and R2 (S3-compatible)
|
|
13
|
+
/// for artifact storage — with no intermediate server. Presigned R2 URLs are
|
|
14
|
+
/// resolved on the device, exactly like [SupabaseUpdateConfig].
|
|
15
|
+
class CloudflareUpdateConfig {
|
|
16
|
+
const CloudflareUpdateConfig({
|
|
17
|
+
// D1 (database)
|
|
18
|
+
required this.databaseId,
|
|
19
|
+
required this.accountId,
|
|
20
|
+
required this.cloudflareApiToken,
|
|
21
|
+
// R2 (storage)
|
|
22
|
+
required this.bucketName,
|
|
23
|
+
required this.accessKeyId,
|
|
24
|
+
required this.secretAccessKey,
|
|
25
|
+
this.basePath,
|
|
26
|
+
this.region = 'auto',
|
|
27
|
+
this.endpoint,
|
|
28
|
+
// targeting
|
|
29
|
+
required this.channel,
|
|
30
|
+
required this.platform,
|
|
31
|
+
required this.updateStrategy,
|
|
32
|
+
this.appVersion,
|
|
33
|
+
this.fingerprintHash,
|
|
34
|
+
this.sdkVersion = '1.0.0',
|
|
35
|
+
this.cohort,
|
|
36
|
+
this.minBundleId = nilUuid,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
final String databaseId;
|
|
40
|
+
final String accountId;
|
|
41
|
+
final String cloudflareApiToken;
|
|
42
|
+
final String bucketName;
|
|
43
|
+
final String accessKeyId;
|
|
44
|
+
final String secretAccessKey;
|
|
45
|
+
final String? basePath;
|
|
46
|
+
final String region;
|
|
47
|
+
final String? endpoint;
|
|
48
|
+
final String channel;
|
|
49
|
+
final Platform platform;
|
|
50
|
+
final UpdateStrategy updateStrategy;
|
|
51
|
+
final String? appVersion;
|
|
52
|
+
final String? fingerprintHash;
|
|
53
|
+
final String sdkVersion;
|
|
54
|
+
final String? cohort;
|
|
55
|
+
final String minBundleId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class CloudflareUpdateSource {
|
|
59
|
+
const CloudflareUpdateSource();
|
|
60
|
+
|
|
61
|
+
Future<ServerUpdateResult> check(
|
|
62
|
+
CloudflareUpdateConfig config, {
|
|
63
|
+
String? currentBundleId,
|
|
64
|
+
Duration timeout = const Duration(seconds: 10),
|
|
65
|
+
}) async {
|
|
66
|
+
final dbConfig = D1DatabaseConfig(
|
|
67
|
+
databaseId: config.databaseId,
|
|
68
|
+
accountId: config.accountId,
|
|
69
|
+
cloudflareApiToken: config.cloudflareApiToken,
|
|
70
|
+
);
|
|
71
|
+
final storageConfig = R2S3StorageConfig(
|
|
72
|
+
accountId: config.accountId,
|
|
73
|
+
bucketName: config.bucketName,
|
|
74
|
+
accessKeyId: config.accessKeyId,
|
|
75
|
+
secretAccessKey: config.secretAccessKey,
|
|
76
|
+
basePath: config.basePath,
|
|
77
|
+
region: config.region,
|
|
78
|
+
endpoint: config.endpoint,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
final db = d1Database(dbConfig)();
|
|
82
|
+
final storage = r2Storage(storageConfig);
|
|
83
|
+
|
|
84
|
+
return performSharedUpdateCheck(
|
|
85
|
+
db: db,
|
|
86
|
+
storage: storage,
|
|
87
|
+
channel: config.channel,
|
|
88
|
+
platform: config.platform,
|
|
89
|
+
updateStrategy: config.updateStrategy,
|
|
90
|
+
appVersion: config.appVersion,
|
|
91
|
+
fingerprintHash: config.fingerprintHash,
|
|
92
|
+
minBundleId: config.minBundleId,
|
|
93
|
+
currentBundleId: currentBundleId,
|
|
94
|
+
timeout: timeout,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import 'package:flutter/widgets.dart';
|
|
2
|
+
|
|
3
|
+
/// App-level host that lets [FlutterPatcher] show the forced-update progress
|
|
4
|
+
/// overlay without the consuming app writing any UI code.
|
|
5
|
+
///
|
|
6
|
+
/// Wrap your app once:
|
|
7
|
+
///
|
|
8
|
+
/// ```dart
|
|
9
|
+
/// void main() => runApp(FlutterOtaApp(child: MyApp()));
|
|
10
|
+
/// ```
|
|
11
|
+
///
|
|
12
|
+
/// The overlay is injected via [MaterialApp.navigatorKey] — your [MaterialApp]
|
|
13
|
+
/// must use [FlutterPatcher.navigatorKey]:
|
|
14
|
+
///
|
|
15
|
+
/// ```dart
|
|
16
|
+
/// MaterialApp(
|
|
17
|
+
/// navigatorKey: FlutterPatcher.navigatorKey,
|
|
18
|
+
/// ...
|
|
19
|
+
/// )
|
|
20
|
+
/// ```
|
|
21
|
+
///
|
|
22
|
+
/// Set [showUpdateUi] to `false` to disable the built-in overlay entirely (the
|
|
23
|
+
/// SDK still applies forced updates, just without the progress UI).
|
|
24
|
+
class FlutterOtaApp extends StatefulWidget {
|
|
25
|
+
final Widget child;
|
|
26
|
+
final bool showUpdateUi;
|
|
27
|
+
|
|
28
|
+
const FlutterOtaApp({
|
|
29
|
+
super.key,
|
|
30
|
+
required this.child,
|
|
31
|
+
this.showUpdateUi = true,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
@override
|
|
35
|
+
State<FlutterOtaApp> createState() => _FlutterOtaAppState();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class _FlutterOtaAppState extends State<FlutterOtaApp> {
|
|
39
|
+
@override
|
|
40
|
+
void initState() {
|
|
41
|
+
super.initState();
|
|
42
|
+
FlutterPatcherShowUpdateUiBinding.update(widget.showUpdateUi);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@override
|
|
46
|
+
void didUpdateWidget(covariant FlutterOtaApp oldWidget) {
|
|
47
|
+
super.didUpdateWidget(oldWidget);
|
|
48
|
+
if (oldWidget.showUpdateUi != widget.showUpdateUi) {
|
|
49
|
+
FlutterPatcherShowUpdateUiBinding.update(widget.showUpdateUi);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
Widget build(BuildContext context) => widget.child;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Internal binding between the widget-level [showUpdateUi] flag and the
|
|
58
|
+
/// [FlutterPatcher] static flag, kept here so [flutter_ota_kit.dart] does not
|
|
59
|
+
/// depend on the overlay widget directly.
|
|
60
|
+
class FlutterPatcherShowUpdateUiBinding {
|
|
61
|
+
const FlutterPatcherShowUpdateUiBinding._();
|
|
62
|
+
|
|
63
|
+
static void update(bool value) {
|
|
64
|
+
// Imported lazily via the patch function set by the SDK entrypoint.
|
|
65
|
+
applyShowUpdateUi?.call(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static void Function(bool)? applyShowUpdateUi;
|
|
69
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import 'dart:convert';
|
|
2
|
+
import 'dart:io';
|
|
3
|
+
import 'dart:typed_data';
|
|
4
|
+
|
|
5
|
+
bool get isAndroid => Platform.isAndroid;
|
|
6
|
+
|
|
7
|
+
String get operatingSystem => Platform.operatingSystem;
|
|
8
|
+
|
|
9
|
+
Future<Map<String, dynamic>> getJson(
|
|
10
|
+
String url, {
|
|
11
|
+
Map<String, String>? headers,
|
|
12
|
+
Duration timeout = const Duration(seconds: 10),
|
|
13
|
+
}) async {
|
|
14
|
+
final uri = Uri.parse(url);
|
|
15
|
+
final client = HttpClient()..connectionTimeout = timeout;
|
|
16
|
+
try {
|
|
17
|
+
final req = await client.getUrl(uri).timeout(timeout);
|
|
18
|
+
headers?.forEach(req.headers.set);
|
|
19
|
+
req.headers.set(HttpHeaders.acceptHeader, 'application/json');
|
|
20
|
+
final resp = await req.close().timeout(timeout);
|
|
21
|
+
if (resp.statusCode < 200 || resp.statusCode >= 300) {
|
|
22
|
+
throw Exception('HTTP ${resp.statusCode}');
|
|
23
|
+
}
|
|
24
|
+
final body = await resp.transform(utf8.decoder).join();
|
|
25
|
+
final decoded = jsonDecode(body);
|
|
26
|
+
if (decoded is! Map) {
|
|
27
|
+
throw Exception('Invalid JSON: expected object');
|
|
28
|
+
}
|
|
29
|
+
return Map<String, dynamic>.from(decoded);
|
|
30
|
+
} finally {
|
|
31
|
+
client.close(force: true);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Future<String> stagePatchBytes(String dir, Uint8List bytes) async {
|
|
36
|
+
final staged = File(
|
|
37
|
+
'$dir/flutter_ota_kit_staged_${DateTime.now().microsecondsSinceEpoch}.so',
|
|
38
|
+
);
|
|
39
|
+
await staged.writeAsBytes(bytes, flush: true);
|
|
40
|
+
return staged.path;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Future<void> deleteFileIfExists(String path) async {
|
|
44
|
+
final file = File(path);
|
|
45
|
+
if (await file.exists()) {
|
|
46
|
+
await file.delete();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import 'dart:typed_data';
|
|
2
|
+
|
|
3
|
+
bool get isAndroid => false;
|
|
4
|
+
|
|
5
|
+
String get operatingSystem => 'unsupported';
|
|
6
|
+
|
|
7
|
+
Future<Map<String, dynamic>> getJson(
|
|
8
|
+
String url, {
|
|
9
|
+
Map<String, String>? headers,
|
|
10
|
+
Duration timeout = const Duration(seconds: 10),
|
|
11
|
+
}) {
|
|
12
|
+
throw UnsupportedError('flutter_ota_kit only supports Android');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Future<String> stagePatchBytes(String dir, Uint8List bytes) {
|
|
16
|
+
throw UnsupportedError('flutter_ota_kit only supports Android');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
Future<void> deleteFileIfExists(String path) async {}
|