@_nazmiforreal/flutter-ota 0.1.12 → 0.1.14

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 (177) 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/CHANGELOG.md +9 -0
  48. package/dart-src/packages/core/README.md +2 -2
  49. package/dart-src/packages/core/example/lib/main.dart +69 -0
  50. package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +1 -1
  51. package/dart-src/packages/core/lib/src/app_update_info.dart +17 -15
  52. package/dart-src/packages/core/lib/src/bundle.dart +102 -94
  53. package/dart-src/packages/core/lib/src/bundle_artifacts.dart +1 -2
  54. package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +15 -16
  55. package/dart-src/packages/core/lib/src/changed_asset.dart +24 -22
  56. package/dart-src/packages/core/lib/src/get_bundles_args.dart +13 -13
  57. package/dart-src/packages/core/lib/src/metadata.dart +9 -7
  58. package/dart-src/packages/core/lib/src/patch_info.dart +8 -7
  59. package/dart-src/packages/core/lib/src/platform.dart +4 -3
  60. package/dart-src/packages/core/lib/src/rollout.dart +14 -11
  61. package/dart-src/packages/core/lib/src/semver_range.dart +49 -39
  62. package/dart-src/packages/core/lib/src/semver_version.dart +8 -7
  63. package/dart-src/packages/core/lib/src/status.dart +4 -3
  64. package/dart-src/packages/core/lib/src/update_bundle_params.dart +7 -7
  65. package/dart-src/packages/core/lib/src/uuid.dart +1 -2
  66. package/dart-src/packages/core/pubspec.yaml +6 -7
  67. package/dart-src/packages/core/test/rollout_test.dart +15 -8
  68. package/dart-src/packages/core/test/semver_test.dart +5 -2
  69. package/dart-src/packages/core/test/types_test.dart +26 -24
  70. package/dart-src/packages/core/test/uuid_test.dart +3 -1
  71. package/dart-src/packages/core/tool/dbg.dart +1 -0
  72. package/dart-src/plugins/aws/CHANGELOG.md +15 -0
  73. package/dart-src/plugins/aws/example/lib/main.dart +60 -0
  74. package/dart-src/plugins/aws/lib/flutter_ota_kit_aws.dart +5 -1
  75. package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +11 -16
  76. package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +7 -2
  77. package/dart-src/plugins/aws/lib/src/aws_database.dart +14 -18
  78. package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +23 -18
  79. package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +5 -4
  80. package/dart-src/plugins/aws/lib/src/aws_storage.dart +2 -3
  81. package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +10 -16
  82. package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +6 -9
  83. package/dart-src/plugins/aws/pubspec.yaml +15 -10
  84. package/dart-src/plugins/aws/test/aws_database_test.dart +51 -57
  85. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +8 -13
  86. package/dart-src/plugins/cloudflare/CHANGELOG.md +15 -0
  87. package/dart-src/plugins/cloudflare/example/lib/main.dart +62 -0
  88. package/dart-src/plugins/cloudflare/lib/flutter_ota_kit_cloudflare.dart +4 -2
  89. package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +3 -3
  90. package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +3 -2
  91. package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +24 -27
  92. package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +22 -30
  93. package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +1 -1
  94. package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +21 -29
  95. package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +7 -7
  96. package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +7 -15
  97. package/dart-src/plugins/cloudflare/pubspec.yaml +14 -9
  98. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +46 -42
  99. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +100 -90
  100. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +23 -18
  101. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +8 -13
  102. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +1 -4
  103. package/dart-src/plugins/plugin-core/CHANGELOG.md +15 -1
  104. package/dart-src/plugins/plugin-core/example/lib/main.dart +61 -0
  105. package/dart-src/plugins/plugin-core/lib/flutter_ota_kit_plugin_core.dart +3 -7
  106. package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +5 -5
  107. package/dart-src/plugins/plugin-core/lib/src/bundle_helpers.dart +23 -0
  108. package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +16 -10
  109. package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +31 -26
  110. package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +5 -1
  111. package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +1 -1
  112. package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +78 -71
  113. package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +56 -35
  114. package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +10 -13
  115. package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +30 -17
  116. package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +3 -1
  117. package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +5 -3
  118. package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +2 -4
  119. package/dart-src/plugins/plugin-core/lib/src/storage_helpers.dart +18 -0
  120. package/dart-src/plugins/plugin-core/lib/src/types.dart +7 -6
  121. package/dart-src/plugins/plugin-core/pubspec.yaml +12 -9
  122. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +13 -9
  123. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +28 -15
  124. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +254 -122
  125. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +36 -32
  126. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +18 -15
  127. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +8 -3
  128. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +682 -693
  129. package/dart-src/plugins/plugin-core/test/helpers_test.dart +45 -21
  130. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +76 -13
  131. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +20 -9
  132. package/dart-src/plugins/pocketbase/CHANGELOG.md +8 -0
  133. package/dart-src/plugins/pocketbase/LICENSE +21 -0
  134. package/dart-src/plugins/pocketbase/README.md +54 -0
  135. package/dart-src/plugins/pocketbase/example/lib/main.dart +62 -0
  136. package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +28 -0
  137. package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +121 -0
  138. package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +349 -0
  139. package/dart-src/plugins/pocketbase/lib/src/pocketbase_config.dart +61 -0
  140. package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +278 -0
  141. package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +255 -0
  142. package/dart-src/plugins/pocketbase/pubspec.yaml +26 -0
  143. package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +12 -0
  144. package/dart-src/plugins/postgres/CHANGELOG.md +16 -0
  145. package/dart-src/plugins/postgres/example/lib/main.dart +67 -0
  146. package/dart-src/plugins/postgres/lib/flutter_ota_kit_postgres.dart +3 -6
  147. package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +32 -52
  148. package/dart-src/plugins/postgres/lib/src/postgres_config.dart +3 -1
  149. package/dart-src/plugins/postgres/lib/src/postgres_database.dart +28 -38
  150. package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +16 -14
  151. package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +26 -14
  152. package/dart-src/plugins/postgres/lib/src/postgres_types.dart +27 -28
  153. package/dart-src/plugins/postgres/pubspec.yaml +14 -9
  154. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +62 -39
  155. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +3 -4
  156. package/dart-src/plugins/postgres/test/postgres_database_test.dart +102 -73
  157. package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +60 -0
  158. package/dart-src/plugins/supabase/CHANGELOG.md +20 -0
  159. package/dart-src/plugins/supabase/README.md +2 -2
  160. package/dart-src/plugins/supabase/example/lib/main.dart +63 -0
  161. package/dart-src/plugins/supabase/lib/flutter_ota_kit_supabase.dart +2 -0
  162. package/dart-src/plugins/supabase/lib/src/error_message.dart +11 -0
  163. package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +34 -58
  164. package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +3 -13
  165. package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +34 -52
  166. package/dart-src/plugins/supabase/lib/src/supabase_database.dart +38 -30
  167. package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +21 -29
  168. package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +7 -19
  169. package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +18 -29
  170. package/dart-src/plugins/supabase/lib/src/types.dart +26 -26
  171. package/dart-src/plugins/supabase/pubspec.yaml +14 -9
  172. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +57 -57
  173. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +30 -27
  174. package/dart-src/plugins/supabase/test/supabase_database_test.dart +26 -35
  175. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +27 -34
  176. package/package.json +3 -3
  177. package/scripts/vendor.sh +1 -1
