@_nazmiforreal/flutter-ota 0.1.19 → 0.1.21

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 (59) hide show
  1. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +4 -3
  2. package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -2
  3. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +22 -10
  4. package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +16 -5
  5. package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +3 -0
  6. package/dart-src/packages/cli-tools/lib/src/commands/console.dart +7 -8
  7. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +12 -3
  8. package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +129 -6
  9. package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +2 -1
  10. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +23 -14
  11. package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +4 -0
  12. package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +266 -26
  13. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1422 -16
  14. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +4 -1
  15. package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +10 -9
  16. package/dart-src/packages/cli-tools/lib/src/config.dart +14 -19
  17. package/dart-src/packages/cli-tools/lib/src/operations.dart +1 -1
  18. package/dart-src/packages/cli-tools/lib/src/pocketbase/installer.dart +1 -1
  19. package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +113 -10
  20. package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +59 -6
  21. package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +2 -2
  22. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +149 -0
  23. package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +14 -9
  24. package/dart-src/packages/core/CHANGELOG.md +6 -1
  25. package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +2 -0
  26. package/dart-src/packages/core/lib/src/app_update_info.dart +73 -1
  27. package/dart-src/packages/core/lib/src/bundle.dart +62 -17
  28. package/dart-src/packages/core/lib/src/bundle_artifacts.dart +7 -3
  29. package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +16 -0
  30. package/dart-src/packages/core/lib/src/changed_asset.dart +33 -3
  31. package/dart-src/packages/core/lib/src/get_bundles_args.dart +24 -2
  32. package/dart-src/packages/core/lib/src/platform.dart +8 -0
  33. package/dart-src/packages/core/lib/src/semver.dart +0 -3
  34. package/dart-src/packages/core/lib/src/status.dart +20 -1
  35. package/dart-src/packages/core/lib/src/strategy.dart +10 -0
  36. package/dart-src/packages/core/lib/src/update_bundle_params.dart +15 -0
  37. package/dart-src/packages/core/lib/src/uuid.dart +11 -0
  38. package/dart-src/plugins/aws/CHANGELOG.md +5 -1
  39. package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +2 -2
  40. package/dart-src/plugins/cloudflare/CHANGELOG.md +5 -1
  41. package/dart-src/plugins/plugin-core/CHANGELOG.md +5 -1
  42. package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +3 -0
  43. package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +17 -0
  44. package/dart-src/plugins/plugin-core/lib/src/types.dart +94 -3
  45. package/dart-src/plugins/pocketbase/CHANGELOG.md +5 -1
  46. package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +2 -1
  47. package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +10 -2
  48. package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +617 -4
  49. package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +262 -42
  50. package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +9 -2
  51. package/dart-src/plugins/postgres/CHANGELOG.md +5 -1
  52. package/dart-src/plugins/supabase/CHANGELOG.md +6 -1
  53. package/dart-src/plugins/supabase/lib/src/supabase_database.dart +4 -0
  54. package/package.json +2 -2
  55. package/bin/flutter-ota-linux-x64 +0 -0
  56. package/bin/flutter-ota.js +0 -36
  57. package/dart-src/packages/cli-tools/.dart_tool/package_config.json +0 -458
  58. package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +0 -707
  59. package/dart-src/packages/cli-tools/pubspec.lock +0 -590
@@ -2,6 +2,7 @@ import 'dart:convert' show utf8;
2
2
  import 'dart:typed_data' show Uint8List;
3
3
 
4
4
  import 'package:crypto/crypto.dart' show Hmac, sha256;
5
+ import 'package:flutter/foundation.dart' show debugPrint;
5
6
  import 'package:http/http.dart' show Client, Request, Response;
6
7
 
7
8
  const String _cloudfrontService = 'cloudfront';
