@_nazmiforreal/flutter-ota 0.1.11 → 0.1.13
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/README.md +1 -1
- package/bin/flutter-ota-linux-x64 +0 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +46 -76
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +140 -166
- package/dart-src/packages/cli-tools/README.md +1 -1
- package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +30 -17
- package/dart-src/packages/cli-tools/lib/src/backend.dart +30 -31
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -1
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +44 -43
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +265 -239
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +75 -66
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +39 -35
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +30 -27
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +88 -72
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +66 -29
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +10 -6
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +299 -190
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +24 -23
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +111 -97
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +237 -0
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +43 -34
- package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +57 -53
- package/dart-src/packages/cli-tools/lib/src/config.dart +227 -93
- package/dart-src/packages/cli-tools/lib/src/operations.dart +9 -8
- package/dart-src/packages/cli-tools/lib/src/pack.dart +26 -25
- package/dart-src/packages/cli-tools/lib/src/pocketbase/data_bootstrap.dart +39 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/hooks/bundles.pb.js +56 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/installer.dart +189 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +123 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +237 -0
- package/dart-src/packages/cli-tools/lib/src/runner.dart +36 -15
- package/dart-src/packages/cli-tools/lib/src/sign.dart +13 -5
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +10 -13
- package/dart-src/packages/cli-tools/lib/src/util.dart +3 -1
- package/dart-src/packages/cli-tools/pubspec.lock +69 -116
- package/dart-src/packages/cli-tools/pubspec.yaml +25 -7
- package/dart-src/packages/cli-tools/test/backends_test.dart +62 -32
- package/dart-src/packages/cli-tools/test/build_pack_test.dart +4 -4
- package/dart-src/packages/cli-tools/test/cli_test.dart +89 -42
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +8 -13
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +23 -18
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +197 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +70 -46
- package/dart-src/packages/cli-tools/test/pocketbase_installer_test.dart +72 -0
- package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +107 -0
- package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +59 -0
- package/dart-src/packages/core/README.md +2 -2
- package/dart-src/packages/core/example/lib/main.dart +69 -0
- package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +1 -1
- package/dart-src/packages/core/lib/src/app_update_info.dart +17 -15
- package/dart-src/packages/core/lib/src/bundle.dart +102 -94
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +1 -2
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +15 -16
- package/dart-src/packages/core/lib/src/changed_asset.dart +24 -22
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +13 -13
- package/dart-src/packages/core/lib/src/metadata.dart +9 -7
- package/dart-src/packages/core/lib/src/patch_info.dart +8 -7
- package/dart-src/packages/core/lib/src/platform.dart +4 -3
- package/dart-src/packages/core/lib/src/rollout.dart +14 -11
- package/dart-src/packages/core/lib/src/semver_range.dart +49 -39
- package/dart-src/packages/core/lib/src/semver_version.dart +8 -7
- package/dart-src/packages/core/lib/src/status.dart +4 -3
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +7 -7
- package/dart-src/packages/core/lib/src/uuid.dart +1 -2
- package/dart-src/packages/core/pubspec.yaml +6 -7
- package/dart-src/packages/core/test/rollout_test.dart +15 -8
- package/dart-src/packages/core/test/semver_test.dart +5 -2
- package/dart-src/packages/core/test/types_test.dart +26 -24
- package/dart-src/packages/core/test/uuid_test.dart +3 -1
- package/dart-src/packages/core/tool/dbg.dart +1 -0
- package/dart-src/plugins/aws/CHANGELOG.md +6 -0
- package/dart-src/plugins/aws/example/lib/main.dart +60 -0
- package/dart-src/plugins/aws/lib/flutter_ota_kit_aws.dart +5 -1
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +11 -16
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +7 -2
- package/dart-src/plugins/aws/lib/src/aws_database.dart +14 -18
- package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +23 -18
- package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +5 -4
- package/dart-src/plugins/aws/lib/src/aws_storage.dart +2 -3
- package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +10 -16
- package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +6 -9
- package/dart-src/plugins/aws/pubspec.yaml +15 -10
- package/dart-src/plugins/aws/test/aws_database_test.dart +51 -57
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +8 -13
- package/dart-src/plugins/cloudflare/CHANGELOG.md +7 -0
- package/dart-src/plugins/cloudflare/example/lib/main.dart +62 -0
- package/dart-src/plugins/cloudflare/lib/flutter_ota_kit_cloudflare.dart +4 -2
- package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +3 -3
- package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +3 -2
- package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +24 -27
- package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +22 -30
- package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +1 -1
- package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +21 -29
- package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +7 -7
- package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +7 -15
- package/dart-src/plugins/cloudflare/pubspec.yaml +14 -9
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +46 -42
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +100 -90
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +23 -18
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +8 -13
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +1 -4
- package/dart-src/plugins/plugin-core/CHANGELOG.md +16 -0
- package/dart-src/plugins/plugin-core/example/lib/main.dart +61 -0
- package/dart-src/plugins/plugin-core/lib/flutter_ota_kit_plugin_core.dart +3 -7
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +5 -5
- package/dart-src/plugins/plugin-core/lib/src/bundle_helpers.dart +23 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +16 -10
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +31 -26
- package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +5 -1
- package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +1 -1
- package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +78 -71
- package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +56 -35
- package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +10 -13
- package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +30 -17
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +3 -1
- package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +5 -3
- package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +2 -4
- package/dart-src/plugins/plugin-core/lib/src/storage_helpers.dart +18 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +7 -6
- package/dart-src/plugins/plugin-core/pubspec.yaml +12 -9
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +13 -9
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +28 -15
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +254 -122
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +36 -32
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +18 -15
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +8 -3
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +682 -693
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +45 -21
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +76 -13
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +20 -9
- package/dart-src/plugins/pocketbase/CHANGELOG.md +8 -0
- package/dart-src/plugins/pocketbase/LICENSE +21 -0
- package/dart-src/plugins/pocketbase/README.md +54 -0
- package/dart-src/plugins/pocketbase/example/lib/main.dart +62 -0
- package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +28 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +121 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +349 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_config.dart +61 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +278 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +255 -0
- package/dart-src/plugins/pocketbase/pubspec.yaml +26 -0
- package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +12 -0
- package/dart-src/plugins/postgres/CHANGELOG.md +6 -0
- package/dart-src/plugins/postgres/example/lib/main.dart +67 -0
- package/dart-src/plugins/postgres/lib/flutter_ota_kit_postgres.dart +3 -6
- package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +32 -52
- package/dart-src/plugins/postgres/lib/src/postgres_config.dart +3 -1
- package/dart-src/plugins/postgres/lib/src/postgres_database.dart +28 -38
- package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +16 -14
- package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +26 -14
- package/dart-src/plugins/postgres/lib/src/postgres_types.dart +27 -28
- package/dart-src/plugins/postgres/pubspec.yaml +14 -9
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +62 -39
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +3 -4
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +102 -73
- package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +60 -0
- package/dart-src/plugins/supabase/CHANGELOG.md +17 -0
- package/dart-src/plugins/supabase/README.md +2 -2
- package/dart-src/plugins/supabase/example/lib/main.dart +63 -0
- package/dart-src/plugins/supabase/lib/flutter_ota_kit_supabase.dart +2 -0
- package/dart-src/plugins/supabase/lib/src/error_message.dart +11 -0
- package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +34 -58
- package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +3 -13
- package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +34 -52
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +38 -30
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +21 -29
- package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +7 -19
- package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +18 -29
- package/dart-src/plugins/supabase/lib/src/types.dart +26 -26
- package/dart-src/plugins/supabase/pubspec.yaml +14 -9
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +57 -57
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +30 -27
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +26 -35
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +27 -34
- package/package.json +2 -2
- package/scripts/vendor.sh +1 -1
|
@@ -3,7 +3,11 @@ import 'types.dart';
|
|
|
3
3
|
/// Calculate pagination metadata from total count, limit, and offset.
|
|
4
4
|
///
|
|
5
5
|
/// Faithful port of hot-updater `calculatePagination.ts`.
|
|
6
|
-
PaginationInfo calculatePagination(
|
|
6
|
+
PaginationInfo calculatePagination(
|
|
7
|
+
int total, {
|
|
8
|
+
required int limit,
|
|
9
|
+
required int offset,
|
|
10
|
+
}) {
|
|
7
11
|
if (total == 0) {
|
|
8
12
|
return const PaginationInfo(
|
|
9
13
|
total: 0,
|
|
@@ -2,7 +2,7 @@ const String contentAddressedAssetPrefix = 'assets';
|
|
|
2
2
|
|
|
3
3
|
/// Build a content-addressed storage path for an asset.
|
|
4
4
|
///
|
|
5
|
-
/// Format: `sha256
|
|
5
|
+
/// Format: `sha256/{hash[:2]}/{hash}.{ext}`
|
|
6
6
|
/// Preserves `.br` brotli extension from the original asset path.
|
|
7
7
|
///
|
|
8
8
|
/// Faithful port of hot-updater `contentAddressedAssets.ts`.
|
|
@@ -3,6 +3,7 @@ import 'dart:convert';
|
|
|
3
3
|
import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
|
|
4
4
|
|
|
5
5
|
import 'bundle_unit_of_work.dart';
|
|
6
|
+
import 'storage_helpers.dart' show updateJsonKeyRegex;
|
|
6
7
|
import 'create_database_plugin.dart';
|
|
7
8
|
import 'filter_compatible_app_versions.dart';
|
|
8
9
|
import 'paginate_bundles.dart';
|
|
@@ -10,6 +11,11 @@ import 'query_bundles.dart';
|
|
|
10
11
|
import 'resolve_update_info_from_bundles.dart';
|
|
11
12
|
import 'types.dart';
|
|
12
13
|
|
|
14
|
+
/// Capture-group variant of [updateJsonKeyRegex] — extracts the channel.
|
|
15
|
+
final RegExp _updateJsonChannelRegex = RegExp(
|
|
16
|
+
r'^([^/]+)/(?:ios|android)/[^/]+/update\.json$',
|
|
17
|
+
);
|
|
18
|
+
|
|
13
19
|
// ---------------------------------------------------------------------------
|
|
14
20
|
// Internal types
|
|
15
21
|
// ---------------------------------------------------------------------------
|
|
@@ -22,10 +28,7 @@ class _BundleWithKey {
|
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
class _TargetVersionMutation {
|
|
25
|
-
_TargetVersionMutation({
|
|
26
|
-
required this.channel,
|
|
27
|
-
required this.platform,
|
|
28
|
-
});
|
|
31
|
+
_TargetVersionMutation({required this.channel, required this.platform});
|
|
29
32
|
|
|
30
33
|
final String channel;
|
|
31
34
|
final String platform;
|
|
@@ -161,15 +164,16 @@ List<String> _getManagementListPrefixes(DatabaseBundleQueryWhere? where) {
|
|
|
161
164
|
if (where?.channel != null &&
|
|
162
165
|
where?.platform != null &&
|
|
163
166
|
where?.targetAppVersion is String) {
|
|
164
|
-
final normalized =
|
|
165
|
-
|
|
167
|
+
final normalized = normalizeTargetAppVersion(
|
|
168
|
+
where!.targetAppVersion as String,
|
|
169
|
+
);
|
|
166
170
|
if (normalized != null) {
|
|
167
171
|
return ['${where.channel}/${where.platform?.value}/$normalized/'];
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
|
|
171
175
|
if (where?.channel != null && where?.platform != null) {
|
|
172
|
-
|
|
176
|
+
return ['${where!.channel}/${where.platform?.value}/'];
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
if (where?.channel != null) {
|
|
@@ -180,13 +184,14 @@ List<String> _getManagementListPrefixes(DatabaseBundleQueryWhere? where) {
|
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
String? _getChannelFromUpdateJsonKey(String key) {
|
|
183
|
-
final match =
|
|
184
|
-
RegExp(r'^([^/]+)/(?:ios|android)/[^/]+/update\.json$').firstMatch(key);
|
|
187
|
+
final match = _updateJsonChannelRegex.firstMatch(key);
|
|
185
188
|
return match?.group(1);
|
|
186
189
|
}
|
|
187
190
|
|
|
188
|
-
const _defaultDescOrder =
|
|
189
|
-
|
|
191
|
+
const _defaultDescOrder = DatabaseBundleQueryOrder(
|
|
192
|
+
field: 'id',
|
|
193
|
+
direction: 'desc',
|
|
194
|
+
);
|
|
190
195
|
|
|
191
196
|
List<Bundle> _sortManagedBundles(
|
|
192
197
|
List<Bundle> bundles,
|
|
@@ -229,7 +234,7 @@ abstract class BlobOperations {
|
|
|
229
234
|
// ---------------------------------------------------------------------------
|
|
230
235
|
|
|
231
236
|
DatabasePlugin Function() Function(TConfig, [DatabasePluginHooks? hooks])
|
|
232
|
-
|
|
237
|
+
createBlobDatabasePlugin<TConfig>({
|
|
233
238
|
required String name,
|
|
234
239
|
required BlobOperations Function(TConfig config) blobFactory,
|
|
235
240
|
}) {
|
|
@@ -297,7 +302,8 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
297
302
|
}
|
|
298
303
|
|
|
299
304
|
Future<List<Bundle>> loadBundleObject(String key) async {
|
|
300
|
-
final bundles =
|
|
305
|
+
final bundles =
|
|
306
|
+
(await loadOptionalObject<List>(key))
|
|
301
307
|
?.map((e) => Bundle.fromJson((e as Map).cast<String, dynamic>()))
|
|
302
308
|
.toList() ??
|
|
303
309
|
<Bundle>[];
|
|
@@ -312,16 +318,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
312
318
|
pendingBundlesMap.clear();
|
|
313
319
|
locallyDeletedBundleIds.clear();
|
|
314
320
|
|
|
315
|
-
final updateJsonKeys =
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
.toList();
|
|
321
|
+
final updateJsonKeys =
|
|
322
|
+
(await _mapWithConcurrency(
|
|
323
|
+
prefixes,
|
|
324
|
+
_storageOperationConcurrency,
|
|
325
|
+
(prefix, _) => ops.listObjects(prefix),
|
|
326
|
+
))
|
|
327
|
+
.expand((keys) => keys)
|
|
328
|
+
.where((key) => updateJsonKeyRegex.hasMatch(key))
|
|
329
|
+
.toList();
|
|
325
330
|
|
|
326
331
|
final allBundles = (await _mapWithConcurrency(
|
|
327
332
|
updateJsonKeys,
|
|
@@ -332,9 +337,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
332
337
|
.map((b) => _BundleWithKey(b, updateJsonKey: key))
|
|
333
338
|
.toList();
|
|
334
339
|
},
|
|
335
|
-
))
|
|
336
|
-
.expand((b) => b)
|
|
337
|
-
.toList();
|
|
340
|
+
)).expand((b) => b).toList();
|
|
338
341
|
|
|
339
342
|
for (final entry in allBundles) {
|
|
340
343
|
bundlesMap[entry.bundle.id] = entry;
|
|
@@ -404,13 +407,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
404
407
|
'${mutation.channel}/${mutation.platform}/target-app-versions.json';
|
|
405
408
|
final oldTargetVersions =
|
|
406
409
|
(await loadOptionalObject<List>(targetKey))
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
+
?.map((e) => e.toString())
|
|
411
|
+
.toList() ??
|
|
412
|
+
<String>[];
|
|
410
413
|
final newTargetVersions = oldTargetVersions
|
|
411
|
-
.where(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
+
.where(
|
|
415
|
+
(v) =>
|
|
416
|
+
!mutation.removals.contains(v) ||
|
|
417
|
+
mutation.additions.contains(v),
|
|
418
|
+
)
|
|
414
419
|
.toList();
|
|
415
420
|
for (final version in mutation.additions) {
|
|
416
421
|
if (!newTargetVersions.contains(version)) {
|
|
@@ -437,8 +442,9 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
437
442
|
return;
|
|
438
443
|
}
|
|
439
444
|
for (final v in _getSemverNormalizedVersions(targetAppVersion)) {
|
|
440
|
-
pathsToInvalidate
|
|
441
|
-
|
|
445
|
+
pathsToInvalidate.add(
|
|
446
|
+
'${ops.apiBasePath}/app-version/$platform/$v/$channel/*',
|
|
447
|
+
);
|
|
442
448
|
}
|
|
443
449
|
}
|
|
444
450
|
|
|
@@ -474,14 +480,17 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
474
480
|
final channel = args.channel;
|
|
475
481
|
final platform = args.platform;
|
|
476
482
|
|
|
477
|
-
final targetVersionsKey =
|
|
483
|
+
final targetVersionsKey =
|
|
484
|
+
'$channel/${platform.value}/target-app-versions.json';
|
|
478
485
|
final targetAppVersions =
|
|
479
486
|
(await loadOptionalObject<List>(targetVersionsKey))
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
final matchingVersions =
|
|
484
|
-
|
|
487
|
+
?.map((e) => e.toString())
|
|
488
|
+
.toList() ??
|
|
489
|
+
<String>[];
|
|
490
|
+
final matchingVersions = filterCompatibleAppVersions(
|
|
491
|
+
targetAppVersions,
|
|
492
|
+
args.appVersion,
|
|
493
|
+
);
|
|
485
494
|
|
|
486
495
|
final bundles = (await _mapWithConcurrency(
|
|
487
496
|
matchingVersions,
|
|
@@ -491,9 +500,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
491
500
|
normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion;
|
|
492
501
|
return loadBundleObject('$channel/${platform.value}/$nv/update.json');
|
|
493
502
|
},
|
|
494
|
-
))
|
|
495
|
-
.expand((b) => b)
|
|
496
|
-
.toList();
|
|
503
|
+
)).expand((b) => b).toList();
|
|
497
504
|
|
|
498
505
|
return resolveUpdateInfoFromBundles(
|
|
499
506
|
ResolveUpdateInfoFromBundlesOptions(
|
|
@@ -571,8 +578,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
571
578
|
Future<Paginated<List<Bundle>>> getBundles(
|
|
572
579
|
DatabaseBundleQueryOptions options,
|
|
573
580
|
) async {
|
|
574
|
-
var allBundles =
|
|
575
|
-
await loadAllBundlesForManagementFallback(options.where);
|
|
581
|
+
var allBundles = await loadAllBundlesForManagementFallback(options.where);
|
|
576
582
|
if (options.where != null) {
|
|
577
583
|
allBundles = allBundles
|
|
578
584
|
.where((b) => bundleMatchesQueryWhere(b, options.where))
|
|
@@ -581,7 +587,8 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
581
587
|
return paginateBundles(
|
|
582
588
|
bundles: allBundles,
|
|
583
589
|
limit: options.limit,
|
|
584
|
-
offset:
|
|
590
|
+
offset:
|
|
591
|
+
options.offset ??
|
|
585
592
|
(options.page != null ? (options.page! - 1) * options.limit : null),
|
|
586
593
|
cursor: options.cursor,
|
|
587
594
|
orderBy: options.orderBy,
|
|
@@ -590,19 +597,18 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
590
597
|
|
|
591
598
|
@override
|
|
592
599
|
Future<List<String>> getChannels() async {
|
|
593
|
-
final channels =
|
|
594
|
-
.
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
600
|
+
final channels =
|
|
601
|
+
(await ops.listObjects(''))
|
|
602
|
+
.map(_getChannelFromUpdateJsonKey)
|
|
603
|
+
.whereType<String>()
|
|
604
|
+
.toSet()
|
|
605
|
+
.toList()
|
|
606
|
+
..sort();
|
|
599
607
|
return channels;
|
|
600
608
|
}
|
|
601
609
|
|
|
602
610
|
@override
|
|
603
|
-
Future<void> commitBundle({
|
|
604
|
-
required List<BundleChange> changedSets,
|
|
605
|
-
}) async {
|
|
611
|
+
Future<void> commitBundle({required List<BundleChange> changedSets}) async {
|
|
606
612
|
if (changedSets.isEmpty) return;
|
|
607
613
|
|
|
608
614
|
for (final change in changedSets) {
|
|
@@ -686,10 +692,7 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
686
692
|
if (change.operation == BundleChangeOperation.update) {
|
|
687
693
|
final merged = _mergeBundleUpdate(bk.bundle, data.toJson());
|
|
688
694
|
final newKey =
|
|
689
|
-
'${merged.channel}/${merged.platform.value}/${_resolveStorageTarget(
|
|
690
|
-
targetAppVersion: merged.targetAppVersion,
|
|
691
|
-
fingerprintHash: merged.fingerprintHash,
|
|
692
|
-
)}/update.json';
|
|
695
|
+
'${merged.channel}/${merged.platform.value}/${_resolveStorageTarget(targetAppVersion: merged.targetAppVersion, fingerprintHash: merged.fingerprintHash)}/update.json';
|
|
693
696
|
|
|
694
697
|
if (newKey != bk.updateJsonKey) {
|
|
695
698
|
// Key changed — remove from old, add to new.
|
|
@@ -791,13 +794,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
791
794
|
(oldKey, _) async {
|
|
792
795
|
final currentBundles =
|
|
793
796
|
(await loadOptionalObject<List>(oldKey))
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
797
|
+
?.map(
|
|
798
|
+
(e) => Bundle.fromJson((e as Map).cast<String, dynamic>()),
|
|
799
|
+
)
|
|
800
|
+
.toList() ??
|
|
801
|
+
<Bundle>[];
|
|
798
802
|
final removalIds = removalsByKey[oldKey]!;
|
|
799
|
-
final updatedBundles =
|
|
800
|
-
|
|
803
|
+
final updatedBundles = currentBundles
|
|
804
|
+
.where((b) => !removalIds.contains(b.id))
|
|
805
|
+
.toList();
|
|
801
806
|
updatedBundles.sort((a, b) => b.id.compareTo(a.id));
|
|
802
807
|
|
|
803
808
|
if (updatedBundles.isEmpty) {
|
|
@@ -818,13 +823,15 @@ class _BlobDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
818
823
|
(key, _) async {
|
|
819
824
|
final currentBundles =
|
|
820
825
|
(await loadOptionalObject<List>(key))
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
826
|
+
?.map(
|
|
827
|
+
(e) => Bundle.fromJson((e as Map).cast<String, dynamic>()),
|
|
828
|
+
)
|
|
829
|
+
.toList() ??
|
|
830
|
+
<Bundle>[];
|
|
825
831
|
for (final changedBundle in changedBundlesByKey[key]!) {
|
|
826
|
-
final idx =
|
|
827
|
-
|
|
832
|
+
final idx = currentBundles.indexWhere(
|
|
833
|
+
(b) => b.id == changedBundle.id,
|
|
834
|
+
);
|
|
828
835
|
if (idx >= 0) {
|
|
829
836
|
currentBundles[idx] = changedBundle;
|
|
830
837
|
} else {
|
|
@@ -4,8 +4,10 @@ import 'calculate_pagination.dart';
|
|
|
4
4
|
import 'types.dart';
|
|
5
5
|
|
|
6
6
|
const List<String> _replaceOnUpdateKeys = ['patches', 'targetCohorts'];
|
|
7
|
-
const DatabaseBundleQueryOrder _defaultDescOrder =
|
|
8
|
-
|
|
7
|
+
const DatabaseBundleQueryOrder _defaultDescOrder = DatabaseBundleQueryOrder(
|
|
8
|
+
field: 'id',
|
|
9
|
+
direction: 'desc',
|
|
10
|
+
);
|
|
9
11
|
|
|
10
12
|
int? _normalizePage(int? value) {
|
|
11
13
|
if (value == null || value < 1) return null;
|
|
@@ -134,11 +136,17 @@ Paginated<List<Bundle>> _createPaginatedResult(
|
|
|
134
136
|
int startIndex,
|
|
135
137
|
List<Bundle> data,
|
|
136
138
|
) {
|
|
137
|
-
final pagination = calculatePagination(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
final pagination = calculatePagination(
|
|
140
|
+
total,
|
|
141
|
+
limit: limit,
|
|
142
|
+
offset: startIndex,
|
|
143
|
+
);
|
|
144
|
+
final nextCursor = data.isNotEmpty && startIndex + data.length < total
|
|
145
|
+
? data.last.id
|
|
146
|
+
: null;
|
|
147
|
+
final previousCursor = data.isNotEmpty && startIndex > 0
|
|
148
|
+
? data.first.id
|
|
149
|
+
: null;
|
|
142
150
|
|
|
143
151
|
return Paginated(
|
|
144
152
|
data: data,
|
|
@@ -204,9 +212,7 @@ abstract class AbstractDatabasePlugin {
|
|
|
204
212
|
|
|
205
213
|
Future<List<String>> getChannels();
|
|
206
214
|
|
|
207
|
-
Future<void> commitBundle({
|
|
208
|
-
required List<BundleChange> changedSets,
|
|
209
|
-
});
|
|
215
|
+
Future<void> commitBundle({required List<BundleChange> changedSets});
|
|
210
216
|
|
|
211
217
|
Future<void> onUnmount();
|
|
212
218
|
}
|
|
@@ -237,7 +243,7 @@ class CreateDatabasePluginOptions<TConfig> {
|
|
|
237
243
|
/// final db = createDb();
|
|
238
244
|
/// ```
|
|
239
245
|
DatabasePlugin Function() Function(TConfig config, [DatabasePluginHooks? hooks])
|
|
240
|
-
|
|
246
|
+
createDatabasePlugin<TConfig>({
|
|
241
247
|
required String name,
|
|
242
248
|
required AbstractDatabasePlugin Function(TConfig config) factory,
|
|
243
249
|
}) {
|
|
@@ -251,14 +257,12 @@ DatabasePlugin Function() Function(TConfig config, [DatabasePluginHooks? hooks])
|
|
|
251
257
|
return () {
|
|
252
258
|
final instanceUnitOfWork = BundleUnitOfWork();
|
|
253
259
|
|
|
254
|
-
BundleUnitOfWork getMutationUnitOfWork(
|
|
255
|
-
Map<String, Object?>? context) =>
|
|
260
|
+
BundleUnitOfWork getMutationUnitOfWork(Map<String, Object?>? context) =>
|
|
256
261
|
getRequestBundleUnitOfWork(context) ?? instanceUnitOfWork;
|
|
257
262
|
|
|
258
263
|
Future<Paginated<List<Bundle>>> runGetBundles(
|
|
259
264
|
DatabaseBundleQueryOptions options,
|
|
260
|
-
) =>
|
|
261
|
-
getMethods().getBundles(options);
|
|
265
|
+
) => getMethods().getBundles(options);
|
|
262
266
|
|
|
263
267
|
Future<Paginated<List<Bundle>>> getBundlesWithLegacyCursorFallback(
|
|
264
268
|
DatabaseBundleQueryOptions options,
|
|
@@ -285,7 +289,12 @@ DatabasePlugin Function() Function(TConfig config, [DatabasePluginHooks? hooks])
|
|
|
285
289
|
orderBy: orderBy,
|
|
286
290
|
),
|
|
287
291
|
);
|
|
288
|
-
return _createPaginatedResult(
|
|
292
|
+
return _createPaginatedResult(
|
|
293
|
+
total,
|
|
294
|
+
options.limit,
|
|
295
|
+
0,
|
|
296
|
+
firstPage.data,
|
|
297
|
+
);
|
|
289
298
|
}
|
|
290
299
|
|
|
291
300
|
final cursorQuery = _buildCursorPageQuery(
|
|
@@ -370,9 +379,9 @@ class _DatabasePluginImpl implements DatabasePlugin {
|
|
|
370
379
|
final BundleUnitOfWork Function(Map<String, Object?>?) getMutationUnitOfWork;
|
|
371
380
|
final BundleUnitOfWork instanceUnitOfWork;
|
|
372
381
|
final Future<Paginated<List<Bundle>>> Function(DatabaseBundleQueryOptions)
|
|
373
|
-
|
|
382
|
+
runGetBundles;
|
|
374
383
|
final Future<Paginated<List<Bundle>>> Function(DatabaseBundleQueryOptions)
|
|
375
|
-
|
|
384
|
+
getBundlesWithLegacyCursorFallback;
|
|
376
385
|
final DatabasePluginHooks? hooks;
|
|
377
386
|
|
|
378
387
|
@override
|
|
@@ -383,11 +392,14 @@ class _DatabasePluginImpl implements DatabasePlugin {
|
|
|
383
392
|
|
|
384
393
|
@override
|
|
385
394
|
Future<Bundle?> getBundleById(String bundleId) async {
|
|
386
|
-
final requestUoW =
|
|
387
|
-
|
|
395
|
+
final requestUoW = getRequestBundleUnitOfWork(
|
|
396
|
+
null,
|
|
397
|
+
); // context not passed through here
|
|
388
398
|
if (requestUoW != null) {
|
|
389
|
-
return requestUoW.getById(
|
|
390
|
-
|
|
399
|
+
return requestUoW.getById(
|
|
400
|
+
bundleId,
|
|
401
|
+
() => getMethods().getBundleById(bundleId),
|
|
402
|
+
);
|
|
391
403
|
}
|
|
392
404
|
final pending = instanceUnitOfWork.peekChanged(bundleId);
|
|
393
405
|
if (pending is TrackedBundleFound) return pending.value;
|
|
@@ -434,7 +446,10 @@ class _DatabasePluginImpl implements DatabasePlugin {
|
|
|
434
446
|
if (normalizedOptions.page != null) {
|
|
435
447
|
final page = normalizedOptions.page!;
|
|
436
448
|
final requestedOffset = (page - 1) * normalizedOptions.limit;
|
|
437
|
-
final fetchOptions = _expandLimitForUnitOfWork(
|
|
449
|
+
final fetchOptions = _expandLimitForUnitOfWork(
|
|
450
|
+
normalizedOptions,
|
|
451
|
+
unitOfWork,
|
|
452
|
+
);
|
|
438
453
|
var pageResult = await runGetBundles(
|
|
439
454
|
DatabaseBundleQueryOptions(
|
|
440
455
|
where: fetchOptions.where,
|
|
@@ -445,12 +460,15 @@ class _DatabasePluginImpl implements DatabasePlugin {
|
|
|
445
460
|
);
|
|
446
461
|
|
|
447
462
|
final total = pageResult.pagination.total;
|
|
448
|
-
final totalPages =
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
463
|
+
final totalPages = total == 0
|
|
464
|
+
? 0
|
|
465
|
+
: (total / normalizedOptions.limit).ceil();
|
|
466
|
+
final maxOffset = totalPages == 0
|
|
467
|
+
? 0
|
|
468
|
+
: (totalPages - 1) * normalizedOptions.limit;
|
|
469
|
+
final resolvedOffset = requestedOffset < maxOffset
|
|
470
|
+
? requestedOffset
|
|
471
|
+
: maxOffset;
|
|
454
472
|
|
|
455
473
|
if (resolvedOffset != requestedOffset) {
|
|
456
474
|
pageResult = await runGetBundles(
|
|
@@ -473,7 +491,10 @@ class _DatabasePluginImpl implements DatabasePlugin {
|
|
|
473
491
|
}
|
|
474
492
|
|
|
475
493
|
if (methods.supportsCursorPagination) {
|
|
476
|
-
final fetchOptions = _expandLimitForUnitOfWork(
|
|
494
|
+
final fetchOptions = _expandLimitForUnitOfWork(
|
|
495
|
+
normalizedOptions,
|
|
496
|
+
unitOfWork,
|
|
497
|
+
);
|
|
477
498
|
final result = await runGetBundles(fetchOptions);
|
|
478
499
|
return shouldOverlay ? overlayResult(result) : result;
|
|
479
500
|
}
|
|
@@ -490,9 +511,7 @@ class _DatabasePluginImpl implements DatabasePlugin {
|
|
|
490
511
|
Future<void> commitBundle() async {
|
|
491
512
|
final methods = getMethods();
|
|
492
513
|
final unitOfWork = getMutationUnitOfWork(null);
|
|
493
|
-
await methods.commitBundle(
|
|
494
|
-
changedSets: unitOfWork.changedSets(),
|
|
495
|
-
);
|
|
514
|
+
await methods.commitBundle(changedSets: unitOfWork.changedSets());
|
|
496
515
|
unitOfWork.clear();
|
|
497
516
|
final cb = hooks?.onDatabaseUpdated;
|
|
498
517
|
if (cb != null) await cb();
|
|
@@ -504,8 +523,10 @@ class _DatabasePluginImpl implements DatabasePlugin {
|
|
|
504
523
|
Map<String, Object?> newBundle,
|
|
505
524
|
) async {
|
|
506
525
|
final unitOfWork = getMutationUnitOfWork(null);
|
|
507
|
-
final current = await unitOfWork.getById(
|
|
508
|
-
|
|
526
|
+
final current = await unitOfWork.getById(
|
|
527
|
+
targetBundleId,
|
|
528
|
+
() => getMethods().getBundleById(targetBundleId),
|
|
529
|
+
);
|
|
509
530
|
if (current == null) {
|
|
510
531
|
throw StateError('targetBundleId not found');
|
|
511
532
|
}
|
|
@@ -2,7 +2,7 @@ import 'types.dart';
|
|
|
2
2
|
|
|
3
3
|
/// Create a deploy/CLI/console storage plugin (node profile only).
|
|
4
4
|
StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
|
|
5
|
-
|
|
5
|
+
createNodeStoragePlugin<TConfig>({
|
|
6
6
|
required String name,
|
|
7
7
|
required String supportedProtocol,
|
|
8
8
|
required NodeStorageProfile Function(TConfig config) factory,
|
|
@@ -24,7 +24,7 @@ StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
|
|
|
24
24
|
|
|
25
25
|
/// Create an update-check runtime storage plugin.
|
|
26
26
|
StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
|
|
27
|
-
|
|
27
|
+
createRuntimeStoragePlugin<TConfig>({
|
|
28
28
|
required String name,
|
|
29
29
|
required String supportedProtocol,
|
|
30
30
|
required RuntimeStorageProfile Function(TConfig config) factory,
|
|
@@ -46,13 +46,13 @@ StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
|
|
|
46
46
|
|
|
47
47
|
/// Create a storage plugin supporting both node and runtime profiles.
|
|
48
48
|
StoragePlugin Function(TConfig config, [StoragePluginHooks? hooks])
|
|
49
|
-
|
|
49
|
+
createUniversalStoragePlugin<TConfig>({
|
|
50
50
|
required String name,
|
|
51
51
|
required String supportedProtocol,
|
|
52
|
-
required ({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
required ({NodeStorageProfile node, RuntimeStorageProfile runtime}) Function(
|
|
53
|
+
TConfig config,
|
|
54
|
+
)
|
|
55
|
+
factory,
|
|
56
56
|
}) {
|
|
57
57
|
return (TConfig config, [StoragePluginHooks? hooks]) {
|
|
58
58
|
({NodeStorageProfile node, RuntimeStorageProfile runtime})? cached;
|
|
@@ -102,8 +102,7 @@ class _WrappedNodeProfile implements NodeStorageProfile {
|
|
|
102
102
|
_node.listObjects(prefix);
|
|
103
103
|
|
|
104
104
|
@override
|
|
105
|
-
Future<void> deleteObjects(List<String> keys) =>
|
|
106
|
-
_node.deleteObjects(keys);
|
|
105
|
+
Future<void> deleteObjects(List<String> keys) => _node.deleteObjects(keys);
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
/// Storage plugin with lazy profile resolution.
|
|
@@ -123,8 +122,6 @@ class _ProfiledStoragePlugin implements StoragePlugin {
|
|
|
123
122
|
final RuntimeStorageProfile Function()? runtimeProfile;
|
|
124
123
|
|
|
125
124
|
@override
|
|
126
|
-
StoragePluginProfiles get profiles =>
|
|
127
|
-
|
|
128
|
-
runtime: runtimeProfile?.call(),
|
|
129
|
-
);
|
|
125
|
+
StoragePluginProfiles get profiles =>
|
|
126
|
+
StoragePluginProfiles(node: nodeProfile, runtime: runtimeProfile?.call());
|
|
130
127
|
}
|
|
@@ -25,13 +25,17 @@ Paginated<List<Bundle>> paginateBundles({
|
|
|
25
25
|
normalizedOffset + limit > total ? total : normalizedOffset + limit,
|
|
26
26
|
)
|
|
27
27
|
: sortedBundles.sublist(normalizedOffset);
|
|
28
|
-
final pagination = calculatePagination(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
final
|
|
34
|
-
|
|
28
|
+
final pagination = calculatePagination(
|
|
29
|
+
total,
|
|
30
|
+
limit: limit,
|
|
31
|
+
offset: normalizedOffset,
|
|
32
|
+
);
|
|
33
|
+
final nextCursor = data.isNotEmpty && normalizedOffset + data.length < total
|
|
34
|
+
? data.last.id
|
|
35
|
+
: null;
|
|
36
|
+
final previousCursor = data.isNotEmpty && normalizedOffset > 0
|
|
37
|
+
? data.first.id
|
|
38
|
+
: null;
|
|
35
39
|
|
|
36
40
|
return Paginated(
|
|
37
41
|
data: data,
|
|
@@ -55,7 +59,12 @@ Paginated<List<Bundle>> paginateBundles({
|
|
|
55
59
|
? bundle.id.compareTo(cursor!.after!) < 0
|
|
56
60
|
: bundle.id.compareTo(cursor!.after!) > 0;
|
|
57
61
|
}).toList();
|
|
58
|
-
data = limit > 0
|
|
62
|
+
data = limit > 0
|
|
63
|
+
? candidates.sublist(
|
|
64
|
+
0,
|
|
65
|
+
limit > candidates.length ? candidates.length : limit,
|
|
66
|
+
)
|
|
67
|
+
: candidates;
|
|
59
68
|
} else if (cursor?.before != null) {
|
|
60
69
|
final candidates = sortedBundles.where((bundle) {
|
|
61
70
|
return direction == 'desc'
|
|
@@ -76,15 +85,19 @@ Paginated<List<Bundle>> paginateBundles({
|
|
|
76
85
|
final startIndex = data.isNotEmpty
|
|
77
86
|
? sortedBundles.indexWhere((b) => b.id == data.first.id)
|
|
78
87
|
: cursor?.after != null
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
final pagination = calculatePagination(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
final
|
|
87
|
-
|
|
88
|
+
? total
|
|
89
|
+
: 0;
|
|
90
|
+
final pagination = calculatePagination(
|
|
91
|
+
total,
|
|
92
|
+
limit: limit,
|
|
93
|
+
offset: startIndex,
|
|
94
|
+
);
|
|
95
|
+
final nextCursor = data.isNotEmpty && startIndex + data.length < total
|
|
96
|
+
? data.last.id
|
|
97
|
+
: null;
|
|
98
|
+
final previousCursor = data.isNotEmpty && startIndex > 0
|
|
99
|
+
? data.first.id
|
|
100
|
+
: null;
|
|
88
101
|
|
|
89
102
|
String? effectiveNextCursor = nextCursor;
|
|
90
103
|
String? effectivePreviousCursor = previousCursor;
|
|
@@ -39,6 +39,8 @@ ParsedStorageUri parseStorageUri(String storageUri, String expectedProtocol) {
|
|
|
39
39
|
return ParsedStorageUri(
|
|
40
40
|
protocol: protocol,
|
|
41
41
|
bucket: uri.host,
|
|
42
|
-
key: decodeStorageObjectKey(
|
|
42
|
+
key: decodeStorageObjectKey(
|
|
43
|
+
uri.path.startsWith('/') ? uri.path.substring(1) : uri.path,
|
|
44
|
+
),
|
|
43
45
|
);
|
|
44
46
|
}
|
|
@@ -47,8 +47,7 @@ bool bundleMatchesQueryWhere(Bundle bundle, DatabaseBundleQueryWhere? where) {
|
|
|
47
47
|
return false;
|
|
48
48
|
}
|
|
49
49
|
if (where.targetAppVersionIn != null &&
|
|
50
|
-
!where.targetAppVersionIn
|
|
51
|
-
.contains(bundle.targetAppVersion ?? '')) {
|
|
50
|
+
!where.targetAppVersionIn!.contains(bundle.targetAppVersion ?? '')) {
|
|
52
51
|
return false;
|
|
53
52
|
}
|
|
54
53
|
if (where.fingerprintHash != null &&
|
|
@@ -59,7 +58,10 @@ bool bundleMatchesQueryWhere(Bundle bundle, DatabaseBundleQueryWhere? where) {
|
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
/// Sort bundles by ID in the specified direction.
|
|
62
|
-
List<Bundle> sortBundles(
|
|
61
|
+
List<Bundle> sortBundles(
|
|
62
|
+
List<Bundle> bundles,
|
|
63
|
+
DatabaseBundleQueryOrder? orderBy,
|
|
64
|
+
) {
|
|
63
65
|
final direction = orderBy?.direction ?? 'desc';
|
|
64
66
|
final sorted = List<Bundle>.from(bundles);
|
|
65
67
|
sorted.sort((a, b) {
|