@@ -33,7 +33,8 @@ import 'postgres_types.dart';
33
33
  DatabasePlugin Function() Function(
34
34
  PostgresConfig config, [
35
35
  DatabasePluginHooks? hooks,
36
- ]) postgresDatabase = createDatabasePlugin<PostgresConfig>(
36
+ ])
37
+ postgresDatabase = createDatabasePlugin<PostgresConfig>(
37
38
  name: 'postgresDatabase',
38
39
  factory: (config) {
39
40
  final client = config.clientFactory != null
@@ -95,18 +96,10 @@ DatabasePlugin Function() Function(
95
96
  add('enabled = ?', where.enabled);
96
97
  }
97
98
  if (where.fingerprintHash != null) {
98
- if (where.fingerprintHash == null) {
99
- conditions.add('fingerprint_hash IS NULL');
100
- } else {
101
- add('fingerprint_hash = ?', where.fingerprintHash);
102
- }
99
+ add('fingerprint_hash = ?', where.fingerprintHash);
103
100
  }
104
101
  if (where.targetAppVersion != null) {
105
- if (where.targetAppVersion == null) {
106
- conditions.add('target_app_version IS NULL');
107
- } else {
108
- add('target_app_version = ?', where.targetAppVersion);
109
- }
102
+ add('target_app_version = ?', where.targetAppVersion);
110
103
  }
111
104
  if (where.targetAppVersionIn != null) {
112
105
  add('target_app_version = ANY(?)', where.targetAppVersionIn);
@@ -134,12 +127,10 @@ class _PostgresDatabasePlugin implements AbstractDatabasePlugin {
134
127
  final PostgresClientLike client;
135
128
  final Future<Map<String, List<PostgresBundlePatchRow>>> Function(
136
129
  List<String> bundleIds,
137
- ) fetchPatchMap;
130
+ )
131
+ fetchPatchMap;
138
132
 
139
- _PostgresDatabasePlugin({
140
- required this.client,
141
- required this.fetchPatchMap,
142
- });
133
+ _PostgresDatabasePlugin({required this.client, required this.fetchPatchMap});
143
134
 
144
135
  @override
145
136
  bool get supportsCursorPagination => false;
@@ -151,17 +142,16 @@ class _PostgresDatabasePlugin implements AbstractDatabasePlugin {
151
142
  @override
152
143
  Future<Bundle?> getBundleById(String bundleId) async {
153
144
  final results = await Future.wait([
154
- client.execute(
155
- 'SELECT * FROM bundles WHERE id = @p0',
156
- {'p0': bundleId},
157
- ),
145
+ client.execute('SELECT * FROM bundles WHERE id = @p0', {'p0': bundleId}),
158
146
  fetchPatchMap([bundleId]),
159
147
  ]);
160
148
 
161
149
  final data = results[0] as List<Map<String, dynamic>>;
162
150
  if (data.isEmpty) return null;
163
151
  final row = PostgresBundleRow.fromJson(data.first);
164
- final patches = (results[1] as Map<String, List<PostgresBundlePatchRow>>)[bundleId] ?? [];
152
+ final patches =
153
+ (results[1] as Map<String, List<PostgresBundlePatchRow>>)[bundleId] ??
154
+ [];
165
155
  return mapRowToBundle(row, patches);
166
156
  }
167
157
 
@@ -174,9 +164,11 @@ class _PostgresDatabasePlugin implements AbstractDatabasePlugin {
174
164
  final orderBy =
175
165
  options.orderBy ?? const DatabaseBundleQueryOrder(direction: 'desc');
176
166
  final offset =
177
- options.offset ?? (options.page != null ? (options.page! - 1) * limit : 0);
167
+ options.offset ??
168
+ (options.page != null ? (options.page! - 1) * limit : 0);
178
169
 
179
- if ((where?.targetAppVersionIn != null && where!.targetAppVersionIn!.isEmpty) ||
170
+ if ((where?.targetAppVersionIn != null &&
171
+ where!.targetAppVersionIn!.isEmpty) ||
180
172
  (where?.id?.ins != null && where!.id!.ins!.isEmpty)) {
181
173
  return Paginated(
182
174
  data: const [],
@@ -230,18 +222,15 @@ class _PostgresDatabasePlugin implements AbstractDatabasePlugin {
230
222
  }
231
223
 
232
224
  @override
233
- Future<void> commitBundle({
234
- required List<BundleChange> changedSets,
235
- }) async {
225
+ Future<void> commitBundle({required List<BundleChange> changedSets}) async {
236
226
  if (changedSets.isEmpty) return;
237
227
 
238
228
  await client.runTx((tx) async {
239
229
  for (final op in changedSets) {
240
230
  if (op.operation == BundleChangeOperation.delete) {
241
- await tx.execute(
242
- 'DELETE FROM bundle_patches WHERE bundle_id = @p0',
243
- {'p0': op.data.id},
244
- );
231
+ await tx.execute('DELETE FROM bundle_patches WHERE bundle_id = @p0', {
232
+ 'p0': op.data.id,
233
+ });
245
234
  await tx.execute(
246
235
  'DELETE FROM bundle_patches WHERE base_bundle_id = @p0',
247
236
  {'p0': op.data.id},
@@ -258,8 +247,9 @@ class _PostgresDatabasePlugin implements AbstractDatabasePlugin {
258
247
  final values = bundleToRowValues(bundle);
259
248
  final columns = values.keys.toList();
260
249
  final insertCols = columns.join(', ');
261
- final placeholders =
262
- columns.map((c) => '@${c.replaceAll('.', '_')}').join(', ');
250
+ final placeholders = columns
251
+ .map((c) => '@${c.replaceAll('.', '_')}')
252
+ .join(', ');
263
253
  final updateSets = columns
264
254
  .where((c) => c != 'id')
265
255
  .map((c) => '$c = EXCLUDED.$c')
@@ -270,17 +260,17 @@ class _PostgresDatabasePlugin implements AbstractDatabasePlugin {
270
260
  {for (final c in columns) c.replaceAll('.', '_'): values[c]},
271
261
  );
272
262
 
273
- await tx.execute(
274
- 'DELETE FROM bundle_patches WHERE bundle_id = @p0',
275
- {'p0': bundle.id},
276
- );
263
+ await tx.execute('DELETE FROM bundle_patches WHERE bundle_id = @p0', {
264
+ 'p0': bundle.id,
265
+ });
277
266
  final patchRows = bundleToPatchRows(bundle);
278
267
  if (patchRows.isNotEmpty) {
279
268
  for (final pr in patchRows) {
280
269
  final patchCols = pr.keys.toList();
281
270
  final patchInsert = patchCols.join(', ');
282
- final patchPlaceholders =
283
- patchCols.map((c) => '@${c.replaceAll('.', '_')}').join(', ');
271
+ final patchPlaceholders = patchCols
272
+ .map((c) => '@${c.replaceAll('.', '_')}')
273
+ .join(', ');
284
274
  await tx.execute(
285
275
  'INSERT INTO bundle_patches ($patchInsert) '
286
276
  'VALUES ($patchPlaceholders) '
@@ -31,13 +31,13 @@ class _UpdateInfoRow {
31
31
  });
32
32
 
33
33
  factory _UpdateInfoRow.fromJson(Map<String, dynamic> row) => _UpdateInfoRow(
34
- id: row['id'] as String,
35
- shouldForceUpdate: row['should_force_update'] as bool,
36
- message: row['message'] as String?,
37
- status: row['status'] as String,
38
- storageUri: row['storage_uri'] as String?,
39
- fileHash: row['file_hash'] as String?,
40
- );
34
+ id: row['id'] as String,
35
+ shouldForceUpdate: row['should_force_update'] as bool,
36
+ message: row['message'] as String?,
37
+ status: row['status'] as String,
38
+ storageUri: row['storage_uri'] as String?,
39
+ fileHash: row['file_hash'] as String?,
40
+ );
41
41
 
42
42
  final String id;
43
43
  final bool shouldForceUpdate;
@@ -48,13 +48,15 @@ class _UpdateInfoRow {
48
48
  }
49
49
 
50
50
  UpdateInfo mapUpdateInfoRow(_UpdateInfoRow row) => UpdateInfo(
51
- id: row.id,
52
- shouldForceUpdate: row.shouldForceUpdate,
53
- message: row.message,
54
- status: row.status == 'ROLLBACK' ? UpdateStatus.rollback : UpdateStatus.update,
55
- storageUri: row.storageUri,
56
- fileHash: row.fileHash,
57
- );
51
+ id: row.id,
52
+ shouldForceUpdate: row.shouldForceUpdate,
53
+ message: row.message,
54
+ status: row.status == 'ROLLBACK'
55
+ ? UpdateStatus.rollback
56
+ : UpdateStatus.update,
57
+ storageUri: row.storageUri,
58
+ fileHash: row.fileHash,
59
+ );
58
60
 
59
61
  /// Resolve update info via the Postgres RPCs.
60
62
  Future<UpdateInfo?> getUpdateInfo(
@@ -29,7 +29,8 @@ class PostgresStorageConfig {
29
29
 
30
30
  /// Resolves the storage client for a config (test seam via [PostgresConfig.clientFactory]).
31
31
  PostgresClientLike resolvePostgresStorageClient(PostgresStorageConfig config) =>
32
- config.db.clientFactory?.call(config.db) ?? PostgresClient.connect(config.db);
32
+ config.db.clientFactory?.call(config.db) ??
33
+ PostgresClient.connect(config.db);
33
34
 
34
35
  String _storageUri(String key, String? servingBaseUrl) {
35
36
  if (servingBaseUrl != null && servingBaseUrl.isNotEmpty) {
@@ -52,8 +53,9 @@ String _strip(String storageUri, String? servingBaseUrl) {
52
53
  return storageUri.substring('postgres://'.length);
53
54
  }
54
55
  if (servingBaseUrl != null && storageUri.startsWith(servingBaseUrl)) {
55
- final base =
56
- servingBaseUrl.endsWith('/') ? servingBaseUrl : '$servingBaseUrl/';
56
+ final base = servingBaseUrl.endsWith('/')
57
+ ? servingBaseUrl
58
+ : '$servingBaseUrl/';
57
59
  return storageUri.startsWith(base)
58
60
  ? storageUri.substring(base.length)
59
61
  : storageUri;
@@ -71,8 +73,7 @@ NodeStorageProfile createPostgresNodeProfile(
71
73
  PostgresClientLike client,
72
74
  String? basePath,
73
75
  String? servingBaseUrl,
74
- ) =>
75
- _PostgresNodeProfile(client, basePath, servingBaseUrl);
76
+ ) => _PostgresNodeProfile(client, basePath, servingBaseUrl);
76
77
 
77
78
  class _PostgresNodeProfile implements NodeStorageProfile {
78
79
  _PostgresNodeProfile(this._client, this._basePath, this._servingBaseUrl);
@@ -92,11 +93,7 @@ class _PostgresNodeProfile implements NodeStorageProfile {
92
93
  'INSERT INTO flutter_ota_kit_storage (key, data, content_type) '
93
94
  'VALUES (@key, @data, @ct) '
94
95
  'ON CONFLICT (key) DO UPDATE SET data = EXCLUDED.data, content_type = EXCLUDED.content_type',
95
- {
96
- '@key': fullKey,
97
- '@data': bytes,
98
- '@ct': _contentType(fullKey),
99
- },
96
+ {'@key': fullKey, '@data': bytes, '@ct': _contentType(fullKey)},
100
97
  );
101
98
  return {'storageUri': _storageUri(fullKey, _servingBaseUrl)};
102
99
  }
@@ -164,8 +161,7 @@ class _PostgresNodeProfile implements NodeStorageProfile {
164
161
  RuntimeStorageProfile createPostgresRuntimeProfile(
165
162
  PostgresClientLike client,
166
163
  String? servingBaseUrl,
167
- ) =>
168
- _PostgresRuntimeProfile(client, servingBaseUrl);
164
+ ) => _PostgresRuntimeProfile(client, servingBaseUrl);
169
165
 
170
166
  class _PostgresRuntimeProfile implements RuntimeStorageProfile {
171
167
  _PostgresRuntimeProfile(this._client, this._servingBaseUrl);
@@ -175,7 +171,19 @@ class _PostgresRuntimeProfile implements RuntimeStorageProfile {
175
171
 
176
172
  @override
177
173
  Future<Map<String, String>> getDownloadUrl(String storageUri) async {
178
- return {'fileUrl': _storageUri(_strip(storageUri, _servingBaseUrl), _servingBaseUrl)};
174
+ if (_servingBaseUrl == null || _servingBaseUrl.isEmpty) {
175
+ throw StateError(
176
+ 'Postgres storage requires a servingBaseUrl to expose bytes over HTTP. '
177
+ 'Set `postgres.servingBaseUrl` in your config to a thin proxy that reads '
178
+ 'from the `flutter_ota_kit_storage` table.',
179
+ );
180
+ }
181
+ return {
182
+ 'fileUrl': _storageUri(
183
+ _strip(storageUri, _servingBaseUrl),
184
+ _servingBaseUrl,
185
+ ),
186
+ };
179
187
  }
180
188
 
181
189
  @override
@@ -198,7 +206,11 @@ StoragePlugin Function(PostgresStorageConfig) postgresStorage =
198
206
  factory: (cfg) {
199
207
  final client = resolvePostgresStorageClient(cfg);
200
208
  return (
201
- node: createPostgresNodeProfile(client, cfg.basePath, cfg.servingBaseUrl),
209
+ node: createPostgresNodeProfile(
210
+ client,
211
+ cfg.basePath,
212
+ cfg.servingBaseUrl,
213
+ ),
202
214
  runtime: createPostgresRuntimeProfile(client, cfg.servingBaseUrl),
203
215
  );
204
216
  },
@@ -1,5 +1,4 @@
1
- import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart'
2
- show Platform;
1
+ import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart' show Platform;
3
2
 
4
3
  /// Database row for the `bundles` table (snake_case columns).
5
4
  ///
@@ -66,24 +65,24 @@ class PostgresBundleRow {
66
65
  final List<String>? targetCohorts;
67
66
 
68
67
  Map<String, dynamic> toJson() => {
69
- 'id': id,
70
- 'channel': channel,
71
- 'enabled': enabled,
72
- 'should_force_update': shouldForceUpdate,
73
- 'file_hash': fileHash,
74
- 'git_commit_hash': gitCommitHash,
75
- 'message': message,
76
- 'platform': platform.name,
77
- 'target_app_version': targetAppVersion,
78
- 'storage_uri': storageUri,
79
- 'fingerprint_hash': fingerprintHash,
80
- 'metadata': metadata ?? const {},
81
- 'manifest_storage_uri': manifestStorageUri,
82
- 'manifest_file_hash': manifestFileHash,
83
- 'asset_base_storage_uri': assetBaseStorageUri,
84
- 'rollout_cohort_count': rolloutCohortCount,
85
- 'target_cohorts': targetCohorts,
86
- };
68
+ 'id': id,
69
+ 'channel': channel,
70
+ 'enabled': enabled,
71
+ 'should_force_update': shouldForceUpdate,
72
+ 'file_hash': fileHash,
73
+ 'git_commit_hash': gitCommitHash,
74
+ 'message': message,
75
+ 'platform': platform.name,
76
+ 'target_app_version': targetAppVersion,
77
+ 'storage_uri': storageUri,
78
+ 'fingerprint_hash': fingerprintHash,
79
+ 'metadata': metadata ?? const {},
80
+ 'manifest_storage_uri': manifestStorageUri,
81
+ 'manifest_file_hash': manifestFileHash,
82
+ 'asset_base_storage_uri': assetBaseStorageUri,
83
+ 'rollout_cohort_count': rolloutCohortCount,
84
+ 'target_cohorts': targetCohorts,
85
+ };
87
86
  }
88
87
 
89
88
  /// Database row for the `bundle_patches` table (snake_case columns).
@@ -121,12 +120,12 @@ class PostgresBundlePatchRow {
121
120
  final int orderIndex;
122
121
 
123
122
  Map<String, dynamic> toJson() => {
124
- 'id': id,
125
- 'bundle_id': bundleId,
126
- 'base_bundle_id': baseBundleId,
127
- 'base_file_hash': baseFileHash,
128
- 'patch_file_hash': patchFileHash,
129
- 'patch_storage_uri': patchStorageUri,
130
- 'order_index': orderIndex,
131
- };
123
+ 'id': id,
124
+ 'bundle_id': bundleId,
125
+ 'base_bundle_id': baseBundleId,
126
+ 'base_file_hash': baseFileHash,
127
+ 'patch_file_hash': patchFileHash,
128
+ 'patch_storage_uri': patchStorageUri,
129
+ 'order_index': orderIndex,
130
+ };
132
131
  }
@@ -1,21 +1,26 @@
1
1
  name: flutter_ota_kit_postgres
2
2
  description: Postgres plugin for flutter_ota_kit — database backend.
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.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.2
15
+ flutter_ota_kit_plugin_core: ^0.0.4
17
16
  postgres: ^3.4.0
18
17
 
19
18
  dev_dependencies:
20
19
  test: ^1.25.0
21
20
  lints: ^4.0.0
21
+
22
+ dependency_overrides:
23
+ flutter_ota_kit_core:
24
+ path: ../../packages/core
25
+ flutter_ota_kit_plugin_core:
26
+ path: ../plugin-core
@@ -51,10 +51,7 @@ class MockPostgresClient implements PostgresClientLike {
51
51
  @override
52
52
  Future<void> close() async {}
53
53
 
54
- List<Map<String, dynamic>> _handle(
55
- String sql,
56
- Map<String, dynamic> params,
57
- ) {
54
+ List<Map<String, dynamic>> _handle(String sql, Map<String, dynamic> params) {
58
55
  if (sql.contains('get_target_app_version_list')) {
59
56
  return _targetAppVersionList(params);
60
57
  }
@@ -126,7 +123,8 @@ class MockPostgresClient implements PostgresClientLike {
126
123
  var rows = _filterBundles(sql, params);
127
124
 
128
125
  if (sql.contains('ORDER BY id asc')) {
129
- rows = rows.toList()..sort((a, b) => (a['id'] as String).compareTo(b['id'] as String));
126
+ rows = rows.toList()
127
+ ..sort((a, b) => (a['id'] as String).compareTo(b['id'] as String));
130
128
  } else {
131
129
  rows = rows.toList()
132
130
  ..sort((a, b) => (b['id'] as String).compareTo(a['id'] as String));
@@ -150,13 +148,18 @@ class MockPostgresClient implements PostgresClientLike {
150
148
  }
151
149
 
152
150
  List<Map<String, dynamic>> _channels() {
153
- final channels = store.bundleRows.map((r) => r['channel'] as String).toSet().toList();
151
+ final channels = store.bundleRows
152
+ .map((r) => r['channel'] as String)
153
+ .toSet()
154
+ .toList();
154
155
  return channels.map((c) => {'channel': c}).toList();
155
156
  }
156
157
 
157
158
  List<Map<String, dynamic>> _patchesForIds(Map<String, dynamic> params) {
158
159
  final ids = (params['bundle_ids'] as List).cast<String>();
159
- return store.bundlePatchRows.where((p) => ids.contains(p['bundle_id'])).toList();
160
+ return store.bundlePatchRows
161
+ .where((p) => ids.contains(p['bundle_id']))
162
+ .toList();
160
163
  }
161
164
 
162
165
  void _upsertBundle(Map<String, dynamic> params) {
@@ -173,7 +176,9 @@ class MockPostgresClient implements PostgresClientLike {
173
176
  void _upsertPatch(Map<String, dynamic> params) {
174
177
  final row = <String, dynamic>{};
175
178
  params.forEach((k, v) => row[k] = v);
176
- final existing = store.bundlePatchRows.indexWhere((r) => r['id'] == row['id']);
179
+ final existing = store.bundlePatchRows.indexWhere(
180
+ (r) => r['id'] == row['id'],
181
+ );
177
182
  if (existing >= 0) {
178
183
  store.bundlePatchRows[existing] = row;
179
184
  } else {
@@ -198,14 +203,24 @@ class MockPostgresClient implements PostgresClientLike {
198
203
  final existed = store.bundleRows.any((r) => r['id'] == id);
199
204
  store.bundleRows.removeWhere((r) => r['id'] == id);
200
205
  store.bundlePatchRows.removeWhere((p) => p['bundle_id'] == id);
201
- return existed ? [{'id': id}] : [];
206
+ return existed
207
+ ? [
208
+ {'id': id},
209
+ ]
210
+ : [];
202
211
  }
203
212
 
204
- List<Map<String, dynamic>> _targetAppVersionList(Map<String, dynamic> params) {
213
+ List<Map<String, dynamic>> _targetAppVersionList(
214
+ Map<String, dynamic> params,
215
+ ) {
205
216
  final platform = params['app_platform'];
206
217
  final min = params['min_bundle_id'] as String? ?? '';
207
218
  final versions = store.bundleRows
208
- .where((r) => r['platform'] == platform && (r['id'] as String).compareTo(min) >= 0)
219
+ .where(
220
+ (r) =>
221
+ r['platform'] == platform &&
222
+ (r['id'] as String).compareTo(min) >= 0,
223
+ )
209
224
  .map((r) => r['target_app_version'])
210
225
  .where((v) => v != null)
211
226
  .toSet()
@@ -222,44 +237,53 @@ class MockPostgresClient implements PostgresClientLike {
222
237
  return matches.isEmpty ? null : matches.first;
223
238
  }
224
239
 
225
- List<Map<String, dynamic>> _updateInfoByAppVersion(Map<String, dynamic> params) {
240
+ List<Map<String, dynamic>> _updateInfoByAppVersion(
241
+ Map<String, dynamic> params,
242
+ ) {
226
243
  final platform = params['app_platform'];
227
244
  final channel = params['target_channel'];
228
245
  final min = params['min_bundle_id'] as String? ?? '';
229
- final list = (params['target_app_version_list'] as List?)?.cast<String>() ?? [];
230
- final row = _newestMatch((r) =>
231
- r['platform'] == platform &&
232
- r['enabled'] == true &&
233
- r['channel'] == channel &&
234
- (r['id'] as String).compareTo(min) >= 0 &&
235
- list.contains(r['target_app_version']));
246
+ final list =
247
+ (params['target_app_version_list'] as List?)?.cast<String>() ?? [];
248
+ final row = _newestMatch(
249
+ (r) =>
250
+ r['platform'] == platform &&
251
+ r['enabled'] == true &&
252
+ r['channel'] == channel &&
253
+ (r['id'] as String).compareTo(min) >= 0 &&
254
+ list.contains(r['target_app_version']),
255
+ );
236
256
  if (row == null) return [];
237
257
  return [_infoRow(row)];
238
258
  }
239
259
 
240
- List<Map<String, dynamic>> _updateInfoByFingerprint(Map<String, dynamic> params) {
260
+ List<Map<String, dynamic>> _updateInfoByFingerprint(
261
+ Map<String, dynamic> params,
262
+ ) {
241
263
  final platform = params['app_platform'];
242
264
  final channel = params['target_channel'];
243
265
  final min = params['min_bundle_id'] as String? ?? '';
244
266
  final fp = params['target_fingerprint_hash'];
245
- final row = _newestMatch((r) =>
246
- r['platform'] == platform &&
247
- r['enabled'] == true &&
248
- r['channel'] == channel &&
249
- (r['id'] as String).compareTo(min) >= 0 &&
250
- r['fingerprint_hash'] == fp);
267
+ final row = _newestMatch(
268
+ (r) =>
269
+ r['platform'] == platform &&
270
+ r['enabled'] == true &&
271
+ r['channel'] == channel &&
272
+ (r['id'] as String).compareTo(min) >= 0 &&
273
+ r['fingerprint_hash'] == fp,
274
+ );
251
275
  if (row == null) return [];
252
276
  return [_infoRow(row)];
253
277
  }
254
278
 
255
279
  Map<String, dynamic> _infoRow(Map<String, dynamic> row) => {
256
- 'id': row['id'],
257
- 'should_force_update': row['should_force_update'],
258
- 'message': row['message'],
259
- 'status': 'UPDATE',
260
- 'storage_uri': row['storage_uri'],
261
- 'file_hash': row['file_hash'],
262
- };
280
+ 'id': row['id'],
281
+ 'should_force_update': row['should_force_update'],
282
+ 'message': row['message'],
283
+ 'status': 'UPDATE',
284
+ 'storage_uri': row['storage_uri'],
285
+ 'file_hash': row['file_hash'],
286
+ };
263
287
  }
264
288
 
265
289
  class _TxClient implements PostgresClientLike {
@@ -271,8 +295,7 @@ class _TxClient implements PostgresClientLike {
271
295
  Future<List<Map<String, dynamic>>> execute(
272
296
  String sql,
273
297
  Map<String, dynamic> parameters,
274
- ) =>
275
- _parent.execute(sql, parameters);
298
+ ) => _parent.execute(sql, parameters);
276
299
 
277
300
  @override
278
301
  Future<T> runTx<T>(Future<T> Function(PostgresClientLike tx) fn) =>
@@ -284,10 +307,10 @@ class _TxClient implements PostgresClientLike {
284
307
 
285
308
  /// Build a [PostgresConfig] wired to [MockPostgresClient].
286
309
  PostgresConfig mockPostgresConfig(Store store) => PostgresConfig(
287
- host: 'mock',
288
- database: 'mock',
289
- clientFactory: (_) => MockPostgresClient(store),
290
- );
310
+ host: 'mock',
311
+ database: 'mock',
312
+ clientFactory: (_) => MockPostgresClient(store),
313
+ );
291
314
 
292
315
  /// Create a [DatabasePlugin] backed by the mock store.
293
316
  DatabasePlugin newPlugin({Store? store}) {
@@ -84,10 +84,9 @@ void main() {
84
84
 
85
85
  final row = bundleToRowValues(bundle);
86
86
  final patchRows = bundleToPatchRows(bundle);
87
- final back = mapRowToBundle(
88
- PostgresBundleRow.fromJson(row),
89
- [PostgresBundlePatchRow.fromJson(patchRows.first)],
90
- );
87
+ final back = mapRowToBundle(PostgresBundleRow.fromJson(row), [
88
+ PostgresBundlePatchRow.fromJson(patchRows.first),
89
+ ]);
91
90
 
92
91
  expect(back.id, bundle.id);
93
92
  expect(back.fileHash, 'hash-1');