@_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.
Files changed (259) hide show
  1. package/README.md +112 -18
  2. package/bin/flutter-ota-linux-x64 +0 -0
  3. package/bin/flutter-ota.js +12 -11
  4. package/dart-src/packages/cli-tools/README.md +146 -2
  5. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +46 -13
  6. package/dart-src/packages/cli-tools/lib/src/backend.dart +6 -6
  7. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +2 -0
  8. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +7 -7
  9. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +4 -1
  10. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +36 -8
  11. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1 -1
  12. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +1 -1
  13. package/dart-src/packages/cli-tools/lib/src/config.dart +5 -5
  14. package/dart-src/packages/cli-tools/lib/src/operations.dart +2 -2
  15. package/dart-src/packages/cli-tools/pubspec.yaml +8 -21
  16. package/dart-src/packages/cli-tools/test/backends_test.dart +3 -3
  17. package/dart-src/packages/cli-tools/test/cli_test.dart +1 -1
  18. package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +2 -2
  19. package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +2 -2
  20. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +4 -1
  21. package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +2 -2
  22. package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +1 -1
  23. package/dart-src/root/README.md +334 -0
  24. package/dart-src/root/analysis_options.yaml +21 -0
  25. package/dart-src/root/android/build.gradle +116 -0
  26. package/dart-src/root/android/consumer-rules.pro +23 -0
  27. package/dart-src/root/android/settings.gradle +1 -0
  28. package/dart-src/root/android/src/main/AndroidManifest.xml +21 -0
  29. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/ApplyResult.kt +52 -0
  30. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BlacklistStore.kt +154 -0
  31. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BootDiagnosticStore.kt +150 -0
  32. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/CrashGuard.kt +271 -0
  33. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherApplication.kt +201 -0
  34. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherAutoInitProvider.kt +93 -0
  35. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherPlugin.kt +245 -0
  36. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/LoaderHook.kt +298 -0
  37. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatchManager.kt +1579 -0
  38. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfig.kt +173 -0
  39. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/SignatureVerifier.kt +177 -0
  40. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/CrashGuardTest.kt +61 -0
  41. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/LoaderHookTest.kt +25 -0
  42. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatchManagerTest.kt +401 -0
  43. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfigTest.kt +30 -0
  44. package/dart-src/root/bin/pack.dart +182 -0
  45. package/dart-src/root/lib/flutter_ota_kit.dart +1170 -0
  46. package/dart-src/root/lib/src/app_version_resolver.dart +36 -0
  47. package/dart-src/root/lib/src/aws_update_source.dart +97 -0
  48. package/dart-src/root/lib/src/blacklist.dart +54 -0
  49. package/dart-src/root/lib/src/boot_diagnostic.dart +138 -0
  50. package/dart-src/root/lib/src/cloudflare_update_source.dart +97 -0
  51. package/dart-src/root/lib/src/flutter_ota_app.dart +69 -0
  52. package/dart-src/root/lib/src/io.dart +48 -0
  53. package/dart-src/root/lib/src/io_stub.dart +19 -0
  54. package/dart-src/root/lib/src/ota_progress_overlay.dart +1186 -0
  55. package/dart-src/root/lib/src/pack/pack.dart +434 -0
  56. package/dart-src/root/lib/src/patch_info.dart +12 -0
  57. package/dart-src/root/lib/src/patcher_channel.dart +160 -0
  58. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_database.dart +1 -1
  59. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_s3_client.dart +1 -1
  60. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage.dart +1 -1
  61. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage_profile.dart +1 -1
  62. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart +1 -1
  63. package/dart-src/root/lib/src/pkg/client/flutter_ota_kit_client.dart +1 -0
  64. package/dart-src/root/lib/src/pkg/client/src/server_update_source.dart +46 -0
  65. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/cloudflare_worker_database.dart +1 -1
  66. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_build_where.dart +1 -1
  67. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_bundle_mapper.dart +3 -3
  68. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database.dart +1 -1
  69. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database_plugin.dart +2 -2
  70. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_s3_client.dart +1 -1
  71. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage.dart +1 -1
  72. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage_profile.dart +1 -1
  73. package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/status.dart +1 -1
  74. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/flutter_ota_kit_plugin_core.dart +1 -1
  75. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_blob_database_plugin.dart +1 -1
  76. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/filter_compatible_app_versions.dart +1 -1
  77. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/get_update_info.dart +1 -1
  78. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/request_update_bundle_state.dart +1 -1
  79. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/resolve_update_info_from_bundles.dart +1 -1
  80. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/types.dart +2 -2
  81. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_bundle_mapper.dart +2 -2
  82. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_database.dart +26 -15
  83. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_storage.dart +1 -1
  84. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_bundle_mapper.dart +2 -2
  85. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_database.dart +2 -2
  86. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_get_update_info.dart +2 -2
  87. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_storage.dart +1 -1
  88. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_types.dart +1 -1
  89. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_bundle_mapper.dart +2 -2
  90. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_database.dart +2 -2
  91. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_database.dart +1 -1
  92. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_storage.dart +1 -1
  93. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_storage.dart +1 -1
  94. package/dart-src/root/lib/src/pocketbase_update_source.dart +107 -0
  95. package/dart-src/root/lib/src/postgres_update_source.dart +95 -0
  96. package/dart-src/root/lib/src/shared_update_check.dart +131 -0
  97. package/dart-src/root/lib/src/supabase_update_source.dart +93 -0
  98. package/dart-src/root/pubspec.yaml +54 -0
  99. package/package.json +1 -1
  100. package/scripts/postinstall.js +17 -13
  101. package/scripts/vendor.sh +22 -7
  102. package/dart-src/packages/core/CHANGELOG.md +0 -21
  103. package/dart-src/packages/core/README.md +0 -23
  104. package/dart-src/packages/core/example/lib/main.dart +0 -69
  105. package/dart-src/packages/core/pubspec.yaml +0 -13
  106. package/dart-src/packages/core/test/rollout_test.dart +0 -135
  107. package/dart-src/packages/core/test/semver_test.dart +0 -55
  108. package/dart-src/packages/core/test/semver_vectors.json +0 -1
  109. package/dart-src/packages/core/test/types_test.dart +0 -131
  110. package/dart-src/packages/core/test/uuid_test.dart +0 -40
  111. package/dart-src/packages/core/tool/dbg.dart +0 -7
  112. package/dart-src/plugins/aws/.gitkeep +0 -0
  113. package/dart-src/plugins/aws/CHANGELOG.md +0 -23
  114. package/dart-src/plugins/aws/LICENSE +0 -21
  115. package/dart-src/plugins/aws/README.md +0 -28
  116. package/dart-src/plugins/aws/example/lib/main.dart +0 -60
  117. package/dart-src/plugins/aws/pubspec.yaml +0 -30
  118. package/dart-src/plugins/aws/test/aws_database_test.dart +0 -161
  119. package/dart-src/plugins/aws/test/aws_storage_test.dart +0 -73
  120. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +0 -78
  121. package/dart-src/plugins/cloudflare/.gitkeep +0 -0
  122. package/dart-src/plugins/cloudflare/CHANGELOG.md +0 -23
  123. package/dart-src/plugins/cloudflare/LICENSE +0 -21
  124. package/dart-src/plugins/cloudflare/README.md +0 -25
  125. package/dart-src/plugins/cloudflare/example/lib/main.dart +0 -62
  126. package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +0 -16
  127. package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +0 -8
  128. package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +0 -38
  129. package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +0 -11
  130. package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +0 -24
  131. package/dart-src/plugins/cloudflare/pubspec.yaml +0 -28
  132. package/dart-src/plugins/cloudflare/sql/bundles.sql +0 -42
  133. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +0 -126
  134. package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +0 -106
  135. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +0 -200
  136. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +0 -261
  137. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +0 -78
  138. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +0 -74
  139. package/dart-src/plugins/plugin-core/CHANGELOG.md +0 -20
  140. package/dart-src/plugins/plugin-core/LICENSE +0 -21
  141. package/dart-src/plugins/plugin-core/README.md +0 -21
  142. package/dart-src/plugins/plugin-core/example/lib/main.dart +0 -61
  143. package/dart-src/plugins/plugin-core/pubspec.yaml +0 -23
  144. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +0 -170
  145. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +0 -74
  146. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +0 -345
  147. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +0 -843
  148. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +0 -298
  149. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +0 -195
  150. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +0 -56
  151. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +0 -1589
  152. package/dart-src/plugins/plugin-core/test/helpers_test.dart +0 -283
  153. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +0 -202
  154. package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +0 -86
  155. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +0 -88
  156. package/dart-src/plugins/pocketbase/CHANGELOG.md +0 -23
  157. package/dart-src/plugins/pocketbase/LICENSE +0 -21
  158. package/dart-src/plugins/pocketbase/README.md +0 -54
  159. package/dart-src/plugins/pocketbase/example/lib/main.dart +0 -62
  160. package/dart-src/plugins/pocketbase/pubspec.yaml +0 -26
  161. package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +0 -12
  162. package/dart-src/plugins/postgres/.gitkeep +0 -0
  163. package/dart-src/plugins/postgres/CHANGELOG.md +0 -23
  164. package/dart-src/plugins/postgres/LICENSE +0 -21
  165. package/dart-src/plugins/postgres/README.md +0 -29
  166. package/dart-src/plugins/postgres/example/lib/main.dart +0 -67
  167. package/dart-src/plugins/postgres/pubspec.yaml +0 -26
  168. package/dart-src/plugins/postgres/sql/bundles.sql +0 -45
  169. package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +0 -21
  170. package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +0 -171
  171. package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +0 -126
  172. package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +0 -125
  173. package/dart-src/plugins/postgres/sql/hash_user_id.sql +0 -30
  174. package/dart-src/plugins/postgres/sql/is_device_eligible.sql +0 -237
  175. package/dart-src/plugins/postgres/sql/prepareSql.ts +0 -11
  176. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +0 -320
  177. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +0 -139
  178. package/dart-src/plugins/postgres/test/postgres_database_test.dart +0 -210
  179. package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +0 -60
  180. package/dart-src/plugins/supabase/CHANGELOG.md +0 -24
  181. package/dart-src/plugins/supabase/LICENSE +0 -21
  182. package/dart-src/plugins/supabase/README.md +0 -53
  183. package/dart-src/plugins/supabase/example/lib/main.dart +0 -63
  184. package/dart-src/plugins/supabase/pubspec.yaml +0 -27
  185. package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +0 -44
  186. package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +0 -968
  187. package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +0 -232
  188. package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -137
  189. package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -206
  190. package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -186
  191. package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -505
  192. package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
  193. package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
  194. package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -75
  195. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +0 -654
  196. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +0 -115
  197. package/dart-src/plugins/supabase/test/supabase_database_test.dart +0 -198
  198. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +0 -168
  199. /package/dart-src/{packages/core → root}/LICENSE +0 -0
  200. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/flutter_ota_kit_aws.dart +0 -0
  201. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_client.dart +0 -0
  202. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_signer.dart +0 -0
  203. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_config.dart +0 -0
  204. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_ssm_client.dart +0 -0
  205. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/flutter_ota_kit_cloudflare.dart +0 -0
  206. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_client.dart +0 -0
  207. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_config.dart +0 -0
  208. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_config.dart +0 -0
  209. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/flutter_ota_kit_core.dart +0 -0
  210. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/app_update_info.dart +0 -0
  211. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle.dart +0 -0
  212. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_artifacts.dart +0 -0
  213. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_patch_artifact.dart +0 -0
  214. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/changed_asset.dart +0 -0
  215. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/get_bundles_args.dart +0 -0
  216. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/metadata.dart +0 -0
  217. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/patch_info.dart +0 -0
  218. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/platform.dart +0 -0
  219. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/rollout.dart +0 -0
  220. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver.dart +0 -0
  221. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_range.dart +0 -0
  222. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_version.dart +0 -0
  223. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/strategy.dart +0 -0
  224. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/update_bundle_params.dart +0 -0
  225. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/uuid.dart +0 -0
  226. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/asset_storage_layout.dart +0 -0
  227. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_helpers.dart +0 -0
  228. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_storage_layout.dart +0 -0
  229. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work.dart +0 -0
  230. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work_store.dart +0 -0
  231. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/calculate_pagination.dart +0 -0
  232. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/compression_format.dart +0 -0
  233. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/content_addressed_assets.dart +0 -0
  234. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_database_plugin.dart +0 -0
  235. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_key_builder.dart +0 -0
  236. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_plugin.dart +0 -0
  237. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/generate_min_bundle_id.dart +0 -0
  238. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/is_object.dart +0 -0
  239. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/legacy_asset_storage_layout.dart +0 -0
  240. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/paginate_bundles.dart +0 -0
  241. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/parse_storage_uri.dart +0 -0
  242. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/query_bundles.dart +0 -0
  243. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_helpers.dart +0 -0
  244. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_profile.dart +0 -0
  245. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/uuidv7.dart +0 -0
  246. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/flutter_ota_kit_pocketbase.dart +0 -0
  247. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_client.dart +0 -0
  248. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_config.dart +0 -0
  249. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/flutter_ota_kit_postgres.dart +0 -0
  250. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_client.dart +0 -0
  251. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_config.dart +0 -0
  252. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/edge.dart +0 -0
  253. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/flutter_ota_kit_supabase.dart +0 -0
  254. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/error_message.dart +0 -0
  255. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_adapter.dart +0 -0
  256. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_http.dart +0 -0
  257. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_config.dart +0 -0
  258. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_signed_url_batcher.dart +0 -0
  259. /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 {}