@_nazmiforreal/flutter-ota 0.1.11 → 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 +299 -190
  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 +2 -2
  176. package/scripts/vendor.sh +1 -1
@@ -46,12 +46,12 @@ class _Comparator {
46
46
  }
47
47
 
48
48
  String _opStr(_Op o) => switch (o) {
49
- _Op.lt => '<',
50
- _Op.lte => '<=',
51
- _Op.gt => '>',
52
- _Op.gte => '>=',
53
- _Op.eq => '=',
54
- };
49
+ _Op.lt => '<',
50
+ _Op.lte => '<=',
51
+ _Op.gt => '>',
52
+ _Op.gte => '>=',
53
+ _Op.eq => '=',
54
+ };
55
55
 
56
56
  final RegExp _xRe = RegExp(r'^[xX*]$');
57
57
 
@@ -131,8 +131,7 @@ List<_Comparator> _parseToken(String raw) {
131
131
  _Comparator(_Op.lt, ver(majS, '${int.parse(minS) + 1}', '0')),
132
132
  ];
133
133
  }
134
- final pre =
135
- hasPre ? preRaw.split('.') : const <String>[];
134
+ final pre = hasPre ? preRaw.split('.') : const <String>[];
136
135
  return [_Comparator(_Op.eq, ver(majS, minS!, patS!, pre))];
137
136
 
138
137
  case '>':
@@ -141,7 +140,7 @@ List<_Comparator> _parseToken(String raw) {
141
140
  }
142
141
  if (!hasPatch) {
143
142
  return [
144
- _Comparator(_Op.gte, ver(majS, '${int.parse(minS!) + 1}', '0'))
143
+ _Comparator(_Op.gte, ver(majS, '${int.parse(minS!) + 1}', '0')),
145
144
  ];
146
145
  }
147
146
  return [_Comparator(_Op.gt, ver(majS, minS!, patS!))];
@@ -154,8 +153,10 @@ List<_Comparator> _parseToken(String raw) {
154
153
  return [_Comparator(_Op.gte, ver(majS, minS!, '0'))];
155
154
  }
156
155
  return [
157
- _Comparator(_Op.gte, ver(majS, minS!, patS!,
158
- hasPre ? preRaw.split('.') : const []))
156
+ _Comparator(
157
+ _Op.gte,
158
+ ver(majS, minS!, patS!, hasPre ? preRaw.split('.') : const []),
159
+ ),
159
160
  ];
160
161
 
161
162
  case '<':
@@ -167,9 +168,9 @@ List<_Comparator> _parseToken(String raw) {
167
168
  }
168
169
  return [
169
170
  _Comparator(
170
- _Op.lt,
171
- ver(majS, minS!, patS!,
172
- hasPre ? preRaw.split('.') : const []))
171
+ _Op.lt,
172
+ ver(majS, minS!, patS!, hasPre ? preRaw.split('.') : const []),
173
+ ),
173
174
  ];
174
175
 
175
176
  case '<=':
@@ -178,13 +179,13 @@ List<_Comparator> _parseToken(String raw) {
178
179
  return [_Comparator(_Op.lt, ver('${int.parse(majS) + 1}', '0', '0'))];
179
180
  }
180
181
  if (!hasPatch) {
181
- return [
182
- _Comparator(_Op.lt, ver(majS, '${int.parse(minS!) + 1}', '0'))
183
- ];
182
+ return [_Comparator(_Op.lt, ver(majS, '${int.parse(minS!) + 1}', '0'))];
184
183
  }
185
184
  return [
186
- _Comparator(_Op.lte, ver(majS, minS!, patS!,
187
- hasPre ? preRaw.split('.') : const []))
185
+ _Comparator(
186
+ _Op.lte,
187
+ ver(majS, minS!, patS!, hasPre ? preRaw.split('.') : const []),
188
+ ),
188
189
  ];
189
190
 
190
191
  case '~':
@@ -202,9 +203,9 @@ List<_Comparator> _parseToken(String raw) {
202
203
  }
