@_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.
Files changed (167) hide show
  1. package/bin/flutter-patcher.js +48 -39
  2. package/dart-src/packages/cli-tools/.gitkeep +0 -0
  3. package/dart-src/packages/cli-tools/bin/flutter_patcher.dart +9 -0
  4. package/dart-src/packages/cli-tools/lib/flutter_patcher_cli.dart +73 -0
  5. package/dart-src/packages/cli-tools/lib/src/backend.dart +162 -0
  6. package/dart-src/packages/cli-tools/lib/src/cli_base.dart +56 -0
  7. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +99 -0
  8. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +151 -0
  9. package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +140 -0
  10. package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +113 -0
  11. package/dart-src/packages/cli-tools/lib/src/commands/console.dart +55 -0
  12. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +95 -0
  13. package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +48 -0
  14. package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +29 -0
  15. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +82 -0
  16. package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +55 -0
  17. package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +132 -0
  18. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +41 -0
  19. package/dart-src/packages/cli-tools/lib/src/config.dart +715 -0
  20. package/dart-src/packages/cli-tools/lib/src/operations.dart +206 -0
  21. package/dart-src/packages/cli-tools/lib/src/pack.dart +417 -0
  22. package/dart-src/packages/cli-tools/lib/src/sign.dart +60 -0
  23. package/dart-src/packages/cli-tools/lib/src/util.dart +86 -0
  24. package/dart-src/packages/cli-tools/migrations/cloudflare/0001_hot-updater_init.sql +16 -0
  25. package/dart-src/packages/cli-tools/migrations/cloudflare/0002_hot-updater_0.13.0.sql +8 -0
  26. package/dart-src/packages/cli-tools/migrations/cloudflare/0003_hot-updater_0.18.0.sql +38 -0
  27. package/dart-src/packages/cli-tools/migrations/cloudflare/0004_hot-updater_0.29.0.sql +11 -0
  28. package/dart-src/packages/cli-tools/migrations/cloudflare/0005_hot-updater_0.31.0.sql +24 -0
  29. package/dart-src/packages/cli-tools/migrations/cloudflare/bundles.sql +42 -0
  30. package/dart-src/packages/cli-tools/migrations/supabase/20250103114225_init.sql +228 -0
  31. package/dart-src/packages/cli-tools/migrations/supabase/20250314000000_hot-updater_0.13.0.sql +134 -0
  32. package/dart-src/packages/cli-tools/migrations/supabase/20250516000000_hot-updater_0.18.0.sql +194 -0
  33. package/dart-src/packages/cli-tools/migrations/supabase/20251014000000_hot-updater_0.21.0.sql +184 -0
  34. package/dart-src/packages/cli-tools/migrations/supabase/20260401000000_hot-updater_0.29.0.sql +503 -0
  35. package/dart-src/packages/cli-tools/migrations/supabase/20260414000000_hot-updater_0.30.0.sql +50 -0
  36. package/dart-src/packages/cli-tools/migrations/supabase/20260422000000_hot-updater_0.31.0.sql +20 -0
  37. package/dart-src/packages/cli-tools/migrations/supabase/20260520014100_hot-updater_rls.sql +48 -0
  38. package/dart-src/packages/cli-tools/pubspec.yaml +37 -0
  39. package/dart-src/packages/core/lib/flutter_patcher_core.dart +29 -0
  40. package/dart-src/packages/core/lib/src/app_update_info.dart +111 -0
  41. package/dart-src/packages/core/lib/src/bundle.dart +191 -0
  42. package/dart-src/packages/core/lib/src/bundle_artifacts.dart +39 -0
  43. package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +47 -0
  44. package/dart-src/packages/core/lib/src/changed_asset.dart +77 -0
  45. package/dart-src/packages/core/lib/src/get_bundles_args.dart +78 -0
  46. package/dart-src/packages/core/lib/src/metadata.dart +31 -0
  47. package/dart-src/packages/core/lib/src/patch_info.dart +284 -0
  48. package/dart-src/packages/core/lib/src/platform.dart +15 -0
  49. package/dart-src/packages/core/lib/src/rollout.dart +209 -0
  50. package/dart-src/packages/core/lib/src/semver.dart +70 -0
  51. package/dart-src/packages/core/lib/src/semver_range.dart +358 -0
  52. package/dart-src/packages/core/lib/src/semver_version.dart +94 -0
  53. package/dart-src/packages/core/lib/src/status.dart +24 -0
  54. package/dart-src/packages/core/lib/src/strategy.dart +10 -0
  55. package/dart-src/packages/core/lib/src/update_bundle_params.dart +30 -0
  56. package/dart-src/packages/core/lib/src/uuid.dart +45 -0
  57. package/dart-src/packages/core/pubspec.yaml +9 -0
  58. package/dart-src/packages/core/tool/dbg.dart +6 -0
  59. package/dart-src/plugins/aws/.gitkeep +0 -0
  60. package/dart-src/plugins/aws/lib/flutter_patcher_aws.dart +12 -0
  61. package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +207 -0
  62. package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +130 -0
  63. package/dart-src/plugins/aws/lib/src/aws_config.dart +59 -0
  64. package/dart-src/plugins/aws/lib/src/aws_database.dart +198 -0
  65. package/dart-src/plugins/aws/lib/src/aws_lambda_edge_storage.dart +72 -0
  66. package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +312 -0
  67. package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +125 -0
  68. package/dart-src/plugins/aws/lib/src/aws_storage.dart +20 -0
  69. package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +123 -0
  70. package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +136 -0
  71. package/dart-src/plugins/aws/pubspec.yaml +22 -0
  72. package/dart-src/plugins/cloudflare/.gitkeep +0 -0
  73. package/dart-src/plugins/cloudflare/lib/flutter_patcher_cloudflare.dart +18 -0
  74. package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +29 -0
  75. package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +93 -0
  76. package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +131 -0
  77. package/dart-src/plugins/cloudflare/lib/src/d1_client.dart +66 -0
  78. package/dart-src/plugins/cloudflare/lib/src/d1_config.dart +30 -0
  79. package/dart-src/plugins/cloudflare/lib/src/d1_database.dart +16 -0
  80. package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +314 -0
  81. package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +50 -0
  82. package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +287 -0
  83. package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +23 -0
  84. package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +110 -0
  85. package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +16 -0
  86. package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +8 -0
  87. package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +38 -0
  88. package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +11 -0
  89. package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +24 -0
  90. package/dart-src/plugins/cloudflare/pubspec.yaml +20 -0
  91. package/dart-src/plugins/cloudflare/sql/bundles.sql +42 -0
  92. package/dart-src/plugins/plugin-core/lib/flutter_patcher_plugin_core.dart +42 -0
  93. package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +94 -0
  94. package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +44 -0
  95. package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +259 -0
  96. package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work_store.dart +20 -0
  97. package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +29 -0
  98. package/dart-src/plugins/plugin-core/lib/src/compression_format.dart +63 -0
  99. package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +22 -0
  100. package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +853 -0
  101. package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +525 -0
  102. package/dart-src/plugins/plugin-core/lib/src/create_storage_key_builder.dart +15 -0
  103. package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +130 -0
  104. package/dart-src/plugins/plugin-core/lib/src/filter_compatible_app_versions.dart +18 -0
  105. package/dart-src/plugins/plugin-core/lib/src/generate_min_bundle_id.dart +19 -0
  106. package/dart-src/plugins/plugin-core/lib/src/get_update_info.dart +241 -0
  107. package/dart-src/plugins/plugin-core/lib/src/is_object.dart +5 -0
  108. package/dart-src/plugins/plugin-core/lib/src/legacy_asset_storage_layout.dart +9 -0
  109. package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +112 -0
  110. package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +44 -0
  111. package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +70 -0
  112. package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +62 -0
  113. package/dart-src/plugins/plugin-core/lib/src/resolve_update_info_from_bundles.dart +43 -0
  114. package/dart-src/plugins/plugin-core/lib/src/storage_profile.dart +27 -0
  115. package/dart-src/plugins/plugin-core/lib/src/types.dart +454 -0
  116. package/dart-src/plugins/plugin-core/lib/src/uuidv7.dart +41 -0
  117. package/dart-src/plugins/plugin-core/pubspec.yaml +15 -0
  118. package/dart-src/plugins/postgres/.gitkeep +0 -0
  119. package/dart-src/plugins/postgres/lib/flutter_patcher_postgres.dart +17 -0
  120. package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +130 -0
  121. package/dart-src/plugins/postgres/lib/src/postgres_client.dart +90 -0
  122. package/dart-src/plugins/postgres/lib/src/postgres_config.dart +35 -0
  123. package/dart-src/plugins/postgres/lib/src/postgres_database.dart +302 -0
  124. package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +120 -0
  125. package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +205 -0
  126. package/dart-src/plugins/postgres/lib/src/postgres_types.dart +132 -0
  127. package/dart-src/plugins/postgres/pubspec.yaml +18 -0
  128. package/dart-src/plugins/postgres/sql/bundles.sql +45 -0
  129. package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +21 -0
  130. package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +171 -0
  131. package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +126 -0
  132. package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +125 -0
  133. package/dart-src/plugins/postgres/sql/hash_user_id.sql +30 -0
  134. package/dart-src/plugins/postgres/sql/is_device_eligible.sql +237 -0
  135. package/dart-src/plugins/postgres/sql/prepareSql.ts +11 -0
  136. package/dart-src/plugins/standalone/.gitkeep +0 -0
  137. package/dart-src/plugins/standalone/lib/flutter_patcher_standalone.dart +12 -0
  138. package/dart-src/plugins/standalone/lib/src/standalone_config.dart +107 -0
  139. package/dart-src/plugins/standalone/lib/src/standalone_database.dart +233 -0
  140. package/dart-src/plugins/standalone/lib/src/standalone_storage.dart +175 -0
  141. package/dart-src/plugins/standalone/pubspec.yaml +18 -0
  142. package/dart-src/plugins/supabase/lib/edge.dart +10 -0
  143. package/dart-src/plugins/supabase/lib/flutter_patcher_supabase.dart +21 -0
  144. package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +129 -0
  145. package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +154 -0
  146. package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +516 -0
  147. package/dart-src/plugins/supabase/lib/src/supabase_config.dart +34 -0
  148. package/dart-src/plugins/supabase/lib/src/supabase_database.dart +422 -0
  149. package/dart-src/plugins/supabase/lib/src/supabase_edge_function_database.dart +32 -0
  150. package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +99 -0
  151. package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +145 -0
  152. package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +298 -0
  153. package/dart-src/plugins/supabase/lib/src/types.dart +130 -0
  154. package/dart-src/plugins/supabase/pubspec.yaml +19 -0
  155. package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +44 -0
  156. package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +968 -0
  157. package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +228 -0
  158. package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +134 -0
  159. package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +194 -0
  160. package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +184 -0
  161. package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +503 -0
  162. package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +50 -0
  163. package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +20 -0
  164. package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +48 -0
  165. package/package.json +2 -1
  166. package/scripts/postinstall.js +53 -25
  167. /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
+ }