@_nazmiforreal/flutter-ota 0.1.12 → 0.1.13

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 (176) hide show
  1. package/README.md +1 -1
  2. package/bin/flutter-ota-linux-x64 +0 -0
  3. package/dart-src/packages/cli-tools/.dart_tool/package_config.json +46 -76
  4. package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +140 -166
  5. package/dart-src/packages/cli-tools/README.md +1 -1
  6. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +30 -17
  7. package/dart-src/packages/cli-tools/lib/src/backend.dart +30 -31
  8. package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -1
  9. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +44 -43
  10. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +265 -239
  11. package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +75 -66
  12. package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +39 -35
  13. package/dart-src/packages/cli-tools/lib/src/commands/console.dart +30 -27
  14. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +88 -72
  15. package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +66 -29
  16. package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +10 -6
  17. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +290 -181
  18. package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +24 -23
  19. package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +111 -97
  20. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +237 -0
  21. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +43 -34
  22. package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +57 -53
  23. package/dart-src/packages/cli-tools/lib/src/config.dart +227 -93
  24. package/dart-src/packages/cli-tools/lib/src/operations.dart +9 -8
  25. package/dart-src/packages/cli-tools/lib/src/pack.dart +26 -25
  26. package/dart-src/packages/cli-tools/lib/src/pocketbase/data_bootstrap.dart +39 -0
  27. package/dart-src/packages/cli-tools/lib/src/pocketbase/hooks/bundles.pb.js +56 -0
  28. package/dart-src/packages/cli-tools/lib/src/pocketbase/installer.dart +189 -0
  29. package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +123 -0
  30. package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +237 -0
  31. package/dart-src/packages/cli-tools/lib/src/runner.dart +36 -15
  32. package/dart-src/packages/cli-tools/lib/src/sign.dart +13 -5
  33. package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +10 -13
  34. package/dart-src/packages/cli-tools/lib/src/util.dart +3 -1
  35. package/dart-src/packages/cli-tools/pubspec.lock +69 -116
  36. package/dart-src/packages/cli-tools/pubspec.yaml +25 -7
  37. package/dart-src/packages/cli-tools/test/backends_test.dart +62 -32
  38. package/dart-src/packages/cli-tools/test/build_pack_test.dart +4 -4
  39. package/dart-src/packages/cli-tools/test/cli_test.dart +89 -42
  40. package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +8 -13
  41. package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +23 -18
  42. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +197 -0
  43. package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +70 -46
  44. package/dart-src/packages/cli-tools/test/pocketbase_installer_test.dart +72 -0
  45. package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +107 -0
  46. package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +59 -0
  47. package/dart-src/packages/core/README.md +2 -2
  48. package/dart-src/packages/core/example/lib/main.dart +69 -0
  49. package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +1 -1
  50. package/dart-src/packages/core/lib/src/app_update_info.dart +17 -15
  51. package/dart-src/packages/core/lib/src/bundle.dart +102 -94
  52. package/dart-src/packages/core/lib/src/bundle_artifacts.dart +1 -2
  53. package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +15 -16
  54. package/dart-src/packages/core/lib/src/changed_asset.dart +24 -22
  55. package/dart-src/packages/core/lib/src/get_bundles_args.dart +13 -13
  56. package/dart-src/packages/core/lib/src/metadata.dart +9 -7
  57. package/dart-src/packages/core/lib/src/patch_info.dart +8 -7
  58. package/dart-src/packages/core/lib/src/platform.dart +4 -3
  59. package/dart-src/packages/core/lib/src/rollout.dart +14 -11
  60. package/dart-src/packages/core/lib/src/semver_range.dart +49 -39
  61. package/dart-src/packages/core/lib/src/semver_version.dart +8 -7
  62. package/dart-src/packages/core/lib/src/status.dart +4 -3
  63. package/dart-src/packages/core/lib/src/update_bundle_params.dart +7 -7
  64. package/dart-src/packages/core/lib/src/uuid.dart +1 -2
  65. package/dart-src/packages/core/pubspec.yaml +6 -7
  66. package/dart-src/packages/core/test/rollout_test.dart +15 -8
  67. package/dart-src/packages/core/test/semver_test.dart +5 -2
  68. package/dart-src/packages/core/test/types_test.dart +26 -24
  69. package/dart-src/packages/core/test/uuid_test.dart +3 -1
  70. package/dart-src/packages/core/tool/dbg.dart +1 -0
  71. package/dart-src/plugins/aws/CHANGELOG.md +6 -0
  72. package/dart-src/plugins/aws/example/lib/main.dart +60 -0
  73. package/dart-src/plugins/aws/lib/flutter_ota_kit_aws.dart +5 -1
  74. package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +11 -16
  75. package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +7 -2
  76. package/dart-src/plugins/aws/lib/src/aws_database.dart +14 -18
  77. package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +23 -18
  78. package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +5 -4
  79. package/dart-src/plugins/aws/lib/src/aws_storage.dart +2 -3
  80. package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +10 -16
  81. package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +6 -9
  82. package/dart-src/plugins/aws/pubspec.yaml +15 -10
  83. package/dart-src/plugins/aws/test/aws_database_test.dart +51 -57
  84. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +8 -13
  85. package/dart-src/plugins/cloudflare/CHANGELOG.md +7 -0
  86. package/dart-src/plugins/cloudflare/example/lib/main.dart +62 -0
  87. package/dart-src/plugins/cloudflare/lib/flutter_ota_kit_cloudflare.dart +4 -2
  88. package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +3 -3
  89. package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +3 -2
  90. package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +24 -27
  91. package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +22 -30
  92. package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +1 -1
  93. package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +21 -29
  94. package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +7 -7
  95. package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +7 -15
  96. package/dart-src/plugins/cloudflare/pubspec.yaml +14 -9
  97. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +46 -42
  98. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +100 -90
  99. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +23 -18
  100. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +8 -13
  101. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +1 -4
  102. package/dart-src/plugins/plugin-core/CHANGELOG.md +16 -0
  103. package/dart-src/plugins/plugin-core/example/lib/main.dart +61 -0
  104. package/dart-src/plugins/plugin-core/lib/flutter_ota_kit_plugin_core.dart +3 -7
  105. package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +5 -5
  106. package/dart-src/plugins/plugin-core/lib/src/bundle_helpers.dart +23 -0
  107. package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +16 -10
  108. package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +31 -26
  109. package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +5 -1
  110. package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +1 -1
  111. package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +78 -71
  112. package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +56 -35
  113. package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +10 -13
  114. package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +30 -17
  115. package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +3 -1
  116. package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +5 -3
  117. package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +2 -4
  118. package/dart-src/plugins/plugin-core/lib/src/storage_helpers.dart +18 -0
  119. package/dart-src/plugins/plugin-core/lib/src/types.dart +7 -6
  120. package/dart-src/plugins/plugin-core/pubspec.yaml +12 -9
  121. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +13 -9
  122. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +28 -15
  123. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +254 -122
  124. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +36 -32
  125. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +18 -15
  126. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +8 -3
  127. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +682 -693
  128. package/dart-src/plugins/plugin-core/test/helpers_test.dart +45 -21
  129. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +76 -13
  130. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +20 -9
  131. package/dart-src/plugins/pocketbase/CHANGELOG.md +8 -0
  132. package/dart-src/plugins/pocketbase/LICENSE +21 -0
  133. package/dart-src/plugins/pocketbase/README.md +54 -0
  134. package/dart-src/plugins/pocketbase/example/lib/main.dart +62 -0
  135. package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +28 -0
  136. package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +121 -0
  137. package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +349 -0
  138. package/dart-src/plugins/pocketbase/lib/src/pocketbase_config.dart +61 -0
  139. package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +278 -0
  140. package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +255 -0
  141. package/dart-src/plugins/pocketbase/pubspec.yaml +26 -0
  142. package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +12 -0
  143. package/dart-src/plugins/postgres/CHANGELOG.md +6 -0
  144. package/dart-src/plugins/postgres/example/lib/main.dart +67 -0
  145. package/dart-src/plugins/postgres/lib/flutter_ota_kit_postgres.dart +3 -6
  146. package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +32 -52
  147. package/dart-src/plugins/postgres/lib/src/postgres_config.dart +3 -1
  148. package/dart-src/plugins/postgres/lib/src/postgres_database.dart +28 -38
  149. package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +16 -14
  150. package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +26 -14
  151. package/dart-src/plugins/postgres/lib/src/postgres_types.dart +27 -28
  152. package/dart-src/plugins/postgres/pubspec.yaml +14 -9
  153. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +62 -39
  154. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +3 -4
  155. package/dart-src/plugins/postgres/test/postgres_database_test.dart +102 -73
  156. package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +60 -0
  157. package/dart-src/plugins/supabase/CHANGELOG.md +17 -0
  158. package/dart-src/plugins/supabase/README.md +2 -2
  159. package/dart-src/plugins/supabase/example/lib/main.dart +63 -0
  160. package/dart-src/plugins/supabase/lib/flutter_ota_kit_supabase.dart +2 -0
  161. package/dart-src/plugins/supabase/lib/src/error_message.dart +11 -0
  162. package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +34 -58
  163. package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +3 -13
  164. package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +34 -52
  165. package/dart-src/plugins/supabase/lib/src/supabase_database.dart +38 -30
  166. package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +21 -29
  167. package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +7 -19
  168. package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +18 -29
  169. package/dart-src/plugins/supabase/lib/src/types.dart +26 -26
  170. package/dart-src/plugins/supabase/pubspec.yaml +14 -9
  171. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +57 -57
  172. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +30 -27
  173. package/dart-src/plugins/supabase/test/supabase_database_test.dart +26 -35
  174. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +27 -34
  175. package/package.json +3 -3
  176. package/scripts/vendor.sh +1 -1