@@ -91,8 +92,7 @@ class AwsCloudFrontClient implements AwsCloudFrontClientLike {
91
92
  if (shouldWait) rethrow;
92
93
  // When not waiting, surface invalidation failures as warnings and
93
94
  // continue (mirrors hot-updater's s3Database behaviour).
94
- // ignore: avoid_print
95
- print(
95
+ debugPrint(
96
96
  '[flutter_ota_kit/aws] CloudFront invalidation failed for '
97
97
  '$distributionId; continuing without cache invalidation: $error',
98
98
  );
@@ -1 +1,5 @@
1
- ## 0.1.5
1
+ ## 0.1.7
2
+
3
+ - General maintenance.
4
+
5
+ ## 0.1.6
@@ -1 +1,5 @@
1
- ## 0.0.6
1
+ ## 0.0.8
2
+
3
+ - General maintenance.
4
+
5
+ ## 0.0.7
@@ -5,7 +5,10 @@ import 'legacy_asset_storage_layout.dart'
5
5
  /// The two supported asset storage layout strategies.
6
6
  typedef AssetStorageLayout = String;
7
7
 
8
+ /// Content-addressed layout: assets stored by SHA-256 hash.
8
9
  const String assetLayoutContentAddressed = 'content-addressed';
10
+
11
+ /// Legacy layout: assets stored by original file path.
9
12
  const String assetLayoutLegacyFiles = 'legacy-files';
10
13
 
11
14
  /// Whether [assetPath] is a brotli-compressed manifest path
@@ -1,4 +1,16 @@
1
1
  /// Parsed components of a storage URI.
2
+ ///
3
+ /// Storage URIs follow a protocol-specific scheme that maps to a backend
4
+ /// storage service. Supported protocols:
5
+ ///
6
+ /// - `s3://bucket/key` — AWS S3
7
+ /// - `r2://bucket/key` — Cloudflare R2
8
+ /// - `supabase-storage://bucket/key` — Supabase Storage
9
+ /// - `postgres://host/db` — Postgres (via HTTP API)
10
+ /// - `pocketbase://host/api/files/bucket/key` — PocketBase
11
+ ///
12
+ /// The [bucket] is the storage container, and [key] is the object path
13
+ /// within that container.
2
14
  class ParsedStorageUri {
3
15
  const ParsedStorageUri({
4
16
  required this.protocol,
@@ -6,8 +18,13 @@ class ParsedStorageUri {
6
18
  required this.key,
7
19
  });
8
20
 
21
+ /// Protocol scheme (e.g. `s3`, `r2`, `supabase-storage`).
9
22
  final String protocol;
23
+
24
+ /// Storage container / bucket name.
10
25
  final String bucket;
26
+
27
+ /// Object path within the bucket (percent-decoded).
11
28
  final String key;
12
29
  }
13
30
 
@@ -128,36 +128,65 @@ class DatabaseBundleQueryOptions {
128
128
  // Database plugin
129
129
  // ---------------------------------------------------------------------------
130
130
 
131
+ /// Database abstraction for bundle storage and querying.
132
+ ///
133
+ /// Implementations handle the backend-specific SQL/API calls while the
134
+ /// plugin-core layer manages pagination, unit-of-work tracking, and
135
+ /// hook callbacks.
136
+ ///
137
+ /// Lifecycle:
138
+ /// 1. Plugin is created via [createDatabasePlugin] factory.
139
+ /// 2. Methods are called by the CLI (deploy, migrate, bundle management)
140
+ /// or the console (bundle listing, editing).
141
+ /// 3. [onUnmount] is called when the plugin is disposed.
131
142
  abstract class DatabasePlugin {
143
+ /// Human-readable plugin name (e.g. `supabaseDatabase`).
132
144
  String get name;
133
145
 
146
+ /// List all distinct channel values across all bundles.
134
147
  Future<List<String>> getChannels();
135
148
 
149
+ /// Fetch a single bundle by its UUID. Returns null if not found.
136
150
  Future<Bundle?> getBundleById(String bundleId);
137
151
 
152
+ /// Resolve the update decision for a device's update-check request.
153
+ ///
154
+ /// Returns the eligible [UpdateInfo] (update or rollback), or null if
155
+ /// the device is already on the latest bundle.
138
156
  Future<UpdateInfo?> getUpdateInfo(GetBundlesArgs args);
139
157
 
158
+ /// Query bundles with filtering, pagination, and ordering.
140
159
  Future<Paginated<List<Bundle>>> getBundles(
141
160
  DatabaseBundleQueryOptions options,
142
161
  );
143
162
 
163
+ /// Update specific fields of an existing bundle.
164
+ ///
165
+ /// [newBundle] contains only the changed fields (partial update).
144
166
  Future<void> updateBundle(
145
167
  String targetBundleId,
146
168
  Map<String, Object?> newBundle,
147
169
  );
148
170
 
171
+ /// Insert a new bundle. The bundle is staged in the unit-of-work
172
+ /// until [commitBundle] is called.
149
173
  Future<void> appendBundle(Bundle insertBundle);
150
174
 
175
+ /// Flush all staged changes (inserts, updates, deletes) to the backend.
151
176
  Future<void> commitBundle();
152
177
 
178
+ /// Delete a bundle and its associated storage artifacts.
153
179
  Future<void> deleteBundle(Bundle deleteBundle);
154
180
 
181
+ /// Release resources (close connections, cancel timers).
155
182
  Future<void> onUnmount();
156
183
  }
157
184
 
185
+ /// Optional lifecycle hooks for database plugin events.
158
186
  class DatabasePluginHooks {
159
187
  const DatabasePluginHooks({this.onDatabaseUpdated});
160
188
 
189
+ /// Called after a bundle mutation is committed to the database.
161
190
  final Future<void> Function()? onDatabaseUpdated;
162
191
  }
163
192
 
@@ -209,25 +238,51 @@ abstract class RuntimeStorageProfile {
209
238
  Future<String?> readText(String storageUri);
210
239
  }
211
240
 
241
+ /// Profiles for accessing storage in different contexts.
242
+ ///
243
+ /// A storage plugin may support one or both profiles:
244
+ /// - [node]: For CLI/deploy operations (file upload, delete, list).
245
+ /// - [runtime]: For device update-check operations (signed URLs, manifest read).
212
246
  class StoragePluginProfiles {
213
247
  const StoragePluginProfiles({this.node, this.runtime});
214
248
 
249
+ /// Node profile for CLI/deploy operations. Null if not supported.
215
250
  final NodeStorageProfile? node;
251
+
252
+ /// Runtime profile for device update-checks. Null if not supported.
216
253
  final RuntimeStorageProfile? runtime;
217
254
 
255
+ /// Whether this plugin supports CLI/deploy operations.
218
256
  bool get hasNode => node != null;
257
+
258
+ /// Whether this plugin supports device update-check operations.
219
259
  bool get hasRuntime => runtime != null;
220
260
  }
221
261
 
262
+ /// Storage backend abstraction for artifact upload/download.
263
+ ///
264
+ /// Each backend (S3, R2, Supabase Storage, PocketBase) implements this
265
+ /// interface to provide a unified storage API.
266
+ ///
267
+ /// Storage URIs follow the scheme defined in [parseStorageUri]:
268
+ /// `protocol://bucket/key` where protocol is `s3`, `r2`, `supabase-storage`,
269
+ /// or `pocketbase`.
222
270
  abstract class StoragePlugin {
271
+ /// Human-readable plugin name (e.g. `s3Storage`, `r2Storage`).
223
272
  String get name;
273
+
274
+ /// Protocol scheme this plugin handles (e.g. `s3`, `r2`, `supabase-storage`).
224
275
  String get supportedProtocol;
276
+
277
+ /// Access profiles for node (CLI) and runtime (device) operations.
225
278
  StoragePluginProfiles get profiles;
226
279
  }
227
280
 
281
+ /// Optional lifecycle hooks for storage plugin events.
228
282
  class StoragePluginHooks {
229
283
  const StoragePluginHooks({this.onStorageUploaded});
230
284
 
285
+ /// Called after a file is successfully uploaded to storage.
231
286
  final Future<void> Function()? onStorageUploaded;
232
287
  }
233
288
 
@@ -235,6 +290,7 @@ class StoragePluginHooks {
235
290
  // Build plugin
236
291
  // ---------------------------------------------------------------------------
237
292
 
293
+ /// Result of a successful build operation.
238
294
  class BuildPluginResult {
239
295
  const BuildPluginResult({
240
296
  required this.buildPath,
@@ -242,17 +298,32 @@ class BuildPluginResult {
242
298
  this.stdout,
243
299
  });
244
300
 
301
+ /// Path to the built artifact (e.g. `build/app/outputs/flutter-apk/app-release.apk`).
245
302
  final String buildPath;
303
+
304
+ /// Generated bundle UUID for this build.
246
305
  final String bundleId;
306
+
307
+ /// Raw build output (stdout + stderr combined).
247
308
  final String? stdout;
248
309
  }
249
310
 
311
+ /// Build system abstraction for creating app artifacts.
312
+ ///
313
+ /// Implementations handle the platform-specific build commands (Gradle,
314
+ /// Xcodebuild) while the plugin-core layer manages versioning and artifact
315
+ /// packaging.
250
316
  abstract class BuildPlugin {
317
+ /// Human-readable plugin name (e.g. `androidBuild`, `iosBuild`).
251
318
  String get name;
252
319
 
320
+ /// Execute the build for the given [platform] and return the artifact path.
253
321
  Future<BuildPluginResult> build(Platform platform);
254
322
 
323
+ /// Pre-build hook (e.g. clean, dependency resolution).
255
324
  Future<void> prebuild(Platform platform);
325
+
326
+ /// Post-build hook (e.g. signing, obfuscation).
256
327
  Future<void> postbuild(Platform platform);
257
328
  }
258
329
 
@@ -341,14 +412,14 @@ class NativeBuildArgs {
341
412
  class SigningConfigDisabled {
342
413
  const SigningConfigDisabled({this.privateKeyPath});
343
414
 
344
- final bool enabled = false;
415
+ static const bool enabled = false;
345
416
  final String? privateKeyPath;
346
417
  }
347
418
 
348
419
  class SigningConfigEnabled {
349
420
  const SigningConfigEnabled({required this.privateKeyPath});
350
421
 
351
- final bool enabled = true;
422
+ static const bool enabled = true;
352
423
  final String privateKeyPath;
353
424
  }
354
425
 
@@ -450,6 +521,26 @@ class NativeBuildOptions {
450
521
  // ---------------------------------------------------------------------------
451
522
 
452
523
  /// Opaque context object carried through a request lifecycle.
453
- /// In Dart we use a plain Map for extensibility.
524
+ ///
525
+ /// Plugins may attach metadata to this map during processing. Known keys:
526
+ ///
527
+ /// - `bundleId` (String) — Current bundle ID being processed
528
+ /// - `channel` (String) — Target release channel
529
+ /// - `platform` (String) — Target platform (`ios`, `android`)
530
+ /// - `appVersion` (String) — Current app version (semver)
531
+ /// - `fingerprintHash` (String) — Device fingerprint hash
532
+ /// - `cohort` (String) — Rollout cohort identifier
533
+ /// - `storageUri` (String) — Resolved storage URI for the artifact
534
+ /// - `signedUrl` (String) — Pre-signed download URL
535
+ ///
536
+ /// Extensions should use namespaced keys (e.g. `myPlugin.fieldName`) to
537
+ /// avoid collisions.
454
538
  typedef HotUpdaterContext = Map<String, Object?>;
539
+
540
+ /// Context for resolving storage URIs to download URLs.
541
+ ///
542
+ /// Inherits all keys from [HotUpdaterContext] and adds:
543
+ ///
544
+ /// - `storageProtocol` (String) — Expected protocol (`s3`, `r2`, `supabase-storage`, etc.)
545
+ /// - `storageBucket` (String) — Target bucket name
455
546
  typedef StorageResolveContext = HotUpdaterContext;
@@ -1 +1,5 @@
1
- ## 0.1.2
1
+ ## 0.1.4
2
+
3
+ - General maintenance.
4
+
5
+ ## 0.1.3
@@ -16,7 +16,8 @@ export 'src/pocketbase_client.dart'
16
16
  PocketBaseClient,
17
17
  PocketBaseClientFactory,
18
18
  PocketBaseException,
19
- PocketBaseList;
19
+ PocketBaseList,
20
+ PocketBaseBackup;
20
21
  export 'src/pocketbase_database.dart'
21
22
  show pocketbaseDatabase, PocketBaseDatabaseConfig;
22
23
  export 'src/pocketbase_storage.dart'
@@ -96,8 +96,16 @@ class PocketBaseBundleRow {
96
96
  }
97
97
 
98
98
  /// Convert a [PocketBaseBundleRow] into a [Bundle].
99
- Bundle mapRowToBundle(PocketBaseBundleRow row) {
99
+ ///
100
+ /// If [patches] is provided, they are attached as [BundlePatchArtifact]s.
101
+ Bundle mapRowToBundle(
102
+ PocketBaseBundleRow row, {
103
+ List<Map<String, dynamic>>? patches,
104
+ }) {
100
105
  final rawMetadata = normalizeMetadata(row.metadata);
106
+ final patchArtifacts = patches
107
+ ?.map((p) => BundlePatchArtifact.fromJson(p.cast<String, dynamic>()))
108
+ .toList();
101
109
  return Bundle(
102
110
  id: row.id,
103
111
  channel: row.channel,
@@ -114,7 +122,7 @@ Bundle mapRowToBundle(PocketBaseBundleRow row) {
114
122
  manifestStorageUri: row.manifestStorageUri,
115
123
  manifestFileHash: row.manifestFileHash,
116
124
  assetBaseStorageUri: row.assetBaseStorageUri,
117
- patches: const [],
125
+ patches: patchArtifacts,
118
126
  rolloutCohortCount: row.rolloutCohortCount,
119
127
  targetCohorts: row.targetCohorts,
120
128
  );