@_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
@@ -38,12 +38,12 @@ class _FilterBuilder implements SupabaseFilterBuilderLike {
38
38
  Map<String, String>? query,
39
39
  String method = 'GET',
40
40
  Object? body,
41
- }) : _baseUrl = baseUrl,
42
- _table = table,
43
- _headers = headers,
44
- _query = query ?? {},
45
- _method = method,
46
- _body = body;
41
+ }) : _baseUrl = baseUrl,
42
+ _table = table,
43
+ _headers = headers,
44
+ _query = query ?? {},
45
+ _method = method,
46
+ _body = body;
47
47
 
48
48
  @override
49
49
  SupabaseFilterBuilderLike eq(String column, Object? value) =>
@@ -78,11 +78,7 @@ class _FilterBuilder implements SupabaseFilterBuilderLike {
78
78
  }
79
79
 
80
80
  @override
81
- SupabaseFilterBuilderLike not(
82
- String column,
83
- String operator,
84
- Object? value,
85
- ) {
81
+ SupabaseFilterBuilderLike not(String column, String operator, Object? value) {
86
82
  _query[column] = 'not.$operator.${_enc(value)}';
87
83
  return this;
88
84
  }
@@ -129,8 +125,7 @@ class _FilterBuilder implements SupabaseFilterBuilderLike {
129
125
  .replace(queryParameters: _query.isEmpty ? null : _query);
130
126
  final headers = <String, String>{
131
127
  ..._headers,
132
- if (_method != 'GET' && _body != null)
133
- 'Content-Type': 'application/json',
128
+ if (_method != 'GET' && _body != null) 'Content-Type': 'application/json',
134
129
  };
135
130
 
136
131
  late http.Response res;
@@ -181,11 +176,7 @@ class _QueryBuilder implements SupabaseQueryBuilderLike {
181
176
  _QueryBuilder(this._baseUrl, this._table, this._headers);
182
177
 
183
178
  @override
184
- SupabaseFilterBuilderLike select(
185
- String columns, {
186
- bool? count,
187
- bool? head,
188
- }) {
179
+ SupabaseFilterBuilderLike select(String columns, {bool? count, bool? head}) {
189
180
  final headers = <String, String>{..._headers};
190
181
  if (count == true) headers['Prefer'] = 'count=exact';
191
182
  if (head == true) headers['Range'] = '0-0';
@@ -199,18 +190,16 @@ class _QueryBuilder implements SupabaseQueryBuilderLike {
199
190
 
200
191
  @override
201
192
  SupabaseFilterBuilderLike delete() => _FilterBuilder(
202
- baseUrl: _baseUrl,
203
- table: _table,
204
- headers: _headers,
205
- method: 'DELETE',
206
- );
193
+ baseUrl: _baseUrl,
194
+ table: _table,
195
+ headers: _headers,
196
+ method: 'DELETE',
197
+ );
207
198
 
208
199
  @override
209
200
  Future<dynamic> upsert(dynamic values, {String? onConflict}) async {
210
201
  final uri = Uri.parse('$_baseUrl/rest/v1/$_table').replace(
211
- queryParameters: onConflict != null
212
- ? {'on_conflict': onConflict}
213
- : null,
202
+ queryParameters: onConflict != null ? {'on_conflict': onConflict} : null,
214
203
  );
215
204
  final headers = <String, String>{
216
205
  ..._headers,
@@ -273,10 +262,7 @@ class _StorageBucket implements SupabaseStorageBucketLike {
273
262
  final res = await http.post(
274
263
  uri,
275
264
  headers: {..._headers, 'Content-Type': 'application/json'},
276
- body: jsonEncode({
277
- 'expiresIn': expiresIn,
278
- 'paths': paths,
279
- }),
265
+ body: jsonEncode({'expiresIn': expiresIn, 'paths': paths}),
280
266
  );
281
267
  if (res.statusCode >= 400) {
282
268
  return _MockSignedUrlListResult(null, jsonDecode(res.body));
@@ -284,14 +270,16 @@ class _StorageBucket implements SupabaseStorageBucketLike {
284
270
  final body = jsonDecode(res.body) as List<dynamic>;
285
271
  return _MockSignedUrlListResult(
286
272
  body
287
- .map((e) => _MockSignedUrlResult(
288
- // Supabase returns a relative path; make it absolute against
289
- // the project base URL, matching [createSignedUrl].
290
- e['signedURL'] == null
291
- ? null
292
- : '$_baseUrl/storage/v1${e['signedURL'] as String}',
293
- e['error'] as String?,
294
- ))
273
+ .map(
274
+ (e) => _MockSignedUrlResult(
275
+ // Supabase returns a relative path; make it absolute against
276
+ // the project base URL, matching [createSignedUrl].
277
+ e['signedURL'] == null
278
+ ? null
279
+ : '$_baseUrl/storage/v1${e['signedURL'] as String}',
280
+ e['error'] as String?,
281
+ ),
282
+ )
295
283
  .toList(),
296
284
  null,
297
285
  );
@@ -367,9 +355,8 @@ class _StorageBucket implements SupabaseStorageBucketLike {
367
355
 
368
356
  @override
369
357
  Future<SupabaseExistsResult> exists(String path) async {
370
- final uri = Uri.parse(
371
- '$_baseUrl/storage/v1/object/$_bucket/$path',
372
- ).replace(queryParameters: {'_method': 'HEAD'});
358
+ final uri = Uri.parse('$_baseUrl/storage/v1/object/$_bucket/$path')
359
+ .replace(queryParameters: {'_method': 'HEAD'});
373
360
  final res = await http.get(uri, headers: _headers);
374
361
  if (res.statusCode == 404) {
375
362
  return _MockExistsResult(false, null);
@@ -404,11 +391,10 @@ class _StorageBucket implements SupabaseStorageBucketLike {
404
391
  final body = jsonDecode(res.body) as Map<String, dynamic>;
405
392
  return _MockListResult(null, body['message'] as String?);
406
393
  }
407
- final body = (res.body.isEmpty ? [] : jsonDecode(res.body)) as List<dynamic>;
394
+ final body =
395
+ (res.body.isEmpty ? [] : jsonDecode(res.body)) as List<dynamic>;
408
396
  return _MockListResult(
409
- body
410
- .map((e) => _MockStorageObject(e as Map<String, dynamic>))
411
- .toList(),
397
+ body.map((e) => _MockStorageObject(e as Map<String, dynamic>)).toList(),
412
398
  null,
413
399
  );
414
400
  }
@@ -510,19 +496,15 @@ class SupabaseHttpClient implements SupabaseClientLike {
510
496
  final Map<String, String> _headers;
511
497
 
512
498
  SupabaseHttpClient(String supabaseUrl, String key)
513
- : _baseUrl = supabaseUrl,
514
- _headers = {
515
- 'apikey': key,
516
- 'Authorization': 'Bearer $key',
517
- };
499
+ : _baseUrl = supabaseUrl,
500
+ _headers = {'apikey': key, 'Authorization': 'Bearer $key'};
518
501
 
519
502
  @override
520
503
  SupabaseQueryBuilderLike from(String table) =>
521
504
  _QueryBuilder(_baseUrl, table, _headers);
522
505
 
523
506
  @override
524
- SupabaseStorageClientLike get storage =>
525
- _StorageClient(_baseUrl, _headers);
507
+ SupabaseStorageClientLike get storage => _StorageClient(_baseUrl, _headers);
526
508
 
527
509
  @override
528
510
  Future<SupabaseRpcResponse> rpc(
@@ -57,13 +57,15 @@ Object createSupabaseError(Object error) {
57
57
  }
58
58
 
59
59
  UpdateInfo mapUpdateInfoRow(_SupabaseUpdateInfoRow row) => UpdateInfo(
60
- id: row.id,
61
- shouldForceUpdate: row.shouldForceUpdate,
62
- message: row.message,
63
- status: row.status == 'ROLLBACK' ? UpdateStatus.rollback : UpdateStatus.update,
64
- storageUri: row.storageUri,
65
- fileHash: row.fileHash,
66
- );
60
+ id: row.id,
61
+ shouldForceUpdate: row.shouldForceUpdate,
62
+ message: row.message,
63
+ status: row.status == 'ROLLBACK'
64
+ ? UpdateStatus.rollback
65
+ : UpdateStatus.update,
66
+ storageUri: row.storageUri,
67
+ fileHash: row.fileHash,
68
+ );
67
69
 
68
70
  /// Supabase-backed [DatabasePlugin] factory.
69
71
  ///
@@ -73,15 +75,13 @@ UpdateInfo mapUpdateInfoRow(_SupabaseUpdateInfoRow row) => UpdateInfo(
73
75
  DatabasePlugin Function() Function(
74
76
  SupabaseDatabaseConfig config, [
75
77
  DatabasePluginHooks? hooks,
76
- ]) supabaseDatabase = createDatabasePlugin<SupabaseDatabaseConfig>(
78
+ ])
79
+ supabaseDatabase = createDatabasePlugin<SupabaseDatabaseConfig>(
77
80
  name: 'supabaseDatabase',
78
81
  factory: (config) {
79
82
  final supabase = (config.clientFactory != null
80
83
  ? config.clientFactory!(config.supabaseUrl, config.resolveKey())
81
- : createSupabaseHttpClient(
82
- config.supabaseUrl,
83
- config.resolveKey(),
84
- ));
84
+ : createSupabaseHttpClient(config.supabaseUrl, config.resolveKey()));
85
85
 
86
86
  Future<Map<String, List<SupabaseBundlePatchRow>>> fetchPatchMap(
87
87
  List<String> bundleIds,
@@ -123,7 +123,8 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
123
123
  final SupabaseClientLike supabase;
124
124
  final Future<Map<String, List<SupabaseBundlePatchRow>>> Function(
125
125
  List<String> bundleIds,
126
- ) fetchPatchMap;
126
+ )
127
+ fetchPatchMap;
127
128
 
128
129
  _SupabaseDatabasePlugin({
129
130
  required this.supabase,
@@ -152,8 +153,11 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
152
153
  }
153
154
  final targetAppVersionList = filterCompatibleAppVersions(
154
155
  ((targetRes.data as List?) ?? [])
155
- .map((row) =>
156
- _SupabaseTargetAppVersionRow((row as Map)['target_app_version']))
156
+ .map(
157
+ (row) => _SupabaseTargetAppVersionRow(
158
+ (row as Map)['target_app_version'],
159
+ ),
160
+ )
157
161
  .where((row) => row.targetAppVersion != null)
158
162
  .map((row) => row.targetAppVersion!)
159
163
  .toList(),
@@ -229,9 +233,11 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
229
233
  final row = SupabaseBundleRow.fromJson(
230
234
  (res.data as Map).cast<String, dynamic>(),
231
235
  );
232
- final patches = (results[1] as Map<String, List<SupabaseBundlePatchRow>>)[bundleId] ?? [];
236
+ final patches =
237
+ (results[1] as Map<String, List<SupabaseBundlePatchRow>>)[bundleId] ??
238
+ [];
233
239
  return mapRowToBundle(row, patches);
234
- }
240
+ }
235
241
 
236
242
  @override
237
243
  Future<Paginated<List<Bundle>>> getBundles(
@@ -239,9 +245,10 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
239
245
  ) async {
240
246
  final where = options.where;
241
247
  final limit = options.limit;
242
- final orderBy = options.orderBy ??
243
- const DatabaseBundleQueryOrder(direction: 'desc');
244
- final offset = options.offset ??
248
+ final orderBy =
249
+ options.orderBy ?? const DatabaseBundleQueryOrder(direction: 'desc');
250
+ final offset =
251
+ options.offset ??
245
252
  (options.page != null ? (options.page! - 1) * limit : 0);
246
253
 
247
254
  if ((where?.targetAppVersionIn != null &&
@@ -279,7 +286,9 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
279
286
  data.map((b) => (b as Map)['id'] as String).toList(),
280
287
  );
281
288
  final bundles = data.map((b) {
282
- final row = SupabaseBundleRow.fromJson((b as Map).cast<String, dynamic>());
289
+ final row = SupabaseBundleRow.fromJson(
290
+ (b as Map).cast<String, dynamic>(),
291
+ );
283
292
  return mapRowToBundle(row, patchMap[row.id] ?? []);
284
293
  }).toList();
285
294
 
@@ -339,15 +348,11 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
339
348
  throw createSupabaseError(res.error!);
340
349
  }
341
350
  final data = (res.data as List?) ?? [];
342
- return data
343
- .map((row) => (row as Map)['channel'] as String)
344
- .toList();
351
+ return data.map((row) => (row as Map)['channel'] as String).toList();
345
352
  }
346
353
 
347
354
  @override
348
- Future<void> commitBundle({
349
- required List<BundleChange> changedSets,
350
- }) async {
355
+ Future<void> commitBundle({required List<BundleChange> changedSets}) async {
351
356
  if (changedSets.isEmpty) return;
352
357
 
353
358
  for (final op in changedSets) {
@@ -405,9 +410,12 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
405
410
  if (patchRows.isNotEmpty) {
406
411
  final patchUpsert = await supabase
407
412
  .from('bundle_patches')
408
- .upsert(patchRows.map((p) => p.toJson()).toList(),
409
- onConflict: 'id');
410
- if (patchUpsert is SupabaseResponseLike && patchUpsert.error != null) {
413
+ .upsert(
414
+ patchRows.map((p) => p.toJson()).toList(),
415
+ onConflict: 'id',
416
+ );
417
+ if (patchUpsert is SupabaseResponseLike &&
418
+ patchUpsert.error != null) {
411
419
  throw createSupabaseError(patchUpsert.error!);
412
420
  } else if (patchUpsert is Map && patchUpsert['error'] != null) {
413
421
  throw createSupabaseError(patchUpsert['error']!);
@@ -6,6 +6,7 @@ import 'package:flutter_ota_kit_plugin_core/flutter_ota_kit_plugin_core.dart';
6
6
  import 'supabase_client_adapter.dart';
7
7
  import 'supabase_client_http.dart';
8
8
  import 'supabase_signed_url_batcher.dart';
9
+ import 'error_message.dart' show errorMessage;
9
10
 
10
11
  /// Config for the Supabase edge-function storage plugin.
11
12
  class SupabaseEdgeFunctionStorageConfig {
@@ -31,26 +32,27 @@ ParsedStorageUri parseSupabaseEdgeStorageUri(String storageUri) =>
31
32
  /// (no node profile) backed by the Supabase client.
32
33
  final supabaseEdgeFunctionStorage =
33
34
  createRuntimeStoragePlugin<SupabaseEdgeFunctionStorageConfig>(
34
- name: 'supabaseEdgeFunctionStorage',
35
- supportedProtocol: 'supabase-storage',
36
- factory: (config) {
37
- final supabase = config.clientFactory(
38
- config.supabaseUrl,
39
- config.supabaseServiceRoleKey,
40
- );
41
- final resolveSignedUrl = createSupabaseSignedUrlBatcher(
42
- createSignedUrls: (bucketName, keys, expiresIn) =>
43
- supabase.storage.from(bucketName).createSignedUrls(keys, expiresIn),
44
- expiresIn: config.signedUrlExpiresIn ?? 3600,
45
- formatObjectPath: (bucketName, key) => '$bucketName/$key',
46
- );
35
+ name: 'supabaseEdgeFunctionStorage',
36
+ supportedProtocol: 'supabase-storage',
37
+ factory: (config) {
38
+ final supabase = config.clientFactory(
39
+ config.supabaseUrl,
40
+ config.supabaseServiceRoleKey,
41
+ );
42
+ final resolveSignedUrl = createSupabaseSignedUrlBatcher(
43
+ createSignedUrls: (bucketName, keys, expiresIn) => supabase.storage
44
+ .from(bucketName)
45
+ .createSignedUrls(keys, expiresIn),
46
+ expiresIn: config.signedUrlExpiresIn ?? 3600,
47
+ formatObjectPath: (bucketName, key) => '$bucketName/$key',
48
+ );
47
49
 
48
- return RuntimeStorageProfileImplEdge(
49
- supabase: supabase,
50
- resolveSignedUrl: resolveSignedUrl,
50
+ return RuntimeStorageProfileImplEdge(
51
+ supabase: supabase,
52
+ resolveSignedUrl: resolveSignedUrl,
53
+ );
54
+ },
51
55
  );
52
- },
53
- );
54
56
 
55
57
  class RuntimeStorageProfileImplEdge implements RuntimeStorageProfile {
56
58
  RuntimeStorageProfileImplEdge({
@@ -69,8 +71,7 @@ class RuntimeStorageProfileImplEdge implements RuntimeStorageProfile {
69
71
  }
70
72
  final res = await supabase.storage.from(parsed.bucket).download(parsed.key);
71
73
  if (res.error != null) {
72
- final msg = (res.message ?? _errorToString(res.error))
73
- .toString();
74
+ final msg = (res.message ?? errorMessage(res.error)).toString();
74
75
  if (msg.contains('not found')) return null;
75
76
  throw StateError('Failed to read storage text: $msg');
76
77
  }
@@ -87,13 +88,4 @@ class RuntimeStorageProfileImplEdge implements RuntimeStorageProfile {
87
88
  final signedUrl = await resolveSignedUrl(parsed.bucket, parsed.key);
88
89
  return {'fileUrl': signedUrl};
89
90
  }
90
-
91
- String _errorToString(Object? error) {
92
- if (error is Error) return error.toString();
93
- if (error is Exception) return error.toString();
94
- if (error is Map && error['message'] is String) {
95
- return error['message'] as String;
96
- }
97
- return error.toString();
98
- }
99
91
  }
@@ -4,6 +4,7 @@ library;
4
4
  import 'dart:async';
5
5
 
6
6
  import 'supabase_client_adapter.dart';
7
+ import 'error_message.dart' show errorMessage;
7
8
 
8
9
  /// A pending signed URL request waiting for the next flush.
9
10
  class _PendingSignedUrl {
@@ -18,16 +19,6 @@ class _PendingSignedUrl {
18
19
  });
19
20
  }
20
21
 
21
- /// Extract a human-readable message from an unknown error value.
22
- String _getErrorMessage(Object? error) {
23
- if (error is Error) return error.toString();
24
- if (error is Exception) return error.toString();
25
- if (error is Map && error['message'] is String) {
26
- return error['message'] as String;
27
- }
28
- return error.toString();
29
- }
30
-
31
22
  /// Signature of the batch signed URL generator. Returns the same shape as the
32
23
  /// Supabase storage `createSignedUrls` call: a list of results aligned to the
33
24
  /// input key order.
@@ -53,14 +44,10 @@ ResolveSignedUrl createSupabaseSignedUrlBatcher({
53
44
  var pendingByBucket = <String, List<_PendingSignedUrl>>{};
54
45
  var flushScheduled = false;
55
46
 
56
- Object createSignedUrlError(
57
- String bucketName,
58
- String key,
59
- Object error,
60
- ) =>
47
+ Object createSignedUrlError(String bucketName, String key, Object error) =>
61
48
  StateError(
62
49
  'Failed to generate download URL for '
63
- '"${formatObjectPath(bucketName, key)}": ${_getErrorMessage(error)}',
50
+ '"${formatObjectPath(bucketName, key)}": ${errorMessage(error)}',
64
51
  );
65
52
 
66
53
  Future<void> flush() async {
@@ -109,7 +96,9 @@ ResolveSignedUrl createSupabaseSignedUrlBatcher({
109
96
  }
110
97
  } catch (error) {
111
98
  for (final request in pending) {
112
- request.reject(createSignedUrlError(bucketName, request.key, error));
99
+ request.reject(
100
+ createSignedUrlError(bucketName, request.key, error),
101
+ );
113
102
  }
114
103
  }
115
104
  }),
@@ -118,8 +107,7 @@ ResolveSignedUrl createSupabaseSignedUrlBatcher({
118
107
 
119
108
  return (String bucketName, String key) {
120
109
  final completer = Completer<String>();
121
- final pending =
122
- pendingByBucket[bucketName] ?? <_PendingSignedUrl>[];
110
+ final pending = pendingByBucket[bucketName] ?? <_PendingSignedUrl>[];
123
111
  pending.add(
124
112
  _PendingSignedUrl(
125
113
  key: key,
@@ -9,6 +9,7 @@ import 'supabase_client_adapter.dart';
9
9
  import 'supabase_client_http.dart';
10
10
  import 'supabase_config.dart';
11
11
  import 'supabase_signed_url_batcher.dart';
12
+ import 'error_message.dart' show errorMessage;
12
13
 
13
14
  /// Parse a `supabase-storage://` URI into bucket + key.
14
15
  ParsedStorageUri parseSupabaseStorageUri(String storageUri) =>
@@ -31,15 +32,6 @@ class SupabaseStorageConfig extends SupabaseServiceRoleConfig {
31
32
  });
32
33
  }
33
34
 
34
- String _getErrorMessage(Object? error) {
35
- if (error is Error) return error.toString();
36
- if (error is Exception) return error.toString();
37
- if (error is Map && error['message'] is String) {
38
- return error['message'] as String;
39
- }
40
- return error.toString();
41
- }
42
-
43
35
  Future<String> _createSignedUrlOrThrow({
44
36
  required SupabaseStorageBucketLike bucket,
45
37
  required String key,
@@ -61,7 +53,7 @@ Future<String> _createSignedUrlOrThrow({
61
53
 
62
54
  throw StateError(
63
55
  'Failed to generate download URL for "$key": '
64
- '${_getErrorMessage(error ?? StateError("missing signed URL"))}',
56
+ '${errorMessage(error ?? StateError("missing signed URL"))}',
65
57
  );
66
58
  }
67
59
 
@@ -69,11 +61,7 @@ Future<void> _verifyObjectCanBeSignedForRuntime({
69
61
  required SupabaseStorageBucketLike bucket,
70
62
  required String key,
71
63
  }) async {
72
- await _createSignedUrlOrThrow(
73
- bucket: bucket,
74
- key: key,
75
- expiresIn: 3600,
76
- );
64
+ await _createSignedUrlOrThrow(bucket: bucket, key: key, expiresIn: 3600);
77
65
  }
78
66
 
79
67
  /// Supabase-backed universal (node + runtime) storage plugin.
@@ -126,12 +114,14 @@ class NodeStorageProfileImpl implements NodeStorageProfile {
126
114
  final Future<void> Function({
127
115
  required SupabaseStorageBucketLike bucket,
128
116
  required String key,
129
- }) verifySigned;
117
+ })
118
+ verifySigned;
130
119
  final Future<String> Function({
131
120
  required SupabaseStorageBucketLike bucket,
132
121
  required String key,
133
122
  required int expiresIn,
134
- }) createSignedUrlOrThrow;
123
+ })
124
+ createSignedUrlOrThrow;
135
125
 
136
126
  @override
137
127
  Future<Map<String, String>> upload(String key, String filePath) async {
@@ -147,13 +137,11 @@ class NodeStorageProfileImpl implements NodeStorageProfile {
147
137
  cacheControl: 'max-age=31536000',
148
138
  );
149
139
  if (upload.error != null) {
150
- throw StateError(_getErrorMessage(upload.error));
140
+ throw StateError(errorMessage(upload.error));
151
141
  }
152
142
 
153
143
  await verifySigned(bucket: bucket, key: storageKey);
154
- return {
155
- 'storageUri': 'supabase-storage://$bucketName/$storageKey',
156
- };
144
+ return {'storageUri': 'supabase-storage://$bucketName/$storageKey'};
157
145
  }
158
146
 
159
147
  @override
@@ -168,7 +156,7 @@ class NodeStorageProfileImpl implements NodeStorageProfile {
168
156
  final res = await bucket.exists(parsed.key);
169
157
  if (res.data == false) return false;
170
158
  if (res.error != null) {
171
- throw StateError(_getErrorMessage(res.error));
159
+ throw StateError(errorMessage(res.error));
172
160
  }
173
161
  await verifySigned(bucket: bucket, key: parsed.key);
174
162
  return res.data as bool;
@@ -185,7 +173,7 @@ class NodeStorageProfileImpl implements NodeStorageProfile {
185
173
  }
186
174
  final res = await bucket.remove([parsed.key]);
187
175
  if (res.error != null) {
188
- final msg = res.message ?? _getErrorMessage(res.error);
176
+ final msg = res.message ?? errorMessage(res.error);
189
177
  if (msg.contains('not found')) {
190
178
  throw StateError('Bundle not found');
191
179
  }
@@ -209,7 +197,10 @@ class NodeStorageProfileImpl implements NodeStorageProfile {
209
197
  if (res.data == null) {
210
198
  throw StateError('Failed to download bundle');
211
199
  }
212
- final dir = filePath.substring(0, filePath.lastIndexOf(io.Platform.pathSeparator));
200
+ final dir = filePath.substring(
201
+ 0,
202
+ filePath.lastIndexOf(io.Platform.pathSeparator),
203
+ );
213
204
  await io.Directory(dir).create(recursive: true);
214
205
  await io.File(filePath).writeAsBytes(res.data!);
215
206
  }
@@ -218,9 +209,7 @@ class NodeStorageProfileImpl implements NodeStorageProfile {
218
209
  Future<List<StorageObject>> listObjects([String? prefix]) async {
219
210
  final res = await bucket.list(prefix);
220
211
  if (res.error != null) {
221
- throw StateError(
222
- 'Failed to list objects: ${_getErrorMessage(res.error)}',
223
- );
212
+ throw StateError('Failed to list objects: ${errorMessage(res.error)}');
224
213
  }
225
214
  final data = res.data ?? const <SupabaseStorageObject>[];
226
215
  return data
@@ -242,7 +231,7 @@ class NodeStorageProfileImpl implements NodeStorageProfile {
242
231
  if (keys.isEmpty) return;
243
232
  final res = await bucket.remove(keys);
244
233
  if (res.error != null) {
245
- final msg = res.message ?? _getErrorMessage(res.error);
234
+ final msg = res.message ?? errorMessage(res.error);
246
235
  if (!msg.contains('not found')) {
247
236
  throw StateError('Failed to delete objects: $msg');
248
237
  }
@@ -272,7 +261,7 @@ class RuntimeStorageProfileImpl implements RuntimeStorageProfile {
272
261
  }
273
262
  final res = await bucket.download(parsed.key);
274
263
  if (res.error != null) {
275
- final msg = res.message ?? _getErrorMessage(res.error);
264
+ final msg = res.message ?? errorMessage(res.error);
276
265
  if (msg.contains('not found')) return null;
277
266
  throw StateError('Failed to read storage text: $msg');
278
267
  }
@@ -67,24 +67,24 @@ class SupabaseBundleRow {
67
67
  );
68
68
 
69
69
  Map<String, dynamic> toJson() => {
70
- 'id': id,
71
- 'channel': channel,
72
- 'enabled': enabled,
73
- 'should_force_update': shouldForceUpdate,
74
- 'file_hash': fileHash,
75
- 'git_commit_hash': gitCommitHash,
76
- 'message': message,
77
- 'platform': platform,
78
- 'target_app_version': targetAppVersion,
79
- 'fingerprint_hash': fingerprintHash,
80
- 'storage_uri': storageUri,
81
- 'metadata': metadata,
82
- 'manifest_storage_uri': manifestStorageUri,
83
- 'manifest_file_hash': manifestFileHash,
84
- 'asset_base_storage_uri': assetBaseStorageUri,
85
- 'rollout_cohort_count': rolloutCohortCount,
86
- 'target_cohorts': targetCohorts,
87
- };
70
+ 'id': id,
71
+ 'channel': channel,
72
+ 'enabled': enabled,
73
+ 'should_force_update': shouldForceUpdate,
74
+ 'file_hash': fileHash,
75
+ 'git_commit_hash': gitCommitHash,
76
+ 'message': message,
77
+ 'platform': platform,
78
+ 'target_app_version': targetAppVersion,
79
+ 'fingerprint_hash': fingerprintHash,
80
+ 'storage_uri': storageUri,
81
+ 'metadata': metadata,
82
+ 'manifest_storage_uri': manifestStorageUri,
83
+ 'manifest_file_hash': manifestFileHash,
84
+ 'asset_base_storage_uri': assetBaseStorageUri,
85
+ 'rollout_cohort_count': rolloutCohortCount,
86
+ 'target_cohorts': targetCohorts,
87
+ };
88
88
  }
89
89
 
90
90
  /// Row type from Supabase `bundle_patches` table.
@@ -119,12 +119,12 @@ class SupabaseBundlePatchRow {
119
119
  );
120
120
 
121
121
  Map<String, dynamic> toJson() => {
122
- 'id': id,
123
- 'bundle_id': bundleId,
124
- 'base_bundle_id': baseBundleId,
125
- 'base_file_hash': baseFileHash,
126
- 'patch_file_hash': patchFileHash,
127
- 'patch_storage_uri': patchStorageUri,
128
- 'order_index': orderIndex,
129
- };
122
+ 'id': id,
123
+ 'bundle_id': bundleId,
124
+ 'base_bundle_id': baseBundleId,
125
+ 'base_file_hash': baseFileHash,
126
+ 'patch_file_hash': patchFileHash,
127
+ 'patch_storage_uri': patchStorageUri,
128
+ 'order_index': orderIndex,
129
+ };
130
130
  }
@@ -1,22 +1,27 @@
1
1
  name: flutter_ota_kit_supabase
2
2
  description: Supabase plugin for flutter_ota_kit — database + storage backends.
3
- version: 0.1.1
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.3
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.3.0 <4.0.0"
11
+ sdk: ">=3.13.2 <4.0.0"
13
12
 
14
13
  dependencies:
15
- flutter_ota_kit_core: ^0.1.0
16
- flutter_ota_kit_plugin_core: ^0.0.1
14
+ flutter_ota_kit_core: ^0.1.1
15
+ flutter_ota_kit_plugin_core: ^0.0.3
17
16
  http: ^1.2.0
18
17
  supabase: ^2.14.0
19
18
 
20
19
  dev_dependencies:
21
20
  test: ^1.25.0
22
21
  lints: ^4.0.0
22
+
23
+ dependency_overrides:
24
+ flutter_ota_kit_core:
25
+ path: ../../packages/core
26
+ flutter_ota_kit_plugin_core:
27
+ path: ../plugin-core