@_nazmiforreal/flutter-ota 0.1.2 → 0.1.4

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/dart-src/packages/cli-tools/.dart_tool/package_config.json +496 -0
  2. package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +743 -0
  3. package/dart-src/packages/cli-tools/lib/src/cli_base.dart +7 -6
  4. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +46 -49
  5. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +44 -23
  6. package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +27 -17
  7. package/dart-src/packages/cli-tools/lib/src/commands/console.dart +11 -6
  8. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +39 -22
  9. package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +22 -13
  10. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +117 -36
  11. package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +15 -9
  12. package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +14 -8
  13. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +37 -12
  14. package/dart-src/packages/cli-tools/lib/src/config.dart +13 -3
  15. package/dart-src/packages/cli-tools/lib/src/operations.dart +45 -0
  16. package/dart-src/packages/cli-tools/lib/src/pack.dart +9 -0
  17. package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +234 -0
  18. package/dart-src/packages/cli-tools/pubspec.lock +638 -0
  19. package/dart-src/packages/cli-tools/test/backends_test.dart +207 -0
  20. package/dart-src/packages/cli-tools/test/build_pack_test.dart +101 -0
  21. package/dart-src/packages/cli-tools/test/cli_test.dart +442 -0
  22. package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +83 -0
  23. package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +256 -0
  24. package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +347 -0
  25. package/dart-src/packages/cli-tools/test/standalone_backend_test.dart +315 -0
  26. package/dart-src/packages/core/test/rollout_test.dart +128 -0
  27. package/dart-src/packages/core/test/semver_test.dart +52 -0
  28. package/dart-src/packages/core/test/semver_vectors.json +1 -0
  29. package/dart-src/packages/core/test/types_test.dart +129 -0
  30. package/dart-src/packages/core/test/uuid_test.dart +38 -0
  31. package/dart-src/plugins/aws/test/aws_database_test.dart +167 -0
  32. package/dart-src/plugins/aws/test/aws_lambda_edge_storage_test.dart +190 -0
  33. package/dart-src/plugins/aws/test/aws_storage_test.dart +73 -0
  34. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +83 -0
  35. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +122 -0
  36. package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +106 -0
  37. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +190 -0
  38. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +256 -0
  39. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +83 -0
  40. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +77 -0
  41. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +166 -0
  42. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +74 -0
  43. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +332 -0
  44. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +711 -0
  45. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +294 -0
  46. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +192 -0
  47. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +51 -0
  48. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +1600 -0
  49. package/dart-src/plugins/plugin-core/test/helpers_test.dart +259 -0
  50. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +139 -0
  51. package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +86 -0
  52. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +77 -0
  53. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +297 -0
  54. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +140 -0
  55. package/dart-src/plugins/postgres/test/postgres_database_test.dart +181 -0
  56. package/dart-src/plugins/standalone/test/standalone_test.dart +264 -0
  57. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +654 -0
  58. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +112 -0
  59. package/dart-src/plugins/supabase/test/supabase_database_test.dart +207 -0
  60. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +175 -0
  61. package/package.json +1 -1
  62. package/scripts/vendor.sh +29 -0
