@_nazmiforreal/flutter-ota 0.1.0 → 0.1.2
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.
- package/bin/flutter-patcher.js +48 -39
- package/dart-src/packages/cli-tools/.gitkeep +0 -0
- package/dart-src/packages/cli-tools/bin/flutter_patcher.dart +9 -0
- package/dart-src/packages/cli-tools/lib/flutter_patcher_cli.dart +73 -0
- package/dart-src/packages/cli-tools/lib/src/backend.dart +162 -0
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +56 -0
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +99 -0
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +151 -0
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +140 -0
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +113 -0
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +95 -0
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +48 -0
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +29 -0
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +82 -0
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +132 -0
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +41 -0
- package/dart-src/packages/cli-tools/lib/src/config.dart +715 -0
- package/dart-src/packages/cli-tools/lib/src/operations.dart +206 -0
- package/dart-src/packages/cli-tools/lib/src/pack.dart +417 -0
- package/dart-src/packages/cli-tools/lib/src/sign.dart +60 -0
- package/dart-src/packages/cli-tools/lib/src/util.dart +86 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0001_hot-updater_init.sql +16 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/bundles.sql +42 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250103114225_init.sql +228 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260520014100_hot-updater_rls.sql +48 -0
- package/dart-src/packages/cli-tools/pubspec.yaml +37 -0
- package/dart-src/packages/core/lib/flutter_patcher_core.dart +29 -0
- package/dart-src/packages/core/lib/src/app_update_info.dart +111 -0
- package/dart-src/packages/core/lib/src/bundle.dart +191 -0
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +39 -0
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +47 -0
- package/dart-src/packages/core/lib/src/changed_asset.dart +77 -0
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +78 -0
- package/dart-src/packages/core/lib/src/metadata.dart +31 -0
- package/dart-src/packages/core/lib/src/patch_info.dart +284 -0
- package/dart-src/packages/core/lib/src/platform.dart +15 -0
- package/dart-src/packages/core/lib/src/rollout.dart +209 -0
- package/dart-src/packages/core/lib/src/semver.dart +70 -0
- package/dart-src/packages/core/lib/src/semver_range.dart +358 -0
- package/dart-src/packages/core/lib/src/semver_version.dart +94 -0
- package/dart-src/packages/core/lib/src/status.dart +24 -0
- package/dart-src/packages/core/lib/src/strategy.dart +10 -0
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +30 -0
- package/dart-src/packages/core/lib/src/uuid.dart +45 -0
- package/dart-src/packages/core/pubspec.yaml +9 -0
- package/dart-src/packages/core/tool/dbg.dart +6 -0
- package/dart-src/plugins/aws/.gitkeep +0 -0
- package/dart-src/plugins/aws/lib/flutter_patcher_aws.dart +12 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +207 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +130 -0
- package/dart-src/plugins/aws/lib/src/aws_config.dart +59 -0
- package/dart-src/plugins/aws/lib/src/aws_database.dart +198 -0
- package/dart-src/plugins/aws/lib/src/aws_lambda_edge_storage.dart +72 -0
- package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +312 -0
- package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +125 -0
- package/dart-src/plugins/aws/lib/src/aws_storage.dart +20 -0
- package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +123 -0
- package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +136 -0
- package/dart-src/plugins/aws/pubspec.yaml +22 -0
- package/dart-src/plugins/cloudflare/.gitkeep +0 -0
- package/dart-src/plugins/cloudflare/lib/flutter_patcher_cloudflare.dart +18 -0
- package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +29 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +93 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +131 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_client.dart +66 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_config.dart +30 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database.dart +16 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +314 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +50 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +287 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +23 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +110 -0
- package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +16 -0
- package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/plugins/cloudflare/pubspec.yaml +20 -0
- package/dart-src/plugins/cloudflare/sql/bundles.sql +42 -0
- package/dart-src/plugins/plugin-core/lib/flutter_patcher_plugin_core.dart +42 -0
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +94 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +259 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work_store.dart +20 -0
- package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +29 -0
- package/dart-src/plugins/plugin-core/lib/src/compression_format.dart +63 -0
- package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +22 -0
- package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +853 -0
- package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +525 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_key_builder.dart +15 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +130 -0
- package/dart-src/plugins/plugin-core/lib/src/filter_compatible_app_versions.dart +18 -0
- package/dart-src/plugins/plugin-core/lib/src/generate_min_bundle_id.dart +19 -0
- package/dart-src/plugins/plugin-core/lib/src/get_update_info.dart +241 -0
- package/dart-src/plugins/plugin-core/lib/src/is_object.dart +5 -0
- package/dart-src/plugins/plugin-core/lib/src/legacy_asset_storage_layout.dart +9 -0
- package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +112 -0
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +70 -0
- package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +62 -0
- package/dart-src/plugins/plugin-core/lib/src/resolve_update_info_from_bundles.dart +43 -0
- package/dart-src/plugins/plugin-core/lib/src/storage_profile.dart +27 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +454 -0
- package/dart-src/plugins/plugin-core/lib/src/uuidv7.dart +41 -0
- package/dart-src/plugins/plugin-core/pubspec.yaml +15 -0
- package/dart-src/plugins/postgres/.gitkeep +0 -0
- package/dart-src/plugins/postgres/lib/flutter_patcher_postgres.dart +17 -0
- package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +130 -0
- package/dart-src/plugins/postgres/lib/src/postgres_client.dart +90 -0
- package/dart-src/plugins/postgres/lib/src/postgres_config.dart +35 -0
- package/dart-src/plugins/postgres/lib/src/postgres_database.dart +302 -0
- package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +120 -0
- package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +205 -0
- package/dart-src/plugins/postgres/lib/src/postgres_types.dart +132 -0
- package/dart-src/plugins/postgres/pubspec.yaml +18 -0
- package/dart-src/plugins/postgres/sql/bundles.sql +45 -0
- package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +21 -0
- package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +171 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +126 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +125 -0
- package/dart-src/plugins/postgres/sql/hash_user_id.sql +30 -0
- package/dart-src/plugins/postgres/sql/is_device_eligible.sql +237 -0
- package/dart-src/plugins/postgres/sql/prepareSql.ts +11 -0
- package/dart-src/plugins/standalone/.gitkeep +0 -0
- package/dart-src/plugins/standalone/lib/flutter_patcher_standalone.dart +12 -0
- package/dart-src/plugins/standalone/lib/src/standalone_config.dart +107 -0
- package/dart-src/plugins/standalone/lib/src/standalone_database.dart +233 -0
- package/dart-src/plugins/standalone/lib/src/standalone_storage.dart +175 -0
- package/dart-src/plugins/standalone/pubspec.yaml +18 -0
- package/dart-src/plugins/supabase/lib/edge.dart +10 -0
- package/dart-src/plugins/supabase/lib/flutter_patcher_supabase.dart +21 -0
- package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +129 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +154 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +516 -0
- package/dart-src/plugins/supabase/lib/src/supabase_config.dart +34 -0
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +422 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_database.dart +32 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +99 -0
- package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +145 -0
- package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +298 -0
- package/dart-src/plugins/supabase/lib/src/types.dart +130 -0
- package/dart-src/plugins/supabase/pubspec.yaml +19 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +44 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +968 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +228 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +48 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +53 -25
- /package/bin/{flutter-patcher-linux → flutter-patcher-linux-x64} +0 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import 'package:flutter_patcher_core/flutter_patcher_core.dart';
|
|
2
|
+
export 'package:flutter_patcher_core/flutter_patcher_core.dart'
|
|
3
|
+
show Bundle, GetBundlesArgs, Platform, UpdateInfo;
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Pagination
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
class PaginationInfo {
|
|
10
|
+
const PaginationInfo({
|
|
11
|
+
required this.total,
|
|
12
|
+
required this.hasNextPage,
|
|
13
|
+
required this.hasPreviousPage,
|
|
14
|
+
required this.currentPage,
|
|
15
|
+
required this.totalPages,
|
|
16
|
+
this.nextCursor,
|
|
17
|
+
this.previousCursor,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
final int total;
|
|
21
|
+
final bool hasNextPage;
|
|
22
|
+
final bool hasPreviousPage;
|
|
23
|
+
final int currentPage;
|
|
24
|
+
final int totalPages;
|
|
25
|
+
final String? nextCursor;
|
|
26
|
+
final String? previousCursor;
|
|
27
|
+
|
|
28
|
+
@override
|
|
29
|
+
String toString() =>
|
|
30
|
+
'PaginationInfo(total: $total, hasNextPage: $hasNextPage, '
|
|
31
|
+
'hasPreviousPage: $hasPreviousPage, currentPage: $currentPage, '
|
|
32
|
+
'totalPages: $totalPages)';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class Paginated<T> {
|
|
36
|
+
const Paginated({required this.data, required this.pagination});
|
|
37
|
+
|
|
38
|
+
final T data;
|
|
39
|
+
final PaginationInfo pagination;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
typedef PaginatedResult = Paginated<List<Bundle>>;
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Database query types
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
class DatabaseBundleIdFilter {
|
|
49
|
+
const DatabaseBundleIdFilter({
|
|
50
|
+
this.eq,
|
|
51
|
+
this.gt,
|
|
52
|
+
this.gte,
|
|
53
|
+
this.lt,
|
|
54
|
+
this.lte,
|
|
55
|
+
this.ins,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
final String? eq;
|
|
59
|
+
final String? gt;
|
|
60
|
+
final String? gte;
|
|
61
|
+
final String? lt;
|
|
62
|
+
final String? lte;
|
|
63
|
+
final List<String>? ins;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class DatabaseBundleQueryWhere {
|
|
67
|
+
const DatabaseBundleQueryWhere({
|
|
68
|
+
this.channel,
|
|
69
|
+
this.platform,
|
|
70
|
+
this.enabled,
|
|
71
|
+
this.id,
|
|
72
|
+
this.targetAppVersion,
|
|
73
|
+
this.targetAppVersionIn,
|
|
74
|
+
this.targetAppVersionNotNull,
|
|
75
|
+
this.fingerprintHash,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
final String? channel;
|
|
79
|
+
final Platform? platform;
|
|
80
|
+
final bool? enabled;
|
|
81
|
+
final DatabaseBundleIdFilter? id;
|
|
82
|
+
final String? targetAppVersion;
|
|
83
|
+
final List<String>? targetAppVersionIn;
|
|
84
|
+
final bool? targetAppVersionNotNull;
|
|
85
|
+
final String? fingerprintHash;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class DatabaseBundleQueryOrder {
|
|
89
|
+
const DatabaseBundleQueryOrder({
|
|
90
|
+
this.field = 'id',
|
|
91
|
+
this.direction = 'desc',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
final String field;
|
|
95
|
+
final String direction;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class DatabaseBundleCursor {
|
|
99
|
+
const DatabaseBundleCursor({this.after, this.before});
|
|
100
|
+
|
|
101
|
+
/// Fetch the next window after this bundle ID.
|
|
102
|
+
final String? after;
|
|
103
|
+
|
|
104
|
+
/// Fetch the previous window before this bundle ID.
|
|
105
|
+
final String? before;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
class DatabaseBundleQueryOptions {
|
|
109
|
+
const DatabaseBundleQueryOptions({
|
|
110
|
+
this.where,
|
|
111
|
+
this.limit = 20,
|
|
112
|
+
this.page,
|
|
113
|
+
this.offset,
|
|
114
|
+
this.cursor,
|
|
115
|
+
this.orderBy,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
final DatabaseBundleQueryWhere? where;
|
|
119
|
+
final int limit;
|
|
120
|
+
final int? page;
|
|
121
|
+
|
|
122
|
+
/// Computed by the [createDatabasePlugin] wrapper from [page] (or cursor);
|
|
123
|
+
/// absent only when using raw cursor pagination. Mirrors the TS
|
|
124
|
+
/// `DatabaseBundleQueryOptions & { offset?: number }` contract.
|
|
125
|
+
final int? offset;
|
|
126
|
+
final DatabaseBundleCursor? cursor;
|
|
127
|
+
final DatabaseBundleQueryOrder? orderBy;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// Database plugin
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
abstract class DatabasePlugin {
|
|
135
|
+
String get name;
|
|
136
|
+
|
|
137
|
+
Future<List<String>> getChannels();
|
|
138
|
+
|
|
139
|
+
Future<Bundle?> getBundleById(String bundleId);
|
|
140
|
+
|
|
141
|
+
Future<UpdateInfo?> getUpdateInfo(GetBundlesArgs args);
|
|
142
|
+
|
|
143
|
+
Future<Paginated<List<Bundle>>> getBundles(
|
|
144
|
+
DatabaseBundleQueryOptions options);
|
|
145
|
+
|
|
146
|
+
Future<void> updateBundle(String targetBundleId, Map<String, Object?> newBundle);
|
|
147
|
+
|
|
148
|
+
Future<void> appendBundle(Bundle insertBundle);
|
|
149
|
+
|
|
150
|
+
Future<void> commitBundle();
|
|
151
|
+
|
|
152
|
+
Future<void> deleteBundle(Bundle deleteBundle);
|
|
153
|
+
|
|
154
|
+
Future<void> onUnmount();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
class DatabasePluginHooks {
|
|
158
|
+
const DatabasePluginHooks({this.onDatabaseUpdated});
|
|
159
|
+
|
|
160
|
+
final Future<void> Function()? onDatabaseUpdated;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Storage plugin
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
class StorageObject {
|
|
168
|
+
const StorageObject({
|
|
169
|
+
required this.key,
|
|
170
|
+
required this.storageUri,
|
|
171
|
+
required this.size,
|
|
172
|
+
this.lastModifiedAt,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
final String key;
|
|
176
|
+
final String storageUri;
|
|
177
|
+
final int size;
|
|
178
|
+
final DateTime? lastModifiedAt;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/// Node (CLI/deploy) storage profile — filesystem-aware operations.
|
|
182
|
+
abstract class NodeStorageProfile {
|
|
183
|
+
/// Upload a local file and return the storage URI.
|
|
184
|
+
Future<Map<String, String>> upload(String key, String filePath);
|
|
185
|
+
|
|
186
|
+
/// Check if an object exists and is signable.
|
|
187
|
+
Future<bool> exists(String storageUri);
|
|
188
|
+
|
|
189
|
+
/// Delete an object by storage URI.
|
|
190
|
+
Future<void> delete(String storageUri);
|
|
191
|
+
|
|
192
|
+
/// Download an object to a local file path.
|
|
193
|
+
Future<void> downloadFile(String storageUri, String filePath);
|
|
194
|
+
|
|
195
|
+
/// List objects under an optional prefix.
|
|
196
|
+
Future<List<StorageObject>> listObjects([String? prefix]);
|
|
197
|
+
|
|
198
|
+
/// Delete multiple objects by key.
|
|
199
|
+
Future<void> deleteObjects(List<String> keys);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Runtime (update-check) storage profile — URL-oriented operations.
|
|
203
|
+
abstract class RuntimeStorageProfile {
|
|
204
|
+
/// Get a signed download URL for the given storage URI.
|
|
205
|
+
Future<Map<String, String>> getDownloadUrl(String storageUri);
|
|
206
|
+
|
|
207
|
+
/// Read the content of a small text object (e.g. manifest).
|
|
208
|
+
Future<String?> readText(String storageUri);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
class StoragePluginProfiles {
|
|
212
|
+
const StoragePluginProfiles({this.node, this.runtime});
|
|
213
|
+
|
|
214
|
+
final NodeStorageProfile? node;
|
|
215
|
+
final RuntimeStorageProfile? runtime;
|
|
216
|
+
|
|
217
|
+
bool get hasNode => node != null;
|
|
218
|
+
bool get hasRuntime => runtime != null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
abstract class StoragePlugin {
|
|
222
|
+
String get name;
|
|
223
|
+
String get supportedProtocol;
|
|
224
|
+
StoragePluginProfiles get profiles;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
class StoragePluginHooks {
|
|
228
|
+
const StoragePluginHooks({this.onStorageUploaded});
|
|
229
|
+
|
|
230
|
+
final Future<void> Function()? onStorageUploaded;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// Build plugin
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
|
|
237
|
+
class BuildPluginResult {
|
|
238
|
+
const BuildPluginResult({
|
|
239
|
+
required this.buildPath,
|
|
240
|
+
required this.bundleId,
|
|
241
|
+
this.stdout,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
final String buildPath;
|
|
245
|
+
final String bundleId;
|
|
246
|
+
final String? stdout;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
abstract class BuildPlugin {
|
|
250
|
+
String get name;
|
|
251
|
+
|
|
252
|
+
Future<BuildPluginResult> build(Platform platform);
|
|
253
|
+
|
|
254
|
+
Future<void> prebuild(Platform platform);
|
|
255
|
+
Future<void> postbuild(Platform platform);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// Android / iOS native build scheme types
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
class NativeBuildAndroidScheme {
|
|
263
|
+
const NativeBuildAndroidScheme({
|
|
264
|
+
this.variant = 'Release',
|
|
265
|
+
this.aab = true,
|
|
266
|
+
this.appModuleName = 'app',
|
|
267
|
+
required this.packageName,
|
|
268
|
+
this.applicationId,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
final String variant;
|
|
272
|
+
final bool aab;
|
|
273
|
+
final String appModuleName;
|
|
274
|
+
final String packageName;
|
|
275
|
+
final String? applicationId;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
class NativeBuildIosScheme {
|
|
279
|
+
const NativeBuildIosScheme({
|
|
280
|
+
required this.bundleIdentifier,
|
|
281
|
+
this.platform = 'ios',
|
|
282
|
+
required this.scheme,
|
|
283
|
+
this.configuration = 'Release',
|
|
284
|
+
this.destination,
|
|
285
|
+
this.exportOptionsPlist,
|
|
286
|
+
this.xcconfig,
|
|
287
|
+
this.installPods = false,
|
|
288
|
+
this.extraParams,
|
|
289
|
+
this.exportExtraParams,
|
|
290
|
+
this.simulator = false,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
final String bundleIdentifier;
|
|
294
|
+
final String platform;
|
|
295
|
+
final String scheme;
|
|
296
|
+
final String configuration;
|
|
297
|
+
final List<String>? destination;
|
|
298
|
+
final String? exportOptionsPlist;
|
|
299
|
+
final String? xcconfig;
|
|
300
|
+
final bool installPods;
|
|
301
|
+
final List<String>? extraParams;
|
|
302
|
+
final List<String>? exportExtraParams;
|
|
303
|
+
final bool simulator;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
class PlatformAndroidConfig {
|
|
307
|
+
const PlatformAndroidConfig({
|
|
308
|
+
this.androidManifestPaths,
|
|
309
|
+
this.stringResourcePaths,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
final List<String>? androidManifestPaths;
|
|
313
|
+
final List<String>? stringResourcePaths;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
class PlatformIosConfig {
|
|
317
|
+
const PlatformIosConfig({this.infoPlistPaths});
|
|
318
|
+
|
|
319
|
+
final List<String>? infoPlistPaths;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
class PlatformConfig {
|
|
323
|
+
const PlatformConfig({this.android, this.ios});
|
|
324
|
+
|
|
325
|
+
final PlatformAndroidConfig? android;
|
|
326
|
+
final PlatformIosConfig? ios;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
class NativeBuildArgs {
|
|
330
|
+
const NativeBuildArgs({this.android, this.ios});
|
|
331
|
+
|
|
332
|
+
final Map<String, NativeBuildAndroidScheme>? android;
|
|
333
|
+
final Map<String, NativeBuildIosScheme>? ios;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// Signing
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
class SigningConfigDisabled {
|
|
341
|
+
const SigningConfigDisabled({this.privateKeyPath});
|
|
342
|
+
|
|
343
|
+
final bool enabled = false;
|
|
344
|
+
final String? privateKeyPath;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
class SigningConfigEnabled {
|
|
348
|
+
const SigningConfigEnabled({required this.privateKeyPath});
|
|
349
|
+
|
|
350
|
+
final bool enabled = true;
|
|
351
|
+
final String privateKeyPath;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
// Fingerprint
|
|
356
|
+
// ---------------------------------------------------------------------------
|
|
357
|
+
|
|
358
|
+
class FingerprintExtraSourcesObject {
|
|
359
|
+
const FingerprintExtraSourcesObject({this.ios, this.android});
|
|
360
|
+
|
|
361
|
+
final List<String>? ios;
|
|
362
|
+
final List<String>? android;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ---------------------------------------------------------------------------
|
|
366
|
+
// ConfigInput — full hot-updater configuration
|
|
367
|
+
// ---------------------------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
class ConfigInput {
|
|
370
|
+
const ConfigInput({
|
|
371
|
+
this.cacheDir,
|
|
372
|
+
this.releaseChannel,
|
|
373
|
+
this.updateStrategy = 'appVersion',
|
|
374
|
+
this.compressStrategy = 'zip',
|
|
375
|
+
this.fingerprint,
|
|
376
|
+
this.patch,
|
|
377
|
+
this.console,
|
|
378
|
+
this.platform,
|
|
379
|
+
this.nativeBuild,
|
|
380
|
+
this.signing,
|
|
381
|
+
required this.build,
|
|
382
|
+
required this.storage,
|
|
383
|
+
required this.database,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
final String? cacheDir;
|
|
387
|
+
final String? releaseChannel;
|
|
388
|
+
final String updateStrategy;
|
|
389
|
+
final String compressStrategy;
|
|
390
|
+
final FingerprintConfig? fingerprint;
|
|
391
|
+
final PatchConfig? patch;
|
|
392
|
+
final ConsoleConfig? console;
|
|
393
|
+
final PlatformConfig? platform;
|
|
394
|
+
final NativeBuildArgs? nativeBuild;
|
|
395
|
+
final Object? signing;
|
|
396
|
+
final Future<BuildPlugin> Function(BasePluginArgs args) build;
|
|
397
|
+
final Future<StoragePlugin> Function() storage;
|
|
398
|
+
final Future<DatabasePlugin> Function() database;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
class BasePluginArgs {
|
|
402
|
+
const BasePluginArgs({required this.cwd});
|
|
403
|
+
|
|
404
|
+
final String cwd;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
class FingerprintConfig {
|
|
408
|
+
const FingerprintConfig({
|
|
409
|
+
this.extraSources,
|
|
410
|
+
this.ignorePaths,
|
|
411
|
+
this.debug = false,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
final Object? extraSources;
|
|
415
|
+
final List<String>? ignorePaths;
|
|
416
|
+
final bool debug;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
class PatchConfig {
|
|
420
|
+
const PatchConfig({this.enabled = true, this.maxBaseBundles = 3});
|
|
421
|
+
|
|
422
|
+
final bool enabled;
|
|
423
|
+
final int maxBaseBundles;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
class ConsoleConfig {
|
|
427
|
+
const ConsoleConfig({this.gitUrl, this.port = 1422});
|
|
428
|
+
|
|
429
|
+
final String? gitUrl;
|
|
430
|
+
final int port;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
class NativeBuildOptions {
|
|
434
|
+
const NativeBuildOptions({
|
|
435
|
+
this.outputPath,
|
|
436
|
+
required this.interactive,
|
|
437
|
+
this.message,
|
|
438
|
+
this.scheme,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
final String? outputPath;
|
|
442
|
+
final bool interactive;
|
|
443
|
+
final String? message;
|
|
444
|
+
final String? scheme;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ---------------------------------------------------------------------------
|
|
448
|
+
// Request context
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
|
|
451
|
+
/// Opaque context object carried through a request lifecycle.
|
|
452
|
+
/// In Dart we use a plain Map for extensibility.
|
|
453
|
+
typedef HotUpdaterContext = Map<String, Object?>;
|
|
454
|
+
typedef StorageResolveContext = HotUpdaterContext;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import 'dart:math';
|
|
2
|
+
|
|
3
|
+
final Random _random = Random.secure();
|
|
4
|
+
|
|
5
|
+
String _hex(int value, int width) =>
|
|
6
|
+
value.toRadixString(16).padLeft(width, '0');
|
|
7
|
+
|
|
8
|
+
/// Extract the millisecond timestamp from a UUIDv7 string.
|
|
9
|
+
int extractTimestampFromUUIDv7(String uuid) {
|
|
10
|
+
final hex = uuid.replaceAll('-', '');
|
|
11
|
+
final tsHex = hex.substring(0, 12);
|
|
12
|
+
return int.parse(tsHex, radix: 16);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// Create a UUIDv7 from a timestamp hex string (12 chars).
|
|
16
|
+
String _createUUIDv7FromTimestampHex(String tsHex) {
|
|
17
|
+
// 12 bits rand_a (version 7, bits 48-59)
|
|
18
|
+
final randA = _random.nextInt(0x1000);
|
|
19
|
+
// 62 bits rand_b (variant 10, bits 64-125)
|
|
20
|
+
// Combine two 32-bit random values to get 62 usable bits
|
|
21
|
+
final randBHi = _random.nextInt(0x100000000); // 32 bits
|
|
22
|
+
final randBLo = _random.nextInt(0x100000000); // 32 bits
|
|
23
|
+
final randB = (randBHi.toUnsigned(32) << 30) | (randBLo.toUnsigned(30));
|
|
24
|
+
|
|
25
|
+
final g3 = _hex((0x7 << 12) | randA, 4);
|
|
26
|
+
final g4 = _hex(0x8000 | ((randB >> 48) & 0x3FFF), 4);
|
|
27
|
+
final g5 = _hex(randB & 0xFFFFFFFFFFFF, 12);
|
|
28
|
+
return '${tsHex.substring(0, 8)}-${tsHex.substring(8, 12)}-$g3-$g4-$g5';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Create a new UUIDv7 with the current time.
|
|
32
|
+
String createUUIDv7() {
|
|
33
|
+
final ts = DateTime.now().millisecondsSinceEpoch;
|
|
34
|
+
return _createUUIDv7FromTimestampHex(ts.toRadixString(16).padLeft(12, '0'));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Create a new UUIDv7 preserving the timestamp from an existing UUIDv7.
|
|
38
|
+
String createUUIDv7WithSameTimestamp(String existingUuid) {
|
|
39
|
+
final tsHex = existingUuid.replaceAll('-', '').substring(0, 12);
|
|
40
|
+
return _createUUIDv7FromTimestampHex(tsHex);
|
|
41
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: flutter_patcher_plugin_core
|
|
2
|
+
description: Plugin core abstractions for flutter_patcher — database, storage, and build interfaces.
|
|
3
|
+
version: 0.0.1
|
|
4
|
+
|
|
5
|
+
environment:
|
|
6
|
+
sdk: ">=3.3.0 <4.0.0"
|
|
7
|
+
|
|
8
|
+
dependencies:
|
|
9
|
+
flutter_patcher_core:
|
|
10
|
+
path: ../../packages/core
|
|
11
|
+
mime: ^1.0.0
|
|
12
|
+
|
|
13
|
+
dev_dependencies:
|
|
14
|
+
test: ^1.25.0
|
|
15
|
+
lints: ^5.1.0
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/// flutter_patcher_postgres — Postgres backend plugin for flutter_patcher.
|
|
2
|
+
///
|
|
3
|
+
/// Faithful port of hot-updater `plugins/postgres`.
|
|
4
|
+
library;
|
|
5
|
+
|
|
6
|
+
export 'src/postgres_config.dart'
|
|
7
|
+
show PostgresConfig, PostgresClientFactory;
|
|
8
|
+
export 'src/postgres_client.dart'
|
|
9
|
+
show PostgresClientLike, PostgresClient;
|
|
10
|
+
export 'src/postgres_database.dart' show postgresDatabase;
|
|
11
|
+
export 'src/postgres_storage.dart'
|
|
12
|
+
show postgresStorage, PostgresStorageConfig, resolvePostgresStorageClient;
|
|
13
|
+
export 'src/postgres_get_update_info.dart' show getUpdateInfo;
|
|
14
|
+
export 'src/postgres_types.dart'
|
|
15
|
+
show PostgresBundleRow, PostgresBundlePatchRow;
|
|
16
|
+
export 'src/postgres_bundle_mapper.dart'
|
|
17
|
+
show mapRowToBundle, bundleToRowValues, bundleToPatchRows;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/// Faithful port of hot-updater `plugins/postgres/src/postgres.ts` mapper
|
|
2
|
+
/// (the `mapRowToBundle` / `bundleToRowValues` / `bundleToPatchRows` helpers).
|
|
3
|
+
library;
|
|
4
|
+
|
|
5
|
+
import 'dart:convert';
|
|
6
|
+
|
|
7
|
+
import 'package:flutter_patcher_core/flutter_patcher_core.dart'
|
|
8
|
+
show
|
|
9
|
+
Bundle,
|
|
10
|
+
BundleMetadata,
|
|
11
|
+
BundlePatchArtifact,
|
|
12
|
+
defaultRolloutCohortCount,
|
|
13
|
+
getAssetBaseStorageUri,
|
|
14
|
+
getBundlePatches,
|
|
15
|
+
getManifestFileHash,
|
|
16
|
+
getManifestStorageUri,
|
|
17
|
+
stripBundleArtifactMetadata;
|
|
18
|
+
|
|
19
|
+
import 'postgres_types.dart';
|
|
20
|
+
|
|
21
|
+
/// Normalize metadata from various shapes to a plain map.
|
|
22
|
+
Map<String, Object?>? _normalizeMetadata(Object? value) {
|
|
23
|
+
if (value == null) return null;
|
|
24
|
+
|
|
25
|
+
if (value is String) {
|
|
26
|
+
try {
|
|
27
|
+
final parsed = jsonDecode(value);
|
|
28
|
+
return _normalizeMetadata(parsed);
|
|
29
|
+
} catch (_) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (value is Map) return value.cast<String, Object?>();
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Build the compound primary key for a patch row.
|
|
40
|
+
String _buildBundlePatchId(String bundleId, String baseBundleId) =>
|
|
41
|
+
'$bundleId:$baseBundleId';
|
|
42
|
+
|
|
43
|
+
/// Map a Postgres bundle row + optional patch rows to a [Bundle].
|
|
44
|
+
Bundle mapRowToBundle(
|
|
45
|
+
PostgresBundleRow data, [
|
|
46
|
+
List<PostgresBundlePatchRow> patchRows = const [],
|
|
47
|
+
]) {
|
|
48
|
+
final rawMetadata = _normalizeMetadata(data.metadata);
|
|
49
|
+
final patches = patchRows.toList()
|
|
50
|
+
..sort((a, b) {
|
|
51
|
+
final cmp = a.orderIndex.compareTo(b.orderIndex);
|
|
52
|
+
if (cmp != 0) return cmp;
|
|
53
|
+
return a.baseBundleId.compareTo(b.baseBundleId);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
final patchArtifacts = patches
|
|
57
|
+
.map((p) => BundlePatchArtifact(
|
|
58
|
+
baseBundleId: p.baseBundleId,
|
|
59
|
+
baseFileHash: p.baseFileHash,
|
|
60
|
+
patchFileHash: p.patchFileHash,
|
|
61
|
+
patchStorageUri: p.patchStorageUri,
|
|
62
|
+
))
|
|
63
|
+
.toList();
|
|
64
|
+
|
|
65
|
+
final primaryPatch = patchArtifacts.isNotEmpty ? patchArtifacts.first : null;
|
|
66
|
+
|
|
67
|
+
return Bundle(
|
|
68
|
+
id: data.id,
|
|
69
|
+
channel: data.channel,
|
|
70
|
+
enabled: data.enabled,
|
|
71
|
+
shouldForceUpdate: data.shouldForceUpdate,
|
|
72
|
+
fileHash: data.fileHash,
|
|
73
|
+
gitCommitHash: data.gitCommitHash,
|
|
74
|
+
message: data.message,
|
|
75
|
+
platform: data.platform,
|
|
76
|
+
targetAppVersion: data.targetAppVersion,
|
|
77
|
+
fingerprintHash: data.fingerprintHash,
|
|
78
|
+
storageUri: data.storageUri,
|
|
79
|
+
metadata:
|
|
80
|
+
rawMetadata != null ? BundleMetadata.fromJson(rawMetadata) : null,
|
|
81
|
+
manifestStorageUri: data.manifestStorageUri,
|
|
82
|
+
manifestFileHash: data.manifestFileHash,
|
|
83
|
+
assetBaseStorageUri: data.assetBaseStorageUri,
|
|
84
|
+
patches: patchArtifacts,
|
|
85
|
+
patchBaseBundleId: primaryPatch?.baseBundleId,
|
|
86
|
+
patchBaseFileHash: primaryPatch?.baseFileHash,
|
|
87
|
+
patchFileHash: primaryPatch?.patchFileHash,
|
|
88
|
+
patchStorageUri: primaryPatch?.patchStorageUri,
|
|
89
|
+
rolloutCohortCount: data.rolloutCohortCount ?? defaultRolloutCohortCount,
|
|
90
|
+
targetCohorts: data.targetCohorts,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Map a [Bundle] to a `bundles` row for insert/update (snake_case columns).
|
|
95
|
+
Map<String, dynamic> bundleToRowValues(Bundle bundle) => {
|
|
96
|
+
'id': bundle.id,
|
|
97
|
+
'enabled': bundle.enabled,
|
|
98
|
+
'should_force_update': bundle.shouldForceUpdate,
|
|
99
|
+
'file_hash': bundle.fileHash,
|
|
100
|
+
'git_commit_hash': bundle.gitCommitHash,
|
|
101
|
+
'message': bundle.message,
|
|
102
|
+
'platform': bundle.platform.value,
|
|
103
|
+
'target_app_version': bundle.targetAppVersion,
|
|
104
|
+
'channel': bundle.channel,
|
|
105
|
+
'storage_uri': bundle.storageUri,
|
|
106
|
+
'fingerprint_hash': bundle.fingerprintHash,
|
|
107
|
+
'metadata': stripBundleArtifactMetadata(bundle.metadata?.toJson()) ?? {},
|
|
108
|
+
'manifest_storage_uri': getManifestStorageUri(bundle),
|
|
109
|
+
'manifest_file_hash': getManifestFileHash(bundle),
|
|
110
|
+
'asset_base_storage_uri': getAssetBaseStorageUri(bundle),
|
|
111
|
+
'rollout_cohort_count': bundle.rolloutCohortCount,
|
|
112
|
+
'target_cohorts': bundle.targetCohorts,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/// Map a [Bundle] to its `bundle_patches` rows for insert.
|
|
116
|
+
List<Map<String, dynamic>> bundleToPatchRows(Bundle bundle) {
|
|
117
|
+
final patchArtifacts = getBundlePatches(bundle);
|
|
118
|
+
return [
|
|
119
|
+
for (var i = 0; i < patchArtifacts.length; i++)
|
|
120
|
+
PostgresBundlePatchRow(
|
|
121
|
+
id: _buildBundlePatchId(bundle.id, patchArtifacts[i].baseBundleId),
|
|
122
|
+
bundleId: bundle.id,
|
|
123
|
+
baseBundleId: patchArtifacts[i].baseBundleId,
|
|
124
|
+
baseFileHash: patchArtifacts[i].baseFileHash,
|
|
125
|
+
patchFileHash: patchArtifacts[i].patchFileHash,
|
|
126
|
+
patchStorageUri: patchArtifacts[i].patchStorageUri,
|
|
127
|
+
orderIndex: i,
|
|
128
|
+
).toJson(),
|
|
129
|
+
];
|
|
130
|
+
}
|