203
204
  return [
204
205
  _Comparator(
205
- _Op.gte,
206
- ver(majS, minS!, patS!,
207
- hasPre ? preRaw.split('.') : const [])),
206
+ _Op.gte,
207
+ ver(majS, minS!, patS!, hasPre ? preRaw.split('.') : const []),
208
+ ),
208
209
  _Comparator(_Op.lt, ver(majS, '${int.parse(minS) + 1}', '0')),
209
210
  ];
210
211
 
@@ -234,8 +235,10 @@ List<_Comparator> _parseToken(String raw) {
234
235
  ? SemVer(maj + 1, 0, 0)
235
236
  : (min > 0 ? SemVer(0, min + 1, 0) : SemVer(0, 0, pat + 1));
236
237
  return [
237
- _Comparator(_Op.gte,
238
- ver(majS, minS, patS, hasPre ? preRaw.split('.') : const [])),
238
+ _Comparator(
239
+ _Op.gte,
240
+ ver(majS, minS, patS, hasPre ? preRaw.split('.') : const []),
241
+ ),
239
242
  _Comparator(_Op.lt, upper),
240
243
  ];
241
244
  }
@@ -263,12 +266,12 @@ List<List<_Comparator>> _parseRange(String range) {
263
266
  for (var set in sets.map((s) => s.trim()).toList()) {
264
267
  if (set.isEmpty) set = '*';
265
268
  // Hyphen ranges first.
266
- final hyphenMatch = RegExp(
267
- r'^\s*(\S+)\s+-\s+(\S+)\s*$')
268
- .firstMatch(set);
269
+ final hyphenMatch = RegExp(r'^\s*(\S+)\s+-\s+(\S+)\s*$').firstMatch(set);
269
270
  if (hyphenMatch != null) {
270
- final comps = _hyphenToComparators(hyphenMatch.group(1)!,
271
- hyphenMatch.group(2)!);
271
+ final comps = _hyphenToComparators(
272
+ hyphenMatch.group(1)!,
273
+ hyphenMatch.group(2)!,
274
+ );
272
275
  result.add(comps);
273
276
  continue;
274
277
  }
@@ -299,11 +302,14 @@ List<_Comparator> _hyphenToComparators(String leftRaw, String rightRaw) {
299
302
  if (_isX(min)) {
300
303
  out.add(_Comparator(_Op.gte, SemVer(int.parse(maj), 0, 0)));
301
304
  } else if (_isX(pat)) {
302
- out.add(
303
- _Comparator(_Op.gte, SemVer(int.parse(maj), int.parse(min!), 0)));
305
+ out.add(_Comparator(_Op.gte, SemVer(int.parse(maj), int.parse(min!), 0)));
304
306
  } else {
305
- out.add(_Comparator(_Op.gte,
306
- SemVer(int.parse(maj), int.parse(min!), int.parse(pat!), pre)));
307
+ out.add(
308
+ _Comparator(
309
+ _Op.gte,
310
+ SemVer(int.parse(maj), int.parse(min!), int.parse(pat!), pre),
311
+ ),
312
+ );
307
313
  }
308
314
  }
309
315
 
@@ -318,11 +324,16 @@ List<_Comparator> _hyphenToComparators(String leftRaw, String rightRaw) {
318
324
  if (_isX(min)) {
319
325
  out.add(_Comparator(_Op.lt, SemVer(int.parse(maj) + 1, 0, 0)));
320
326
  } else if (_isX(pat)) {
321
- out.add(_Comparator(
322
- _Op.lt, SemVer(int.parse(maj), int.parse(min!) + 1, 0)));
327
+ out.add(
328
+ _Comparator(_Op.lt, SemVer(int.parse(maj), int.parse(min!) + 1, 0)),
329
+ );
323
330
  } else {
324
- out.add(_Comparator(_Op.lte,
325
- SemVer(int.parse(maj), int.parse(min!), int.parse(pat!), pre)));
331
+ out.add(
332
+ _Comparator(
333
+ _Op.lte,
334
+ SemVer(int.parse(maj), int.parse(min!), int.parse(pat!), pre),
335
+ ),
336
+ );
326
337
  }
327
338
  }
328
339
  return out;
@@ -348,8 +359,7 @@ bool satisfies(SemVer version, String range) {
348
359
  }
349
360
  if (!ok) continue;
350
361
  if (version.isPrerelease) {
351
- final allowed =
352
- set.any((c) => c.allowsPrereleaseOf(version));
362
+ final allowed = set.any((c) => c.allowsPrereleaseOf(version));
353
363
  if (!allowed) continue;
354
364
  }
355
365
  return true;
@@ -38,17 +38,18 @@ class SemVer implements Comparable<SemVer> {
38
38
  }
39
39
 
40
40
  factory SemVer.fromJson(Map<String, dynamic> j) => SemVer(
41
- j['major'] as int,
42
- j['minor'] as int,
43
- j['patch'] as int,
44
- (j['prerelease'] as List?)?.map((e) => '$e').toList() ?? const [],
45
- j['build'] as String?,
46
- );
41
+ j['major'] as int,
42
+ j['minor'] as int,
43
+ j['patch'] as int,
44
+ (j['prerelease'] as List?)?.map((e) => '$e').toList() ?? const [],
45
+ j['build'] as String?,
46
+ );
47
47
 
48
48
  bool get isPrerelease => prerelease.isNotEmpty;
49
49
 
50
50
  @override
51
- String toString() => '$major.$minor.$patch'
51
+ String toString() =>
52
+ '$major.$minor.$patch'
52
53
  '${prerelease.isEmpty ? '' : '-${prerelease.join('.')}'}'
53
54
  '${build == null ? '' : '+$build'}';
54
55
 
@@ -8,9 +8,10 @@ enum UpdateStatus {
8
8
  final String value;
9
9
  const UpdateStatus(this.value);
10
10
 
11
- static UpdateStatus fromValue(String v) => UpdateStatus.values
12
- .firstWhere((s) => s.value == v,
13
- orElse: () => throw ArgumentError('unknown status: $v'));
11
+ static UpdateStatus fromValue(String v) => UpdateStatus.values.firstWhere(
12
+ (s) => s.value == v,
13
+ orElse: () => throw ArgumentError('unknown status: $v'),
14
+ );
14
15
  }
15
16
 
16
17
  /// What the client derives: UP_TO_DATE | ROLLBACK | UPDATE.
@@ -20,11 +20,11 @@ class UpdateBundleParams {
20
20
  });