@@ -0,0 +1,112 @@
1
+ import 'package:flutter_patcher_core/flutter_patcher_core.dart';
2
+ import 'package:flutter_patcher_supabase/src/supabase_bundle_mapper.dart';
3
+ import 'package:flutter_patcher_supabase/src/types.dart';
4
+ import 'package:test/test.dart';
5
+
6
+ Bundle _makeBundle({List<BundlePatchArtifact> patches = const []}) => Bundle(
7
+ id: '018f0000-0000-7000-8000-000000000001',
8
+ platform: Platform.android,
9
+ shouldForceUpdate: false,
10
+ enabled: true,
11
+ fileHash: 'file-hash-1',
12
+ gitCommitHash: 'git-1',
13
+ message: 'hello',
14
+ channel: 'production',
15
+ storageUri: 'supabase-storage://updates/bundles/b1.zip',
16
+ targetAppVersion: '1.0.0',
17
+ fingerprintHash: 'fp-1',
18
+ metadata: const BundleMetadata(appVersion: '1.2.3'),
19
+ manifestStorageUri: 'supabase-storage://updates/manifests/m1.json',
20
+ manifestFileHash: 'mf-1',
21
+ assetBaseStorageUri: 'supabase-storage://updates/assets',
22
+ rolloutCohortCount: 500,
23
+ targetCohorts: ['team-a'],
24
+ patches: patches,
25
+ patchBaseBundleId: patches.isNotEmpty ? patches.first.baseBundleId : null,
26
+ patchBaseFileHash:
27
+ patches.isNotEmpty ? patches.first.baseFileHash : null,
28
+ patchFileHash: patches.isNotEmpty ? patches.first.patchFileHash : null,
29
+ patchStorageUri:
30
+ patches.isNotEmpty ? patches.first.patchStorageUri : null,
31
+ );
32
+
33
+ void main() {
34
+ group('bundleToRow / mapRowToBundle round-trip', () {
35
+ test('preserves scalar fields and stripBundleArtifactMetadata', () {
36
+ final bundle = _makeBundle();
37
+ final row = bundleToRow(bundle);
38
+
39
+ expect(row.id, bundle.id);
40
+ expect(row.channel, 'production');
41
+ expect(row.enabled, true);
42
+ expect(row.shouldForceUpdate, false);
43
+ expect(row.fileHash, 'file-hash-1');
44
+ expect(row.gitCommitHash, 'git-1');
45
+ expect(row.message, 'hello');
46
+ expect(row.platform, 'android');
47
+ expect(row.targetAppVersion, '1.0.0');
48
+ expect(row.fingerprintHash, 'fp-1');
49
+ expect(row.storageUri, 'supabase-storage://updates/bundles/b1.zip');
50
+ expect(row.manifestStorageUri, bundle.manifestStorageUri);
51
+ expect(row.manifestFileHash, 'mf-1');
52
+ expect(row.assetBaseStorageUri, bundle.assetBaseStorageUri);
53
+ expect(row.rolloutCohortCount, 500);
54
+ expect(row.targetCohorts, ['team-a']);
55
+ expect(row.metadata, {'app_version': '1.2.3'});
56
+ });
57
+
58
+ test('mapRowToBundle rebuilds derived patch fields', () {
59
+ final baseBundleId = '018f0000-0000-7000-8000-000000000000';
60
+ final bundle = _makeBundle(
61
+ patches: [
62
+ BundlePatchArtifact(
63
+ baseBundleId: baseBundleId,
64
+ baseFileHash: 'base-hash',
65
+ patchFileHash: 'patch-hash',
66
+ patchStorageUri: 'supabase-storage://updates/patches/p1.patch',
67
+ ),
68
+ ],
69
+ );
70
+
71
+ final row = bundleToRow(bundle);
72
+ final patchRows = bundleToPatchRows(bundle);
73
+ expect(patchRows, hasLength(1));
74
+ expect(patchRows.first.id, '${bundle.id}:$baseBundleId');
75
+ expect(patchRows.first.bundleId, bundle.id);
76
+ expect(patchRows.first.baseBundleId, baseBundleId);
77
+ expect(patchRows.first.orderIndex, 0);
78
+
79
+ final back = mapRowToBundle(
80
+ SupabaseBundleRow.fromJson(row.toJson()),
81
+ patchRows.map((p) => SupabaseBundlePatchRow.fromJson(p.toJson())).toList(),
82
+ );
83
+
84
+ expect(back.id, bundle.id);
85
+ expect(back.patches, hasLength(1));
86
+ expect(back.patchBaseBundleId, baseBundleId);
87
+ expect(back.patchBaseFileHash, 'base-hash');
88
+ expect(back.patches!.first.patchFileHash, 'patch-hash');
89
+ expect(back.patchStorageUri, 'supabase-storage://updates/patches/p1.patch');
90
+ expect(back.metadata?.appVersion, '1.2.3');
91
+ });
92
+
93
+ test('defaults when row fields are missing', () {
94
+ final row = SupabaseBundleRow.fromJson({
95
+ 'id': 'id-1',
96
+ 'channel': 'production',
97
+ 'enabled': true,
98
+ 'platform': 'android',
99
+ 'should_force_update': false,
100
+ 'file_hash': 'fh',
101
+ 'storage_uri': 's',
102
+ 'target_app_version': '1.0.0',
103
+ 'metadata': {'app_version': '9.9.9'},
104
+ });
105
+ final bundle = mapRowToBundle(row);
106
+ expect(bundle.rolloutCohortCount, 1000);
107
+ expect(bundle.targetCohorts, isNull);
108
+ expect(bundle.metadata?.appVersion, '9.9.9');
109
+ expect(bundle.manifestStorageUri, isNull);
110
+ });
111
+ });
112
+ }
@@ -0,0 +1,207 @@
1
+ import 'package:flutter_patcher_core/flutter_patcher_core.dart';
2
+ import 'package:flutter_patcher_plugin_core/flutter_patcher_plugin_core.dart';
3
+ import 'package:flutter_patcher_supabase/flutter_patcher_supabase.dart';
4
+ import 'package:test/test.dart';
5
+
6
+ import 'mock_supabase_client.dart';
7
+
8
+ late Store store;
9
+ late FakeStorageBucket bucket;
10
+ late SupabaseClientLike client;
11
+
12
+ DatabasePlugin newPlugin() => supabaseDatabase(
13
+ SupabaseServiceRoleConfig(
14
+ supabaseUrl: 'https://test.supabase.invalid',
15
+ supabaseAnonKey: 'test-anon-key',
16
+ clientFactory: (_, __) => client,
17
+ ),
18
+ )();
19
+
20
+ Bundle _bundle(
21
+ String id, {
22
+ bool enabled = true,
23
+ String? fp,
24
+ String? message,
25
+ String channel = 'production',
26
+ }) =>
27
+ Bundle(
28
+ id: id,
29
+ platform: Platform.android,
30
+ shouldForceUpdate: false,
31
+ enabled: enabled,
32
+ fileHash: 'file-$id',
33
+ gitCommitHash: 'git-$id',
34
+ message: message ?? 'msg-$id',
35
+ channel: channel,
36
+ storageUri: 'supabase-storage://updates/bundles/$id.zip',
37
+ targetAppVersion: '1.0.0',
38
+ fingerprintHash: fp,
39
+ metadata: const BundleMetadata(),
40
+ manifestStorageUri: 'supabase-storage://updates/manifests/$id.json',
41
+ manifestFileHash: 'mf-$id',
42
+ assetBaseStorageUri: 'supabase-storage://updates/assets',
43
+ );
44
+
45
+ void main() {
46
+ setUp(() {
47
+ store = Store();
48
+ bucket = FakeStorageBucket();
49
+ client = createMockSupabaseClient(store: store, bucket: bucket);
50
+ });
51
+
52
+ group('supabaseDatabase bundle methods', () {
53
+ test('insert + commit + getBundleById round-trips', () async {
54
+ final plugin = newPlugin();
55
+ await plugin.appendBundle(_bundle('b1'));
56
+ await plugin.commitBundle();
57
+ final got = await plugin.getBundleById('b1');
58
+ expect(got, isNotNull);
59
+ expect(got!.id, 'b1');
60
+ expect(got.fileHash, 'file-b1');
61
+ expect(got.targetAppVersion, '1.0.0');
62
+ expect(got.rolloutCohortCount, 1000);
63
+ });
64
+
65
+ test('getBundles filters by where + pagination', () async {
66
+ final plugin = newPlugin();
67
+ for (var i = 0; i < 5; i++) {
68
+ await plugin.appendBundle(_bundle('b$i'));
69
+ }
70
+ await plugin.commitBundle();
71
+
72
+ final res = await plugin.getBundles(
73
+ DatabaseBundleQueryOptions(
74
+ where: DatabaseBundleQueryWhere(
75
+ channel: 'production',
76
+ platform: Platform.android,
77
+ ),
78
+ limit: 3,
79
+ ),
80
+ );
81
+ expect(res.data, hasLength(3));
82
+ expect(res.pagination.total, 5);
83
+ });
84
+
85
+ test('getChannels returns distinct channels', () async {
86
+ final plugin = newPlugin();
87
+ await plugin.appendBundle(_bundle('b1'));
88
+ await plugin.appendBundle(
89
+ _bundle('b2', channel: 'staging'),
90
+ );
91
+ await plugin.commitBundle();
92
+ final channels = await plugin.getChannels();
93
+ expect(channels, containsAll(['production', 'staging']));
94
+ expect(channels, hasLength(2));
95
+ });
96
+
97
+ test('updateBundle persists changes', () async {
98
+ final plugin = newPlugin();
99
+ await plugin.appendBundle(_bundle('b1'));
100
+ await plugin.commitBundle();
101
+ final updated = _bundle('b1', message: 'updated');
102
+ await plugin.updateBundle('b1', updated.toJson());
103
+ await plugin.commitBundle();
104
+ final got = await plugin.getBundleById('b1');
105
+ expect(got!.message, 'updated');
106
+ });
107
+
108
+ test('deleteBundle removes the row', () async {
109
+ final plugin = newPlugin();
110
+ await plugin.appendBundle(_bundle('b1'));
111
+ await plugin.commitBundle();
112
+ final got = await plugin.getBundleById('b1');
113
+ await plugin.deleteBundle(got!);
114
+ await plugin.commitBundle();
115
+ expect(await plugin.getBundleById('b1'), isNull);
116
+ });
117
+ });
118
+
119
+ group('supabaseDatabase getUpdateInfo via RPC', () {
120
+ test('appVersion strategy returns newer enabled bundle', () async {
121
+ final plugin = newPlugin();
122
+ await plugin.appendBundle(
123
+ _bundle('018f0000-0000-7000-8000-000000000001'),
124
+ );
125
+ await plugin.appendBundle(
126
+ _bundle('018f0000-0000-7000-8000-000000000002'),
127
+ );
128
+ await plugin.commitBundle();
129
+
130
+ final info = await plugin.getUpdateInfo(
131
+ AppVersionGetBundlesArgs(
132
+ appVersion: '1.0.0',
133
+ bundleId: '018f0000-0000-7000-8000-000000000001',
134
+ channel: 'production',
135
+ minBundleId: nilUuid,
136
+ platform: Platform.android,
137
+ ),
138
+ );
139
+ expect(info, isNotNull);
140
+ expect(info!.id, '018f0000-0000-7000-8000-000000000002');
141
+ expect(info.status, UpdateStatus.update);
142
+ });
143
+
144
+ test('fingerprint strategy returns matching bundle', () async {
145
+ final plugin = newPlugin();
146
+ await plugin.appendBundle(
147
+ _bundle(
148
+ '018f0000-0000-7000-8000-000000000001',
149
+ fp: 'fp-current',
150
+ ),
151
+ );
152
+ await plugin.appendBundle(
153
+ _bundle(
154
+ '018f0000-0000-7000-8000-000000000002',
155
+ fp: 'fp-target',
156
+ ),
157
+ );
158
+ await plugin.commitBundle();
159
+
160
+ final info = await plugin.getUpdateInfo(
161
+ FingerprintGetBundlesArgs(
162
+ fingerprintHash: 'fp-target',
163
+ bundleId: '018f0000-0000-7000-8000-000000000001',
164
+ channel: 'production',
165
+ minBundleId: nilUuid,
166
+ platform: Platform.android,
167
+ ),
168
+ );
169
+ expect(info, isNotNull);
170
+ expect(info!.id, '018f0000-0000-7000-8000-000000000002');
171
+ });
172
+
173
+ test('returns rollback when no compatible newer bundle exists', () async {
174
+ final plugin = newPlugin();
175
+ await plugin.appendBundle(
176
+ _bundle('018f0000-0000-7000-8000-000000000001'),
177
+ );
178
+ await plugin.commitBundle();
179
+
180
+ final info = await plugin.getUpdateInfo(
181
+ AppVersionGetBundlesArgs(
182
+ appVersion: '9.9.9',
183
+ bundleId: '018f0000-0000-7000-8000-000000000001',
184
+ channel: 'production',
185
+ minBundleId: nilUuid,
186
+ platform: Platform.android,
187
+ ),
188
+ );
189
+ expect(info, isNotNull);
190
+ expect(info!.status, UpdateStatus.rollback);
191
+ });
192
+
193
+ test('returns null for an init bundle with no candidates', () async {
194
+ final plugin = newPlugin();
195
+ final info = await plugin.getUpdateInfo(
196
+ AppVersionGetBundlesArgs(
197
+ appVersion: '9.9.9',
198
+ bundleId: nilUuid,
199
+ channel: 'production',
200
+ minBundleId: nilUuid,
201
+ platform: Platform.android,
202
+ ),
203
+ );
204
+ expect(info, isNull);
205
+ });
206
+ });
207
+ }
@@ -0,0 +1,175 @@
1
+ import 'dart:io' as io;
2
+
3
+ import 'package:flutter_patcher_plugin_core/flutter_patcher_plugin_core.dart';
4
+ import 'package:flutter_patcher_supabase/flutter_patcher_supabase.dart';
5
+ import 'package:test/test.dart';
6
+
7
+ import 'mock_supabase_client.dart';
8
+
9
+ late Store store;
10
+ late FakeStorageBucket bucket;
11
+ late SupabaseClientLike client;
12
+ late StoragePlugin storage;
13
+ late NodeStorageProfile node;
14
+ late RuntimeStorageProfile runtime;
15
+
16
+ void main() {
17
+ setUp(() {
18
+ store = Store();
19
+ bucket = FakeStorageBucket();
20
+ client = createMockSupabaseClient(store: store, bucket: bucket);
21
+ storage = supabaseStorage(
22
+ SupabaseStorageConfig(
23
+ bucketName: 'updates',
24
+ supabaseUrl: 'https://example.supabase.co',
25
+ supabaseAnonKey: 'anon-key',
26
+ clientFactory: (_, __) => client,
27
+ ),
28
+ );
29
+ node = storage.profiles.node!;
30
+ runtime = storage.profiles.runtime!;
31
+ });
32
+
33
+ group('supabaseStorage node.exists', () {
34
+ test('returns true for existing, signable objects', () async {
35
+ bucket.existsData = true;
36
+ bucket.signedUrlData = 'https://example.supabase.co/signed-url';
37
+ final result = await node.exists(
38
+ 'supabase-storage://updates/assets/sha256/fi/file-hash.png',
39
+ );
40
+ expect(result, isTrue);
41
+ expect(bucket.existsCalls, ['assets/sha256/fi/file-hash.png']);
42
+ expect(bucket.createSignedUrlCalls, ['assets/sha256/fi/file-hash.png']);
43
+ });
44
+
45
+ test('returns false when object is missing', () async {
46
+ bucket.existsData = false;
47
+ bucket.existsError = 'Object not found';
48
+ final result = await node.exists(
49
+ 'supabase-storage://updates/assets/sha256/fi/file-hash.png',
50
+ );
51
+ expect(result, isFalse);
52
+ expect(bucket.createSignedUrlCalls, isEmpty);
53
+ });
54
+
55
+ test('rejects existing objects that are not signable', () async {
56
+ bucket.existsData = true;
57
+ bucket.signedUrlError = 'Object not found';
58
+ await expectLater(
59
+ node.exists(
60
+ 'supabase-storage://updates/assets/sha256/fi/file-hash.png',
61
+ ),
62
+ throwsA(
63
+ isA<StateError>().having(
64
+ (e) => e.message,
65
+ 'message',
66
+ contains(
67
+ 'Failed to generate download URL for '
68
+ '"assets/sha256/fi/file-hash.png": Object not found',
69
+ ),
70
+ ),
71
+ ),
72
+ );
73
+ expect(bucket.createSignedUrlCalls, hasLength(1));
74
+ });
75
+
76
+ test('rejects a different bucket', () async {
77
+ await expectLater(
78
+ node.exists(
79
+ 'supabase-storage://other/assets/sha256/fi/file-hash.png',
80
+ ),
81
+ throwsA(
82
+ isA<StateError>().having(
83
+ (e) => e.message,
84
+ 'message',
85
+ contains('Bucket name mismatch: expected "updates"'),
86
+ ),
87
+ ),
88
+ );
89
+ expect(bucket.existsCalls, isEmpty);
90
+ });
91
+ });
92
+
93
+ group('supabaseStorage runtime.getDownloadUrl', () {
94
+ test('batches concurrent signed URL requests', () async {
95
+ final paths = List.generate(
96
+ 20,
97
+ (i) => 'assets/sha256/file-$i.png',
98
+ );
99
+ final results = await Future.wait(
100
+ paths.map((p) => runtime.getDownloadUrl('supabase-storage://updates/$p')),
101
+ );
102
+ expect(
103
+ results,
104
+ paths
105
+ .map((p) => {'fileUrl': 'https://example.supabase.co/3600/$p'})
106
+ .toList(),
107
+ );
108
+ expect(bucket.createSignedUrlsCalls, hasLength(1));
109
+ expect(bucket.createSignedUrlsCalls.first, paths);
110
+ });
111
+
112
+ test('surfaces non-missing signed URL errors', () async {
113
+ bucket.signedUrlError = 'Storage API failed';
114
+ await expectLater(
115
+ runtime.getDownloadUrl(
116
+ 'supabase-storage://updates/assets/sha256/fi/file-hash.png',
117
+ ),
118
+ throwsA(
119
+ isA<StateError>().having(
120
+ (e) => e.message,
121
+ 'message',
122
+ contains(
123
+ 'Failed to generate download URL for '
124
+ '"assets/sha256/fi/file-hash.png": Storage API failed',
125
+ ),
126
+ ),
127
+ ),
128
+ );
129
+ expect(bucket.createSignedUrlsCalls, hasLength(1));
130
+ });
131
+
132
+ test('decodes percent-encoded object keys before signing and removing',
133
+ () async {
134
+ const uri = 'supabase-storage://updates/'
135
+ 'assets/bootsplash/logo-ios%402x.png';
136
+ final url = await runtime.getDownloadUrl(uri);
137
+ expect(
138
+ url,
139
+ {
140
+ 'fileUrl': 'https://example.supabase.co/3600/'
141
+ 'assets/bootsplash/logo-ios@2x.png'
142
+ },
143
+ );
144
+ expect(
145
+ bucket.createSignedUrlsCalls.first,
146
+ ['assets/bootsplash/logo-ios@2x.png'],
147
+ );
148
+
149
+ await node.delete(uri);
150
+ expect(
151
+ bucket.removeCalls.first,
152
+ ['assets/bootsplash/logo-ios@2x.png'],
153
+ );
154
+ });
155
+ });
156
+
157
+ group('supabaseStorage node.upload', () {
158
+ test('returns storageUri for a signable upload', () async {
159
+ final dir = await io.Directory.systemTemp.createTemp('hu-supabase-');
160
+ final uploadPath = '${dir.path}/bundle.zip';
161
+ await io.File(uploadPath).writeAsString('bundle');
162
+ bucket.uploadData = {'fullPath': 'updates/bundles/bundle.zip'};
163
+
164
+ final result = await node.upload('bundles', uploadPath);
165
+ expect(
166
+ result,
167
+ {'storageUri': 'supabase-storage://updates/bundles/bundle.zip'},
168
+ );
169
+ expect(bucket.uploadCalls, ['bundles/bundle.zip']);
170
+ expect(bucket.createSignedUrlCalls, ['bundles/bundle.zip']);
171
+
172
+ await dir.delete(recursive: true);
173
+ });
174
+ });
175
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_nazmiforreal/flutter-ota",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Command-line interface for flutter_patcher — an OTA code-push tool for Flutter, hot-updater compatible.",
5
5
  "bin": {
6
6
  "flutter-patcher": "./bin/flutter-patcher.js"
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ # Regenerate the vendored Dart source under dart-src/ from the monorepo so the
3
+ # npm package can `dart compile exe` on any architecture (e.g. arm64).
4
+ set -euo pipefail
5
+
6
+ ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
7
+ DST="$(cd "$(dirname "$0")/.." && pwd)/dart-src"
8
+ SRC="$ROOT"
9
+
10
+ echo "Vendoring Dart source from $SRC into $DST"
11
+
12
+ rm -rf "$DST"
13
+ mkdir -p "$DST/packages" "$DST/plugins"
14
+
15
+ for p in packages/core packages/cli-tools; do
16
+ cp -R "$SRC/$p" "$DST/$p"
17
+ done
18
+ for p in plugins/cloudflare plugins/plugin-core plugins/postgres plugins/aws plugins/standalone plugins/supabase; do
19
+ cp -R "$SRC/$p" "$DST/$p"
20
+ done
21
+
22
+ # Drop tooling/lock artifacts so `dart pub get` resolves fresh on install.
23
+ find "$DST" -name pubspec.lock -delete
24
+ find "$DST" -name .dart_tool -type d -prune -exec rm -rf {} +
25
+ find "$DST" -name build -type d -prune -exec rm -rf {} +
26
+ find "$DST" -name .git -type d -prune -exec rm -rf {} +
27
+ find "$DST" -name coverage -type d -prune -exec rm -rf {} +
28
+
29
+ echo "Done. dart-src size: $(du -sh "$DST" | cut -f1)"