@_nazmiforreal/flutter-ota 0.1.20 → 0.1.22

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 (62) hide show
  1. package/bin/flutter-ota-linux-x64 +0 -0
  2. package/dart-src/packages/cli-tools/.dart_tool/package_config.json +1 -1
  3. package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +340 -340
  4. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +9 -5
  5. package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -2
  6. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +106 -31
  7. package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +34 -7
  8. package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +12 -4
  9. package/dart-src/packages/cli-tools/lib/src/commands/console.dart +7 -8
  10. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +41 -9
  11. package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +124 -13
  12. package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +2 -1
  13. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +58 -28
  14. package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +11 -5
  15. package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +283 -32
  16. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +347 -121
  17. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +18 -9
  18. package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +32 -15
  19. package/dart-src/packages/cli-tools/lib/src/config.dart +16 -22
  20. package/dart-src/packages/cli-tools/lib/src/operations.dart +1 -3
  21. package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +26 -17
  22. package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +0 -1
  23. package/dart-src/packages/cli-tools/lib/src/runner.dart +3 -1
  24. package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +10 -16
  25. package/dart-src/packages/cli-tools/pubspec.lock +7 -7
  26. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +7 -3
  27. package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +13 -16
  28. package/dart-src/packages/core/CHANGELOG.md +15 -0
  29. package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +2 -0
  30. package/dart-src/packages/core/lib/src/app_update_info.dart +71 -1
  31. package/dart-src/packages/core/lib/src/bundle.dart +59 -17
  32. package/dart-src/packages/core/lib/src/bundle_artifacts.dart +7 -3
  33. package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +16 -0
  34. package/dart-src/packages/core/lib/src/changed_asset.dart +33 -3
  35. package/dart-src/packages/core/lib/src/get_bundles_args.dart +24 -2
  36. package/dart-src/packages/core/lib/src/platform.dart +8 -0
  37. package/dart-src/packages/core/lib/src/semver.dart +0 -3
  38. package/dart-src/packages/core/lib/src/status.dart +20 -1
  39. package/dart-src/packages/core/lib/src/strategy.dart +10 -0
  40. package/dart-src/packages/core/lib/src/update_bundle_params.dart +15 -0
  41. package/dart-src/packages/core/lib/src/uuid.dart +11 -0
  42. package/dart-src/packages/core/pubspec.yaml +1 -1
  43. package/dart-src/plugins/aws/CHANGELOG.md +18 -0
  44. package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +0 -1
  45. package/dart-src/plugins/aws/pubspec.yaml +3 -3
  46. package/dart-src/plugins/cloudflare/CHANGELOG.md +18 -0
  47. package/dart-src/plugins/cloudflare/pubspec.yaml +3 -3
  48. package/dart-src/plugins/plugin-core/CHANGELOG.md +15 -0
  49. package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +3 -0
  50. package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +17 -0
  51. package/dart-src/plugins/plugin-core/lib/src/types.dart +94 -3
  52. package/dart-src/plugins/plugin-core/pubspec.yaml +2 -2
  53. package/dart-src/plugins/pocketbase/CHANGELOG.md +18 -0
  54. package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +3 -0
  55. package/dart-src/plugins/pocketbase/pubspec.yaml +3 -3
  56. package/dart-src/plugins/postgres/CHANGELOG.md +18 -0
  57. package/dart-src/plugins/postgres/pubspec.yaml +3 -3
  58. package/dart-src/plugins/supabase/CHANGELOG.md +18 -0
  59. package/dart-src/plugins/supabase/pubspec.yaml +3 -3
  60. package/package.json +2 -2
  61. package/scripts/postinstall.js +5 -1
  62. package/bin/flutter-ota.js +0 -36
@@ -6,14 +6,25 @@ import '../ui/ui.dart';
6
6
  /// `flutter-ota rollback` — roll a channel back to a previous bundle.