21
21
 
22
22
  Map<String, dynamic> toJson() => {
23
- 'platform': platform.value,
24
- 'bundleId': bundleId,
25
- 'minBundleId': minBundleId,
26
- 'channel': channel,
27
- 'appVersion': appVersion,
28
- if (fingerprintHash != null) 'fingerprintHash': fingerprintHash,
29
- };
23
+ 'platform': platform.value,
24
+ 'bundleId': bundleId,
25
+ 'minBundleId': minBundleId,
26
+ 'channel': channel,
27
+ 'appVersion': appVersion,
28
+ if (fingerprintHash != null) 'fingerprintHash': fingerprintHash,
29
+ };
30
30
  }
@@ -28,8 +28,7 @@ String uuidV7({DateTime? now}) {
28
28
  final rand = List.generate(10, (_) => _random.nextInt(256));
29
29
  final tsHex = _hex(ts, 12);
30
30
  final g3 = '${_hex(rand[0] & 0x0F, 1)}${_hex(rand[1], 2)}';
31
- final g4 =
32
- '${_hex((rand[2] & 0x3F) | 0x80, 2)}${_hex(rand[3], 2)}';
31
+ final g4 = '${_hex((rand[2] & 0x3F) | 0x80, 2)}${_hex(rand[3], 2)}';
33
32
  final g5 = StringBuffer();
34
33
  for (var i = 4; i < 10; i++) {
35
34
  g5.write(_hex(rand[i], 2));
@@ -1,14 +1,13 @@
1
1
  name: flutter_ota_kit_core
2
2
  description: Core data model, rollout cohorts, and shared utilities for flutter_ota_kit — Dart translation of hot-updater's packages/core.
3
- version: 0.1.0
4
- publish_to: https://pub.dev
5
- homepage: https://github.com/HYPER12755/flutter_ota_kit
6
- repository: https://github.com/HYPER12755/flutter_ota_kit
7
- issue_tracker: https://github.com/HYPER12755/flutter_ota_kit/issues
8
- documentation: https://github.com/HYPER12755/flutter_ota_kit#readme
3
+ version: 0.1.1
4
+ homepage: https://github.com/HYPER12755/flutter-ota-kit
5
+ repository: https://github.com/HYPER12755/flutter-ota-kit
6
+ issue_tracker: https://github.com/HYPER12755/flutter-ota-kit/issues
7
+ documentation: https://github.com/HYPER12755/flutter-ota-kit#readme
9
8
  license: MIT
10
9
 
11
10
  environment:
12
- sdk: '>=3.0.0 <4.0.0'
11
+ sdk: '>=3.13.2 <4.0.0'
13
12
  dev_dependencies:
14
13
  test: ^1.31.2
@@ -37,8 +37,10 @@ void main() {
37
37
  for (final c in cohorts) {
38
38
  expect(isCohortEligibleForUpdate('x-bundle', '$c', 137, null), isTrue);
39
39
  }
40
- final excluded = List.generate(1000, (i) => i + 1)
41
- .firstWhere((c) => !cohorts.contains(c));
40
+ final excluded = List.generate(
41
+ 1000,
42
+ (i) => i + 1,
43
+ ).firstWhere((c) => !cohorts.contains(c));
42
44
  expect(
43
45
  isCohortEligibleForUpdate('x-bundle', '$excluded', 137, null),
44
46
  isFalse,
@@ -64,8 +66,9 @@ void main() {
64
66
  const bundleId = 'd-bundle';
65
67
  final cohorts = getRolledOutNumericCohorts(bundleId, 137);
66
68
  expect(
67
- isCohortEligibleForUpdate(
68
- bundleId, '${cohorts.first}', 137, ['qa-group']),
69
+ isCohortEligibleForUpdate(bundleId, '${cohorts.first}', 137, [
70
+ 'qa-group',
71
+ ]),
69
72
  isTrue,
70
73
  );
71
74
  });
@@ -80,8 +83,10 @@ void main() {
80
83
  test('targeted numeric cohort wins even when excluded by rollout', () {
81
84
  const bundleId = 'f-bundle';
82
85
  final rolledOut = getRolledOutNumericCohorts(bundleId, 10).toSet();
83
- final excluded = List.generate(1000, (i) => i + 1)
84
- .firstWhere((c) => !rolledOut.contains(c));
86
+ final excluded = List.generate(
87
+ 1000,
88
+ (i) => i + 1,
89
+ ).firstWhere((c) => !rolledOut.contains(c));
85
90
  expect(
86
91
  isCohortEligibleForUpdate(bundleId, '$excluded', 10, ['$excluded']),
87
92
  isTrue,
@@ -95,8 +100,10 @@ void main() {
95
100
  });
96
101
 
97
102
  test('custom cohorts never match percentage rollouts', () {
98
- expect(isCohortEligibleForUpdate('h-bundle', 'qa-team', 999, null),
99
- isFalse);
103
+ expect(
104
+ isCohortEligibleForUpdate('h-bundle', 'qa-team', 999, null),
105
+ isFalse,
106
+ );
100
107
  expect(
101
108
  isCohortEligibleForUpdate('h-bundle', 'qa-team', 999, ['qa-team']),
102
109
  isTrue,
@@ -31,8 +31,11 @@ void main() {
31
31
  // verkit threw on invalid input; we treat as non-match.
32
32
  expect(satisfies(parsed, range), isFalse);
33
33
  } else {
34
- expect(satisfies(parsed, range), expected,
35
- reason: '$version vs range "$range"');
34
+ expect(
35
+ satisfies(parsed, range),
36
+ expected,
37
+ reason: '$version vs range "$range"',
38
+ );
36
39
  }
37
40
  });
38
41
  }
@@ -2,28 +2,28 @@ import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
2
2
  import 'package:test/test.dart';
3
3
 
4
4
  Bundle _sample() => Bundle(
5
- id: '019b76da-a800-7cc2-b5a5-c8f4a1e9d001',
6
- platform: Platform.android,
7
- shouldForceUpdate: false,
8
- enabled: true,
9
- fileHash: 'a' * 64,
10
- storageUri: 'supabase-storage://flutter-ota-storage/bundles/x/bundle.zip',
11
- gitCommitHash: 'deadbeef',
12
- message: 'test bundle',
13
- channel: 'production',
14
- targetAppVersion: '^1.2.0',
15
- metadata: const BundleMetadata(appVersion: '1.2.3'),
16
- rolloutCohortCount: 137,
17
- targetCohorts: ['qa-group'],
18
- patches: [
19
- BundlePatchArtifact(
20
- baseBundleId: 'base-id',
21
- baseFileHash: 'b' * 64,
22
- patchFileHash: 'c' * 64,
23
- patchStorageUri: 'supabase-storage://bucket/patches/p.bsdiff',
24
- ),
25
- ],
26
- );
5
+ id: '019b76da-a800-7cc2-b5a5-c8f4a1e9d001',
6
+ platform: Platform.android,
7
+ shouldForceUpdate: false,
8
+ enabled: true,
9
+ fileHash: 'a' * 64,
10
+ storageUri: 'supabase-storage://flutter-ota-storage/bundles/x/bundle.zip',
11
+ gitCommitHash: 'deadbeef',
12
+ message: 'test bundle',
13
+ channel: 'production',
14
+ targetAppVersion: '^1.2.0',
15
+ metadata: const BundleMetadata(appVersion: '1.2.3'),
16
+ rolloutCohortCount: 137,
17
+ targetCohorts: ['qa-group'],
18
+ patches: [
19
+ BundlePatchArtifact(
20
+ baseBundleId: 'base-id',
21
+ baseFileHash: 'b' * 64,
22
+ patchFileHash: 'c' * 64,
23
+ patchStorageUri: 'supabase-storage://bucket/patches/p.bsdiff',
24
+ ),
25
+ ],
26
+ );
27
27
 
28
28
  void main() {
29
29
  group('Bundle JSON', () {
@@ -100,8 +100,10 @@ void main() {
100
100
  },
101
101
  }) as AppUpdateAvailableInfo;
102
102
  expect(info.status, UpdateStatus.update);
103
- expect(info.changedAssets?['assets/logo.png']?.file?.url,
104
- 'https://cdn/logo.png');
103
+ expect(
104
+ info.changedAssets?['assets/logo.png']?.file?.url,
105
+ 'https://cdn/logo.png',
106
+ );
105
107
  });
106
108
 
107
109
  test('available info serializes', () {
@@ -12,7 +12,9 @@ void main() {
12
12
  test('encodes provided timestamp', () {
13
13
  final ts = DateTime.utc(2026, 1, 1);
14
14
  final id = uuidV7(now: ts);
15
- final tsHex = ts.millisecondsSinceEpoch.toRadixString(16).padLeft(12, '0');
15
+ final tsHex = ts.millisecondsSinceEpoch
16
+ .toRadixString(16)
17
+ .padLeft(12, '0');
16
18
  expect(id.substring(0, 8), tsHex.substring(0, 8));
17
19
  expect(id.substring(9, 13), tsHex.substring(8, 12));
18
20
  expect(id[14], '7');
@@ -1,4 +1,5 @@
1
1
  import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
2
+
2
3
  void main() {
3
4
  // ignore: avoid_print
4
5
  print('coerce=${semverCoerce('app version 2.3.6!')}');
@@ -1,3 +1,9 @@
1
+ ## 0.1.1
2
+
3
+ - Add `withCloudFrontSignedUrl` helper for time-limited download URLs.
4
+ - Add `defaultAwsCloudFrontClientFactory` export.
5
+ - Repository moved to `https://github.com/HYPER12755/flutter-ota-kit`.
6
+
1
7
  ## 0.1.0
2
8
 
3
9
  - Initial release of `flutter_ota_kit_aws` as part of the flutter_ota_kit OTA toolkit.
@@ -0,0 +1,60 @@
1
+ // flutter_ota_kit_aws example
2
+ //
3
+ // `flutter_ota_kit_aws` provides AWS S3 storage + S3/DynamoDB database
4
+ // plugins for `flutter_ota_kit`. The typical setup uses S3 for both
5
+ // (storing a `bundles.json` manifest plus patch blobs), with optional
6
+ // CloudFront for signed URLs and faster edge delivery.
7
+ //
8
+ // Most apps don't import this package directly; they wire it through
9
+ // `flutter_ota_kit.configureAws(...)` in their app boot path. This file
10
+ // shows the low-level config types in case you're writing a custom
11
+ // server or a CLI extension.
12
+ //
13
+ // Run: `dart run example/main.dart`
14
+
15
+ import 'package:flutter_ota_kit_aws/flutter_ota_kit_aws.dart';
16
+
17
+ void main() {
18
+ // ── 1. S3 storage config (for storing patch blobs). ──────────────
19
+ // `bucketName` + `region` are required. `accessKeyId` and
20
+ // `secretAccessKey` are read-write credentials; in production use an
21
+ // IAM role with the minimum required permissions.
22
+ final s3Config = AwsS3StorageConfig(
23
+ bucketName: 'flutter-ota-bundles',
24
+ region: 'us-east-1',
25
+ accessKeyId: 'AKIA...',
26
+ secretAccessKey: 'wJal...',
27
+ basePath: 'bundles',
28
+ );
29
+ print('S3 bucket: ${s3Config.bucketName} (${s3Config.region})');
30
+ print('Base path: ${s3Config.basePath}');
31
+
32
+ // ── 2. S3 database config (for storing the bundles manifest). ───
33
+ // The "database" plugin reads/writes a `bundles.json` manifest in
34
+ // the same S3 bucket. `cloudfrontDistributionId` enables signed URL
35
+ // serving for the patch downloads.
36
+ final dbConfig = S3DatabaseConfig(
37
+ bucketName: 'flutter-ota-bundles',
38
+ region: 'us-east-1',
39
+ accessKeyId: 'AKIA...',
40
+ secretAccessKey: 'wJal...',
41
+ cloudfrontDistributionId: 'E1ABC2DEF3GHIJ',
42
+ );
43
+ print('S3 DB bucket: ${dbConfig.bucketName}');
44
+ print('CloudFront distribution: ${dbConfig.cloudfrontDistributionId}');
45
+
46
+ // ── 3. CloudFront URL signing (for time-limited download URLs). ──
47
+ // `cloudfrontSignedUrl` produces a URL valid for `expiresInSeconds`
48
+ // (default 1 hour). The signing key is a private RSA key in PEM
49
+ // format — never ship it in a Flutter app.
50
+ // final key = parseRsaPrivateKeyPem(
51
+ // File('cloudfront-private-key.pem').readAsStringSync(),
52
+ // );
53
+ // final signedUrl = cloudfrontSignedUrl(
54
+ // url: 'https://d1abc2def3ghij.cloudfront.net/patch.zip',
55
+ // privateKey: key,
56
+ // keyPairId: 'APKA...',
57
+ // expiresInSeconds: 600,
58
+ // );
59
+ // print('Signed URL: $signedUrl');
60
+ }
@@ -1,6 +1,10 @@
1
1
  export 'src/aws_storage.dart' show s3Storage;
2
2
  export 'src/aws_database.dart'
3
- show S3DatabaseConfig, s3Database, defaultAwsS3DatabaseClientFactory, defaultAwsCloudFrontClientFactory;
3
+ show
4
+ S3DatabaseConfig,
5
+ s3Database,
6
+ defaultAwsS3DatabaseClientFactory,
7
+ defaultAwsCloudFrontClientFactory;
4
8
  export 'src/aws_config.dart' show AwsS3StorageConfig, defaultAwsS3ClientFactory;
5
9
  export 'src/aws_s3_client.dart' show AwsS3ClientLike;
6
10
  export 'src/aws_cloudfront_client.dart' show AwsCloudFrontClientLike;
@@ -118,11 +118,7 @@ class AwsCloudFrontClient implements AwsCloudFrontClientLike {
118
118
  return m?.group(1);
119
119
  }
120
120
 
121
- Map<String, String> _authHeaders(
122
- String method,
123
- Uri uri,
124
- List<int>? body,
125
- ) {
121
+ Map<String, String> _authHeaders(String method, Uri uri, List<int>? body) {
126
122
  final now = DateTime.now().toUtc();
127
123
  final amzDate = _amzDate(now);
128
124
  final dateStamp = amzDate.substring(0, 8);
@@ -130,7 +126,8 @@ class AwsCloudFrontClient implements AwsCloudFrontClientLike {
130
126
  '$dateStamp/$_cloudfrontRegion/$_cloudfrontService/aws4_request';
131
127
  final payloadHash = body == null ? _hashHex(const []) : _hashHex(body);
132
128
 
133
- final canonicalHeaders = 'host:${uri.host}\n'
129
+ final canonicalHeaders =
130
+ 'host:${uri.host}\n'
134
131
  'x-amz-content-sha256:$payloadHash\n'
135
132
  'x-amz-date:$amzDate\n'
136
133
  '${config.sessionToken != null ? 'x-amz-security-token:${config.sessionToken}\n' : ''}';
@@ -138,7 +135,8 @@ class AwsCloudFrontClient implements AwsCloudFrontClientLike {
138
135
  ? 'host;x-amz-content-sha256;x-amz-date;x-amz-security-token'
139
136
  : 'host;x-amz-content-sha256;x-amz-date';
140
137
 
141
- final canonicalRequest = '$method\n${uri.path}\n'
138
+ final canonicalRequest =
139
+ '$method\n${uri.path}\n'
142
140
  '\n$canonicalHeaders$signedHeaders\n$payloadHash';
143
141
  final stringToSign = _stringToSign(amzDate, scope, canonicalRequest);
144
142
  final signingKey = _signingKey(dateStamp);
@@ -181,23 +179,20 @@ class AwsCloudFrontClient implements AwsCloudFrontClientLike {
181
179
  static String _amzDate(DateTime utc) =>
182
180
  '${utc.toIso8601String().replaceAll('-', '').replaceAll(':', '').split('.').first}Z';
183
181
 
184
- static String _xmlEscape(String value) =>
185
- value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
182
+ static String _xmlEscape(String value) => value
183
+ .replaceAll('&', '&amp;')
184
+ .replaceAll('<', '&lt;')
185
+ .replaceAll('>', '&gt;');
186
186
 
187
187
  static Uint8List _hmac(List<int> key, String data) =>
188
188
  Hmac(sha256, key).convert(utf8.encode(data)).bytes as Uint8List;
189
189
 
190
- static String _hashHex(List<int> data) =>
191
- _toHex(sha256.convert(data).bytes);
190
+ static String _hashHex(List<int> data) => _toHex(sha256.convert(data).bytes);
192
191
 
193
192
  static String _toHex(List<int> bytes) =>
194
193
  bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
195
194
 
196
- static void _assertStatus(
197
- Response response,
198
- List<int> ok,
199
- String operation,
200
- ) {
195
+ static void _assertStatus(Response response, List<int> ok, String operation) {
201
196
  if (!ok.contains(response.statusCode)) {
202
197
  throw Exception(
203
198
  'CloudFront $operation failed (${response.statusCode}): ${response.body}',
@@ -16,7 +16,10 @@ import 'package:pointycastle/export.dart'
16
16
  const String _sha1DigestIdentifierHex = '3021300906052b0e03021a05000414';
17
17
 
18
18
  String _base64UrlNoPad(List<int> bytes) =>
19
- base64Encode(bytes).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
19
+ base64Encode(bytes)
20
+ .replaceAll('+', '-')
21
+ .replaceAll('/', '_')
22
+ .replaceAll('=', '');
20
23
 
21
24
  /// Parse a PEM-encoded RSA private key (PKCS#1 `RSA PRIVATE KEY` or PKCS#8
22
25
  /// `PRIVATE KEY`) into a [RSAPrivateKey].
@@ -84,7 +87,9 @@ String cloudfrontSignedUrl({
84
87
 
85
88
  final signer = RSASigner(SHA1Digest(), _sha1DigestIdentifierHex);
86
89
  signer.init(true, PrivateKeyParameter<RSAPrivateKey>(privateKey));
87
- final signature = signer.generateSignature(Uint8List.fromList(policyBytes)).bytes;
90
+ final signature = signer
91
+ .generateSignature(Uint8List.fromList(policyBytes))
92
+ .bytes;
88
93
  final signatureB64 = _base64UrlNoPad(signature);
89
94
 
90
95
  return '$url?Policy=$policyB64&Signature=$signatureB64'
@@ -1,7 +1,7 @@
1
1
  import 'dart:convert' show jsonDecode, jsonEncode, utf8;
2
2
 
3
3
  import 'package:flutter_ota_kit_plugin_core/flutter_ota_kit_plugin_core.dart'
4
- show BlobOperations, createBlobDatabasePlugin;
4
+ show BlobOperations, createBlobDatabasePlugin, updateJsonKeyRegex;
5
5
 
6
6
  import 'aws_cloudfront_client.dart'
7
7
  show AwsCloudFrontClient, AwsCloudFrontClientLike, AwsCloudFrontConfig;
@@ -70,7 +70,7 @@ class S3DatabaseConfig {
70
70
 
71
71
  /// Test seam: override the concrete CloudFront client.
72
72
  final AwsCloudFrontClientLike Function(S3DatabaseConfig config)
73
- cloudfrontClientFactory;
73
+ cloudfrontClientFactory;
74
74
  }
75
75
 
76
76
  /// Default factory producing a real [AwsS3Client] with runtime region applied.
@@ -92,25 +92,21 @@ AwsS3ClientLike defaultAwsS3DatabaseClientFactory(S3DatabaseConfig config) =>
92
92
  /// Default factory producing a real [AwsCloudFrontClient].
93
93
  AwsCloudFrontClientLike defaultAwsCloudFrontClientFactory(
94
94
  S3DatabaseConfig config,
95
- ) =>
96
- AwsCloudFrontClient(
97
- AwsCloudFrontConfig(
98
- accessKeyId: config.accessKeyId,
99
- secretAccessKey: config.secretAccessKey,
100
- sessionToken: config.sessionToken,
101
- ),
102
- );
103
-
104
- bool _isUpdateJsonKey(String key) =>
105
- RegExp(r'^[^/]+/(?:ios|android)/[^/]+/update\.json$').hasMatch(key);
95
+ ) => AwsCloudFrontClient(
96
+ AwsCloudFrontConfig(
97
+ accessKeyId: config.accessKeyId,
98
+ secretAccessKey: config.secretAccessKey,
99
+ sessionToken: config.sessionToken,
100
+ ),
101
+ );
106
102
 
107
103
  /// S3-backed [BlobOperations] for the AWS `s3Database` plugin.
108
104
  class S3BlobOperations implements BlobOperations {
109
105
  S3BlobOperations(this.config)
110
- : _client = config.clientFactory(config),
111
- _cloudfront = config.cloudfrontDistributionId != null
112
- ? config.cloudfrontClientFactory(config)
113
- : null {
106
+ : _client = config.clientFactory(config),
107
+ _cloudfront = config.cloudfrontDistributionId != null
108
+ ? config.cloudfrontClientFactory(config)
109
+ : null {
114
110
  final keyBuilder = createDatabaseKeyBuilder(config.basePath);
115
111
  _toStorageKey = keyBuilder.$1;
116
112
  _fromStorageKey = keyBuilder.$2;
@@ -130,7 +126,7 @@ class S3BlobOperations implements BlobOperations {
130
126
  final objects = await _client.listObjects(_toStorageKey(prefix));
131
127
  return objects
132
128
  .map((o) => _fromStorageKey(o.key))
133
- .where(_isUpdateJsonKey)
129
+ .where((key) => updateJsonKeyRegex.hasMatch(key))
134
130
  .toList();
135
131
  }
136
132