@@ -14,7 +14,8 @@ const _defaultBundleFields = <String, Object?>{
14
14
  'message': null,
15
15
  'enabled': true,
16
16
  'shouldForceUpdate': false,
17
- 'storageUri': 'storage://my-app/00000000-0000-0000-0000-000000000000/bundle.zip',
17
+ 'storageUri':
18
+ 'storage://my-app/00000000-0000-0000-0000-000000000000/bundle.zip',
18
19
  'fingerprintHash': null,
19
20
  };
20
21
 
@@ -127,8 +128,7 @@ void _seedUpdateManifests(_FakeBlobStore store, List<Bundle> bundles) {
127
128
  }
128
129
 
129
130
  for (final entry in bundlesByKey.entries) {
130
- final sorted = entry.value
131
- ..sort((a, b) => b.id.compareTo(a.id));
131
+ final sorted = entry.value..sort((a, b) => b.id.compareTo(a.id));
132
132
  store.data[entry.key] = jsonEncode(sorted.map((b) => b.toJson()).toList());
133
133
  }
134
134
 
@@ -159,10 +159,18 @@ void main() {
159
159
 
160
160
  group('update checks', () {
161
161
  test('uses direct app-version manifests for update checks', () async {
162
- final latest = createBundle('production', 'ios', '*',
163
- '00000000-0000-0000-0000-000000000002');
164
- final previous = createBundle('production', 'ios', '*',
165
- '00000000-0000-0000-0000-000000000001');
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
+ );
166
174
 
167
175
  _seedUpdateManifests(store, [previous, latest]);
168
176
 
@@ -217,13 +225,20 @@ void main() {
217
225
 
218
226
  test('respects cohort eligibility', () async {
219
227
  final gatedBundle = Bundle.fromJson({
220
- ...createBundle('production', 'ios', '*',
221
- '00000000-0000-0000-0000-000000000021')
222
- .toJson(),
228
+ ...createBundle(
229
+ 'production',
230
+ 'ios',
231
+ '*',
232
+ '00000000-0000-0000-0000-000000000021',
233
+ ).toJson(),
223
234
  'targetCohorts': ['beta'],
224
235
  });
225
- final fallback = createBundle('production', 'ios', '*',
226
- '00000000-0000-0000-0000-000000000020');
236
+ final fallback = createBundle(
237
+ 'production',
238
+ 'ios',
239
+ '*',
240
+ '00000000-0000-0000-0000-000000000020',
241
+ );
227
242
  final fallbackWithCohort = Bundle.fromJson({
228
243
  ...fallback.toJson(),
229
244
  'targetCohorts': ['stable'],
@@ -260,8 +275,12 @@ void main() {
260
275
 
261
276
  group('append and commit', () {
262
277
  test('appends a new bundle and commits to blob store', () async {
263
- final bundle = createBundle('production', 'ios', '1.0.0',
264
- '00000000-0000-0000-0000-000000000001');
278
+ final bundle = createBundle(
279
+ 'production',
280
+ 'ios',
281
+ '1.0.0',
282
+ '00000000-0000-0000-0000-000000000001',
283
+ );
265
284
 
266
285
  await plugin.appendBundle(bundle);
267
286
  await plugin.commitBundle();
@@ -273,14 +292,19 @@ void main() {
273
292
  expect(stored.length, equals(1));
274
293
 
275
294
  final fetched = await plugin.getBundleById(
276
- '00000000-0000-0000-0000-000000000001');
295
+ '00000000-0000-0000-0000-000000000001',
296
+ );
277
297
  expect(fetched, isNotNull);
278
298
  expect(fetched!.id, equals(bundle.id));
279
299
  });
280
300
 
281
301
  test('does not write to store until commitBundle', () async {
282
- final bundle = createBundle('production', 'ios', '1.0.0',
283
- '00000000-0000-0000-0000-000000000010');
302
+ final bundle = createBundle(
303
+ 'production',
304
+ 'ios',
305
+ '1.0.0',
306
+ '00000000-0000-0000-0000-000000000010',
307
+ );
284
308
 
285
309
  expect(store.data, isEmpty);
286
310
 
@@ -288,35 +312,46 @@ void main() {
288
312
  expect(store.data, isEmpty);
289
313
 
290
314
  await plugin.commitBundle();
291
- expect(store.data.containsKey('production/ios/1.0.0/update.json'), isTrue);
315
+ expect(
316
+ store.data.containsKey('production/ios/1.0.0/update.json'),
317
+ isTrue,
318
+ );
292
319
  });
293
320
 
294
321
  test('appends multiple bundles to correct keys', () async {
295
322
  final b1 = createBundle('production', 'ios', '1.0.0', 'multi-1');
296
- final b2 =
297
- createBundle('production', 'android', '2.0.0', 'multi-2');
323
+ final b2 = createBundle('production', 'android', '2.0.0', 'multi-2');
298
324
 
299
325
  await plugin.appendBundle(b1);
300
326
  await plugin.appendBundle(b2);
301
327
  await plugin.commitBundle();
302
328
 
303
- expect(store.data.containsKey('production/ios/1.0.0/update.json'), isTrue);
304
- expect(store.data.containsKey('production/android/2.0.0/update.json'),
305
- isTrue);
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
+ );
306
337
  });
307
338
  });
308
339
 
309
340
  group('update and delete', () {
310
341
  test('updates an existing bundle', () async {
311
- final bundle = createBundle('production', 'android', '2.0.0',
312
- '00000000-0000-0000-0000-000000000002');
342
+ final bundle = createBundle(
343
+ 'production',
344
+ 'android',
345
+ '2.0.0',
346
+ '00000000-0000-0000-0000-000000000002',
347
+ );
313
348
  final bundleKey = 'production/android/2.0.0/update.json';
314
349
  store.data[bundleKey] = jsonEncode([bundle.toJson()]);
315
350
 
316
- await plugin.getBundles(
317
- const DatabaseBundleQueryOptions(limit: 20));
318
- await plugin.updateBundle(
319
- '00000000-0000-0000-0000-000000000002', {'enabled': false});
351
+ await plugin.getBundles(const DatabaseBundleQueryOptions(limit: 20));
352
+ await plugin.updateBundle('00000000-0000-0000-0000-000000000002', {
353
+ 'enabled': false,
354
+ });
320
355
  await plugin.commitBundle();
321
356
 
322
357
  final updated = jsonDecode(store.data[bundleKey]!) as List;
@@ -332,10 +367,8 @@ void main() {
332
367
  });
333
368
 
334
369
  test('deletes a bundle successfully', () async {
335
- final b1 = createBundle(
336
- 'production', 'ios', '1.1.1', 'bundleX');
337
- final b2 = createBundle(
338
- 'production', 'android', '1.1.1', 'bundleY');
370
+ final b1 = createBundle('production', 'ios', '1.1.1', 'bundleX');
371
+ final b2 = createBundle('production', 'android', '1.1.1', 'bundleY');
339
372
 
340
373
  await plugin.appendBundle(b1);
341
374
  await plugin.appendBundle(b2);
@@ -351,14 +384,14 @@ void main() {
351
384
  });
352
385
 
353
386
  test('deletes entire update.json when no bundles remain', () async {
354
- final b1 = createBundle(
355
- 'production', 'ios', '1.1.1', 'bundleX');
387
+ final b1 = createBundle('production', 'ios', '1.1.1', 'bundleX');
356
388
 
357
389
  await plugin.appendBundle(b1);
358
390
  await plugin.commitBundle();
359
391
 
360
- final updateJsonKeys =
361
- store.data.keys.where((k) => k.contains('update.json')).toList();
392
+ final updateJsonKeys = store.data.keys
393
+ .where((k) => k.contains('update.json'))
394
+ .toList();
362
395
  expect(updateJsonKeys, isNotEmpty);
363
396
 
364
397
  await plugin.deleteBundle(b1);
@@ -368,10 +401,20 @@ void main() {
368
401
  });
369
402
 
370
403
  test('keeps update.json when other bundles remain', () async {
371
- final b1 = {..._defaultBundleFields, 'id': 'bundleA', 'channel': 'production',
372
- 'platform': 'ios', 'targetAppVersion': '1.1.1'};
373
- final b2 = {..._defaultBundleFields, 'id': 'bundleB', 'channel': 'production',
374
- 'platform': 'ios', 'targetAppVersion': '1.1.1'};
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
+ };
375
418
 
376
419
  final bundle1 = Bundle.fromJson(b1);
377
420
  final bundle2 = Bundle.fromJson(b2);
@@ -394,25 +437,51 @@ void main() {
394
437
  final keyNew = 'production/ios/1.0.2/update.json';
395
438
 
396
439
  final oldBundles = [
397
- createBundle('production', 'ios', '1.x.x',
398
- '00000000-0000-0000-0000-000000000003'),
399
- createBundle('production', 'ios', '1.x.x',
400
- '00000000-0000-0000-0000-000000000002'),
401
- createBundle('production', 'ios', '1.x.x',
402
- '00000000-0000-0000-0000-000000000001'),
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
+ ),
403
458
  ];
404
459
 
405
460
  final newBundles = [
406
- createBundle('production', 'ios', '1.0.2',
407
- '00000000-0000-0000-0000-000000000005'),
408
- createBundle('production', 'ios', '1.0.2',
409
- '00000000-0000-0000-0000-000000000004'),
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
+ ),
410
473
  ];
411
474
 
412
- store.data[keyOld] = jsonEncode(oldBundles.map((b) => b.toJson()).toList());
413
- store.data[keyNew] = jsonEncode(newBundles.map((b) => b.toJson()).toList());
414
- store.data['production/ios/target-app-versions.json'] =
415
- jsonEncode(['1.x.x', '1.0.2']);
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
+ ]);
416
485
 
417
486
  await plugin.getBundles(const DatabaseBundleQueryOptions(limit: 20));
418
487
 
@@ -445,8 +514,9 @@ void main() {
445
514
 
446
515
  group('getBundles', () {
447
516
  test('returns empty for no data', () async {
448
- final result =
449
- await plugin.getBundles(const DatabaseBundleQueryOptions(limit: 20));
517
+ final result = await plugin.getBundles(
518
+ const DatabaseBundleQueryOptions(limit: 20),
519
+ );
450
520
  expect(result.data, isEmpty);
451
521
  expect(result.pagination.total, equals(0));
452
522
  });
@@ -456,13 +526,17 @@ void main() {
456
526
  final bB = createBundle('production', 'ios', '1.0.0', 'B');
457
527
  final bC = createBundle('production', 'ios', '2.0.0', 'C');
458
528
 
459
- store.data['production/ios/1.0.0/update.json'] =
460
- jsonEncode([bB.toJson(), bA.toJson()]);
461
- store.data['production/ios/2.0.0/update.json'] =
462
- jsonEncode([bC.toJson()]);
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
+ ]);
463
536
 
464
- final result =
465
- await plugin.getBundles(const DatabaseBundleQueryOptions(limit: 20));
537
+ final result = await plugin.getBundles(
538
+ const DatabaseBundleQueryOptions(limit: 20),
539
+ );
466
540
  expect(result.data.length, equals(3));
467
541
  expect(result.data[0].id, 'C');
468
542
  expect(result.data[1].id, 'B');
@@ -471,21 +545,29 @@ void main() {
471
545
 
472
546
  test('filters by channel and platform', () async {
473
547
  final ios = createBundle('production', 'ios', '1.0.0', 'ios-1');
474
- final android =
475
- createBundle('production', 'android', '1.0.0', 'android-1');
476
-
477
- store.data['production/ios/1.0.0/update.json'] =
478
- jsonEncode([ios.toJson()]);
479
- store.data['production/android/1.0.0/update.json'] =
480
- jsonEncode([android.toJson()]);
481
-
482
- final result = await plugin.getBundles(DatabaseBundleQueryOptions(
483
- limit: 20,
484
- where: const DatabaseBundleQueryWhere(
485
- channel: 'production',
486
- platform: Platform.ios,
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
+ ),
487
569
  ),
488
- ));
570
+ );
489
571
  expect(result.data.length, equals(1));
490
572
  expect(result.data[0].id, 'ios-1');
491
573
  });
@@ -493,16 +575,21 @@ void main() {
493
575
 
494
576
  group('CloudFront invalidation', () {
495
577
  test('invalidates correct paths on new bundle', () async {
496
- final bundle = createBundle('production', 'ios', '1.0.0',
497
- 'cloudfront-new-test');
578
+ final bundle = createBundle(
579
+ 'production',
580
+ 'ios',
581
+ '1.0.0',
582
+ 'cloudfront-new-test',
583
+ );
498
584
 
499
585
  await plugin.appendBundle(bundle);
500
586
  await plugin.commitBundle();
501
587
 
502
588
  expect(store.invalidationCalls, isNotEmpty);
503
589
  expect(
504
- store.invalidationCalls
505
- .any((p) => p.contains('app-version') && p.contains('1.0.0')),
590
+ store.invalidationCalls.any(
591
+ (p) => p.contains('app-version') && p.contains('1.0.0'),
592
+ ),
506
593
  isTrue,
507
594
  );
508
595
  expect(
@@ -512,16 +599,21 @@ void main() {
512
599
  });
513
600
 
514
601
  test('invalidates semver pattern path', () async {
515
- final bundle = createBundle('production', 'ios', '3.0.x',
516
- 'cloudfront-semver');
602
+ final bundle = createBundle(
603
+ 'production',
604
+ 'ios',
605
+ '3.0.x',
606
+ 'cloudfront-semver',
607
+ );
517
608
 
518
609
  store.invalidationCalls.clear();
519
610
  await plugin.appendBundle(bundle);
520
611
  await plugin.commitBundle();
521
612
 
522
613
  expect(
523
- store.invalidationCalls
524
- .any((p) => p == '/api/check-update/app-version/ios/*'),
614
+ store.invalidationCalls.any(
615
+ (p) => p == '/api/check-update/app-version/ios/*',
616
+ ),
525
617
  isTrue,
526
618
  );
527
619
  });
@@ -535,26 +627,37 @@ void main() {
535
627
 
536
628
  group('targetAppVersion with spaces', () {
537
629
  test('normalizes space-containing targetAppVersion', () async {
538
- final bundle = createBundle('production', 'ios', '>= 10.7.0',
539
- 'space-normalization-test');
630
+ final bundle = createBundle(
631
+ 'production',
632
+ 'ios',
633
+ '>= 10.7.0',
634
+ 'space-normalization-test',
635
+ );
540
636
 
541
637
  await plugin.appendBundle(bundle);
542
638
  await plugin.commitBundle();
543
639
 
544
- expect(store.data.containsKey('production/ios/>=10.7.0/update.json'),
545
- isTrue);
640
+ expect(
641
+ store.data.containsKey('production/ios/>=10.7.0/update.json'),
642
+ isTrue,
643
+ );
546
644
  });
547
645
 
548
646
  test('normalizes multiple spaces', () async {
549
- final bundle = createBundle('production', 'android', '> 1.0.0 < 2.0.0',
550
- 'multi-space-test');
647
+ final bundle = createBundle(
648
+ 'production',
649
+ 'android',
650
+ '> 1.0.0 < 2.0.0',
651
+ 'multi-space-test',
652
+ );
551
653
 
552
654
  await plugin.appendBundle(bundle);
553
655
  await plugin.commitBundle();
554
656
 
555
657
  expect(
556
- store.data.containsKey('production/android/>1.0.0 <2.0.0/update.json'),
557
- isTrue);
658
+ store.data.containsKey('production/android/>1.0.0 <2.0.0/update.json'),
659
+ isTrue,
660
+ );
558
661
  });
559
662
 
560
663
  test('handles various semver range formats', () async {
@@ -585,30 +688,38 @@ void main() {
585
688
  });
586
689
 
587
690
  test('normalizes target-app-versions.json', () async {
588
- final bundle = createBundle('production', 'ios', '>= 10.7.0',
589
- 'target-versions-test');
691
+ final bundle = createBundle(
692
+ 'production',
693
+ 'ios',
694
+ '>= 10.7.0',
695
+ 'target-versions-test',
696
+ );
590
697
 
591
698
  await plugin.appendBundle(bundle);
592
699
  await plugin.commitBundle();
593
700
 
594
701
  final versions = jsonDecode(
595
- store.data['production/ios/target-app-versions.json']!) as List;
702
+ store.data['production/ios/target-app-versions.json']!,
703
+ ) as List;
596
704
  expect(versions, contains('>=10.7.0'));
597
705
  });
598
706
  });
599
707
 
600
708
  group('semver normalization', () {
601
709
  test('invalidates all paths for 1.0.0', () async {
602
- final bundle = createBundle('production', 'ios', '1.0.0',
603
- 'semver-normalization-test');
710
+ final bundle = createBundle(
711
+ 'production',
712
+ 'ios',
713
+ '1.0.0',
714
+ 'semver-normalization-test',
715
+ );
604
716
 
605
717
  store.invalidationCalls.clear();
606
718
  await plugin.appendBundle(bundle);
607
719
  await plugin.commitBundle();
608
720
 
609
721
  expect(
610
- store.invalidationCalls
611
- .any((p) => p.contains('app-version/ios/1.0.0')),
722
+ store.invalidationCalls.any((p) => p.contains('app-version/ios/1.0.0')),
612
723
  isTrue,
613
724
  );
614
725
  expect(
@@ -622,32 +733,44 @@ void main() {
622
733
  });
623
734
 
624
735
  test('only normalizes 2.x when patch=0', () async {
625
- final bundle = createBundle('production', 'android', '2.1.0',
626
- 'semver-norm-test-2');
736
+ final bundle = createBundle(
737
+ 'production',
738
+ 'android',
739
+ '2.1.0',
740
+ 'semver-norm-test-2',
741
+ );
627
742
 
628
743
  store.invalidationCalls.clear();
629
744
  await plugin.appendBundle(bundle);
630
745
  await plugin.commitBundle();
631
746
 
632
747
  expect(
633
- store.invalidationCalls
634
- .any((p) => p.contains('app-version/android/2.1.0')),
748
+ store.invalidationCalls.any(
749
+ (p) => p.contains('app-version/android/2.1.0'),
750
+ ),
635
751
  isTrue,
636
752
  );
637
753
  expect(
638
- store.invalidationCalls.any((p) => p.contains('app-version/android/2.1')),
754
+ store.invalidationCalls.any(
755
+ (p) => p.contains('app-version/android/2.1'),
756
+ ),
639
757
  isTrue,
640
758
  );
641
759
  expect(
642
- store.invalidationCalls
643
- .any((p) => RegExp(r'app-version/android/2/').hasMatch(p)),
760
+ store.invalidationCalls.any(
761
+ (p) => RegExp(r'app-version/android/2/').hasMatch(p),
762
+ ),
644
763
  isFalse,
645
764
  );
646
765
  });
647
766
 
648
767
  test('no extra normalization for 1.2.3', () async {
649
- final bundle = createBundle('production', 'ios', '1.2.3',
650
- 'no-normalization-test');
768
+ final bundle = createBundle(
769
+ 'production',
770
+ 'ios',
771
+ '1.2.3',
772
+ 'no-normalization-test',
773
+ );
651
774
 
652
775
  store.invalidationCalls.clear();
653
776
  await plugin.appendBundle(bundle);
@@ -655,17 +778,20 @@ void main() {
655
778
 
656
779
  expect(
657
780
  store.invalidationCalls.any(
658
- (p) => p == '/api/check-update/app-version/ios/1.2.3/production/*'),
781
+ (p) => p == '/api/check-update/app-version/ios/1.2.3/production/*',
782
+ ),
659
783
  isTrue,
660
784
  );
661
785
  expect(
662
786
  store.invalidationCalls.any(
663
- (p) => p == '/api/check-update/app-version/ios/1.2/production/*'),
787
+ (p) => p == '/api/check-update/app-version/ios/1.2/production/*',
788
+ ),
664
789
  isFalse,
665
790
  );
666
791
  expect(
667
792
  store.invalidationCalls.any(
668
- (p) => p == '/api/check-update/app-version/ios/1/production/*'),
793
+ (p) => p == '/api/check-update/app-version/ios/1/production/*',
794
+ ),
669
795
  isFalse,
670
796
  );
671
797
  });
@@ -673,8 +799,12 @@ void main() {
673
799
 
674
800
  group('issue #745 promotion scenario', () {
675
801
  test('updates target-app-versions when promoting channel', () async {
676
- final bundle = createBundle('test', 'android', '8.1.3',
677
- 'issue-745-promote-bundle');
802
+ final bundle = createBundle(
803
+ 'test',
804
+ 'android',
805
+ '8.1.3',
806
+ 'issue-745-promote-bundle',
807
+ );
678
808
 
679
809
  await plugin.appendBundle(bundle);
680
810
  await plugin.commitBundle();
@@ -687,24 +817,26 @@ void main() {
687
817
  await plugin.commitBundle();
688
818
 
689
819
  expect(
690
- store.invalidationCalls
691
- .any((p) => p.contains('app-version/android/8.1.3/test')),
820
+ store.invalidationCalls.any(
821
+ (p) => p.contains('app-version/android/8.1.3/test'),
822
+ ),
692
823
  isTrue,
693
824
  );
694
825
  expect(
695
- store.invalidationCalls
696
- .any((p) => p.contains('app-version/android/8.1.3/prod')),
826
+ store.invalidationCalls.any(
827
+ (p) => p.contains('app-version/android/8.1.3/prod'),
828
+ ),
697
829
  isTrue,
698
830
  );
699
831
 
700
832
  final prodVersions = jsonDecode(
701
- store.data['prod/android/target-app-versions.json'] ?? '[]')
702
- as List;
833
+ store.data['prod/android/target-app-versions.json'] ?? '[]',
834
+ ) as List;
703
835
  expect(prodVersions, contains('8.1.3'));
704
836
 
705
837
  final testVersions = jsonDecode(
706
- store.data['test/android/target-app-versions.json'] ?? '[]')
707
- as List;
838
+ store.data['test/android/target-app-versions.json'] ?? '[]',
839
+ ) as List;
708
840
  expect(testVersions, isNot(contains('8.1.3')));
709
841
  });
710
842
  });