7
7
  class RollbackCommand extends FlutterPatcherCommand {
8
8
  RollbackCommand({this.config, this.backendOverride}) {
9
- argParser.addOption('backend', abbr: 'b', help: 'Backend provider.');
9
+ final detected = config?.provider ?? loadConfig()?.provider;
10
+ argParser.addOption(
11
+ 'backend',
12
+ abbr: 'b',
13
+ help: detected != null
14
+ ? 'Backend provider [detected: $detected].'
15
+ : 'Backend provider.',
16
+ );
10
17
  argParser.addOption('channel', abbr: 'c', help: 'Channel to roll back.');
11
18
  argParser.addOption(
12
19
  'bundle-id',
13
20
  abbr: 'i',
14
21
  help: 'Roll back to this specific bundle id.',
15
22
  );
16
- argParser.addOption('platform', abbr: 'p', help: 'Platform filter (e.g. android).');
23
+ argParser.addOption(
24
+ 'platform',
25
+ abbr: 'p',
26
+ help: 'Platform filter (e.g. android).',
27
+ );
17
28
  }
18
29
 
19
30
  final FlutterPatcherConfig? config;
@@ -46,14 +57,12 @@ class RollbackCommand extends FlutterPatcherCommand {
46
57
 
47
58
  banner('rollback');
48
59
  final steps = Steps('rollback');
49
- final (disabled, live) = await steps.run('Rolling back channel "$channel"', () async {
60
+ final (
61
+ disabled,
62
+ live,
63
+ ) = await steps.run('Rolling back channel "$channel"', () async {
50
64
  if (bundleId != null && bundleId.isNotEmpty) {
51
- return rollbackToBundle(
52
- backend,
53
- channel,
54
- bundleId,
55
- platform: platform,
56
- );
65
+ return rollbackToBundle(backend, channel, bundleId, platform: platform);
57
66
  }
58
67
  final id = await rollbackChannel(backend, channel);
59
68
  final nowLive = (await getChannel(backend, channel))?.id ?? '';
@@ -22,12 +22,19 @@ class StorageCommand extends FlutterPatcherCommand {
22
22
 
23
23
  @override
24
24
  String get description =>
25
- 'Inspect and manage bundle storage objects (find / delete orphans).';
25
+ 'Inspect and manage bundle storage objects (list / delete).';
26
26
  }
27
27
 
28
28
  class StorageListCommand extends FlutterPatcherCommand {
29
29
  StorageListCommand({this.config, this.backendOverride}) {
30
- argParser.addOption('backend', abbr: 'b', help: 'Backend provider.');
30
+ final detected = config?.provider ?? loadConfig()?.provider;
31
+ argParser.addOption(
32
+ 'backend',
33
+ abbr: 'b',
34
+ help: detected != null
35
+ ? 'Backend provider [detected: $detected].'
36
+ : 'Backend provider.',
37
+ );
31
38
  argParser.addOption('prefix', help: 'Key prefix filter (e.g. bundles).');
32
39
  }
33
40
 
@@ -47,9 +54,12 @@ class StorageListCommand extends FlutterPatcherCommand {
47
54
  final prefix = argResults!['prefix'] as String?;
48
55
  banner('storage · list');
49
56
  final steps = Steps('list');
50
- final objects = await steps.run('Listing storage objects', () =>
51
- backend.storage.listObjects(
52
- prefix == null || prefix.isEmpty ? null : prefix));
57
+ final objects = await steps.run(
58
+ 'Listing storage objects',
59
+ () => backend.storage.listObjects(
60
+ prefix == null || prefix.isEmpty ? null : prefix,
61
+ ),
62
+ );
53
63
  if (objects.isEmpty) {
54
64
  steps.skip('(no objects)');
55
65
  steps.summary();
@@ -57,12 +67,7 @@ class StorageListCommand extends FlutterPatcherCommand {
57
67
  }
58
68
  final rows = <List<String>>[];
59
69
  for (final o in objects) {
60
- final size = o.size >= 1024 * 1024
61
- ? '${(o.size / (1024 * 1024)).toStringAsFixed(2)} MB'
62
- : o.size >= 1024
63
- ? '${(o.size / 1024).toStringAsFixed(1)} KB'
64
- : '${o.size} B';
65
- rows.add([cyan(o.key), size]);
70
+ rows.add([cyan(o.key), humanSize(o.size)]);
66
71
  }
67
72
  table('${objects.length} objects', ['KEY', 'SIZE'], rows);
68
73
  steps.summary();
@@ -71,8 +76,18 @@ class StorageListCommand extends FlutterPatcherCommand {
71
76
 
72
77
  class StorageDeleteCommand extends FlutterPatcherCommand {
73
78
  StorageDeleteCommand({this.config, this.backendOverride}) {
74
- argParser.addOption('backend', abbr: 'b', help: 'Backend provider.');
75
- argParser.addMultiOption('key', help: 'Storage key to delete (repeatable).');
79
+ final detected = config?.provider ?? loadConfig()?.provider;
80
+ argParser.addOption(
81
+ 'backend',
82
+ abbr: 'b',
83
+ help: detected != null
84
+ ? 'Backend provider [detected: $detected].'
85
+ : 'Backend provider.',
86
+ );
87
+ argParser.addMultiOption(
88
+ 'key',
89
+ help: 'Storage key to delete (repeatable).',
90
+ );
76
91
  argParser.addOption('uri', help: 'Full storage URI to delete.');
77
92
  }
78
93
 
@@ -101,8 +116,10 @@ class StorageDeleteCommand extends FlutterPatcherCommand {
101
116
  banner('storage · delete');
102
117
  final steps = Steps('delete');
103
118
  if (keys.isNotEmpty) {
104
- await steps.run('Deleting ${keys.length} object(s)',
105
- () => backend.storage.deleteObjects(keys));
119
+ await steps.run(
120
+ 'Deleting ${keys.length} object(s)',
121
+ () => backend.storage.deleteObjects(keys),
122
+ );
106
123
  } else {
107
124
  await steps.run('Deleting $uri', () => backend.storage.delete(uri!));
108
125
  }
@@ -170,9 +170,7 @@ FlutterPatcherConfig? loadConfig() {
170
170
  jsonDecode(raw) as Map<String, dynamic>,
171
171
  );
172
172
  } on FormatException {
173
- stderr.writeln(
174
- 'Warning: invalid config at ${file.path} — ignoring.',
175
- );
173
+ stderr.writeln('Warning: invalid config at ${file.path} — ignoring.');
176
174
  return null;
177
175
  }
178
176
  }
@@ -232,8 +230,6 @@ SupabaseServiceRoleConfig resolveSupabaseConfig(
232
230
  String? url,
233
231
  String? serviceRoleKey,
234
232
  String? anonKey,
235
- String? bucket,
236
- String? basePath,
237
233
  SupabaseClientFactory? clientFactory,
238
234
  }) {
239
235
  final env = Platform.environment;
@@ -247,8 +243,8 @@ SupabaseServiceRoleConfig resolveSupabaseConfig(
247
243
 
248
244
  if (resolvedUrl == null || resolvedUrl.isEmpty) {
249
245
  throw StateError(
250
- 'Supabase URL is required. Set it via --url, SUPABASE_URL, or '
251
- '`flutter_ota_kit config set supabase.url <url>`.',
246
+ 'Supabase URL is required. Set it via SUPABASE_URL env var, '
247
+ 'or `flutter-ota config set supabase.url <url>`.',
252
248
  );
253
249
  }
254
250
 
@@ -275,8 +271,6 @@ SupabaseStorageConfig resolveSupabaseStorageConfig(
275
271
  url: url,
276
272
  serviceRoleKey: serviceRoleKey,
277
273
  anonKey: anonKey,
278
- bucket: bucket,
279
- basePath: basePath,
280
274
  clientFactory: clientFactory,
281
275
  );
282
276
  return SupabaseStorageConfig(
@@ -284,12 +278,11 @@ SupabaseStorageConfig resolveSupabaseStorageConfig(
284
278
  supabaseServiceRoleKey: db.supabaseServiceRoleKey,
285
279
  supabaseAnonKey: db.supabaseAnonKey,
286
280
  clientFactory: db.clientFactory,
287
- bucketName: db.supabaseServiceRoleKey != null || db.supabaseAnonKey != null
288
- ? (bucket ??
289
- Platform.environment['SUPABASE_BUCKET'] ??
290
- config.supabase.bucket ??
291
- 'bundles')
292
- : (bucket ?? config.supabase.bucket ?? 'bundles'),
281
+ bucketName:
282
+ bucket ??
283
+ Platform.environment['SUPABASE_BUCKET'] ??
284
+ config.supabase.bucket ??
285
+ 'bundles',
293
286
  basePath:
294
287
  basePath ??
295
288
  Platform.environment['SUPABASE_BASE_PATH'] ??
@@ -387,8 +380,8 @@ PostgresConfig resolvePostgresDatabaseConfig(
387
380
 
388
381
  if (resolvedHost == null || resolvedHost.isEmpty) {
389
382
  throw StateError(
390
- 'Postgres host is required. Set --pg-host, POSTGRES_HOST, or '
391
- '`flutter_ota_kit config set postgres.host <host>`.',
383
+ 'Postgres host is required. Set POSTGRES_HOST env var, '
384
+ 'or `flutter-ota config set postgres.host <host>`.',
392
385
  );
393
386
  }
394
387
 
@@ -765,15 +758,16 @@ PocketBaseDatabaseConfig resolvePocketBaseDatabaseConfig(
765
758
 
766
759
  if (resolvedUrl == null || resolvedUrl.isEmpty) {
767
760
  throw StateError(
768
- 'PocketBase URL is required. Set --pb-url, POCKETBASE_URL, or '
769
- '`flutter_ota_kit config set pocketbase.url <url>`.',
761
+ 'PocketBase URL is required. Set POCKETBASE_URL env var, '
762
+ 'or `flutter-ota config set pocketbase.url <url>`.',
770
763
  );
771
764
  }
772
765
  if (resolvedEmail == null || resolvedPassword == null) {
773
766
  throw StateError(
774
- 'PocketBase admin credentials are required. Set --pb-email, '
775
- '--pb-password, POCKETBASE_ADMIN_EMAIL/PASSWORD, or '
776
- '`flutter_ota_kit config set pocketbase.*`.',
767
+ 'PocketBase admin credentials are required. Set '
768
+ 'POCKETBASE_ADMIN_EMAIL / POCKETBASE_ADMIN_PASSWORD env vars, or '
769
+ '`flutter-ota config set pocketbase.adminEmail/<email> '
770
+ 'pocketbase.adminPassword/<password>`.',
777
771
  );
778
772
  }
779
773
 
@@ -61,9 +61,7 @@ class ListOptions {
61
61
  /// Build the zip, upload it, and register a [Bundle] via the backend.
62
62
  Future<Bundle> deployBundle(Backend backend, DeployOptions opts) async {
63
63
  if (opts.targetAppVersion != null && opts.fingerprintHash != null) {
64
- throw StateError(
65
- 'Use only one of target-app-version / fingerprint-hash (DB CHECK).',
66
- );
64
+ throw StateError('Use only one of target-app-version / fingerprint-hash.');
67
65
  }
68
66
  if (opts.targetAppVersion == null && opts.fingerprintHash == null) {
69
67
  throw StateError(
@@ -16,13 +16,17 @@ import 'dart:io';
16
16
  import 'package:path/path.dart' as p;
17
17
 
18
18
  class PocketBaseProcess {
19
- PocketBaseProcess._(this.process, this.dataDir, this.binaryPath);
19
+ PocketBaseProcess._(this.process, this.dataDir, this.binaryPath)
20
+ : _exitCodeCompleted = false {
21
+ process.exitCode.then((_) => _exitCodeCompleted = true);
22
+ }
20
23
 
21
24
  final Process process;
22
25
  final Directory dataDir;
23
26
  final File binaryPath;
27
+ bool _exitCodeCompleted;
24
28
 
25
- bool get isRunning => true;
29
+ bool get isRunning => !_exitCodeCompleted;
26
30
 
27
31
  int get pid => process.pid;
28
32
 
@@ -88,12 +92,14 @@ class PocketBaseProcessManager {
88
92
  }
89
93
 
90
94
  void _writeConfigFile(int pid) {
91
- configFile.writeAsStringSync(jsonEncode({
92
- 'pid': pid,
93
- 'host': host,
94
- 'port': port,
95
- 'url': 'http://$host:$port',
96
- }));
95
+ configFile.writeAsStringSync(
96
+ jsonEncode({
97
+ 'pid': pid,
98
+ 'host': host,
99
+ 'port': port,
100
+ 'url': 'http://$host:$port',
101
+ }),
102
+ );
97
103
  }
98
104
 
99
105
  Map<String, dynamic>? readConfig() {
@@ -131,19 +137,18 @@ class PocketBaseProcessManager {
131
137
  final hooksDir = Directory(p.join(dataDir.path, 'pb_hooks'));
132
138
  await hooksDir.create(recursive: true);
133
139
 
134
- final mergedEnv = <String, String>{
135
- ...Platform.environment,
136
- ...?env,
137
- };
140
+ final mergedEnv = <String, String>{...Platform.environment, ...?env};
138
141
 
139
142
  // Create superuser if credentials provided (PB 0.40+ doesn't support
140
143
  // PB_ADMIN_EMAIL env var, so we use the CLI directly).
141
144
  if (adminEmail != null && adminPassword != null) {
142
- final result = await Process.run(
143
- binaryPath.path,
144
- ['superuser', 'upsert', '--dir=${dataDir.path}', adminEmail, adminPassword],
145
- environment: mergedEnv,
146
- );
145
+ final result = await Process.run(binaryPath.path, [
146
+ 'superuser',
147
+ 'upsert',
148
+ '--dir=${dataDir.path}',
149
+ adminEmail,
150
+ adminPassword,
151
+ ], environment: mergedEnv);
147
152
  if (result.exitCode != 0) {
148
153
  // Non-fatal; serve will continue but schema install may fail.
149
154
  }
@@ -214,5 +219,9 @@ class PocketBaseProcessManager {
214
219
  .transform(utf8.decoder)
215
220
  .transform(const LineSplitter())
216
221
  .map((line) => '[stdout] $line');
222
+ yield* proc.errors
223
+ .transform(utf8.decoder)
224
+ .transform(const LineSplitter())
225
+ .map((line) => '[stderr] $line');
217
226
  }
218
227
  }
@@ -221,7 +221,6 @@ class PocketBaseSchemaInstaller {
221
221
  required this.url,
222
222
  required this.adminEmail,
223
223
  required this.adminPassword,
224
- http.Client? httpClient,
225
224
  this.bundlesCollection = 'bundles',
226
225
  this.channelsCollection = 'channels',
227
226
  this.auditLogCollection = 'audit_log',
@@ -24,7 +24,9 @@ class FlutterPatcherRunner extends CommandRunner<int> {
24
24
  // Intercept missing subcommand for parent commands so we show our
25
25
  // colored usage text instead of the args package's plain text.
26
26
  if (results.command == null && results.rest.isEmpty) {
27
- final cmdName = results.arguments.isNotEmpty ? results.arguments.first : null;
27
+ final cmdName = results.arguments.isNotEmpty
28
+ ? results.arguments.first
29
+ : null;
28
30
  if (cmdName != null) {
29
31
  final cmd = commands[cmdName];
30
32
  if (cmd != null && cmd.subcommands.isNotEmpty) {
@@ -127,7 +127,8 @@ Future<T> spinner<T>(
127
127
  final r = await task();
128
128
  // Keep spinner visible for at least 400ms so fast ops are readable.
129
129
  final elapsed = sw.elapsedMilliseconds;
130
- if (elapsed < 400) await Future.delayed(Duration(milliseconds: 400 - elapsed));
130
+ if (elapsed < 400)
131
+ await Future.delayed(Duration(milliseconds: 400 - elapsed));
131
132
  timer.cancel();
132
133
  sw.stop();
133
134
  stdout.write('\r\x1b[K ${green('✓')} ${done ?? label}\n');
@@ -292,13 +293,11 @@ class Steps {
292
293
  _echo(' ${dim('·')} $label');
293
294
  }
294
295
 
295
- /// Print boxed summary: "N applied · N skipped · N errors · TIME"
296
+ /// Print summary. Always inline — no box for a single line of text.
296
297
  void summary() {
297
298
  _sw.stop();
298
299
  final ms = _sw.elapsedMilliseconds;
299
- final time = ms >= 1000
300
- ? '${(ms / 1000).toStringAsFixed(1)}s'
301
- : '${ms}ms';
300
+ final time = ms >= 1000 ? '${(ms / 1000).toStringAsFixed(1)}s' : '${ms}ms';
302
301
  final parts = <String>[];
303
302
  if (_done > 0) parts.add('${_done} completed');
304
303
  if (_skipped > 0) parts.add('${_skipped} skipped');
@@ -307,7 +306,7 @@ class Steps {
307
306
  }
308
307
  if (parts.isEmpty) parts.add('no steps');
309
308
  parts.add(time);
310
- box('summary', [parts.join(' · ')]);
309
+ _echo(' ${dim(parts.join(' · '))}');
311
310
  }
312
311
 
313
312
  /// Run a step that shows a spinner while working, then marks it
@@ -401,9 +400,7 @@ class Steps {
401
400
  _activeTimer = null;
402
401
  _activeLabel = null;
403
402
  if (_colorOn) {
404
- stdout.write(
405
- '\r\x1b[K ${success ? green('✓') : red('✗')} $label\n',
406
- );
403
+ stdout.write('\r\x1b[K ${success ? green('✓') : red('✗')} $label\n');
407
404
  }
408
405
  if (success) {
409
406
  _done++;
@@ -455,9 +452,7 @@ class ProgressBar {
455
452
  _spin = (_spin + 1) % _frames.length;
456
453
  final pctStr = '${(pct * 100).round()}%'.padLeft(4);
457
454
 
458
- stdout.write(
459
- '\r\x1b[K ${cyan(_frames[_spin])} $_label $bar $pctStr',
460
- );
455
+ stdout.write('\r\x1b[K ${cyan(_frames[_spin])} $_label $bar $pctStr');
461
456
  }
462
457
 
463
458
  /// Finish: print the final status line. This is intentionally async and
@@ -470,11 +465,10 @@ class ProgressBar {
470
465
  _sw.stop();
471
466
  // Keep bar visible for at least 500ms so fast ops are readable.
472
467
  final elapsed = _sw.elapsedMilliseconds;
473
- if (elapsed < 500) await Future.delayed(Duration(milliseconds: 500 - elapsed));
468
+ if (elapsed < 500)
469
+ await Future.delayed(Duration(milliseconds: 500 - elapsed));
474
470
  final ms = _sw.elapsedMilliseconds;
475
- final time = ms >= 1000
476
- ? '${(ms / 1000).toStringAsFixed(1)}s'
477
- : '${ms}ms';
471
+ final time = ms >= 1000 ? '${(ms / 1000).toStringAsFixed(1)}s' : '${ms}ms';
478
472
  if (_colorOn) {
479
473
  stdout.write('\r\x1b[K ${green('✓')} $_label in $time\n');
480
474
  } else {
@@ -159,49 +159,49 @@ packages:
159
159
  path: "../../plugins/aws"
160
160
  relative: true
161
161
  source: path
162
- version: "0.1.7"
162
+ version: "0.1.10"
163
163
  flutter_ota_kit_cloudflare:
164
164
  dependency: "direct main"
165
165
  description:
166
166
  path: "../../plugins/cloudflare"
167
167
  relative: true
168
168
  source: path
169
- version: "0.1.7"
169
+ version: "0.1.10"
170
170
  flutter_ota_kit_core:
171
171
  dependency: "direct main"
172
172
  description:
173
173
  path: "../core"
174
174
  relative: true
175
175
  source: path
176
- version: "0.1.6"
176
+ version: "0.1.8"
177
177
  flutter_ota_kit_plugin_core:
178
178
  dependency: "direct main"
179
179
  description:
180
180
  path: "../../plugins/plugin-core"
181
181
  relative: true
182
182
  source: path
183
- version: "0.0.8"
183
+ version: "0.0.10"
184
184
  flutter_ota_kit_pocketbase:
185
185
  dependency: "direct main"
186
186
  description:
187
187
  path: "../../plugins/pocketbase"
188
188
  relative: true
189
189
  source: path
190
- version: "0.1.4"
190
+ version: "0.1.7"
191
191
  flutter_ota_kit_postgres:
192
192
  dependency: "direct main"
193
193
  description:
194
194
  path: "../../plugins/postgres"
195
195
  relative: true
196
196
  source: path
197
- version: "0.1.7"
197
+ version: "0.1.10"
198
198
  flutter_ota_kit_supabase:
199
199
  dependency: "direct main"
200
200
  description:
201
201
  path: "../../plugins/supabase"
202
202
  relative: true
203
203
  source: path
204
- version: "0.1.8"
204
+ version: "0.1.11"
205
205
  frontend_server_client:
206
206
  dependency: transitive
207
207
  description:
@@ -226,8 +226,9 @@ class MockPocketBaseClient implements PocketBaseClient {
226
226
  Future<void> deleteAdmin(String id) async {}
227
227
 
228
228
  @override
229
- Future<List<Map<String, dynamic>>> exportCollection(String collection) async =>
230
- [];
229
+ Future<List<Map<String, dynamic>>> exportCollection(
230
+ String collection,
231
+ ) async => [];
231
232
 
232
233
  @override
233
234
  Future<void> importCollection(
@@ -247,7 +248,10 @@ class MockPocketBaseClient implements PocketBaseClient {
247
248
 
248
249
  @override
249
250
  Future<Map<String, dynamic>> listLogs({
250
- String? filter, String? sort, int page = 1, int perPage = 30,
251
+ String? filter,
252
+ String? sort,
253
+ int page = 1,
254
+ int perPage = 30,
251
255
  }) async => {'items': [], 'totalItems': 0};
252
256
 
253
257
  @override
@@ -80,22 +80,19 @@ void main() {
80
80
  await mock.stop();
81
81
  });
82
82
 
83
- test(
84
- 'schema installer creates all five collections on a fresh PB',
85
- () async {
86
- final installer = PocketBaseSchemaInstaller(
87
- url: mock.baseUrl,
88
- adminEmail: 'admin@x.com',
89
- adminPassword: 'secret',
90
- );
91
- final result = await installer.install();
92
- expect(mock.authCalled, isTrue);
93
- expect(mock.lastAuthEmail, 'admin@x.com');
94
- expect(result.created, containsAll(_allCollections));
95
- expect(result.skipped, isEmpty);
96
- expect(mock.createdCollections, containsAll(_allCollections));
97
- },
98
- );
83
+ test('schema installer creates all five collections on a fresh PB', () async {
84
+ final installer = PocketBaseSchemaInstaller(
85
+ url: mock.baseUrl,
86
+ adminEmail: 'admin@x.com',
87
+ adminPassword: 'secret',
88
+ );
89
+ final result = await installer.install();
90
+ expect(mock.authCalled, isTrue);
91
+ expect(mock.lastAuthEmail, 'admin@x.com');
92
+ expect(result.created, containsAll(_allCollections));
93
+ expect(result.skipped, isEmpty);
94
+ expect(mock.createdCollections, containsAll(_allCollections));
95
+ });
99
96
 
100
97
  test('schema installer skips collections that already exist', () async {
101
98
  mock.existingCollections = _allCollections;
@@ -1,3 +1,18 @@
1
+ ## 0.1.8
2
+
3
+ - Release 2026-09-10
4
+
5
+ ## 0.1.8
6
+
7
+ - Automated release 2026-09-10
8
+
9
+ ## 0.1.7
10
+
11
+ - Added comprehensive documentation for all public APIs.
12
+ - Improved `Bundle` equality and hashCode implementation.
13
+ - Fixed `toSqlJson()` nullable field handling.
14
+ - Added `semver` exports to barrel file.
15
+
1
16
  ## 0.1.6
2
17
 
3
18
  - Added `gitCommitHash` field to `UpdateInfo` for commit tracking.
@@ -21,6 +21,8 @@ export 'src/update_bundle_params.dart';
21
21
  // Algorithms.
22
22
  export 'src/rollout.dart';
23
23
  export 'src/semver.dart';
24
+ export 'src/semver_range.dart' show satisfies;
25
+ export 'src/semver_version.dart' show SemVer;
24
26
  export 'src/uuid.dart';
25
27
 
26
28
  // Bundle artifact helpers.