@_nazmiforreal/flutter-ota 0.1.26 → 0.1.27

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 (256) hide show
  1. package/bin/flutter-ota-linux-x64 +0 -0
  2. package/bin/flutter-ota.js +12 -11
  3. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +46 -13
  4. package/dart-src/packages/cli-tools/lib/src/backend.dart +6 -6
  5. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +2 -0
  6. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +7 -7
  7. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +4 -1
  8. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1 -1
  9. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +1 -1
  10. package/dart-src/packages/cli-tools/lib/src/config.dart +5 -5
  11. package/dart-src/packages/cli-tools/lib/src/operations.dart +2 -2
  12. package/dart-src/packages/cli-tools/pubspec.yaml +8 -21
  13. package/dart-src/packages/cli-tools/test/backends_test.dart +3 -3
  14. package/dart-src/packages/cli-tools/test/cli_test.dart +1 -1
  15. package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +2 -2
  16. package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +2 -2
  17. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +4 -1
  18. package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +2 -2
  19. package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +1 -1
  20. package/dart-src/root/README.md +394 -0
  21. package/dart-src/root/analysis_options.yaml +21 -0
  22. package/dart-src/root/android/build.gradle +116 -0
  23. package/dart-src/root/android/consumer-rules.pro +23 -0
  24. package/dart-src/root/android/settings.gradle +1 -0
  25. package/dart-src/root/android/src/main/AndroidManifest.xml +21 -0
  26. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/ApplyResult.kt +52 -0
  27. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BlacklistStore.kt +154 -0
  28. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BootDiagnosticStore.kt +150 -0
  29. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/CrashGuard.kt +271 -0
  30. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherApplication.kt +201 -0
  31. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherAutoInitProvider.kt +93 -0
  32. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherPlugin.kt +245 -0
  33. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/LoaderHook.kt +298 -0
  34. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatchManager.kt +1579 -0
  35. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfig.kt +173 -0
  36. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/SignatureVerifier.kt +177 -0
  37. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/CrashGuardTest.kt +61 -0
  38. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/LoaderHookTest.kt +25 -0
  39. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatchManagerTest.kt +401 -0
  40. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfigTest.kt +30 -0
  41. package/dart-src/root/bin/pack.dart +182 -0
  42. package/dart-src/root/lib/flutter_ota_kit.dart +1170 -0
  43. package/dart-src/root/lib/src/app_version_resolver.dart +36 -0
  44. package/dart-src/root/lib/src/aws_update_source.dart +97 -0
  45. package/dart-src/root/lib/src/blacklist.dart +54 -0
  46. package/dart-src/root/lib/src/boot_diagnostic.dart +138 -0
  47. package/dart-src/root/lib/src/cloudflare_update_source.dart +97 -0
  48. package/dart-src/root/lib/src/flutter_ota_app.dart +69 -0
  49. package/dart-src/root/lib/src/io.dart +48 -0
  50. package/dart-src/root/lib/src/io_stub.dart +19 -0
  51. package/dart-src/root/lib/src/ota_progress_overlay.dart +987 -0
  52. package/dart-src/root/lib/src/pack/pack.dart +434 -0
  53. package/dart-src/root/lib/src/patch_info.dart +12 -0
  54. package/dart-src/root/lib/src/patcher_channel.dart +160 -0
  55. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_database.dart +1 -1
  56. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_s3_client.dart +1 -1
  57. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage.dart +1 -1
  58. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage_profile.dart +1 -1
  59. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart +1 -1
  60. package/dart-src/root/lib/src/pkg/client/flutter_ota_kit_client.dart +1 -0
  61. package/dart-src/root/lib/src/pkg/client/src/server_update_source.dart +46 -0
  62. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/cloudflare_worker_database.dart +1 -1
  63. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_build_where.dart +1 -1
  64. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_bundle_mapper.dart +3 -3
  65. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database.dart +1 -1
  66. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database_plugin.dart +2 -2
  67. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_s3_client.dart +1 -1
  68. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage.dart +1 -1
  69. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage_profile.dart +1 -1
  70. package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/status.dart +1 -1
  71. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/flutter_ota_kit_plugin_core.dart +1 -1
  72. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_blob_database_plugin.dart +1 -1
  73. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/filter_compatible_app_versions.dart +1 -1
  74. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/get_update_info.dart +1 -1
  75. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/request_update_bundle_state.dart +1 -1
  76. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/resolve_update_info_from_bundles.dart +1 -1
  77. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/types.dart +2 -2
  78. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_bundle_mapper.dart +2 -2
  79. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_database.dart +26 -15
  80. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_storage.dart +1 -1
  81. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_bundle_mapper.dart +2 -2
  82. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_database.dart +2 -2
  83. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_get_update_info.dart +2 -2
  84. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_storage.dart +1 -1
  85. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_types.dart +1 -1
  86. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_bundle_mapper.dart +2 -2
  87. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_database.dart +2 -2
  88. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_database.dart +1 -1
  89. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_storage.dart +1 -1
  90. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_storage.dart +1 -1
  91. package/dart-src/root/lib/src/pocketbase_update_source.dart +107 -0
  92. package/dart-src/root/lib/src/postgres_update_source.dart +95 -0
  93. package/dart-src/root/lib/src/shared_update_check.dart +131 -0
  94. package/dart-src/root/lib/src/supabase_update_source.dart +93 -0
  95. package/dart-src/root/pubspec.yaml +54 -0
  96. package/package.json +1 -1
  97. package/scripts/postinstall.js +17 -13
  98. package/scripts/vendor.sh +22 -7
  99. package/dart-src/packages/core/CHANGELOG.md +0 -21
  100. package/dart-src/packages/core/README.md +0 -23
  101. package/dart-src/packages/core/example/lib/main.dart +0 -69
  102. package/dart-src/packages/core/pubspec.yaml +0 -13
  103. package/dart-src/packages/core/test/rollout_test.dart +0 -135
  104. package/dart-src/packages/core/test/semver_test.dart +0 -55
  105. package/dart-src/packages/core/test/semver_vectors.json +0 -1
  106. package/dart-src/packages/core/test/types_test.dart +0 -131
  107. package/dart-src/packages/core/test/uuid_test.dart +0 -40
  108. package/dart-src/packages/core/tool/dbg.dart +0 -7
  109. package/dart-src/plugins/aws/.gitkeep +0 -0
  110. package/dart-src/plugins/aws/CHANGELOG.md +0 -23
  111. package/dart-src/plugins/aws/LICENSE +0 -21
  112. package/dart-src/plugins/aws/README.md +0 -28
  113. package/dart-src/plugins/aws/example/lib/main.dart +0 -60
  114. package/dart-src/plugins/aws/pubspec.yaml +0 -30
  115. package/dart-src/plugins/aws/test/aws_database_test.dart +0 -161
  116. package/dart-src/plugins/aws/test/aws_storage_test.dart +0 -73
  117. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +0 -78
  118. package/dart-src/plugins/cloudflare/.gitkeep +0 -0
  119. package/dart-src/plugins/cloudflare/CHANGELOG.md +0 -23
  120. package/dart-src/plugins/cloudflare/LICENSE +0 -21
  121. package/dart-src/plugins/cloudflare/README.md +0 -25
  122. package/dart-src/plugins/cloudflare/example/lib/main.dart +0 -62
  123. package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +0 -16
  124. package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +0 -8
  125. package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +0 -38
  126. package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +0 -11
  127. package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +0 -24
  128. package/dart-src/plugins/cloudflare/pubspec.yaml +0 -28
  129. package/dart-src/plugins/cloudflare/sql/bundles.sql +0 -42
  130. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +0 -126
  131. package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +0 -106
  132. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +0 -200
  133. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +0 -261
  134. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +0 -78
  135. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +0 -74
  136. package/dart-src/plugins/plugin-core/CHANGELOG.md +0 -20
  137. package/dart-src/plugins/plugin-core/LICENSE +0 -21
  138. package/dart-src/plugins/plugin-core/README.md +0 -21
  139. package/dart-src/plugins/plugin-core/example/lib/main.dart +0 -61
  140. package/dart-src/plugins/plugin-core/pubspec.yaml +0 -23
  141. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +0 -170
  142. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +0 -74
  143. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +0 -345
  144. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +0 -843
  145. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +0 -298
  146. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +0 -195
  147. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +0 -56
  148. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +0 -1589
  149. package/dart-src/plugins/plugin-core/test/helpers_test.dart +0 -283
  150. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +0 -202
  151. package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +0 -86
  152. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +0 -88
  153. package/dart-src/plugins/pocketbase/CHANGELOG.md +0 -23
  154. package/dart-src/plugins/pocketbase/LICENSE +0 -21
  155. package/dart-src/plugins/pocketbase/README.md +0 -54
  156. package/dart-src/plugins/pocketbase/example/lib/main.dart +0 -62
  157. package/dart-src/plugins/pocketbase/pubspec.yaml +0 -26
  158. package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +0 -12
  159. package/dart-src/plugins/postgres/.gitkeep +0 -0
  160. package/dart-src/plugins/postgres/CHANGELOG.md +0 -23
  161. package/dart-src/plugins/postgres/LICENSE +0 -21
  162. package/dart-src/plugins/postgres/README.md +0 -29
  163. package/dart-src/plugins/postgres/example/lib/main.dart +0 -67
  164. package/dart-src/plugins/postgres/pubspec.yaml +0 -26
  165. package/dart-src/plugins/postgres/sql/bundles.sql +0 -45
  166. package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +0 -21
  167. package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +0 -171
  168. package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +0 -126
  169. package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +0 -125
  170. package/dart-src/plugins/postgres/sql/hash_user_id.sql +0 -30
  171. package/dart-src/plugins/postgres/sql/is_device_eligible.sql +0 -237
  172. package/dart-src/plugins/postgres/sql/prepareSql.ts +0 -11
  173. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +0 -320
  174. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +0 -139
  175. package/dart-src/plugins/postgres/test/postgres_database_test.dart +0 -210
  176. package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +0 -60
  177. package/dart-src/plugins/supabase/CHANGELOG.md +0 -24
  178. package/dart-src/plugins/supabase/LICENSE +0 -21
  179. package/dart-src/plugins/supabase/README.md +0 -53
  180. package/dart-src/plugins/supabase/example/lib/main.dart +0 -63
  181. package/dart-src/plugins/supabase/pubspec.yaml +0 -27
  182. package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +0 -44
  183. package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +0 -968
  184. package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +0 -232
  185. package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -137
  186. package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -206
  187. package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -186
  188. package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -505
  189. package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
  190. package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
  191. package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -75
  192. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +0 -654
  193. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +0 -115
  194. package/dart-src/plugins/supabase/test/supabase_database_test.dart +0 -198
  195. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +0 -168
  196. /package/dart-src/{packages/core → root}/LICENSE +0 -0
  197. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/flutter_ota_kit_aws.dart +0 -0
  198. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_client.dart +0 -0
  199. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_signer.dart +0 -0
  200. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_config.dart +0 -0
  201. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_ssm_client.dart +0 -0
  202. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/flutter_ota_kit_cloudflare.dart +0 -0
  203. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_client.dart +0 -0
  204. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_config.dart +0 -0
  205. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_config.dart +0 -0
  206. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/flutter_ota_kit_core.dart +0 -0
  207. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/app_update_info.dart +0 -0
  208. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle.dart +0 -0
  209. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_artifacts.dart +0 -0
  210. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_patch_artifact.dart +0 -0
  211. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/changed_asset.dart +0 -0
  212. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/get_bundles_args.dart +0 -0
  213. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/metadata.dart +0 -0
  214. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/patch_info.dart +0 -0
  215. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/platform.dart +0 -0
  216. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/rollout.dart +0 -0
  217. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver.dart +0 -0
  218. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_range.dart +0 -0
  219. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_version.dart +0 -0
  220. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/strategy.dart +0 -0
  221. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/update_bundle_params.dart +0 -0
  222. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/uuid.dart +0 -0
  223. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/asset_storage_layout.dart +0 -0
  224. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_helpers.dart +0 -0
  225. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_storage_layout.dart +0 -0
  226. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work.dart +0 -0
  227. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work_store.dart +0 -0
  228. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/calculate_pagination.dart +0 -0
  229. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/compression_format.dart +0 -0
  230. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/content_addressed_assets.dart +0 -0
  231. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_database_plugin.dart +0 -0
  232. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_key_builder.dart +0 -0
  233. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_plugin.dart +0 -0
  234. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/generate_min_bundle_id.dart +0 -0
  235. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/is_object.dart +0 -0
  236. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/legacy_asset_storage_layout.dart +0 -0
  237. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/paginate_bundles.dart +0 -0
  238. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/parse_storage_uri.dart +0 -0
  239. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/query_bundles.dart +0 -0
  240. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_helpers.dart +0 -0
  241. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_profile.dart +0 -0
  242. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/uuidv7.dart +0 -0
  243. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/flutter_ota_kit_pocketbase.dart +0 -0
  244. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_client.dart +0 -0
  245. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_config.dart +0 -0
  246. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/flutter_ota_kit_postgres.dart +0 -0
  247. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_client.dart +0 -0
  248. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_config.dart +0 -0
  249. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/edge.dart +0 -0
  250. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/flutter_ota_kit_supabase.dart +0 -0
  251. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/error_message.dart +0 -0
  252. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_adapter.dart +0 -0
  253. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_http.dart +0 -0
  254. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_config.dart +0 -0
  255. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_signed_url_batcher.dart +0 -0
  256. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/types.dart +0 -0
