@_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
@@ -3,7 +3,11 @@ import 'types.dart';
3
3
  /// Calculate pagination metadata from total count, limit, and offset.
4
4
  ///
5
5
  /// Faithful port of hot-updater `calculatePagination.ts`.
6
- PaginationInfo calculatePagination(int total, {required int limit, required int offset}) {
6
+ PaginationInfo calculatePagination(
7
+ int total, {
8
+ required int limit,
9
+ required int offset,
10
+ }) {
7
11
  if (total == 0) {
8
12
  return const PaginationInfo(
9
13
  total: 0,
@@ -2,7 +2,7 @@ const String contentAddressedAssetPrefix = 'assets';
2
2
 
3
3
  /// Build a content-addressed storage path for an asset.
4
4
  ///
5
- /// Format: `sha256/<hash[:2]>/<hash>.<ext>`
5
+ /// Format: `sha256/{hash[:2]}/{hash}.{ext}`
6
6
  /// Preserves `.br` brotli extension from the original asset path.
7
7
  ///
8
8
  /// Faithful port of hot-updater `contentAddressedAssets.ts`.
@@ -3,6 +3,7 @@ import 'dart:convert';
3
3
  import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
4
4
 
5
5
  import 'bundle_unit_of_work.dart';
6
+ import 'storage_helpers.dart' show updateJsonKeyRegex;
6
7
  import 'create_database_plugin.dart';
7
8
  import 'filter_compatible_app_versions.dart';
8
9
  import 'paginate_bundles.dart';
@@ -10,6 +11,11 @@ import 'query_bundles.dart';
10
11
  import 'resolve_update_info_from_bundles.dart';
11
12
  import 'types.dart';
12
13
 
14
+ /// Capture-group variant of [updateJsonKeyRegex] — extracts the channel.
15
+ final RegExp _updateJsonChannelRegex = RegExp(
16
+ r'^([^/]+)/(?:ios|android)/[^/]+/update\.json$',
17
+ );
18
+
13
19
  // ---------------------------------------------------------------------------
14
20
  // Internal types
15
21
  // ---------------------------------------------------------------------------
@@ -22,10 +28,7 @@ class _BundleWithKey {
22
28
  }
23
29
 
24
30
  class _TargetVersionMutation {
25
- _TargetVersionMutation({
26
- required this.channel,
27
- required this.platform,
28
- });
31
+ _TargetVersionMutation({required this.channel, required this.platform});
29
32
 
30
33
  final String channel;
31
34
  final String platform;
@@ -161,15 +164,16 @@ List<String> _getManagementListPrefixes(DatabaseBundleQueryWhere? where) {
161
164
  if (where?.channel != null &&
162
165
  where?.platform != null &&
163
166
  where?.targetAppVersion is String) {
164
- final normalized =
165
- normalizeTargetAppVersion(where!.targetAppVersion as String);
167
+ final normalized = normalizeTargetAppVersion(
168
+ where!.targetAppVersion as String,
169
+ );
166
170
  if (normalized != null) {
167
171
  return ['${where.channel}/${where.platform?.value}/$normalized/'];
168
172
  }
169
173
  }
170
174
 
171
175
  if (where?.channel != null && where?.platform != null) {
172
- return ['${where!.channel}/${where.platform?.value}/'];
176
+ return ['${where!.channel}/${where.platform?.value}/'];
173
177
  }
174
178
 
175
179
  if (where?.channel != null) {
@@ -180,13 +184,14 @@ List<String> _getManagementListPrefixes(DatabaseBundleQueryWhere? where) {
180
184
  }
181
185
 
182
186
  String? _getChannelFromUpdateJsonKey(String key) {
183
- final match =
184
- RegExp(r'^([^/]+)/(?:ios|android)/[^/]+/update\.json$').firstMatch(key);
187
+ final match = _updateJsonChannelRegex.firstMatch(key);
185
188
  return match?.group(1);
186
189
  }
187
190
 
188
- const _defaultDescOrder =
189
- DatabaseBundleQueryOrder(field: 'id', direction: 'desc');
191
+ const _defaultDescOrder = DatabaseBundleQueryOrder(
192
+ field: 'id',
193
+ direction: 'desc',
194
+ );
190
195
 
191
196
  List<Bundle> _sortManagedBundles(
192
197
  List<Bundle> bundles,
@@ -229,7 +234,7 @@ abstract class BlobOperations {
229
234
  // ---------------------------------------------------------------------------
230
235
 
231
236
  DatabasePlugin Function() Function(TConfig, [DatabasePluginHooks? hooks])
232
- createBlobDatabasePlugin<TConfig>({
237
+ createBlobDatabasePlugin<TConfig>({
233
238
  required String name,
234
239
  required BlobOperations Function(TConfig config) blobFactory,
235
240
  }) {
@@ -297,7 +302,8 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
297
302
  }
298
303
 
299
304
  Future<List<Bundle>> loadBundleObject(String key) async {
300
- final bundles = (await loadOptionalObject<List>(key))
305
+ final bundles =
306
+ (await loadOptionalObject<List>(key))
301
307
  ?.map((e) => Bundle.fromJson((e as Map).cast<String, dynamic>()))
302
308
  .toList() ??
303
309
  <Bundle>[];
@@ -312,16 +318,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
312
318
  pendingBundlesMap.clear();
313
319
  locallyDeletedBundleIds.clear();
314
320
 
315
- final updateJsonKeys = (await _mapWithConcurrency(
316
- prefixes,
317
- _storageOperationConcurrency,
318
- (prefix, _) => ops.listObjects(prefix),
319
- ))
320
- .expand((keys) => keys)
321
- .where((key) =>
322
- RegExp(r'^[^/]+/(?:ios|android)/[^/]+/update\.json$')
323
- .hasMatch(key))
324
- .toList();
321
+ final updateJsonKeys =
322
+ (await _mapWithConcurrency(
323
+ prefixes,
324
+ _storageOperationConcurrency,
325
+ (prefix, _) => ops.listObjects(prefix),
326
+ ))
327
+ .expand((keys) => keys)
328
+ .where((key) => updateJsonKeyRegex.hasMatch(key))
329
+ .toList();
325
330
 
326
331
  final allBundles = (await _mapWithConcurrency(
327
332
  updateJsonKeys,
@@ -332,9 +337,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
332
337
  .map((b) => _BundleWithKey(b, updateJsonKey: key))
333
338
  .toList();
334
339
  },
335
- ))
336
- .expand((b) => b)
337
- .toList();
340
+ )).expand((b) => b).toList();
338
341
 
339
342
  for (final entry in allBundles) {
340
343
  bundlesMap[entry.bundle.id] = entry;
@@ -404,13 +407,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
404
407
  '${mutation.channel}/${mutation.platform}/target-app-versions.json';
405
408
  final oldTargetVersions =
406
409
  (await loadOptionalObject<List>(targetKey))
407
- ?.map((e) => e.toString())
408
- .toList() ??
409
- <String>[];
410
+ ?.map((e) => e.toString())
411
+ .toList() ??
412
+ <String>[];
410
413
  final newTargetVersions = oldTargetVersions
411
- .where((v) =>
412
- !mutation.removals.contains(v) ||
413
- mutation.additions.contains(v))
414
+ .where(
415
+ (v) =>
416
+ !mutation.removals.contains(v) ||
417
+ mutation.additions.contains(v),
418
+ )
414
419
  .toList();
415
420
  for (final version in mutation.additions) {
416
421
  if (!newTargetVersions.contains(version)) {
@@ -437,8 +442,9 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
437
442
  return;
438
443
  }
439
444
  for (final v in _getSemverNormalizedVersions(targetAppVersion)) {
440
- pathsToInvalidate
441
- .add('${ops.apiBasePath}/app-version/$platform/$v/$channel/*');
445
+ pathsToInvalidate.add(
446
+ '${ops.apiBasePath}/app-version/$platform/$v/$channel/*',
447
+ );
442
448
  }
443
449
  }
444
450
 
@@ -474,14 +480,17 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
474
480
  final channel = args.channel;
475
481
  final platform = args.platform;
476
482
 
477
- final targetVersionsKey = '$channel/${platform.value}/target-app-versions.json';
483
+ final targetVersionsKey =
484
+ '$channel/${platform.value}/target-app-versions.json';
478
485
  final targetAppVersions =
479
486
  (await loadOptionalObject<List>(targetVersionsKey))
480
- ?.map((e) => e.toString())
481
- .toList() ??
482
- <String>[];
483
- final matchingVersions =
484
- filterCompatibleAppVersions(targetAppVersions, args.appVersion);
487
+ ?.map((e) => e.toString())
488
+ .toList() ??
489
+ <String>[];
490
+ final matchingVersions = filterCompatibleAppVersions(
491
+ targetAppVersions,
492
+ args.appVersion,
493
+ );
485
494
 
486
495
  final bundles = (await _mapWithConcurrency(
487
496
  matchingVersions,
@@ -491,9 +500,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
491
500
  normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion;
492
501
  return loadBundleObject('$channel/${platform.value}/$nv/update.json');
493
502
  },
494
- ))
495
- .expand((b) => b)
496
- .toList();
503
+ )).expand((b) => b).toList();
497
504
 
498
505
  return resolveUpdateInfoFromBundles(
499
506
  ResolveUpdateInfoFromBundlesOptions(
@@ -571,8 +578,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
571
578
  Future<Paginated<List<Bundle>>> getBundles(
572
579
  DatabaseBundleQueryOptions options,
573
580
  ) async {
574
- var allBundles =
575
- await loadAllBundlesForManagementFallback(options.where);
581
+ var allBundles = await loadAllBundlesForManagementFallback(options.where);
576
582
  if (options.where != null) {
577
583
  allBundles = allBundles
578
584
  .where((b) => bundleMatchesQueryWhere(b, options.where))
@@ -581,7 +587,8 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
581
587
  return paginateBundles(
582
588
  bundles: allBundles,
583
589
  limit: options.limit,
584
- offset: options.offset ??
590
+ offset:
591
+ options.offset ??
585
592
  (options.page != null ? (options.page! - 1) * options.limit : null),
586
593
  cursor: options.cursor,
587
594
  orderBy: options.orderBy,
@@ -590,19 +597,18 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
590
597
 
591
598
  @override
592
599
  Future<List<String>> getChannels() async {
593
- final channels = (await ops.listObjects(''))
594
- .map(_getChannelFromUpdateJsonKey)
595
- .whereType<String>()
596
- .toSet()
597
- .toList()
598
- ..sort();
600
+ final channels =
601
+ (await ops.listObjects(''))
602
+ .map(_getChannelFromUpdateJsonKey)
603
+ .whereType<String>()
604
+ .toSet()
605
+ .toList()
606
+ ..sort();
599
607
  return channels;
600
608
  }
601
609
 
602
610
  @override
603
- Future<void> commitBundle({
604
- required List<BundleChange> changedSets,
605
- }) async {
611
+ Future<void> commitBundle({required List<BundleChange> changedSets}) async {
606
612
  if (changedSets.isEmpty) return;
607
613
 
608
614
  for (final change in changedSets) {
@@ -686,10 +692,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
686
692
  if (change.operation == BundleChangeOperation.update) {
687
693
  final merged = _mergeBundleUpdate(bk.bundle, data.toJson());
688
694
  final newKey =
689
- '${merged.channel}/${merged.platform.value}/${_resolveStorageTarget(
690
- targetAppVersion: merged.targetAppVersion,
691
- fingerprintHash: merged.fingerprintHash,
692
- )}/update.json';
695
+ '${merged.channel}/${merged.platform.value}/${_resolveStorageTarget(targetAppVersion: merged.targetAppVersion, fingerprintHash: merged.fingerprintHash)}/update.json';
693
696
 
694
697
  if (newKey != bk.updateJsonKey) {
695
698
  // Key changed — remove from old, add to new.
@@ -791,13 +794,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
791
794
  (oldKey, _) async {
792
795
  final currentBundles =
793
796
  (await loadOptionalObject<List>(oldKey))
794
- ?.map((e) =>
795
- Bundle.fromJson((e as Map).cast<String, dynamic>()))
796
- .toList() ??
797
- <Bundle>[];
797
+ ?.map(
798
+ (e) => Bundle.fromJson((e as Map).cast<String, dynamic>()),
799
+ )
800
+ .toList() ??
801
+ <Bundle>[];
798
802
  final removalIds = removalsByKey[oldKey]!;
799
- final updatedBundles =
800
- currentBundles.where((b) => !removalIds.contains(b.id)).toList();
803
+ final updatedBundles = currentBundles
804
+ .where((b) => !removalIds.contains(b.id))
805
+ .toList();
801
806
  updatedBundles.sort((a, b) => b.id.compareTo(a.id));
802
807
 
803
808
  if (updatedBundles.isEmpty) {
@@ -818,13 +823,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
818
823
  (key, _) async {
819
824
  final currentBundles =
820
825
  (await loadOptionalObject<List>(key))
821
- ?.map((e) =>
822
- Bundle.fromJson((e as Map).cast<String, dynamic>()))
823
- .toList() ??
824
- <Bundle>[];
826
+ ?.map(
827
+ (e) => Bundle.fromJson((e as Map).cast<String, dynamic>()),
828
+ )
829
+ .toList() ??
830
+ <Bundle>[];
825
831
  for (final changedBundle in changedBundlesByKey[key]!) {
826
- final idx =
827
- currentBundles.indexWhere((b) => b.id == changedBundle.id);
832
+ final idx = currentBundles.indexWhere(
833
+ (b) => b.id == changedBundle.id,
834
+ );
828
835
  if (idx >= 0) {
829
836
  currentBundles[idx] = changedBundle;
830
837
  } else {
@@ -4,8 +4,10 @@ import 'calculate_pagination.dart';
4
4
  import 'types.dart';
5
5
 
6
6
  const List<String> _replaceOnUpdateKeys = ['patches', 'targetCohorts'];
7
- const DatabaseBundleQueryOrder _defaultDescOrder =
8
- DatabaseBundleQueryOrder(field: 'id', direction: 'desc');
7
+ const DatabaseBundleQueryOrder _defaultDescOrder = DatabaseBundleQueryOrder(
8
+ field: 'id',
9
+ direction: 'desc',
10
+ );
9
11
 
10
12
  int? _normalizePage(int? value) {
11
13
  if (value == null || value < 1) return null;
@@ -134,11 +136,17 @@ Paginated<List<Bundle>> _createPaginatedResult(
134
136
  int startIndex,
135
137
  List<Bundle> data,
136
138
  ) {
137
- final pagination = calculatePagination(total, limit: limit, offset: startIndex);
138
- final nextCursor =
139
- data.isNotEmpty && startIndex + data.length < total ? data.last.id : null;
140
- final previousCursor =
141
- data.isNotEmpty && startIndex > 0 ? data.first.id : null;
139
+ final pagination = calculatePagination(
140
+ total,
141
+ limit: limit,
142
+ offset: startIndex,
143
+ );
144
+ final nextCursor = data.isNotEmpty && startIndex + data.length < total
145
+ ? data.last.id
146
+ : null;
147
+ final previousCursor = data.isNotEmpty && startIndex > 0
148
+ ? data.first.id
149
+ : null;
142
150
 
143
151
  return Paginated(
144
152
  data: data,
@@ -204,9 +212,7 @@ abstract class AbstractDatabasePlugin {
204
212
 
205
213
  Future<List<String>> getChannels();
206
214
 
207
- Future<void> commitBundle({
208
- required List<BundleChange> changedSets,
209
- });
215
+ Future<void> commitBundle({required List<BundleChange> changedSets});
210
216
 
211
217
  Future<void> onUnmount();
212
218
  }
@@ -237,7 +243,7 @@ class CreateDatabasePluginOptions<TConfig> {
237
243
  /// final db = createDb();
238
244
  /// ```
239
245
  DatabasePlugin Function() Function(TConfig config, [DatabasePluginHooks? hooks])
240
- createDatabasePlugin<TConfig>({
246
+ createDatabasePlugin<TConfig>({
241
247
  required String name,
242
248
  required AbstractDatabasePlugin Function(TConfig config) factory,
243
249
  }) {
@@ -251,14 +257,12 @@ DatabasePlugin Function() Function(TConfig config, [DatabasePluginHooks? hooks])
251
257
  return () {
252
258
  final instanceUnitOfWork = BundleUnitOfWork();
253
259
 
254
- BundleUnitOfWork getMutationUnitOfWork(
255
- Map<String, Object?>? context) =>
260
+ BundleUnitOfWork getMutationUnitOfWork(Map<String, Object?>? context) =>
256
261
  getRequestBundleUnitOfWork(context) ?? instanceUnitOfWork;
257
262
 
258
263
  Future<Paginated<List<Bundle>>> runGetBundles(
259
264
  DatabaseBundleQueryOptions options,
260
- ) =>
261
- getMethods().getBundles(options);
265
+ ) => getMethods().getBundles(options);
262
266
 
263
267
  Future<Paginated<List<Bundle>>> getBundlesWithLegacyCursorFallback(
264
268
  DatabaseBundleQueryOptions options,
@@ -285,7 +289,12 @@ DatabasePlugin Function() Function(TConfig config, [DatabasePluginHooks? hooks])
285
289
  orderBy: orderBy,
286
290
  ),
287
291
  );
288
- return _createPaginatedResult(total, options.limit, 0, firstPage.data);
292
+ return _createPaginatedResult(
293
+ total,
294
+ options.limit,
295
+ 0,
296
+ firstPage.data,
297
+ );
289
298
  }
290
299
 
291
300
  final cursorQuery = _buildCursorPageQuery(
@@ -370,9 +379,9 @@ class _DatabasePluginImpl implements DatabasePlugin {
370
379
  final BundleUnitOfWork Function(Map<String, Object?>?) getMutationUnitOfWork;
371
380
  final BundleUnitOfWork instanceUnitOfWork;
372
381
  final Future<Paginated<List<Bundle>>> Function(DatabaseBundleQueryOptions)
373
- runGetBundles;
382
+ runGetBundles;
374
383
  final Future<Paginated<List<Bundle>>> Function(DatabaseBundleQueryOptions)
375
- getBundlesWithLegacyCursorFallback;
384
+ getBundlesWithLegacyCursorFallback;
376
385
  final DatabasePluginHooks? hooks;
377
386
 
378
387
  @override
@@ -383,11 +392,14 @@ class _DatabasePluginImpl implements DatabasePlugin {
383
392
 
384
393
  @override
385
394
  Future<Bundle?> getBundleById(String bundleId) async {
386
- final requestUoW =
387
- getRequestBundleUnitOfWork(null); // context not passed through here
395
+ final requestUoW = getRequestBundleUnitOfWork(
396
+ null,
397
+ ); // context not passed through here
388
398
  if (requestUoW != null) {
389
- return requestUoW.getById(bundleId, () =>
390
- getMethods().getBundleById(bundleId));
399
+ return requestUoW.getById(
400
+ bundleId,
401
+ () => getMethods().getBundleById(bundleId),
402
+ );
391
403
  }
392
404
  final pending = instanceUnitOfWork.peekChanged(bundleId);
393
405
  if (pending is TrackedBundleFound) return pending.value;
@@ -434,7 +446,10 @@ class _DatabasePluginImpl implements DatabasePlugin {
434
446
  if (normalizedOptions.page != null) {
435
447
  final page = normalizedOptions.page!;
436
448
  final requestedOffset = (page - 1) * normalizedOptions.limit;
437
- final fetchOptions = _expandLimitForUnitOfWork(normalizedOptions, unitOfWork);
449
+ final fetchOptions = _expandLimitForUnitOfWork(
450
+ normalizedOptions,
451
+ unitOfWork,
452
+ );
438
453
  var pageResult = await runGetBundles(
439
454
  DatabaseBundleQueryOptions(
440
455
  where: fetchOptions.where,
@@ -445,12 +460,15 @@ class _DatabasePluginImpl implements DatabasePlugin {
445
460
  );
446
461
 
447
462
  final total = pageResult.pagination.total;
448
- final totalPages =
449
- total == 0 ? 0 : (total / normalizedOptions.limit).ceil();
450
- final maxOffset =
451
- totalPages == 0 ? 0 : (totalPages - 1) * normalizedOptions.limit;
452
- final resolvedOffset =
453
- requestedOffset < maxOffset ? requestedOffset : maxOffset;
463
+ final totalPages = total == 0
464
+ ? 0
465
+ : (total / normalizedOptions.limit).ceil();
466
+ final maxOffset = totalPages == 0
467
+ ? 0
468
+ : (totalPages - 1) * normalizedOptions.limit;
469
+ final resolvedOffset = requestedOffset < maxOffset
470
+ ? requestedOffset
471
+ : maxOffset;
454
472
 
455
473
  if (resolvedOffset != requestedOffset) {
456
474
  pageResult = await runGetBundles(
@@ -473,7 +491,10 @@ class _DatabasePluginImpl implements DatabasePlugin {
473
491
  }
474
492
 
475
493
  if (methods.supportsCursorPagination) {
476
- final fetchOptions = _expandLimitForUnitOfWork(normalizedOptions, unitOfWork);
494
+ final fetchOptions = _expandLimitForUnitOfWork(
495
+ normalizedOptions,
496
+ unitOfWork,
497
+ );
477
498
  final result = await runGetBundles(fetchOptions);
478
499
  return shouldOverlay ? overlayResult(result) : result;
479
500
  }
@@ -490,9 +511,7 @@ class _DatabasePluginImpl implements DatabasePlugin {
490
511
  Future<void> commitBundle() async {
491
512
  final methods = getMethods();
492
513
  final unitOfWork = getMutationUnitOfWork(null);
493
- await methods.commitBundle(
494
- changedSets: unitOfWork.changedSets(),
495
- );
514
+ await methods.commitBundle(changedSets: unitOfWork.changedSets());
496
515
  unitOfWork.clear();
497
516
  final cb = hooks?.onDatabaseUpdated;
498
517
  if (cb != null) await cb();
@@ -504,8 +523,10 @@ class _DatabasePluginImpl implements DatabasePlugin {
504
523
  Map<String, Object?> newBundle,
505
524
  ) async {
506
525
  final unitOfWork = getMutationUnitOfWork(null);
507
- final current = await unitOfWork.getById(targetBundleId, () =>
508
- getMethods().getBundleById(targetBundleId));
526
+ final current = await unitOfWork.getById(
527
+ targetBundleId,
528
+ () => getMethods().getBundleById(targetBundleId),
529
+ );
509
530
  if (current == null) {
510
531
  throw StateError('targetBundleId not found');
511
532
  }
@@ -2,7 +2,7 @@ import 'types.dart';
2
2
 
3
3
  /// Create a deploy/CLI/console storage plugin (node profile only).
4
4
  StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
5
- createNodeStoragePlugin<TConfig>({
5
+ createNodeStoragePlugin<TConfig>({
6
6
  required String name,
7
7
  required String supportedProtocol,
8
8
  required NodeStorageProfile Function(TConfig config) factory,
@@ -24,7 +24,7 @@ StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
24
24
 
25
25
  /// Create an update-check runtime storage plugin.
26
26
  StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
27
- createRuntimeStoragePlugin<TConfig>({
27
+ createRuntimeStoragePlugin<TConfig>({
28
28
  required String name,
29
29
  required String supportedProtocol,
30
30
  required RuntimeStorageProfile Function(TConfig config) factory,
@@ -46,13 +46,13 @@ StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
46
46
 
47
47
  /// Create a storage plugin supporting both node and runtime profiles.
48
48
  StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
49
- createUniversalStoragePlugin<TConfig>({
49
+ createUniversalStoragePlugin<TConfig>({
50
50
  required String name,
51
51
  required String supportedProtocol,
52
- required ({
53
- NodeStorageProfile node,
54
- RuntimeStorageProfile runtime,
55
- }) Function(TConfig config) factory,
52
+ required ({NodeStorageProfile node, RuntimeStorageProfile runtime}) Function(
53
+ TConfig config,
54
+ )
55
+ factory,
56
56
  }) {
57
57
  return (TConfig config, [StoragePluginHooks? hooks]) {
58
58
  ({NodeStorageProfile node, RuntimeStorageProfile runtime})? cached;
@@ -102,8 +102,7 @@ class _WrappedNodeProfile implements NodeStorageProfile {
102
102
  _node.listObjects(prefix);
103
103
 
104
104
  @override
105
- Future<void> deleteObjects(List<String> keys) =>
106
- _node.deleteObjects(keys);
105
+ Future<void> deleteObjects(List<String> keys) => _node.deleteObjects(keys);
107
106
  }
108
107
 
109
108
  /// Storage plugin with lazy profile resolution.
@@ -123,8 +122,6 @@ class _ProfiledStoragePlugin implements StoragePlugin {
123
122
  final RuntimeStorageProfile Function()? runtimeProfile;
124
123
 
125
124
  @override
126
- StoragePluginProfiles get profiles => StoragePluginProfiles(
127
- node: nodeProfile,
128
- runtime: runtimeProfile?.call(),
129
- );
125
+ StoragePluginProfiles get profiles =>
126
+ StoragePluginProfiles(node: nodeProfile, runtime: runtimeProfile?.call());
130
127
  }
@@ -25,13 +25,17 @@ Paginated<List<Bundle>> paginateBundles({
25
25
  normalizedOffset + limit > total ? total : normalizedOffset + limit,
26
26
  )
27
27
  : sortedBundles.sublist(normalizedOffset);
28
- final pagination = calculatePagination(total, limit: limit, offset: normalizedOffset);
29
- final nextCursor =
30
- data.isNotEmpty && normalizedOffset + data.length < total
31
- ? data.last.id
32
- : null;
33
- final previousCursor =
34
- data.isNotEmpty && normalizedOffset > 0 ? data.first.id : null;
28
+ final pagination = calculatePagination(
29
+ total,
30
+ limit: limit,
31
+ offset: normalizedOffset,
32
+ );
33
+ final nextCursor = data.isNotEmpty && normalizedOffset + data.length < total
34
+ ? data.last.id
35
+ : null;
36
+ final previousCursor = data.isNotEmpty && normalizedOffset > 0
37
+ ? data.first.id
38
+ : null;
35
39
 
36
40
  return Paginated(
37
41
  data: data,
@@ -55,7 +59,12 @@ Paginated<List<Bundle>> paginateBundles({
55
59
  ? bundle.id.compareTo(cursor!.after!) < 0
56
60
  : bundle.id.compareTo(cursor!.after!) > 0;
57
61
  }).toList();
58
- data = limit > 0 ? candidates.sublist(0, limit > candidates.length ? candidates.length : limit) : candidates;
62
+ data = limit > 0
63
+ ? candidates.sublist(
64
+ 0,
65
+ limit > candidates.length ? candidates.length : limit,
66
+ )
67
+ : candidates;
59
68
  } else if (cursor?.before != null) {
60
69
  final candidates = sortedBundles.where((bundle) {
61
70
  return direction == 'desc'
@@ -76,15 +85,19 @@ Paginated<List<Bundle>> paginateBundles({
76
85
  final startIndex = data.isNotEmpty
77
86
  ? sortedBundles.indexWhere((b) => b.id == data.first.id)
78
87
  : cursor?.after != null
79
- ? total
80
- : 0;
81
- final pagination = calculatePagination(total, limit: limit, offset: startIndex);
82
- final nextCursor =
83
- data.isNotEmpty && startIndex + data.length < total
84
- ? data.last.id
85
- : null;
86
- final previousCursor =
87
- data.isNotEmpty && startIndex > 0 ? data.first.id : null;
88
+ ? total
89
+ : 0;
90
+ final pagination = calculatePagination(
91
+ total,
92
+ limit: limit,
93
+ offset: startIndex,
94
+ );
95
+ final nextCursor = data.isNotEmpty && startIndex + data.length < total
96
+ ? data.last.id
97
+ : null;
98
+ final previousCursor = data.isNotEmpty && startIndex > 0
99
+ ? data.first.id
100
+ : null;
88
101
 
89
102
  String? effectiveNextCursor = nextCursor;
90
103
  String? effectivePreviousCursor = previousCursor;
@@ -39,6 +39,8 @@ ParsedStorageUri parseStorageUri(String storageUri, String expectedProtocol) {
39
39
  return ParsedStorageUri(
40
40
  protocol: protocol,
41
41
  bucket: uri.host,
42
- key: decodeStorageObjectKey(uri.path.startsWith('/') ? uri.path.substring(1) : uri.path),
42
+ key: decodeStorageObjectKey(
43
+ uri.path.startsWith('/') ? uri.path.substring(1) : uri.path,
44
+ ),
43
45
  );
44
46
  }
@@ -47,8 +47,7 @@ bool bundleMatchesQueryWhere(Bundle bundle, DatabaseBundleQueryWhere? where) {
47
47
  return false;
48
48
  }
49
49
  if (where.targetAppVersionIn != null &&
50
- !where.targetAppVersionIn!
51
- .contains(bundle.targetAppVersion ?? '')) {
50
+ !where.targetAppVersionIn!.contains(bundle.targetAppVersion ?? '')) {
52
51
  return false;
53
52
  }
54
53
  if (where.fingerprintHash != null &&
@@ -59,7 +58,10 @@ bool bundleMatchesQueryWhere(Bundle bundle, DatabaseBundleQueryWhere? where) {
59
58
  }
60
59
 
61
60
  /// Sort bundles by ID in the specified direction.
62
- List<Bundle> sortBundles(List<Bundle> bundles, DatabaseBundleQueryOrder? orderBy) {
61
+ List<Bundle> sortBundles(
62
+ List<Bundle> bundles,
63
+ DatabaseBundleQueryOrder? orderBy,
64
+ ) {
63
65
  final direction = orderBy?.direction ?? 'desc';
64
66
  final sorted = List<Bundle>.from(bundles);
65
67
  sorted.sort((a, b) {