@@ -1,843 +0,0 @@
1
- import 'dart:convert';
2
-
3
- import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
4
- import 'package:flutter_ota_kit_plugin_core/flutter_ota_kit_plugin_core.dart';
5
- import 'package:test/test.dart';
6
-
7
- // ---------------------------------------------------------------------------
8
- // Helpers
9
- // ---------------------------------------------------------------------------
10
-
11
- const _defaultBundleFields = <String, Object?>{
12
- 'fileHash': 'hash',
13
- 'gitCommitHash': null,
14
- 'message': null,
15
- 'enabled': true,
16
- 'shouldForceUpdate': false,
17
- 'storageUri':
18
- 'storage://my-app/00000000-0000-0000-0000-000000000000/bundle.zip',
19
- 'fingerprintHash': null,
20
- };
21
-
22
- Bundle createBundle(
23
- String channel,
24
- String platform,
25
- String targetAppVersion,
26
- String id,
27
- ) {
28
- return Bundle.fromJson({
29
- ..._defaultBundleFields,
30
- 'channel': channel,
31
- 'platform': platform,
32
- 'targetAppVersion': targetAppVersion,
33
- 'id': id,
34
- });
35
- }
36
-
37
- // ---------------------------------------------------------------------------
38
- // Fake blob store
39
- // ---------------------------------------------------------------------------
40
-
41
- class _FakeBlobStore {
42
- final Map<String, String> data = {};
43
- final List<String> listObjectCalls = [];
44
- final List<String> loadObjectCalls = [];
45
- final List<String> invalidationCalls = [];
46
-
47
- void clear() {
48
- data.clear();
49
- listObjectCalls.clear();
50
- loadObjectCalls.clear();
51
- invalidationCalls.clear();
52
- }
53
-
54
- Future<List<String>> listObjects(String prefix) async {
55
- listObjectCalls.add(prefix);
56
- return data.keys.where((k) => k.startsWith(prefix)).toList();
57
- }
58
-
59
- Future<T?> loadObject<T>(String key) async {
60
- loadObjectCalls.add(key);
61
- final raw = data[key];
62
- if (raw == null) return null;
63
- return jsonDecode(raw) as T;
64
- }
65
-
66
- Future<void> uploadObject<T>(String key, T value) async {
67
- data[key] = jsonEncode(value);
68
- }
69
-
70
- Future<void> deleteObject(String key) async {
71
- data.remove(key);
72
- }
73
-
74
- Future<void> invalidatePaths(List<String> paths) async {
75
- invalidationCalls.addAll(paths);
76
- }
77
-
78
- bool shouldSkipLoadObjectError(Object error, String key) => false;
79
- void validateChannel(String channel) {}
80
- }
81
-
82
- _BlobOps _createOps(_FakeBlobStore store) => _BlobOps(store);
83
-
84
- class _BlobOps implements BlobOperations {
85
- _BlobOps(this._store);
86
- final _FakeBlobStore _store;
87
-
88
- @override
89
- Future<List<String>> listObjects(String prefix) => _store.listObjects(prefix);
90
- @override
91
- Future<T?> loadObject<T>(String key) => _store.loadObject<T>(key);
92
- @override
93
- Future<void> uploadObject<T>(String key, T data) =>
94
- _store.uploadObject<T>(key, data);
95
- @override
96
- Future<void> deleteObject(String key) => _store.deleteObject(key);
97
- @override
98
- bool shouldSkipLoadObjectError(Object error, String key) =>
99
- _store.shouldSkipLoadObjectError(error, key);
100
- @override
101
- void validateChannel(String channel) => _store.validateChannel(channel);
102
- @override
103
- Future<void> invalidatePaths(List<String> paths) =>
104
- _store.invalidatePaths(paths);
105
- @override
106
- String get apiBasePath => '/api/check-update';
107
- }
108
-
109
- void _seedUpdateManifests(_FakeBlobStore store, List<Bundle> bundles) {
110
- final bundlesByKey = <String, List<Bundle>>{};
111
- final targetVersionsByKey = <String, Set<String>>{};
112
-
113
- for (final bundle in bundles) {
114
- final target = bundle.targetAppVersion ?? bundle.fingerprintHash;
115
- if (target == null) continue;
116
-
117
- final key =
118
- '${bundle.channel}/${bundle.platform.value}/$target/update.json';
119
- bundlesByKey.putIfAbsent(key, () => <Bundle>[]).add(bundle);
120
-
121
- if (bundle.targetAppVersion != null) {
122
- final tvKey =
123
- '${bundle.channel}/${bundle.platform.value}/target-app-versions.json';
124
- targetVersionsByKey
125
- .putIfAbsent(tvKey, () => <String>{})
126
- .add(bundle.targetAppVersion!);
127
- }
128
- }
129
-
130
- for (final entry in bundlesByKey.entries) {
131
- final sorted = entry.value..sort((a, b) => b.id.compareTo(a.id));
132
- store.data[entry.key] = jsonEncode(sorted.map((b) => b.toJson()).toList());
133
- }
134
-
135
- for (final entry in targetVersionsByKey.entries) {
136
- store.data[entry.key] = jsonEncode(entry.value.toList());
137
- }
138
- }
139
-
140
- // ---------------------------------------------------------------------------
141
- // Tests
142
- // ---------------------------------------------------------------------------
143
-
144
- void main() {
145
- late _FakeBlobStore store;
146
- late DatabasePlugin plugin;
147
-
148
- DatabasePlugin createPlugin() {
149
- return createBlobDatabasePlugin(
150
- name: 'blobDatabase',
151
- blobFactory: (_) => _createOps(store),
152
- )(null as dynamic)();
153
- }
154
-
155
- setUp(() {
156
- store = _FakeBlobStore();
157
- plugin = createPlugin();
158
- });
159
-
160
- group('update checks', () {
161
- test('uses direct app-version manifests for update checks', () async {
162
- final latest = createBundle(
163
- 'production',
164
- 'ios',
165
- '*',
166
- '00000000-0000-0000-0000-000000000002',
167
- );
168
- final previous = createBundle(
169
- 'production',
170
- 'ios',
171
- '*',
172
- '00000000-0000-0000-0000-000000000001',
173
- );
174
-
175
- _seedUpdateManifests(store, [previous, latest]);
176
-
177
- final updateInfo = await plugin.getUpdateInfo(
178
- AppVersionGetBundlesArgs(
179
- appVersion: '1.0.0',
180
- bundleId: '00000000-0000-0000-0000-000000000000',
181
- platform: Platform.ios,
182
- ),
183
- );
184
-
185
- expect(updateInfo, isNotNull);
186
- expect(updateInfo!.id, equals(latest.id));
187
- expect(updateInfo.status, equals(UpdateStatus.update));
188
-
189
- expect(store.listObjectCalls, isEmpty);
190
- expect(store.loadObjectCalls, [
191
- 'production/ios/target-app-versions.json',
192
- 'production/ios/*/update.json',
193
- ]);
194
- });
195
-
196
- test('uses fingerprint manifests directly for update checks', () async {
197
- final fingerprintBundle = Bundle.fromJson({
198
- ..._defaultBundleFields,
199
- 'channel': 'production',
200
- 'platform': 'ios',
201
- 'targetAppVersion': null,
202
- 'fingerprintHash': 'fingerprint-1',
203
- 'id': '00000000-0000-0000-0000-000000000010',
204
- });
205
-
206
- _seedUpdateManifests(store, [fingerprintBundle]);
207
-
208
- final updateInfo = await plugin.getUpdateInfo(
209
- FingerprintGetBundlesArgs(
210
- bundleId: '00000000-0000-0000-0000-000000000000',
211
- fingerprintHash: 'fingerprint-1',
212
- platform: Platform.ios,
213
- ),
214
- );
215
-
216
- expect(updateInfo, isNotNull);
217
- expect(updateInfo!.id, equals(fingerprintBundle.id));
218
- expect(updateInfo.status, equals(UpdateStatus.update));
219
-
220
- expect(store.listObjectCalls, isEmpty);
221
- expect(store.loadObjectCalls, [
222
- 'production/ios/fingerprint-1/update.json',
223
- ]);
224
- });
225
-
226
- test('respects cohort eligibility', () async {
227
- final gatedBundle = Bundle.fromJson({
228
- ...createBundle(
229
- 'production',
230
- 'ios',
231
- '*',
232
- '00000000-0000-0000-0000-000000000021',
233
- ).toJson(),
234
- 'targetCohorts': ['beta'],
235
- });
236
- final fallback = createBundle(
237
- 'production',
238
- 'ios',
239
- '*',
240
- '00000000-0000-0000-0000-000000000020',
241
- );
242
- final fallbackWithCohort = Bundle.fromJson({
243
- ...fallback.toJson(),
244
- 'targetCohorts': ['stable'],
245
- });
246
-
247
- _seedUpdateManifests(store, [fallbackWithCohort, gatedBundle]);
248
-
249
- final updateInfo = await plugin.getUpdateInfo(
250
- AppVersionGetBundlesArgs(
251
- appVersion: '1.0.0',
252
- bundleId: '00000000-0000-0000-0000-000000000000',
253
- cohort: 'stable',
254
- platform: Platform.ios,
255
- ),
256
- );
257
-
258
- expect(updateInfo, isNotNull);
259
- expect(updateInfo!.id, equals(fallbackWithCohort.id));
260
- expect(updateInfo.status, equals(UpdateStatus.update));
261
- });
262
-
263
- test('returns null when no candidates and bundleId = nil', () async {
264
- final updateInfo = await plugin.getUpdateInfo(
265
- AppVersionGetBundlesArgs(
266
- appVersion: '1.0.0',
267
- bundleId: '00000000-0000-0000-0000-000000000040',
268
- minBundleId: '00000000-0000-0000-0000-000000000040',
269
- platform: Platform.ios,
270
- ),
271
- );
272
- expect(updateInfo, isNull);
273
- });
274
- });
275
-
276
- group('append and commit', () {
277
- test('appends a new bundle and commits to blob store', () async {
278
- final bundle = createBundle(
279
- 'production',
280
- 'ios',
281
- '1.0.0',
282
- '00000000-0000-0000-0000-000000000001',
283
- );
284
-
285
- await plugin.appendBundle(bundle);
286
- await plugin.commitBundle();
287
-
288
- final bundleKey = 'production/ios/1.0.0/update.json';
289
- expect(store.data.containsKey(bundleKey), isTrue);
290
-
291
- final stored = jsonDecode(store.data[bundleKey]!) as List;
292
- expect(stored.length, equals(1));
293
-
294
- final fetched = await plugin.getBundleById(
295
- '00000000-0000-0000-0000-000000000001',
296
- );
297
- expect(fetched, isNotNull);
298
- expect(fetched!.id, equals(bundle.id));
299
- });
300
-
301
- test('does not write to store until commitBundle', () async {
302
- final bundle = createBundle(
303
- 'production',
304
- 'ios',
305
- '1.0.0',
306
- '00000000-0000-0000-0000-000000000010',
307
- );
308
-
309
- expect(store.data, isEmpty);
310
-
311
- await plugin.appendBundle(bundle);
312
- expect(store.data, isEmpty);
313
-
314
- await plugin.commitBundle();
315
- expect(
316
- store.data.containsKey('production/ios/1.0.0/update.json'),
317
- isTrue,
318
- );
319
- });
320
-
321
- test('appends multiple bundles to correct keys', () async {
322
- final b1 = createBundle('production', 'ios', '1.0.0', 'multi-1');
323
- final b2 = createBundle('production', 'android', '2.0.0', 'multi-2');
324
-
325
- await plugin.appendBundle(b1);
326
- await plugin.appendBundle(b2);
327
- await plugin.commitBundle();
328
-
329
- expect(
330
- store.data.containsKey('production/ios/1.0.0/update.json'),
331
- isTrue,
332
- );
333
- expect(
334
- store.data.containsKey('production/android/2.0.0/update.json'),
335
- isTrue,
336
- );
337
- });
338
- });
339
-
340
- group('update and delete', () {
341
- test('updates an existing bundle', () async {
342
- final bundle = createBundle(
343
- 'production',
344
- 'android',
345
- '2.0.0',
346
- '00000000-0000-0000-0000-000000000002',
347
- );
348
- final bundleKey = 'production/android/2.0.0/update.json';
349
- store.data[bundleKey] = jsonEncode([bundle.toJson()]);
350
-
351
- await plugin.getBundles(const DatabaseBundleQueryOptions(limit: 20));
352
- await plugin.updateBundle('00000000-0000-0000-0000-000000000002', {
353
- 'enabled': false,
354
- });
355
- await plugin.commitBundle();
356
-
357
- final updated = jsonDecode(store.data[bundleKey]!) as List;
358
- expect(updated.length, equals(1));
359
- expect(updated[0]['enabled'], isFalse);
360
- });
361
-
362
- test('throws when updating non-existent bundle', () async {
363
- expect(
364
- () => plugin.updateBundle('nonexistent', {'enabled': true}),
365
- throwsA(isA<StateError>()),
366
- );
367
- });
368
-
369
- test('deletes a bundle successfully', () async {
370
- final b1 = createBundle('production', 'ios', '1.1.1', 'bundleX');
371
- final b2 = createBundle('production', 'android', '1.1.1', 'bundleY');
372
-
373
- await plugin.appendBundle(b1);
374
- await plugin.appendBundle(b2);
375
- await plugin.commitBundle();
376
-
377
- expect(await plugin.getBundleById('bundleX'), isNotNull);
378
-
379
- await plugin.deleteBundle(b1);
380
- await plugin.commitBundle();
381
-
382
- expect(await plugin.getBundleById('bundleX'), isNull);
383
- expect(await plugin.getBundleById('bundleY'), isNotNull);
384
- });
385
-
386
- test('deletes entire update.json when no bundles remain', () async {
387
- final b1 = createBundle('production', 'ios', '1.1.1', 'bundleX');
388
-
389
- await plugin.appendBundle(b1);
390
- await plugin.commitBundle();
391
-
392
- final updateJsonKeys = store.data.keys
393
- .where((k) => k.contains('update.json'))
394
- .toList();
395
- expect(updateJsonKeys, isNotEmpty);
396
-
397
- await plugin.deleteBundle(b1);
398
- await plugin.commitBundle();
399
-
400
- expect(store.data.containsKey(updateJsonKeys.first), isFalse);
401
- });
402
-
403
- test('keeps update.json when other bundles remain', () async {
404
- final b1 = {
405
- ..._defaultBundleFields,
406
- 'id': 'bundleA',
407
- 'channel': 'production',
408
- 'platform': 'ios',
409
- 'targetAppVersion': '1.1.1',
410
- };
411
- final b2 = {
412
- ..._defaultBundleFields,
413
- 'id': 'bundleB',
414
- 'channel': 'production',
415
- 'platform': 'ios',
416
- 'targetAppVersion': '1.1.1',
417
- };
418
-
419
- final bundle1 = Bundle.fromJson(b1);
420
- final bundle2 = Bundle.fromJson(b2);
421
-
422
- await plugin.appendBundle(bundle1);
423
- await plugin.appendBundle(bundle2);
424
- await plugin.commitBundle();
425
-
426
- await plugin.deleteBundle(bundle1);
427
- await plugin.commitBundle();
428
-
429
- final remaining = await plugin.getBundleById('bundleB');
430
- expect(remaining, isNotNull);
431
- });
432
- });
433
-
434
- group('targetAppVersion move', () {
435
- test('moves bundle between version paths', () async {
436
- final keyOld = 'production/ios/1.x.x/update.json';
437
- final keyNew = 'production/ios/1.0.2/update.json';
438
-
439
- final oldBundles = [
440
- createBundle(
441
- 'production',
442
- 'ios',
443
- '1.x.x',
444
- '00000000-0000-0000-0000-000000000003',
445
- ),
446
- createBundle(
447
- 'production',
448
- 'ios',
449
- '1.x.x',
450
- '00000000-0000-0000-0000-000000000002',
451
- ),
452
- createBundle(
453
- 'production',
454
- 'ios',
455
- '1.x.x',
456
- '00000000-0000-0000-0000-000000000001',
457
- ),
458
- ];
459
-
460
- final newBundles = [
461
- createBundle(
462
- 'production',
463
- 'ios',
464
- '1.0.2',
465
- '00000000-0000-0000-0000-000000000005',
466
- ),
467
- createBundle(
468
- 'production',
469
- 'ios',
470
- '1.0.2',
471
- '00000000-0000-0000-0000-000000000004',
472
- ),
473
- ];
474
-
475
- store.data[keyOld] = jsonEncode(
476
- oldBundles.map((b) => b.toJson()).toList(),
477
- );
478
- store.data[keyNew] = jsonEncode(
479
- newBundles.map((b) => b.toJson()).toList(),
480
- );
481
- store.data['production/ios/target-app-versions.json'] = jsonEncode([
482
- '1.x.x',
483
- '1.0.2',
484
- ]);
485
-
486
- await plugin.getBundles(const DatabaseBundleQueryOptions(limit: 20));
487
-
488
- await plugin.updateBundle('00000000-0000-0000-0000-000000000003', {
489
- 'targetAppVersion': '1.0.2',
490
- });
491
- await plugin.commitBundle();
492
-
493
- final newFileBundles = jsonDecode(store.data[keyNew]!) as List;
494
- expect(newFileBundles.length, equals(3));
495
-
496
- final oldFileBundles = jsonDecode(store.data[keyOld]!) as List;
497
- expect(oldFileBundles.length, equals(2));
498
- });
499
- });
500
-
501
- group('getChannels', () {
502
- test('returns sorted unique channels', () async {
503
- final p1 = createBundle('production', 'ios', '1.0.0', 'ch-prod');
504
- final p2 = createBundle('beta', 'ios', '1.0.0', 'ch-beta');
505
-
506
- await plugin.appendBundle(p1);
507
- await plugin.appendBundle(p2);
508
- await plugin.commitBundle();
509
-
510
- final channels = await plugin.getChannels();
511
- expect(channels, ['beta', 'production']);
512
- });
513
- });
514
-
515
- group('getBundles', () {
516
- test('returns empty for no data', () async {
517
- final result = await plugin.getBundles(
518
- const DatabaseBundleQueryOptions(limit: 20),
519
- );
520
- expect(result.data, isEmpty);
521
- expect(result.pagination.total, equals(0));
522
- });
523
-
524
- test('sorts bundles descending by id', () async {
525
- final bA = createBundle('production', 'ios', '1.0.0', 'A');
526
- final bB = createBundle('production', 'ios', '1.0.0', 'B');
527
- final bC = createBundle('production', 'ios', '2.0.0', 'C');
528
-
529
- store.data['production/ios/1.0.0/update.json'] = jsonEncode([
530
- bB.toJson(),
531
- bA.toJson(),
532
- ]);
533
- store.data['production/ios/2.0.0/update.json'] = jsonEncode([
534
- bC.toJson(),
535
- ]);
536
-
537
- final result = await plugin.getBundles(
538
- const DatabaseBundleQueryOptions(limit: 20),
539
- );
540
- expect(result.data.length, equals(3));
541
- expect(result.data[0].id, 'C');
542
- expect(result.data[1].id, 'B');
543
- expect(result.data[2].id, 'A');
544
- });
545
-
546
- test('filters by channel and platform', () async {
547
- final ios = createBundle('production', 'ios', '1.0.0', 'ios-1');
548
- final android = createBundle(
549
- 'production',
550
- 'android',
551
- '1.0.0',
552
- 'android-1',
553
- );
554
-
555
- store.data['production/ios/1.0.0/update.json'] = jsonEncode([
556
- ios.toJson(),
557
- ]);
558
- store.data['production/android/1.0.0/update.json'] = jsonEncode([
559
- android.toJson(),
560
- ]);
561
-
562
- final result = await plugin.getBundles(
563
- DatabaseBundleQueryOptions(
564
- limit: 20,
565
- where: const DatabaseBundleQueryWhere(
566
- channel: 'production',
567
- platform: Platform.ios,
568
- ),
569
- ),
570
- );
571
- expect(result.data.length, equals(1));
572
- expect(result.data[0].id, 'ios-1');
573
- });
574
- });
575
-
576
- group('CloudFront invalidation', () {
577
- test('invalidates correct paths on new bundle', () async {
578
- final bundle = createBundle(
579
- 'production',
580
- 'ios',
581
- '1.0.0',
582
- 'cloudfront-new-test',
583
- );
584
-
585
- await plugin.appendBundle(bundle);
586
- await plugin.commitBundle();
587
-
588
- expect(store.invalidationCalls, isNotEmpty);
589
- expect(
590
- store.invalidationCalls.any(
591
- (p) => p.contains('app-version') && p.contains('1.0.0'),
592
- ),
593
- isTrue,
594
- );
595
- expect(
596
- store.invalidationCalls.any((p) => p.contains('update.json')),
597
- isFalse,
598
- );
599
- });
600
-
601
- test('invalidates semver pattern path', () async {
602
- final bundle = createBundle(
603
- 'production',
604
- 'ios',
605
- '3.0.x',
606
- 'cloudfront-semver',
607
- );
608
-
609
- store.invalidationCalls.clear();
610
- await plugin.appendBundle(bundle);
611
- await plugin.commitBundle();
612
-
613
- expect(
614
- store.invalidationCalls.any(
615
- (p) => p == '/api/check-update/app-version/ios/*',
616
- ),
617
- isTrue,
618
- );
619
- });
620
-
621
- test('no invalidation when commitBundle has no changes', () async {
622
- store.invalidationCalls.clear();
623
- await plugin.commitBundle();
624
- expect(store.invalidationCalls, isEmpty);
625
- });
626
- });
627
-
628
- group('targetAppVersion with spaces', () {
629
- test('normalizes space-containing targetAppVersion', () async {
630
- final bundle = createBundle(
631
- 'production',
632
- 'ios',
633
- '>= 10.7.0',
634
- 'space-normalization-test',
635
- );
636
-
637
- await plugin.appendBundle(bundle);
638
- await plugin.commitBundle();
639
-
640
- expect(
641
- store.data.containsKey('production/ios/>=10.7.0/update.json'),
642
- isTrue,
643
- );
644
- });
645
-
646
- test('normalizes multiple spaces', () async {
647
- final bundle = createBundle(
648
- 'production',
649
- 'android',
650
- '> 1.0.0 < 2.0.0',
651
- 'multi-space-test',
652
- );
653
-
654
- await plugin.appendBundle(bundle);
655
- await plugin.commitBundle();
656
-
657
- expect(
658
- store.data.containsKey('production/android/>1.0.0 <2.0.0/update.json'),
659
- isTrue,
660
- );
661
- });
662
-
663
- test('handles various semver range formats', () async {
664
- final cases = [
665
- ('> 1.0.0', '>1.0.0'),
666
- ('< 2.0.0', '<2.0.0'),
667
- ('>= 1.0.0', '>=1.0.0'),
668
- ('<= 2.0.0', '<=2.0.0'),
669
- ('~ 1.0.0', '~1.0.0'),
670
- ('^ 2.0.0', '^2.0.0'),
671
- ];
672
-
673
- for (var i = 0; i < cases.length; i++) {
674
- final (version, normalized) = cases[i];
675
- final bundle = createBundle('production', 'ios', version, 'format-$i');
676
- await plugin.appendBundle(bundle);
677
- }
678
-
679
- await plugin.commitBundle();
680
-
681
- for (final (_, normalized) in cases) {
682
- expect(
683
- store.data.containsKey('production/ios/$normalized/update.json'),
684
- isTrue,
685
- reason: 'Missing key for $normalized',
686
- );
687
- }
688
- });
689
-
690
- test('normalizes target-app-versions.json', () async {
691
- final bundle = createBundle(
692
- 'production',
693
- 'ios',
694
- '>= 10.7.0',
695
- 'target-versions-test',
696
- );
697
-
698
- await plugin.appendBundle(bundle);
699
- await plugin.commitBundle();
700
-
701
- final versions = jsonDecode(
702
- store.data['production/ios/target-app-versions.json']!,
703
- ) as List;
704
- expect(versions, contains('>=10.7.0'));
705
- });
706
- });
707
-
708
- group('semver normalization', () {
709
- test('invalidates all paths for 1.0.0', () async {
710
- final bundle = createBundle(
711
- 'production',
712
- 'ios',
713
- '1.0.0',
714
- 'semver-normalization-test',
715
- );
716
-
717
- store.invalidationCalls.clear();
718
- await plugin.appendBundle(bundle);
719
- await plugin.commitBundle();
720
-
721
- expect(
722
- store.invalidationCalls.any((p) => p.contains('app-version/ios/1.0.0')),
723
- isTrue,
724
- );
725
- expect(
726
- store.invalidationCalls.any((p) => p.contains('app-version/ios/1.0')),
727
- isTrue,
728
- );
729
- expect(
730
- store.invalidationCalls.any((p) => p.contains('app-version/ios/1/')),
731
- isTrue,
732
- );
733
- });
734
-
735
- test('only normalizes 2.x when patch=0', () async {
736
- final bundle = createBundle(
737
- 'production',
738
- 'android',
739
- '2.1.0',
740
- 'semver-norm-test-2',
741
- );
742
-
743
- store.invalidationCalls.clear();
744
- await plugin.appendBundle(bundle);
745
- await plugin.commitBundle();
746
-
747
- expect(
748
- store.invalidationCalls.any(
749
- (p) => p.contains('app-version/android/2.1.0'),
750
- ),
751
- isTrue,
752
- );
753
- expect(
754
- store.invalidationCalls.any(
755
- (p) => p.contains('app-version/android/2.1'),
756
- ),
757
- isTrue,
758
- );
759
- expect(
760
- store.invalidationCalls.any(
761
- (p) => RegExp(r'app-version/android/2/').hasMatch(p),
762
- ),
763
- isFalse,
764
- );
765
- });
766
-
767
- test('no extra normalization for 1.2.3', () async {
768
- final bundle = createBundle(
769
- 'production',
770
- 'ios',
771
- '1.2.3',
772
- 'no-normalization-test',
773
- );
774
-
775
- store.invalidationCalls.clear();
776
- await plugin.appendBundle(bundle);
777
- await plugin.commitBundle();
778
-
779
- expect(
780
- store.invalidationCalls.any(
781
- (p) => p == '/api/check-update/app-version/ios/1.2.3/production/*',
782
- ),
783
- isTrue,
784
- );
785
- expect(
786
- store.invalidationCalls.any(
787
- (p) => p == '/api/check-update/app-version/ios/1.2/production/*',
788
- ),
789
- isFalse,
790
- );
791
- expect(
792
- store.invalidationCalls.any(
793
- (p) => p == '/api/check-update/app-version/ios/1/production/*',
794
- ),
795
- isFalse,
796
- );
797
- });
798
- });
799
-
800
- group('issue #745 promotion scenario', () {
801
- test('updates target-app-versions when promoting channel', () async {
802
- final bundle = createBundle(
803
- 'test',
804
- 'android',
805
- '8.1.3',
806
- 'issue-745-promote-bundle',
807
- );
808
-
809
- await plugin.appendBundle(bundle);
810
- await plugin.commitBundle();
811
-
812
- store.invalidationCalls.clear();
813
-
814
- await plugin.updateBundle('issue-745-promote-bundle', {
815
- 'channel': 'prod',
816
- });
817
- await plugin.commitBundle();
818
-
819
- expect(
820
- store.invalidationCalls.any(
821
- (p) => p.contains('app-version/android/8.1.3/test'),
822
- ),
823
- isTrue,
824
- );
825
- expect(
826
- store.invalidationCalls.any(
827
- (p) => p.contains('app-version/android/8.1.3/prod'),
828
- ),
829
- isTrue,
830
- );
831
-
832
- final prodVersions = jsonDecode(
833
- store.data['prod/android/target-app-versions.json'] ?? '[]',
834
- ) as List;
835
- expect(prodVersions, contains('8.1.3'));
836
-
837
- final testVersions = jsonDecode(
838
- store.data['test/android/target-app-versions.json'] ?? '[]',
839
- ) as List;
840
- expect(testVersions, isNot(contains('8.1.3')));
841
- });
842
- });
843
- }