diff --git a/actions/lib/src/node/actions/http_request.dart b/actions/lib/src/node/actions/http_request.dart index 959637604b0..0b693292656 100644 --- a/actions/lib/src/node/actions/http_request.dart +++ b/actions/lib/src/node/actions/http_request.dart @@ -3,6 +3,8 @@ import 'dart:js_interop'; +import 'package:web/web.dart'; + @JS() extension type HttpClient._(JSObject it) { external HttpClient([ @@ -18,7 +20,11 @@ extension type HttpClient._(JSObject it) { String requestUrl, { Map headers = const {}, }) async { - final jsHeaders = headers.jsify() as JSObject; + final jsHeaders = Headers(); + for(final entry in headers.entries) { + jsHeaders.append(entry.key, entry.value); + } + final response = await _getJson(requestUrl, jsHeaders).toDart; final result = response as TypedResponse; if (result.statusCode != 200) { diff --git a/actions/pubspec.yaml b/actions/pubspec.yaml index 1af428d06b7..7a0bbaf535f 100644 --- a/actions/pubspec.yaml +++ b/actions/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: source_maps: ^0.10.12 stack_trace: ^1.10.0 stream_transform: ^2.1.0 + web: ^1.1.1 dev_dependencies: amplify_lints: ^3.0.0 diff --git a/packages/amplify_core/lib/src/platform/platform.dart b/packages/amplify_core/lib/src/platform/platform.dart index dd3eeaf0f77..1dc074cf7bd 100644 --- a/packages/amplify_core/lib/src/platform/platform.dart +++ b/packages/amplify_core/lib/src/platform/platform.dart @@ -3,4 +3,4 @@ export 'platform_stub.dart' if (dart.library.io) 'platform_io.dart' - if (dart.library.html) 'platform_html.dart'; + if (dart.library.js_interop) 'platform_html.dart'; diff --git a/packages/analytics/amplify_analytics_pinpoint/lib/src/device_context_info_provider/device_info_provider/device_info_provider.dart b/packages/analytics/amplify_analytics_pinpoint/lib/src/device_context_info_provider/device_info_provider/device_info_provider.dart index 4e4da89cdda..3e3617c0c03 100644 --- a/packages/analytics/amplify_analytics_pinpoint/lib/src/device_context_info_provider/device_info_provider/device_info_provider.dart +++ b/packages/analytics/amplify_analytics_pinpoint/lib/src/device_context_info_provider/device_info_provider/device_info_provider.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'device_info_provider_stub.dart' - if (dart.library.html) 'device_info_provider_html.dart' + if (dart.library.js_interop) 'device_info_provider_html.dart' if (dart.library.io) 'device_info_provider_io.dart'; diff --git a/packages/analytics/amplify_analytics_pinpoint/lib/src/flutter_path_provider/flutter_path_provider.dart b/packages/analytics/amplify_analytics_pinpoint/lib/src/flutter_path_provider/flutter_path_provider.dart index a82439198a3..8f80aa869f8 100644 --- a/packages/analytics/amplify_analytics_pinpoint/lib/src/flutter_path_provider/flutter_path_provider.dart +++ b/packages/analytics/amplify_analytics_pinpoint/lib/src/flutter_path_provider/flutter_path_provider.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'flutter_path_provider_stub.dart' - if (dart.library.html) 'flutter_path_provider_html.dart' + if (dart.library.js_interop) 'flutter_path_provider_html.dart' if (dart.library.io) 'flutter_path_provider_io.dart'; diff --git a/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/dart_queued_item_store.dart b/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/dart_queued_item_store.dart index 716a6b83eaf..26bcc358661 100644 --- a/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/dart_queued_item_store.dart +++ b/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/dart_queued_item_store.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'dart_queued_item_store.stub.dart' - if (dart.library.html) 'dart_queued_item_store.web.dart' + if (dart.library.js_interop) 'dart_queued_item_store.web.dart' if (dart.library.io) 'dart_queued_item_store.vm.dart'; diff --git a/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/index_db/indexed_db_adapter.dart b/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/index_db/indexed_db_adapter.dart index fafc3404786..8e353e5b9e2 100644 --- a/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/index_db/indexed_db_adapter.dart +++ b/packages/analytics/amplify_analytics_pinpoint_dart/lib/src/impl/analytics_client/event_client/queued_item_store/index_db/indexed_db_adapter.dart @@ -2,14 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; -//ignore: deprecated_member_use -import 'dart:js_util'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'package:amplify_analytics_pinpoint_dart/src/impl/analytics_client/event_client/queued_item_store/queued_item_store.dart'; import 'package:amplify_core/amplify_core.dart'; // ignore: implementation_imports import 'package:aws_common/src/js/indexed_db.dart'; import 'package:collection/collection.dart'; +import 'package:web/web.dart'; // TODO(kylechen): Consider merging/refactoring with existing 'amplify_secure_storage_web - _IndexedDBStorage' class /// {@template amplify_analytics_pinpoint_dart.indexed_db_adapter} @@ -39,28 +40,33 @@ class IndexedDbAdapter implements QueuedItemStore { if (db == null) { throw const InvalidStateException('IndexedDB is not available'); } + void onUpgradeNeeded(IDBVersionChangeEvent event) { + final database = event.target?.getProperty('result'.toJS); + final objectStoreNames = database?.objectStoreNames; + if (!(objectStoreNames?.contains(storeName) ?? false)) { + database?.createObjectStore( + storeName, + IDBObjectStoreParameters(keyPath: 'id'.toJS, autoIncrement: true), + ); + } + } + final openRequest = db.open(databaseName, 1) - ..onupgradeneeded = (event) { - final database = event.target.result; - final objectStoreNames = database.objectStoreNames; - if (!objectStoreNames.contains(storeName)) { - database.createObjectStore( - storeName, - keyPath: 'id', - autoIncrement: true, - ); - } - }; - _database = await openRequest.future; + ..onupgradeneeded = onUpgradeNeeded.toJS; + + final result = await openRequest.future; + if (result.isA()) { + result as IDBDatabase; + _database = result; + } else { + throw const InvalidStateException('IDBOpenDBRequest failed'); + } } /// Returns a new [IDBObjectStore] instance after waiting for initialization /// to complete. IDBObjectStore _getObjectStore() { - final transaction = _database.transaction( - storeName, - mode: IDBTransactionMode.readwrite, - ); + final transaction = _database.transaction(storeName.toJS, 'readwrite'); final store = transaction.objectStore(storeName); return store; } @@ -68,7 +74,9 @@ class IndexedDbAdapter implements QueuedItemStore { @override Future addItem(String string) async { await _databaseOpenEvent; - await _getObjectStore().push({'value': string}).future; + final item = JSObject()..setProperty('value'.toJS, string.toJS); + + await _getObjectStore().add(item).future; } @override @@ -77,14 +85,26 @@ class IndexedDbAdapter implements QueuedItemStore { await _databaseOpenEvent; final store = _getObjectStore(); - final request = store.getAll(null, count); + + late IDBRequest request; + if (count == null) { + request = store.getAll(); + } else { + request = store.getAll(null, count); + } await request.future; + final jsResult = request.result; + var result = []; + if (jsResult.isA>()) { + jsResult as JSArray; + result = jsResult.toDart; + } + + for (final elem in result) { + final id = elem.getProperty('id'.toJS).toDartInt; + final string = elem.getProperty('value'.toJS).toDart; - for (final elem in request.result) { - final value = elem as Object; - final id = getProperty(value, 'id'); - final string = getProperty(value, 'value'); readValues.add(QueuedItem(id: id, value: string)); } return readValues; @@ -101,10 +121,8 @@ class IndexedDbAdapter implements QueuedItemStore { final ranges = idsToDelete .splitBetween((a, b) => b != a + 1) - .map((range) => IDBKeyRange.bound(range.first, range.last)); - await Future.wait( - ranges.map((range) => store.deleteByKeyRange(range).future), - ); + .map((range) => IDBKeyRange.bound(range.first.toJS, range.last.toJS)); + await Future.wait(ranges.map((range) => store.delete(range).future)); } /// Clear the database. diff --git a/packages/analytics/amplify_analytics_pinpoint_dart/pubspec.yaml b/packages/analytics/amplify_analytics_pinpoint_dart/pubspec.yaml index 4ed13316e1d..b58bd6c2a27 100644 --- a/packages/analytics/amplify_analytics_pinpoint_dart/pubspec.yaml +++ b/packages/analytics/amplify_analytics_pinpoint_dart/pubspec.yaml @@ -24,6 +24,7 @@ dependencies: smithy: ">=0.7.5 <0.8.0" smithy_aws: ">=0.7.5 <0.8.0" uuid: ">=3.0.6 <5.0.0" + web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.2 <3.2.0" diff --git a/packages/auth/amplify_auth_cognito/lib/src/auth_plugin_impl.dart b/packages/auth/amplify_auth_cognito/lib/src/auth_plugin_impl.dart index 18614b53cd0..4cba4dd4401 100644 --- a/packages/auth/amplify_auth_cognito/lib/src/auth_plugin_impl.dart +++ b/packages/auth/amplify_auth_cognito/lib/src/auth_plugin_impl.dart @@ -15,7 +15,7 @@ import 'package:amplify_auth_cognito_dart/amplify_auth_cognito_dart.dart'; import 'package:amplify_auth_cognito_dart/src/credentials/legacy_credential_provider.dart'; // ignore: implementation_imports import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_stub.dart' - if (dart.library.html) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_html.dart' + if (dart.library.js_interop) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_html.dart' if (dart.library.ui) 'flows/hosted_ui/hosted_ui_platform_flutter.dart'; // ignore: implementation_imports import 'package:amplify_auth_cognito_dart/src/model/hosted_ui/oauth_parameters.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.dart index f3d1fdb5f21..bf51d024e59 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:amplify_auth_cognito_dart/src/asf/asf_context_data.dart'; import 'package:amplify_auth_cognito_dart/src/asf/asf_device_info_collector.stub.dart' if (dart.library.io) 'package:amplify_auth_cognito_dart/src/asf/asf_device_info_collector.vm.dart' - if (dart.library.js_util) 'package:amplify_auth_cognito_dart/src/asf/asf_device_info_collector.js.dart'; + if (dart.library.js_interop) 'package:amplify_auth_cognito_dart/src/asf/asf_device_info_collector.js.dart'; import 'package:amplify_core/amplify_core.dart'; // ignore: implementation_imports import 'package:amplify_core/src/platform/platform.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.js.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.js.dart index 21bc63414b0..3f92b0c477f 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.js.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_device_info_collector.js.dart @@ -10,9 +10,8 @@ import 'package:amplify_auth_cognito_dart/src/asf/asf_device_info_collector.dart import 'package:amplify_auth_cognito_dart/src/asf/package_info.dart'; import 'package:async/async.dart'; import 'package:aws_common/aws_common.dart'; -// ignore: implementation_imports -import 'package:aws_common/src/js/common.dart'; import 'package:path/path.dart'; +import 'package:web/web.dart'; /// {@template amplify_auth_cognito_dart.asf.asf_device_info_js} /// The JS/Browser implementation of [NativeASFDeviceInfoCollector]. @@ -63,10 +62,10 @@ final class ASFDeviceInfoPlatform extends NativeASFDeviceInfoCollector { window.navigator.userAgentData?.platform ?? window.navigator.platform; @override - Future get screenHeightPixels async => window.screen.height?.toInt(); + Future get screenHeightPixels async => window.screen.height; @override - Future get screenWidthPixels async => window.screen.width?.toInt(); + Future get screenWidthPixels async => window.screen.width; @override Future get thirdPartyDeviceId async => null; @@ -79,34 +78,11 @@ String get _baseUrl { return url.join(window.location.origin, basePath); } -extension on Window { - external _Screen get screen; - external _Navigator get navigator; -} - -@JS('Screen') -@staticInterop -class _Screen {} - -extension on _Screen { - external double? get width; - external double? get height; -} - -@JS('Navigator') -@staticInterop -class _Navigator {} - -extension on _Navigator { - external String? get language; - external String? get platform; +extension _PropsNavigator on Navigator { external _NavigatorUAData? get userAgentData; } @JS('NavigatorUAData') -@staticInterop -class _NavigatorUAData {} - -extension on _NavigatorUAData { +extension type _NavigatorUAData._(JSObject _) implements JSObject { external String? get platform; } diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_worker.worker.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_worker.worker.dart index 31f1746574f..fe38311ec83 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_worker.worker.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/asf/asf_worker.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'asf_worker.worker.vm.dart' - if (dart.library.js) 'asf_worker.worker.js.dart'; + if (dart.library.js_interop) 'asf_worker.worker.js.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/auth_plugin_impl.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/auth_plugin_impl.dart index e4677352d06..ea3cc9d189b 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/auth_plugin_impl.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/auth_plugin_impl.dart @@ -11,7 +11,7 @@ import 'package:amplify_auth_cognito_dart/src/credentials/device_metadata_reposi import 'package:amplify_auth_cognito_dart/src/flows/helpers.dart'; import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform.dart'; import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/initial_parameters_stub.dart' - if (dart.library.html) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/initial_parameters_html.dart'; + if (dart.library.js_interop) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/initial_parameters_html.dart'; import 'package:amplify_auth_cognito_dart/src/model/hosted_ui/oauth_parameters.dart'; import 'package:amplify_auth_cognito_dart/src/model/session/cognito_sign_in_details.dart'; import 'package:amplify_auth_cognito_dart/src/model/sign_in_parameters.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/device/confirm_device_worker.worker.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/device/confirm_device_worker.worker.dart index e908e392389..9b66659309e 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/device/confirm_device_worker.worker.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/device/confirm_device_worker.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'confirm_device_worker.worker.vm.dart' - if (dart.library.js) 'confirm_device_worker.worker.js.dart'; + if (dart.library.js_interop) 'confirm_device_worker.worker.js.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform.dart index 3737f6b5408..f826494b962 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform.dart @@ -8,7 +8,7 @@ import 'package:amplify_auth_cognito_dart/src/credentials/cognito_keys.dart'; import 'package:amplify_auth_cognito_dart/src/crypto/oauth.dart'; import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_config.dart'; import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_stub.dart' - if (dart.library.html) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_html.dart' + if (dart.library.js_interop) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_html.dart' if (dart.library.io) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_io.dart'; import 'package:amplify_auth_cognito_dart/src/model/hosted_ui/oauth_parameters.dart'; import 'package:amplify_auth_cognito_dart/src/state/state.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform_html.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform_html.dart index 2112676d351..6a4d30f067f 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform_html.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/hosted_ui_platform_html.dart @@ -3,9 +3,8 @@ import 'package:amplify_auth_cognito_dart/amplify_auth_cognito_dart.dart'; import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform.dart'; -// ignore: implementation_imports -import 'package:aws_common/src/js/common.dart'; import 'package:path/path.dart' show url; +import 'package:web/web.dart'; /// {@macro amplify_auth_cognito.hosted_ui_platform} class HostedUiPlatformImpl extends HostedUiPlatform { diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/initial_parameters_html.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/initial_parameters_html.dart index 793f3b46dc9..0c36d4cdcff 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/initial_parameters_html.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/hosted_ui/initial_parameters_html.dart @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:amplify_auth_cognito_dart/src/model/hosted_ui/oauth_parameters.dart'; -// ignore:implementation_imports -import 'package:aws_common/src/js/common.dart'; +import 'package:web/web.dart'; /// {@macro amplify_auth_cognito.initial_parameters} final OAuthParameters? initialParameters = OAuthParameters.fromUri( diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_device_password_verifier_worker.worker.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_device_password_verifier_worker.worker.dart index eb566af070a..c4386eb6591 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_device_password_verifier_worker.worker.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_device_password_verifier_worker.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'srp_device_password_verifier_worker.worker.vm.dart' - if (dart.library.js) 'srp_device_password_verifier_worker.worker.js.dart'; + if (dart.library.js_interop) 'srp_device_password_verifier_worker.worker.js.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_init_worker.worker.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_init_worker.worker.dart index 22b7ca3c082..3675964bfb7 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_init_worker.worker.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_init_worker.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'srp_init_worker.worker.vm.dart' - if (dart.library.js) 'srp_init_worker.worker.js.dart'; + if (dart.library.js_interop) 'srp_init_worker.worker.js.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_password_verifier_worker.worker.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_password_verifier_worker.worker.dart index 520d43cf399..9bdaefdbb7c 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_password_verifier_worker.worker.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/flows/srp/srp_password_verifier_worker.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'srp_password_verifier_worker.worker.vm.dart' - if (dart.library.js) 'srp_password_verifier_worker.worker.js.dart'; + if (dart.library.js_interop) 'srp_password_verifier_worker.worker.js.dart'; diff --git a/packages/auth/amplify_auth_cognito_dart/pubspec.yaml b/packages/auth/amplify_auth_cognito_dart/pubspec.yaml index 1cfe197b760..1c39ee73424 100644 --- a/packages/auth/amplify_auth_cognito_dart/pubspec.yaml +++ b/packages/auth/amplify_auth_cognito_dart/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: smithy_aws: ">=0.7.5 <0.8.0" stream_transform: ^2.0.0 uuid: ">=3.0.6 <5.0.0" + web: ^1.1.1 win32: ">=4.1.2 <6.0.0" win32_registry: ^2.1.0 worker_bee: ">=0.3.5 <0.4.0" diff --git a/packages/auth/amplify_auth_cognito_test/lib/amplify_auth_cognito_test.dart b/packages/auth/amplify_auth_cognito_test/lib/amplify_auth_cognito_test.dart index f3bb042b1a2..76ea5e6bb0d 100644 --- a/packages/auth/amplify_auth_cognito_test/lib/amplify_auth_cognito_test.dart +++ b/packages/auth/amplify_auth_cognito_test/lib/amplify_auth_cognito_test.dart @@ -14,4 +14,4 @@ export 'common/mock_legacy_credential_provider.dart'; export 'common/mock_oauth_server.dart'; export 'common/mock_secure_storage.dart'; export 'src/asf/asf_tests.dart'; -export 'src/utils.vm.dart' if (dart.library.js_util) 'src/utils.js.dart'; +export 'src/utils.vm.dart' if (dart.library.js_interop) 'src/utils.js.dart'; diff --git a/packages/auth/amplify_auth_cognito_test/lib/common/mock_hosted_ui.dart b/packages/auth/amplify_auth_cognito_test/lib/common/mock_hosted_ui.dart index 7435bf23c53..bd55742b98e 100644 --- a/packages/auth/amplify_auth_cognito_test/lib/common/mock_hosted_ui.dart +++ b/packages/auth/amplify_auth_cognito_test/lib/common/mock_hosted_ui.dart @@ -4,7 +4,7 @@ import 'package:amplify_auth_cognito_dart/amplify_auth_cognito_dart.dart'; import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform.dart'; import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_stub.dart' - if (dart.library.html) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_html.dart' + if (dart.library.js_interop) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_html.dart' if (dart.library.io) 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform_io.dart'; import 'package:amplify_core/amplify_core.dart'; diff --git a/packages/aws_common/lib/src/http/aws_http_client.dart b/packages/aws_common/lib/src/http/aws_http_client.dart index 18a24e79b6e..1fa2da9cf20 100644 --- a/packages/aws_common/lib/src/http/aws_http_client.dart +++ b/packages/aws_common/lib/src/http/aws_http_client.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'package:aws_common/aws_common.dart'; import 'package:aws_common/src/http/aws_http_client_io.dart' - if (dart.library.js) 'package:aws_common/src/http/aws_http_client_js.dart'; + if (dart.library.js_interop) 'package:aws_common/src/http/aws_http_client_js.dart'; import 'package:meta/meta.dart'; /// {@template aws_common.http.aws_http_client} diff --git a/packages/aws_common/lib/src/http/aws_http_client_js.dart b/packages/aws_common/lib/src/http/aws_http_client_js.dart index e9a2d72967a..02da9bc856e 100644 --- a/packages/aws_common/lib/src/http/aws_http_client_js.dart +++ b/packages/aws_common/lib/src/http/aws_http_client_js.dart @@ -2,14 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:aws_common/aws_common.dart'; -import 'package:aws_common/src/js/abort.dart'; import 'package:aws_common/src/js/fetch.dart'; +import 'package:aws_common/src/js/readable_stream.dart'; import 'package:meta/meta.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'package:web/web.dart'; /// {@macro aws_common.http.http_client_impl} class AWSHttpClientImpl extends AWSHttpClient { @@ -46,12 +49,12 @@ class AWSHttpClientImpl extends AWSHttpClient { final RequestRedirect redirect; if (request.followRedirects) { if (request.maxRedirects == 0) { - redirect = RequestRedirect.error; + redirect = RequestRedirectValues.error.jsValue!; } else { - redirect = RequestRedirect.follow; + redirect = RequestRedirectValues.follow.jsValue!; } } else { - redirect = RequestRedirect.manual; + redirect = RequestRedirectValues.manual.jsValue!; } try { // ReadableStream bodies are only supported in fetch on HTTPS calls to @@ -73,19 +76,39 @@ class AWSHttpClientImpl extends AWSHttpClient { }, ) .takeUntil(cancelTrigger.future); - final body = Uint8List.fromList(await collectBytes(stream)); + JSAny? body; + // `fetch` does not allow bodies for these methods. + if (request.method != AWSHttpMethod.get && + request.method != AWSHttpMethod.head) { + if (request.scheme == 'http' || + supportedProtocols.supports(AlpnProtocol.http1_1)) { + body = Uint8List.fromList(await collectBytes(stream)).toJS; + } else { + body = stream.asReadableStream(); + } + } if (completer.isCanceled) return; - final resp = await fetch( - request.uri.toString(), - RequestInit( - method: request.method, - headers: request.headers, - body: body, - signal: abortController.signal, - redirect: redirect, - ), - ); + + final requestHeaders = Headers(); + for (final entry in request.headers.entries) { + requestHeaders.append(entry.key, entry.value); + } + + final resp = + await window + .fetch( + request.uri.toString().toJS, + RequestInit( + method: request.method.value, + headers: requestHeaders, + body: body, + signal: abortController.signal, + redirect: redirect, + duplex: 'half', + ), + ) + .toDart; final streamView = resp.body; final bodyController = StreamController>( @@ -105,18 +128,34 @@ class AWSHttpClientImpl extends AWSHttpClient { ..addError(const CancellationException()) ..close(); } - responseProgressController.close(); + if (!responseProgressController.isClosed) { + responseProgressController.close(); + } + if (!requestProgressController.isClosed) { + requestProgressController.close(); + } }; + unawaited( - streamView.progress.forward( + streamView?.progress.forward( responseProgressController, cancelOnError: true, ), ); - unawaited(streamView.forward(bodyController, cancelOnError: true)); + unawaited( + streamView?.stream.forward(bodyController, cancelOnError: true), + ); + + final responseHeaders = {}; + void headerBuilder(JSString value, JSString key, JSAny object) { + responseHeaders[key.toDart] = value.toDart; + } + + resp.headers.callMethod('forEach'.toJS, headerBuilder.toJS); + final streamedResponse = AWSStreamedHttpResponse( statusCode: resp.status, - headers: resp.headers, + headers: responseHeaders, body: bodyController.stream.tap( null, onDone: () { diff --git a/packages/aws_common/lib/src/io/aws_file.dart b/packages/aws_common/lib/src/io/aws_file.dart index 104510b016f..9bf2a7e63bf 100644 --- a/packages/aws_common/lib/src/io/aws_file.dart +++ b/packages/aws_common/lib/src/io/aws_file.dart @@ -4,7 +4,7 @@ import 'package:async/async.dart'; import 'package:aws_common/aws_common.dart'; import 'package:aws_common/src/io/aws_file_platform.dart' - if (dart.library.html) 'aws_file_platform_html.dart' + if (dart.library.js_interop) 'aws_file_platform_html.dart' if (dart.library.io) 'aws_file_platform_io.dart'; import 'package:meta/meta.dart'; diff --git a/packages/aws_common/lib/src/io/aws_file_platform_html.dart b/packages/aws_common/lib/src/io/aws_file_platform_html.dart index ac8113a3cad..caf040d777a 100644 --- a/packages/aws_common/lib/src/io/aws_file_platform_html.dart +++ b/packages/aws_common/lib/src/io/aws_file_platform_html.dart @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; -//ignore: deprecated_member_use -import 'dart:html'; +import 'dart:js_interop'; import 'package:async/async.dart'; import 'package:aws_common/aws_common.dart'; +import 'package:http/browser_client.dart'; +import 'package:http/http.dart' as http; +import 'package:web/web.dart'; // Dart io.File openRead chunk size const _readStreamChunkSize = 64 * 1024; @@ -174,9 +176,9 @@ class AWSFilePlatform extends AWSFile { throw const InvalidFileException(); } - late HttpRequest request; + late http.Response response; try { - request = await HttpRequest.request(path, responseType: 'blob'); + response = await BrowserClient().get(Uri.parse(path)); } on ProgressEvent catch (e) { if (e.type == 'error') { throw const InvalidFileException( @@ -188,15 +190,10 @@ class AWSFilePlatform extends AWSFile { rethrow; } - final retrievedBlob = request.response as Blob?; - - if (retrievedBlob == null) { - throw const InvalidFileException( - message: 'The retrieved blob cannot be null.', - recoverySuggestion: - 'Ensure the file `path` in Web is a valid source to retrieve content blob.', - ); - } + final blobParts = [response.bodyBytes.toJS].toJS; + final type = response.headers['content-type'] ?? ''; + final options = BlobPropertyBag(type: type); + final retrievedBlob = Blob(blobParts, options); _size = retrievedBlob.size; @@ -221,9 +218,23 @@ class AWSFilePlatform extends AWSFile { ? blob.size : currentPosition + _readStreamChunkSize; final blobToRead = blob.slice(currentPosition, readRange); - fileReader.readAsArrayBuffer(blobToRead); - await fileReader.onLoad.first; - yield fileReader.result as List; + + final loaded = Completer(); + void onLoadEnd() { + loaded.complete(); + } + + fileReader + ..onloadend = onLoadEnd.toJS + ..readAsArrayBuffer(blobToRead); + + await loaded.future; + final jsResult = fileReader.result; + jsResult as JSArrayBuffer; + + final bytebuffer = jsResult.toDart; + yield bytebuffer.asUint8List().toList(); + currentPosition += _readStreamChunkSize; } } diff --git a/packages/aws_common/lib/src/js/abort.dart b/packages/aws_common/lib/src/js/abort.dart deleted file mode 100644 index 9e45abeb7c4..00000000000 --- a/packages/aws_common/lib/src/js/abort.dart +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// ignore_for_file: avoid_classes_with_only_static_members -//ignore: deprecated_member_use -import 'package:js/js.dart'; -//ignore: deprecated_member_use -import 'package:js/js_util.dart' as js_util; - -/// {@template aws_http.js.abort_signal} -/// A signal object that allows you to communicate with a DOM request (such as -/// a fetch request) and abort it if required via an [AbortController] object. -/// {@endtemplate} -@JS() -@staticInterop -abstract class AbortSignal { - /// An [AbortSignal] instance that is already set as aborted. - external static AbortSignal abort([String? reason]); - - /// An [AbortSignal] instance that will automatically abort after a specified - /// time. - external static AbortSignal timeout(int millis); -} - -/// {@macro aws_http.js.abort_signal} -extension PropsAbortSignal on AbortSignal { - /// Whether the request(s) the signal is communicating with is/are aborted - /// (`true`) or not (`false`). - external bool get aborted; - - /// The abort reason, once the signal has aborted. - String? get reason => - js_util.getProperty(this, 'reason')?.toString(); -} - -/// {@template aws_http.js.abort_controller} -/// A controller object that allows you to abort one or more Web requests as -/// and when desired. -/// {@endtemplate} -@JS() -@staticInterop -abstract class AbortController { - /// {@macro aws_http.js.abort_controller} - external factory AbortController(); -} - -/// {@macro aws_http.js.abort_controller} -extension PropsAbortController on AbortController { - /// The [AbortSignal], which can be used to communicate with, or to abort, - /// a DOM request. - external AbortSignal get signal; - - /// Aborts a DOM request before it has completed. - external void abort([String? reason]); -} diff --git a/packages/aws_common/lib/src/js/common.dart b/packages/aws_common/lib/src/js/common.dart index b9ccc5fded6..5b0e833f6c2 100644 --- a/packages/aws_common/lib/src/js/common.dart +++ b/packages/aws_common/lib/src/js/common.dart @@ -3,13 +3,11 @@ // ignore_for_file: avoid_classes_with_only_static_members, prefer_void_to_null -import 'dart:async'; -//ignore: deprecated_member_use -import 'dart:js_util' as js_util; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'package:aws_common/src/util/recase.dart'; -//ignore: deprecated_member_use -import 'package:js/js.dart'; +import 'package:web/web.dart'; /// The JS `undefined`. @JS() @@ -38,141 +36,17 @@ mixin JSEnum on Enum { external GlobalScope get self; /// Whether the current script is running in a web worker. -final bool zIsWebWorker = js_util.getProperty(self, 'window') == null; - -/// The [Window] object of the current context. -/// -/// Throws a [StateError] if unavailable in this context. Use [zIsWebWorker] -/// to check whether this will throw or not. -Window get window { - final window = js_util.getProperty(self, 'window'); - if (window == null) { - throw StateError('window is not available in this context'); - } - return window; -} - -/// The [Document] object of the current context. -/// -/// Throws a [StateError] if unavailable. Use [zIsWebWorker] to check whether -/// this will throw or not. -Document get document { - final document = js_util.getProperty(self, 'document'); - if (document == null) { - throw StateError('document is not available in this context'); - } - return document; -} - -/// {@template aws_common.js.window} -/// The Window interface represents a window containing a DOM document. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Window implements GlobalScope {} - -/// {@macro aws_common.js.window} -extension PropsWindow on Window { - /// Loads a specified resource into a new or existing browsing context - /// (that is, a tab, a window, or an iframe) under a specified name. - external void open([String? url, String? target]); -} - -/// {@template aws_common.js.document} -/// The Document interface represents any web page loaded in the browser and -/// serves as an entry point into the web page's content, which is the DOM tree. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Document {} - -/// {@macro aws_common.js.document} -extension PropsDocument on Document { - /// Returns the first [Element] within the document that matches the - /// specified selector, or group of selectors. - /// - /// If no matches are found, `null` is returned. - external Element? querySelector(String selectors); -} - -/// {@template aws_common.js.element} -/// The most general base class from which all element objects (i.e. objects -/// that represent elements) in a [Document] inherit. -/// -/// It only has methods and properties common to all kinds of elements. More -/// specific classes inherit from Element. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Element {} - -/// {@macro aws_common.js.element} -extension PropsElement on Element { - /// Returns the value of a specified attribute on the element. - /// - /// If the given attribute does not exist, the value returned will either be - /// `null` or `""` (the empty string); - external String? getAttribute(String name); -} +final bool zIsWebWorker = self.getProperty('window'.toJS) == null; /// A function which handles DOM events. typedef EventHandler = void Function(T event); -/// {@template amplify_secure_storage_dart.event} -/// The Event interface represents an event which takes place in the DOM. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Event {} - -/// {@macro amplify_secure_storage_dart.event} -extension PropsEvent on Event { - /// A reference to the object onto which this event was dispatched. - external EventTarget? get target; -} - -/// {@template worker_bee.js.interop.event_target} -/// The EventTarget interface is implemented by objects that can receive events -/// and may have listeners for them. -/// {@endtemplate} -@JS() -@staticInterop -abstract class EventTarget {} - -/// {@macro worker_bee.js.interop.event_target} -extension PropsEventTarget on EventTarget { - /// Registers [listener] as a callback for events of type [type]. - void addEventListener( - String type, - EventHandler listener, - ) => js_util.callMethod(this, 'addEventListener', [ - type, - allowInterop(listener), - false, - ]); - - /// Removes [listener] as a callback for events of type [type]. - void removeEventListener( - String type, - EventHandler listener, - ) => js_util.callMethod(this, 'removeEventListener', [ - type, - allowInterop(listener), - false, - ]); -} - /// {@template worker_bee.js.interop.global_scope} /// The global execution context, referred to by [self]. /// /// Either a [Window] object or a `WorkerGlobalScope` object. /// {@endtemplate} -@JS() -@staticInterop -abstract class GlobalScope extends EventTarget {} - -/// {@macro worker_bee.js.interop.global_scope} -extension PropsGlobalScope on GlobalScope { +extension type GlobalScope._(JSObject _) implements EventTarget, JSObject { /// A [Location] object with information about the current location of the /// document. external Location get location; @@ -182,182 +56,8 @@ extension PropsGlobalScope on GlobalScope { /// /// When called on a [Window], this sends a message to the parent window /// object. - void postMessage(Object? o, [List? transfer]) => js_util.callMethod( - this, - 'postMessage', - [js_util.jsify(o), transfer?.map(js_util.jsify).toList()], - ); -} - -/// {@template worker_bee.js.interop.message_event} -/// The MessageEvent interface represents a message received by a target object. -/// {@endtemplate} -@JS() -@staticInterop -abstract class MessageEvent extends Event {} - -/// {@macro worker_bee.js.interop.message_event} -extension PropsMessageEvent on MessageEvent { - /// The data sent by the message emitter. - Object? get data { - final Object? data = js_util.getProperty(this, 'data'); - return js_util.dartify(data); - } - - /// An array of [MessagePort] objects representing the ports associated with - /// the channel the message is being sent through. - List get ports { - final Object ports = js_util.getProperty(this, 'ports'); - return (js_util.dartify(ports) as List).cast(); - } -} - -/// {@template worker_bee.js.interop.message_port} -/// The MessagePort interface of the Channel Messaging API represents one of the -/// two ports of a [MessageChannel], allowing messages to be sent from one port -/// and listening out for them arriving at the other. -/// {@endtemplate} -@JS() -@staticInterop -abstract class MessagePort extends EventTarget {} - -/// {@macro worker_bee.js.interop.message_port} -extension PropsMessagePort on MessagePort { - /// Fired when a MessagePort object receives a message. - Stream get onMessage { - final controller = StreamController(); - addEventListener('message', controller.add); - addEventListener('messageerror', (event) { - controller - ..addError(event) - ..close(); - }); - scheduleMicrotask(start); - return controller.stream; - } - - /// Sends a message from the port, and optionally, transfers ownership of - /// objects to other browsing contexts. - void postMessage(Object? o, [List? transfer]) => js_util.callMethod( - this, - 'postMessage', - [js_util.jsify(o), transfer?.map(js_util.jsify).toList()], - ); - - /// Starts the sending of messages queued on the port. - /// - /// Only needed when using `EventTarget.addEventListener`; it is implied when - /// using [onMessage]. - void start() => _start(); - - @JS('start') - external void _start(); - - /// Disconnects the port, so it is no longer active. - external void close(); -} - -/// {@template worker_bee.js.interop.location} -/// The Location interface represents the location (URL) of the object it is -/// linked to. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Location {} - -/// {@macro worker_bee.js.interop.location} -extension PropsLocation on Location { - /// The entire URL. - external String get href; - - /// Returns a string containing the canonical form of the origin of the - /// specific location. - external String get origin; -} - -/// {@template worker_bee.js.interop.worker_init} -/// An object containing option properties that can be set when creating a -/// [Worker] instance. -/// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class WorkerInit { - /// {@macro worker_bee.js.interop.worker_init} - external factory WorkerInit({String? type}); -} - -/// {@template worker_bee.js.interop.worker} -/// The Worker interface of the Web Workers API represents a background task -/// that can be created via script, which can send messages back to its creator. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Worker extends EventTarget { - /// {@macro worker_bee.js.interop.worker} - external factory Worker(String url, [WorkerInit? init]); -} - -/// {@macro worker_bee.js.interop.worker} -extension PropsWorker on Worker { - /// The error event of the Worker interface fires when an error occurs in the - /// worker. - set onError(EventHandler listener) { - js_util.setProperty(this, 'onerror', allowInterop(listener)); - } - - /// The `message` event is fired on a Worker object when the worker's parent - /// receives a message from its worker. - set onMessage(EventHandler listener) { - js_util.setProperty(this, 'onmessage', allowInterop(listener)); - } - - /// Sends a message to the worker's inner scope. - external void postMessage(Object? o, [List? transfer]); - - /// Immediately terminates the Worker. - /// - /// This does not offer the worker an opportunity to finish its operations; - /// it is stopped at once. - external void terminate(); -} - -/// {@template worker_bee.js.interop.error_event} -/// The ErrorEvent interface represents events providing information related to -/// errors in scripts or in files. -/// {@endtemplate} -@JS() -@staticInterop -abstract class ErrorEvent extends Event {} - -/// {@macro worker_bee.js.interop.error_event} -extension PropsErrorEvent on ErrorEvent { - /// The error object associated with the event. - external Object? get error; - - /// A string containing a human-readable error message describing the problem. - external String? get message; -} - -/// {@template worker_bee.js.interop.message_channel} -/// The MessageChannel interface of the Channel Messaging API allows us to -/// create a new message channel and send data through it via its two -/// [MessagePort] properties. -/// {@endtemplate} -@JS() -@staticInterop -abstract class MessageChannel { - /// {@macro worker_bee.js.interop.message_channel} - external factory MessageChannel(); -} - -/// {@macro worker_bee.js.interop.message_channel} -extension PropsMessageChannel on MessageChannel { - /// Port 1 of the channel. - external MessagePort get port1; - - /// Port 2 of the channel. - external MessagePort get port2; + void postMessage(JSAny? o, [JSArray? transfer]) => + callMethod('postMessage'.toJS, o, transfer); } /// Browser-based JSON utilities. @@ -365,23 +65,5 @@ extension PropsMessageChannel on MessageChannel { @staticInterop abstract class JSON { /// Stringifies a JSON-like object. - external static String stringify(Object? object); -} - -/// {@template worker_bee.js.interop.js_object} -/// The base class for all JavaScript objects. -/// {@endtemplate} -@JS('Object') -@staticInterop -abstract class JSObject { - /// Returns an array of a given [object]'s own enumerable property names, - /// iterated in the same order that a normal loop would. - external static List keys(Object object); - - /// Returns the prototype (i.e. the value of the internal `[[Prototype]]` - /// property) of the specified [object]. - external static Object? getPrototypeOf(Object? object); - - /// The prototype of the JS `Object` class. - external static Object get prototype; + external static String stringify(JSAny? object); } diff --git a/packages/aws_common/lib/src/js/fetch.dart b/packages/aws_common/lib/src/js/fetch.dart index f7a8c4979aa..a862cdca433 100644 --- a/packages/aws_common/lib/src/js/fetch.dart +++ b/packages/aws_common/lib/src/js/fetch.dart @@ -1,92 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'package:aws_common/aws_common.dart'; -import 'package:aws_common/src/js/abort.dart'; import 'package:aws_common/src/js/common.dart'; -import 'package:aws_common/src/js/promise.dart'; -import 'package:aws_common/src/js/readable_stream.dart'; -//ignore: deprecated_member_use -import 'package:js/js.dart'; -//ignore: deprecated_member_use -import 'package:js/js_util.dart' as js_util; - -/// How a [Request] will interact with the browser's HTTP cache. -enum RequestCache with JSEnum { - /// The browser looks for a matching request in its HTTP cache. - /// - /// - If there is a match and it is fresh, it will be returned from the cache. - /// - If there is a match but it is stale, the browser will make a conditional - /// request to the remote server. If the server indicates that the resource - /// has not changed, it will be returned from the cache. Otherwise the - /// resource will be downloaded from the server and the cache will be - /// updated. - /// - If there is no match, the browser will make a normal request, and will - /// update the cache with the downloaded resource. - default$, - - /// The browser looks for a matching request in its HTTP cache. - /// - /// - If there is a match, fresh or stale, it will be returned from the cache. - /// - If there is no match, the browser will make a normal request, and will - /// update the cache with the downloaded resource. - forceCache, - - /// The browser looks for a matching request in its HTTP cache. - /// - /// - If there is a match, fresh or stale, the browser will make a conditional - /// request to the remote server. If the server indicates that the resource - /// has not changed, it will be returned from the cache. Otherwise the - /// resource will be downloaded from the server and the cache will be - /// updated. - /// - If there is no match, the browser will make a normal request, and will - /// update the cache with the downloaded resource. - noCache, - - /// The browser fetches the resource from the remote server without first - /// looking in the cache, and will not update the cache with the downloaded - /// resource. - noStore, - - /// The browser looks for a matching request in its HTTP cache. - /// - /// - If there is a match, fresh or stale, it will be returned from the cache. - /// - If there is no match, the browser will respond with a 504 Gateway - /// timeout status. - /// - /// The "only-if-cached" mode can only be used if the request's `mode` is - /// "same-origin". Cached redirects will be followed if the request's - /// `redirect` property is "follow" and the redirects do not violate the - /// "same-origin" mode. - onlyIfCached, - - /// The browser fetches the resource from the remote server without first - /// looking in the cache, *but then will* update the cache with the downloaded - /// resource. - reload, -} - -/// Controls what browsers do with credentials (cookies, HTTP authentication -/// entries, and TLS client certificates). -enum RequestCredentials with JSEnum { - /// The default behavior. - default$, - - /// Tells browsers to include credentials in both same- and cross-origin - /// requests, and always use any credentials sent back in responses. - include, - - /// Tells browsers to exclude credentials from the request, and ignore any - /// credentials sent back in the response (e.g., any `Set-Cookie` header). - omit, - - /// Tells browsers to include credentials with requests to same-origin URLs, - /// and use any credentials sent back in responses from same-origin URLs. - sameOrigin, -} +import 'package:web/web.dart'; /// How to handle a redirect response of a [Request]. -enum RequestRedirect with JSEnum { +enum RequestRedirectValues with JSEnum { /// The default behavior. default$, @@ -99,271 +18,3 @@ enum RequestRedirect with JSEnum { /// Caller intends to process the response in another context. manual, } - -/// The type of content being requested in a [Request]. -enum RequestDestination with JSEnum { - /// The default value of destination is used for destinations that do not have - /// their own value. - default$, - - /// The target is audio data. - audio, - - /// The target is data being fetched for use by an audio worklet. - audioworklet, - - /// The target is a document (HTML or XML). - document, - - /// The target is embedded content. - embed, - - /// The target is a font. - font, - - /// The target is an image. - image, - - /// The target is a manifest. - manifest, - - /// The target is an object. - object, - - /// The target is a paint worklet. - paintworklet, - - /// The target is a report. - report, - - /// The target is a script. - script, - - /// The target is a shared worker. - sharedworker, - - /// The target is a style. - style, - - /// The target is an HTML ``. - track, - - /// The target is video data. - video, - - /// The target is a worker. - worker, - - /// The target is an XSLT transform. - xslt, -} - -/// The mode used for a [Request]. -enum RequestMode with JSEnum { - /// The default behavior. - default$, - - /// Allows cross-origin requests, for example to access various APIs offered - /// by 3rd party vendors. - cors, - - /// Prevents the method from being anything other than `HEAD`, `GET` or - /// `POST`, and the headers from being anything other than simple headers. - /// - /// If any ServiceWorkers intercept these requests, they may not add or - /// override any headers except for those that are simple headers. In - /// addition, JavaScript may not access any properties of the resulting - /// [Response]. This ensures that ServiceWorkers do not affect the semantics - /// of the Web and prevents security and privacy issues arising from leaking - /// data across domains. - noCors, - - /// If a request is made to another origin with this mode set, the result is - /// an error. You could use this to ensure that a request is always being made - /// to your origin. - sameOrigin, -} - -/// {@template aws_common.js.request_init} -/// Defines the resource that you wish to [fetch]. -/// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class RequestInit { - /// {@macro aws_common.js.request_init} - factory RequestInit({ - RequestCache cache = RequestCache.default$, - RequestCredentials credentials = RequestCredentials.default$, - RequestMode mode = RequestMode.default$, - RequestDestination destination = RequestDestination.default$, - RequestRedirect redirect = RequestRedirect.default$, - - /// A string specifying the referrer of the request. This can be a - /// same-origin URL, about:client, or an empty string. - String? referrer, - - /// Contains the subresource integrity value of the request. - String? integrity, - - /// The keepalive option can be used to allow the request to outlive the - /// page. - bool? keepalive, - AbortSignal? signal, - AWSHttpMethod method = AWSHttpMethod.get, - Map? headers, - Object? /*Stream>|List|null*/ body, - }) { - // `fetch` does not allow bodies for these methods. - final cannotHaveBody = - method == AWSHttpMethod.get || method == AWSHttpMethod.head; - if (cannotHaveBody) { - body = null; - } - if (body is Stream>) { - body = body.asReadableStream(); - } - return RequestInit._( - cache: cache.jsValue, - credentials: credentials.jsValue, - mode: mode.jsValue, - destination: destination.jsValue, - redirect: redirect.jsValue, - referrer: referrer ?? undefined, - headers: headers != null ? js_util.jsify(headers) : undefined, - integrity: integrity ?? undefined, - keepalive: keepalive ?? undefined, - method: method.value, - signal: signal ?? undefined, - body: body ?? undefined, - // Added for full compatibility with all `fetch` impls: - // https://developer.chrome.com/articles/fetch-streaming-requests/#half-duplex - duplex: 'half', - ); - } - - external factory RequestInit._({ - String? cache, - String? credentials, - String? mode, - String? destination, - String? redirect, - String? referrer, - Object? headers, - String? integrity, - String? duplex, - AbortSignal? signal, - bool? keepalive, - String? method, - Object? body, - }); -} - -/// {@template aws_common.js.headers} -/// The Headers interface of the Fetch API allows you to perform various -/// actions on HTTP request and response headers. -/// -/// These actions include retrieving, setting, adding to, and removing headers -/// from the list of the request's headers. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Headers { - /// {@macro aws_common.js.headers} - external factory Headers(Map headers); -} - -/// {@macro aws_common.js.headers} -extension PropsHeaders on Headers { - /// Alias for [get]. - String? operator [](String name) => get(name); - - /// Alias for [set]. - void operator []=(String name, String value) => set(name, value); - - /// Appends a new value onto an existing header inside a Headers object, or - /// adds the header if it does not already exist. - external void append(String name, String value); - - /// Deletes a header. - external void delete(String name); - - /// Returns a String sequence of all the values of a header within a - /// [Headers] object with a given [name]. - external String? get(String name); - - /// Returns a boolean stating whether a [Headers] object contains a certain - /// [header]. - external bool has(String header); - - /// Sets a new value for an existing header inside a [Headers] object, or adds - /// the header if it does not already exist. - external void set(String name, String value); - - /// Executes [callback] once for each array element. - void forEach( - void Function(String value, String key, Headers parent) callback, - ) => js_util.callMethod(this, 'forEach', [allowInterop(callback)]); -} - -/// {@template aws_common.js.request} -/// The Request interface of the Fetch API represents a resource request. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Request { - /// {@macro aws_common.js.request} - external factory Request(String url, [RequestInit? init]); -} - -/// {@template aws_common.js.response} -/// The Response interface of the Fetch API represents the response to a -/// request. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Response { - /// {@macro aws_common.js.response} - external factory Response(String url, [RequestInit? init]); -} - -/// Used to expand [Response] and treat `Response.body` as a `late final` -/// property so that multiple accesses return the same value. -final Expando _responseStreams = Expando('ResponseStreams'); - -/// {@macro aws_common.js.response} -extension PropsResponse on Response { - /// The response's body as a Dart [Stream]. - ReadableStreamView get body => - _responseStreams[this] ??= - js_util.getProperty(this, 'body')?.stream ?? - const ReadableStreamView.empty(); - - /// The response's headers. - Map get headers { - final Map headers = CaseInsensitiveMap({}); - js_util.getProperty(this, 'headers').forEach((value, key, _) { - headers[key] = value; - }); - return headers; - } - - /// The status code of the response. - external int get status; - - /// The status message corresponding to [status]. - external String get statusText; - - /// Whether or not the response is the result of a redirect. - external bool get redirected; -} - -@JS('fetch') -external Promise _fetch(String url, [RequestInit? init]); - -/// The global fetch() method starts the process of fetching a resource from -/// the network, returning a promise which is fulfilled once the response is -/// available. -Future fetch(String url, [RequestInit? init]) { - return _fetch(url, init).future; -} diff --git a/packages/aws_common/lib/src/js/indexed_db.dart b/packages/aws_common/lib/src/js/indexed_db.dart index b62e2eb163b..1c1c2dc71d2 100644 --- a/packages/aws_common/lib/src/js/indexed_db.dart +++ b/packages/aws_common/lib/src/js/indexed_db.dart @@ -2,272 +2,55 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; -//ignore: deprecated_member_use -import 'dart:js_util' as js_util; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'package:aws_common/src/js/common.dart'; -//ignore: deprecated_member_use -import 'package:js/js.dart'; +import 'package:web/web.dart'; /// The global read-only [IDBFactory] instance. @JS() external IDBFactory? get indexedDB; -/// {@template amplify_secure_storage_dart.dom_string_list} -/// A type returned by some APIs which contains a list of DOMString (strings). -/// {@endtemplate} -@JS() -@staticInterop -abstract class DOMStringList {} - -/// {@macro amplify_secure_storage_dart.dom_string_list} -extension PropsDOMStringList on DOMStringList { - /// Checks if the given string is in the list. - bool contains(String string) => - js_util.callMethod(this, 'contains', [string]); -} - -/// {@template amplify_secure_storage_dart.idb_version_change_event} -/// The IDBVersionChangeEvent interface of the IndexedDB API indicates that the -/// version of the database has changed, as the result of an `onupgradeneeded` -/// event handler function. -/// {@endtemplate} -@JS() -@staticInterop -abstract class IDBVersionChangeEvent extends Event {} - /// {@macro amplify_secure_storage_dart.idb_version_change_event} extension PropsIDBVersionChangeEvent on IDBVersionChangeEvent { /// The target of this event, the DB open request. - IDBOpenDBRequest get target => js_util.getProperty(this, 'target'); + IDBOpenDBRequest get target => getProperty('target'.toJS); } -/// {@template amplify_secure_storage_dart.idb_request} -/// The IDBRequest interface of the IndexedDB API provides access to results of -/// asynchronous requests to databases and database objects using event handler -/// attributes. -/// -/// Each reading and writing operation on a database is done using a request. -/// {@endtemplate} -@JS() -@staticInterop -abstract class IDBRequest {} - /// {@macro amplify_secure_storage_dart.idb_request} -extension PropsIDBRequest on IDBRequest { - /// The result of the request. - /// - /// If the request failed and the result is not available, an - /// `InvalidStateError` exception is thrown. - T get result => js_util.getProperty(this, 'result'); +extension PropsIDBRequest on IDBRequest { + /// Returns a [Future] which completes with the [result] of this request. + Future get future { + final completer = Completer.sync(); - /// Fired when an IDBRequest succeeds. - set onsuccess(EventHandler newValue) { - js_util.setProperty(this, 'onsuccess', allowInterop(newValue)); - } + void onSuccess(Event _) => completer.complete(result); - /// Fired when an error caused a request to fail. - set onerror(EventHandler newValue) { - js_util.setProperty(this, 'onerror', allowInterop(newValue)); - } + void onError(Event _) => + completer.completeError('Could not complete IDBRequest'.toJS); - /// Returns a [Future] which completes with the [result] of this request. - Future get future { - final completer = Completer.sync(); - onsuccess = (_) { - completer.complete(result); - }; - onerror = (_) { - completer.completeError('Could not complete IDBRequest'); - }; + onsuccess = onSuccess.toJS; + onerror = onError.toJS; return completer.future; } } -/// {@template amplify_secure_storage_dart.idb_open_db_request} -/// The IDBOpenDBRequest interface of the IndexedDB API provides access to the -/// results of requests to open or delete databases (performed using -/// `IDBFactory.open` and `IDBFactory.deleteDatabase`), using specific event -/// handler attributes. -/// {@endtemplate} -@JS() -@staticInterop -abstract class IDBOpenDBRequest extends IDBRequest {} - -/// {@macro amplify_secure_storage_dart.idb_open_db_request} -extension PropsIDBOpenDBRequest on IDBOpenDBRequest { - /// Fired when an attempt was made to open a database with a version number - /// higher than its current version. - set onupgradeneeded(EventHandler newValue) { - js_util.setProperty(this, 'onupgradeneeded', allowInterop(newValue)); - } -} - -/// {@template amplify_secure_storage_dart.idb_factory} -/// The IDBFactory interface of the IndexedDB API lets applications -/// asynchronously access the indexed databases. -/// {@endtemplate} -@JS() -@staticInterop -abstract class IDBFactory {} - -/// {@macro amplify_secure_storage_dart.idb_factory} -extension PropsIDBFactory on IDBFactory { - /// The current method to request opening a connection to a database. - IDBOpenDBRequest open(String name, [int? version]) => - js_util.callMethod(this, 'open', [name, if (version != null) version]); -} - -/// {@template amplify_secure_storage_dart.idb_database} -/// The IDBDatabase interface of the IndexedDB API provides a connection to a -/// database; you can use an IDBDatabase object to open a transaction on your -/// database then create, manipulate, and delete objects (data) in that -/// database. -/// {@endtemplate} -@JS() -@staticInterop -abstract class IDBDatabase {} - /// {@macro amplify_secure_storage_dart.idb_database} extension PropsIDBDatabase on IDBDatabase { - /// The list of the names of object stores in the database. - DOMStringList get objectStoreNames => - js_util.getProperty(this, 'objectStoreNames'); - - /// Returns a new transaction with the given mode (`readonly` or `readwrite`) - /// and scope which can be a single object store name or an array of names. - IDBTransaction transaction( - String storeNames, { - IDBTransactionMode mode = IDBTransactionMode.readonly, - }) => js_util.callMethod(this, 'transaction', [storeNames, mode.name]); - - /// Creates a new object store with the given name and options and returns a - /// new [IDBObjectStore]. - /// - /// Throws an `InvalidStateError` DOMException if not called within an upgrade - /// transaction. - IDBObjectStore createObjectStore( - String name, { - String? keyPath, - bool? autoIncrement, - }) { - final params = {}; - if (keyPath != null) { - params['keyPath'] = keyPath; - } - if (autoIncrement != null) { - params['autoIncrement'] = autoIncrement; - } - - return js_util.callMethod(this, 'createObjectStore', [ - name, - js_util.jsify(params), - ]); - } - /// Returns the object store for [storeName] in a new transaction. IDBObjectStore getObjectStore(String storeName) { final transaction = this.transaction( - storeName, - mode: IDBTransactionMode.readwrite, + storeName.toJS, + IDBTransactionMode.readwrite.jsValue!, ); final store = transaction.objectStore(storeName); return store; } } -/// {@template amplify_secure_storage_dart.idb_object_store} -/// The IDBObjectStore interface of the IndexedDB API represents an object store -/// in a database. -/// -/// Records within an object store are sorted according to their keys. -/// {@endtemplate} -@JS() -@staticInterop -abstract class IDBObjectStore {} - -/// {@macro amplify_secure_storage_dart.idb_object_store} -extension PropsIDBObjectStore on IDBObjectStore { - /// Returns an [IDBRequest] object, and, in a separate thread, creates a - /// structured clone of the value, and stores the cloned value in the object - /// store. - /// - /// This is for updating existing records in an object store when the - /// transaction's mode is `readwrite`. - IDBRequest put(String value, String key) => - js_util.callMethod(this, 'put', [value, key]); - - /// Returns an [IDBRequest] object, and, in a separate thread, creates a - /// structured clone of the value, and stores the cloned value in the object - /// store. - /// - /// This is for adding new records to an object store. - IDBRequest add(String value, String key) => - js_util.callMethod(this, 'add', [value, key]); - - /// Returns an [IDBRequest] object, and, in a separate thread, creates a - /// structured clone of the value, and stores the cloned value in the object - /// store. - /// - /// This is for adding new records to an object store created with keyPath set and autoincrement = true - IDBRequest push(Map item) => - js_util.callMethod(this, 'add', [js_util.jsify(item)]); - - /// Returns an [IDBRequest] object, and, in a separate thread, deletes the - /// store object selected by the specified key. - /// - /// This is for deleting individual records out of an object store. - IDBRequest delete(String query) => - js_util.callMethod(this, 'delete', [query]); - - /// Returns an [IDBRequest] object, and, in a separate thread, deletes the - /// store objects within the provided [IDBKeyRange]. - /// - /// This is for deleting ranges of records out of an object store. - IDBRequest deleteByKeyRange(IDBKeyRange range) => - js_util.callMethod(this, 'delete', [range]); - - /// Returns an [IDBRequest] object, and, in a separate thread, deletes all - /// store objects. - /// - /// This is for deleting all records in an object store. - IDBRequest clear() => js_util.callMethod(this, 'clear', []); - - /// Returns an [IDBRequest] object, and, in a separate thread, returns the - /// store object store selected by the specified key. - /// - /// This is for retrieving specific records from an object store. - IDBRequest getObject(String query) => - js_util.callMethod(this, 'get', [query]); - - /// Returns an [IDBRequest] object, and, in a separate thread, returns - /// [count] records from the object store. - /// - /// This is for retrieving a specific [count] of records from the object store. - IDBRequest> getAll(String? query, int? count) => - js_util.callMethod(this, 'getAll', [query, count]); -} - -/// {@template amplify_secure_storage_dart.idb_transaction} -/// The IDBTransaction interface of the IndexedDB API provides a static, -/// asynchronous transaction on a database using event handler attributes. -/// -/// All reading and writing of data is done within transactions. -/// {@endtemplate} -@JS() -@staticInterop -abstract class IDBTransaction {} - -/// {@macro amplify_secure_storage_dart.idb_transaction} -extension PropsIDBTransaction on IDBTransaction { - /// Returns an [IDBObjectStore] in the transaction's scope. - IDBObjectStore objectStore(String name) => - js_util.callMethod(this, 'objectStore', [name]); -} - /// The mode for isolating access to data in the object stores that are in the /// scope of an [IDBTransaction]. -enum IDBTransactionMode { +enum IDBTransactionMode with JSEnum { /// Allows data to be read but not changed. readonly, @@ -285,12 +68,3 @@ enum IDBTransactionMode { /// Transactions in this mode are known as "upgrade transactions." versionchange, } - -// ignore: avoid_classes_with_only_static_members -/// Represents an interval of some data type that is used for keys -@JS() -@staticInterop -abstract class IDBKeyRange { - /// Create key range with specified lower and upper bounds (inclusive) - external static IDBKeyRange bound(int lower, int upper); -} diff --git a/packages/aws_common/lib/src/js/promise.dart b/packages/aws_common/lib/src/js/promise.dart deleted file mode 100644 index 0313d01db77..00000000000 --- a/packages/aws_common/lib/src/js/promise.dart +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import 'dart:async'; - -//ignore: deprecated_member_use -import 'package:js/js.dart'; -//ignore: deprecated_member_use -import 'package:js/js_util.dart' as js_util; - -/// A [Promise] executor callback. -typedef Executor = - void Function(void Function(T) resolve, void Function(Object) reject); - -/// {@template aws_common.js.promise} -/// Represents the eventual completion (or failure) of an asynchronous operation -/// and its resulting value. -/// {@endtemplate} -@JS() -@staticInterop -abstract class Promise { - /// Creates a JS Promise. - factory Promise(Executor executor) => Promise._(allowInterop(executor)); - - external factory Promise._(Executor executor); - - /// Creates a Promise from a Dart [future]. - /// - /// If [captureError] is `true`, all errors will be caught by the promise - /// and not reported as unhandled errors in the current [Zone]. This can - /// decrease the visibility of errors in Dart code depending on the level of - /// integration with JS APIs and their error-handling specifics. - factory Promise.fromFuture(Future future, {bool captureError = false}) => - Promise((resolve, reject) async { - try { - resolve(await future); - } on Object catch (e) { - reject(e); - if (!captureError) { - rethrow; - } - } - }); -} - -/// {@macro aws_common.js.promise} -extension PropsPromise on Promise { - /// Resolves `this` as a Dart [Future]. - Future get future => js_util.promiseToFuture(this); -} diff --git a/packages/aws_common/lib/src/js/readable_stream.dart b/packages/aws_common/lib/src/js/readable_stream.dart index 679b21bad76..3bb1eeff687 100644 --- a/packages/aws_common/lib/src/js/readable_stream.dart +++ b/packages/aws_common/lib/src/js/readable_stream.dart @@ -2,129 +2,108 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:aws_common/src/js/common.dart'; -import 'package:aws_common/src/js/promise.dart'; -//ignore: deprecated_member_use -import 'package:js/js.dart'; -//ignore: deprecated_member_use -import 'package:js/js_util.dart' as js_util; +import 'package:web/web.dart'; /// {@template aws_common.js.readable_stream} /// An object containing methods and properties that define how the constructed /// [ReadableStream] will behave. /// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class UnderlyingSource { +extension type UnderlyingSource._(JSObject _) + implements JSObject { /// {@macro aws_common.js.readable_stream} factory UnderlyingSource({ - /// This is a method, called immediately when the object is constructed. - /// - /// The contents of this method are defined by the developer, and should aim - /// to get access to the stream source, and do anything else required to set - /// up the stream functionality. If this process is to be done - /// asynchronously, it can return a promise to signal success or failure. - /// - /// The `controller` parameter passed to this method is a - /// [ReadableStreamDefaultController] or a [ReadableByteStreamController], - /// depending on the value of the `type` property. This can be used by the - /// developer to control the stream during set up. - FutureOr Function(ReadableStreamController controller)? start, - - /// This method, also defined by the developer, will be called repeatedly - /// when the stream's internal queue of chunks is not full, up until it - /// reaches its high water mark. - /// - /// If pull() returns a promise, then it won't be called again until that - /// promise fulfills; if the promise rejects, the stream will become - /// errored. - /// - /// The `controller` parameter passed to this method is a - /// [ReadableStreamDefaultController] or a [ReadableByteStreamController], - /// depending on the value of the type property. This can be used by the - /// developer to control the stream as more chunks are fetched. - FutureOr Function(ReadableStreamController controller)? pull, - - /// This method, also defined by the developer, will be called if the app - /// signals that the stream is to be cancelled - /// (e.g. if `ReadableStream.cancel` is called). - /// - /// The contents should do whatever is necessary to release access to the - /// stream source. If this process is asynchronous, it can return a promise - /// to signal success or failure. The reason parameter contains a - /// `DOMString` describing why the stream was cancelled. - FutureOr Function([ - String? reason, - ReadableStreamController? controller, - ])? - cancel, - - /// This property controls what type of readable stream is being dealt with. + Future Function(ReadableStreamDefaultController controller)? start, + Future Function(ReadableStreamDefaultController controller)? pull, + Future Function([JSString? reason])? cancel, ReadableStreamType type = ReadableStreamType.default$, - - /// For byte streams, the developer can set the autoAllocateChunkSize with - /// a positive integer value to turn on the stream's auto-allocation - /// feature. - /// - /// With this turned on, the stream implementation will automatically - /// allocate an `ArrayBuffer` with a size of the given integer, and the - /// consumer can also use a default reader. int? autoAllocateChunkSize, }) { - final startFn = - start == null - ? undefined - : start is Future Function(ReadableStreamController) - ? allowInterop((ReadableStreamController controller) { - return Promise.fromFuture(start(controller)); - }) - : allowInterop(start); - final pullFn = - pull == null - ? undefined - : pull is Future Function(ReadableStreamController) - ? allowInterop((ReadableStreamController controller) { - return Promise.fromFuture(pull(controller)); - }) - : allowInterop(pull); - final cancelFn = - cancel == null - ? undefined - : cancel - is Future Function([ - String? reason, - ReadableStreamController? controller, - ]) - ? allowInterop(( - String? reason, - ReadableStreamController? controller, - ) { - return Promise.fromFuture(cancel(reason, controller)); - }) - : allowInterop(cancel); - return UnderlyingSource._( - start: startFn, - pull: pullFn, - cancel: cancelFn, - type: type.jsValue, - autoAllocateChunkSize: autoAllocateChunkSize ?? undefined, + JSPromise promiseStart(ReadableStreamDefaultController controller) { + return start!.call(controller).toJS; + } + + JSPromise promisePull(ReadableStreamDefaultController controller) { + return pull!.call(controller).toJS; + } + + JSPromise promiseCancel([JSString? reason]) { + return cancel!.call(reason).toJS; + } + + return UnderlyingSource.__( + start: start == null ? undefined : promiseStart.toJS, + pull: pull == null ? undefined : promisePull.toJS, + cancel: cancel == null ? undefined : promiseCancel.toJS, + type: type.jsValue?.toJS ?? undefined, + autoAllocateChunkSize: autoAllocateChunkSize?.toJS ?? undefined, ); } - external factory UnderlyingSource._({ - Object? start, - Object? pull, - Object? cancel, - String? type, - int? autoAllocateChunkSize, + external factory UnderlyingSource.__({ + JSFunction? start, + JSFunction? pull, + JSFunction? cancel, + JSAny? type, + JSAny? autoAllocateChunkSize, }); + + /// This is a method, called immediately when the object is constructed. + /// + /// The contents of this method are defined by the developer, and should aim + /// to get access to the stream source, and do anything else required to set + /// up the stream functionality. If this process is to be done + /// asynchronously, it can return a promise to signal success or failure. + /// + /// The `controller` parameter passed to this method is a + /// [ReadableStreamDefaultController] or a [ReadableByteStreamController], + /// depending on the value of the `type` property. This can be used by the + /// developer to control the stream during set up. + external JSFunction? start; + + /// This method, also defined by the developer, will be called repeatedly + /// when the stream's internal queue of chunks is not full, up until it + /// reaches its high water mark. + /// + /// If pull() returns a promise, then it won't be called again until that + /// promise fulfills; if the promise rejects, the stream will become + /// errored. + /// + /// The `controller` parameter passed to this method is a + /// [ReadableStreamDefaultController] or a [ReadableByteStreamController], + /// depending on the value of the type property. This can be used by the + /// developer to control the stream as more chunks are fetched. + external JSFunction? pull; + + /// This method, also defined by the developer, will be called if the app + /// signals that the stream is to be cancelled + /// (e.g. if `ReadableStream.cancel` is called). + /// + /// The contents should do whatever is necessary to release access to the + /// stream source. If this process is asynchronous, it can return a promise + /// to signal success or failure. The reason parameter contains a + /// `DOMString` describing why the stream was cancelled. + external JSFunction? cancel; + + /// This property controls what type of readable stream is being dealt with. + external JSString? type; + + /// For byte streams, the developer can set the autoAllocateChunkSize with + /// a positive integer value to turn on the stream's auto-allocation + /// feature. + /// + /// With this turned on, the stream implementation will automatically + /// allocate an `ArrayBuffer` with a size of the given integer, and the + /// consumer can also use a default reader. + external JSNumber? autoAllocateChunkSize; } -/// The type of [ReadableStream] and its associated [ReadableStreamController]. +/// The type of [ReadableStream] and its associated [ReadableStreamDefaultController]. enum ReadableStreamType with JSEnum { /// Creates a [ReadableByteStreamController] capable of handling a BYOB /// (bring your own buffer)/byte stream. @@ -134,57 +113,6 @@ enum ReadableStreamType with JSEnum { default$, } -/// {@template aws_common.js.readable_stream_controller} -/// Interface for accessing the internal state/queue of a [ReadableStream]. -/// -/// Similar to a Dart [StreamController]. -/// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableStreamController {} - -/// {@macro aws_common.js.readable_stream_controller} -extension PropsReadableStreamController on ReadableStreamController { - /// The desired size required to fill the stream's internal queue. - external int get desiredSize; - - /// Closes the associated stream. - external void close(); - - /// Enqueues a given chunk in the associated stream. - external void enqueue(Uint8List chunk); -} - -/// {@template aws_common.js.readable_stream_default_controller} -/// A default [ReadableStreamController], for [ReadableStream]s which are not -/// byte streams. -/// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableStreamDefaultController - extends ReadableStreamController {} - -/// {@template aws_common.js.readable_byte_stream_controller} -/// A [ReadableStreamController] for [ReadableStream]s which are not -/// byte streams. -/// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableByteStreamController extends ReadableStreamController {} - -/// {@template aws_common.js.readable_stream} -/// Represents a readable stream of byte data. -/// {@endtemplate} -@JS() -@staticInterop -abstract class ReadableStream { - /// {@macro aws_common.js.readable_stream} - external factory ReadableStream([UnderlyingSource? underlyingSource]); -} - /// Used to expand [ReadableStream] and treat `ReadableStream.stream` as a /// `late final` property so that multiple accesses return the same value. final Expando _readableStreamViews = Expando( @@ -193,25 +121,6 @@ final Expando _readableStreamViews = Expando( /// {@macro aws_common.js.readable_stream} extension PropsReadableStream on ReadableStream { - /// Whether or not the readable stream is locked to a reader. - external bool get locked; - - /// Returns a Promise that resolves when the stream is canceled. - /// - /// Calling this method signals a loss of interest in the stream by a - /// consumer. The supplied reason argument will be given to the underlying - /// source, which may or may not use it. - Future cancel([String? reason]) => - js_util.promiseToFuture(js_util.callMethod(this, 'cancel', [reason])); - - /// Creates a reader and locks the stream to it. - /// - /// While the stream is locked, no other reader can be acquired until this one - /// is released. - ReadableStreamReader getReader({ - ReadableStreamReaderMode mode = ReadableStreamReaderMode.default$, - }) => js_util.callMethod(this, 'getReader', [mode.jsValue]); - /// Creates a Dart [Stream] from `this`. ReadableStreamView get stream => _readableStreamViews[this] ??= ReadableStreamView(this); @@ -220,14 +129,6 @@ extension PropsReadableStream on ReadableStream { Stream get progress => stream.progress; } -/// {@template aws_common.js.readable_stream_reader} -/// Interface for reading data from a [ReadableStream]. -/// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableStreamReader {} - /// {@macro aws_common.js.readable_stream_reader} extension PropsReadableStreamReader on ReadableStreamReader { /// Returns a Promise that fulfills when the stream closes, or rejects if the @@ -235,8 +136,7 @@ extension PropsReadableStreamReader on ReadableStreamReader { /// /// This property enables you to write code that responds to an end to the /// streaming process. - Future get closed => - js_util.promiseToFuture(js_util.getProperty(this, 'closed')); + Future get closed => getProperty('closed'.toJS).toDart; /// Returns a Promise that resolves when the stream is canceled. /// @@ -244,67 +144,33 @@ extension PropsReadableStreamReader on ReadableStreamReader { /// consumer. The supplied reason argument will be given to the underlying /// source, which may or may not use it. Future cancel([String? reason]) => - js_util.promiseToFuture(js_util.callMethod(this, 'cancel', [reason])); + getProperty('cancel'.toJS).toDart; /// Releases the reader's lock on the stream. external void releaseLock(); } -/// {@template aws_common.js.readable_stream_byob_reader} -/// A reader for a [ReadableStream] that supports zero-copy reading from an -/// underlying byte source. -/// -/// It is used for efficient copying from underlying sources where the data is -/// delivered as an "anonymous" sequence of bytes, such as files. -/// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableStreamBYOBReader extends ReadableStreamReader {} - /// {@template aws_common.js.readable_stream_default_reader} /// A default reader that can be used to read stream data supplied from a /// network (such as a fetch request). /// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableStreamDefaultReader extends ReadableStreamReader {} - -/// {@macro aws_common.js.readable_stream_default_reader} -extension PropsReadableStreamDefaultReader on ReadableStreamDefaultReader { +extension type ReadableStreamDefaultReader._(JSObject _) + implements ReadableStreamReader { /// Returns a promise providing access to the next chunk in the stream's /// internal queue. Future read() => - js_util.promiseToFuture(js_util.callMethod(this, 'read', [])); -} - -/// Specifies the type of [ReadableStreamReader] to create. -enum ReadableStreamReaderMode with JSEnum { - /// Results in a [ReadableStreamBYOBReader] being created that can read - /// readable byte streams (i.e. can handle "bring your own buffer" reading). - byob, - - /// Results in a [ReadableStreamDefaultReader] being created that can read - /// individual chunks from a stream. - default$, + callMethod>('read'.toJS).toDart; } /// {@template aws_common.js.readable_stream_chunk} /// A chunk in a [ReadableStream]'s internal queue, obtained using a /// [ReadableStreamReader]. /// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableStreamChunk {} - -/// {@macro aws_common.js.readable_stream_chunk} -extension PropsReadableStreamChunk on ReadableStreamChunk { +extension type ReadableStreamChunk._(JSObject _) implements JSObject { /// The chunk of data. /// /// Always `null` when [done] is `true`. - external Uint8List? get value; + external JSUint8Array? get value; /// Whether the stream is done producing values. external bool get done; @@ -346,10 +212,11 @@ final class ReadableStreamView extends StreamView> { var bytesRead = 0; while (true) { final chunk = await reader.read(); - final value = chunk.value; - if (chunk.done || value == null) { + final jsValue = chunk.value; + if (chunk.done || jsValue == null) { break; } + final value = jsValue.toDart; bytesRead += value.length; sink.add(value); progressSink.add(bytesRead); @@ -372,34 +239,33 @@ extension StreamToReadableStream on Stream> { void Function(Object, StackTrace)? onError, }) { final queue = StreamQueue(this); - return ReadableStream( - UnderlyingSource( - pull: (controller) async { - if (!await queue.hasNext) { - await queue.cancel(); - controller.close(); - return; - } + Future pull(ReadableStreamDefaultController controller) async { + if (!await queue.hasNext) { + await queue.cancel(); + controller.close(); + return; + } + try { + final chunk = await queue.next; + controller.enqueue(Uint8List.fromList(chunk).toJS); + } on Object catch (e, st) { + await queue.cancel(); + // Allow error to propagate before closing. + scheduleMicrotask(() { try { - final chunk = await queue.next; - controller.enqueue(Uint8List.fromList(chunk)); - } on Object catch (e, st) { - await queue.cancel(); - // Allow error to propagate before closing. - scheduleMicrotask(() { - try { - controller.close(); - } on Object { - // ignore errors closing the controller - } - }); - if (onError == null) { - rethrow; - } - onError.call(e, st); + controller.close(); + } on Object { + // ignore errors closing the controller } - }, - ), - ); + }); + if (onError == null) { + controller.error(e.toString().toJS); + rethrow; + } + onError.call(e, st); + } + } + + return ReadableStream(UnderlyingSource(pull: pull)); } } diff --git a/packages/aws_common/lib/src/util/get_base_element_href_from_dom.dart b/packages/aws_common/lib/src/util/get_base_element_href_from_dom.dart index 9b754526f02..7d2e80f9fae 100644 --- a/packages/aws_common/lib/src/util/get_base_element_href_from_dom.dart +++ b/packages/aws_common/lib/src/util/get_base_element_href_from_dom.dart @@ -1,8 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -//ignore: deprecated_member_use -import 'dart:html'; +import 'package:web/web.dart'; /// Returns the href attribute of the base element if it is present. /// diff --git a/packages/aws_common/pubspec.yaml b/packages/aws_common/pubspec.yaml index 78506070c00..f74f5e21396 100644 --- a/packages/aws_common/pubspec.yaml +++ b/packages/aws_common/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: built_collection: ^5.0.0 built_value: ^8.10.1 collection: ^1.15.0 + http: ^1.3.0 http2: ^2.0.0 js: ">=0.6.4 <0.8.0" json_annotation: ">=4.9.0 <4.10.0" @@ -23,6 +24,7 @@ dependencies: path: ">=1.8.0 <2.0.0" stream_transform: ^2.0.0 uuid: ">=3.0.6 <5.0.0" + web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.2 <3.2.0" diff --git a/packages/aws_common/test/http/cancellation_test.dart b/packages/aws_common/test/http/cancellation_test.dart index 6e4005752e4..c17faef46ce 100644 --- a/packages/aws_common/test/http/cancellation_test.dart +++ b/packages/aws_common/test/http/cancellation_test.dart @@ -7,7 +7,7 @@ import 'package:aws_common/aws_common.dart'; import 'package:test/test.dart'; import 'cancellation_server_vm.dart' - if (dart.library.js) 'cancellation_server_web.dart'; + if (dart.library.js_interop) 'cancellation_server_web.dart'; import 'http_common.dart'; void main() { diff --git a/packages/aws_common/test/http/client_conformance_tests/redirect_server_test.dart b/packages/aws_common/test/http/client_conformance_tests/redirect_server_test.dart index 02d8ecf153f..834c9a327ca 100644 --- a/packages/aws_common/test/http/client_conformance_tests/redirect_server_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/redirect_server_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'redirect_server_vm.dart' - if (dart.library.js) 'redirect_server_web.dart'; + if (dart.library.js_interop) 'redirect_server_web.dart'; /// Tests that the [AWSHttpClient] correctly implements HTTP redirect logic. void main() { diff --git a/packages/aws_common/test/http/client_conformance_tests/request_body_streamed_test.dart b/packages/aws_common/test/http/client_conformance_tests/request_body_streamed_test.dart index a3f678f53e9..e08b72d370a 100644 --- a/packages/aws_common/test/http/client_conformance_tests/request_body_streamed_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/request_body_streamed_test.dart @@ -11,7 +11,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'request_body_streamed_server_vm.dart' - if (dart.library.js) 'request_body_streamed_server_web.dart'; + if (dart.library.js_interop) 'request_body_streamed_server_web.dart'; /// Tests that the [AWSHttpClient] correctly implements streamed request body /// uploading. diff --git a/packages/aws_common/test/http/client_conformance_tests/request_body_test.dart b/packages/aws_common/test/http/client_conformance_tests/request_body_test.dart index 524b15565de..e8c0651a324 100644 --- a/packages/aws_common/test/http/client_conformance_tests/request_body_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/request_body_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'request_body_server_vm.dart' - if (dart.library.js) 'request_body_server_web.dart'; + if (dart.library.js_interop) 'request_body_server_web.dart'; class _Plus2Decoder extends Converter, String> { @override diff --git a/packages/aws_common/test/http/client_conformance_tests/request_headers_test.dart b/packages/aws_common/test/http/client_conformance_tests/request_headers_test.dart index f66fd3bb3ae..6bf29aec4a0 100644 --- a/packages/aws_common/test/http/client_conformance_tests/request_headers_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/request_headers_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'request_headers_server_vm.dart' - if (dart.library.js) 'request_headers_server_web.dart'; + if (dart.library.js_interop) 'request_headers_server_web.dart'; /// Tests that the [AWSHttpClient] correctly sends headers in the request. void main() { diff --git a/packages/aws_common/test/http/client_conformance_tests/response_body_streamed_test.dart b/packages/aws_common/test/http/client_conformance_tests/response_body_streamed_test.dart index 13992462010..b44763cd178 100644 --- a/packages/aws_common/test/http/client_conformance_tests/response_body_streamed_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/response_body_streamed_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'response_body_streamed_server_vm.dart' - if (dart.library.js) 'response_body_streamed_server_web.dart'; + if (dart.library.js_interop) 'response_body_streamed_server_web.dart'; /// Tests that the [AWSHttpClient] correctly implements HTTP responses with /// bodies of unbounded size. diff --git a/packages/aws_common/test/http/client_conformance_tests/response_body_test.dart b/packages/aws_common/test/http/client_conformance_tests/response_body_test.dart index 61be9dd1491..548d43f780a 100644 --- a/packages/aws_common/test/http/client_conformance_tests/response_body_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/response_body_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'response_body_server_vm.dart' - if (dart.library.js) 'response_body_server_web.dart'; + if (dart.library.js_interop) 'response_body_server_web.dart'; /// Tests that the [AWSHttpClient] correctly implements HTTP responses with /// bodies. diff --git a/packages/aws_common/test/http/client_conformance_tests/response_headers_test.dart b/packages/aws_common/test/http/client_conformance_tests/response_headers_test.dart index 3308158c9db..d609e110f88 100644 --- a/packages/aws_common/test/http/client_conformance_tests/response_headers_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/response_headers_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'response_headers_server_vm.dart' - if (dart.library.js) 'response_headers_server_web.dart'; + if (dart.library.js_interop) 'response_headers_server_web.dart'; /// Tests that the [AWSHttpClient] correctly processes response headers. void main() { diff --git a/packages/aws_common/test/http/client_conformance_tests/server_errors_test.dart b/packages/aws_common/test/http/client_conformance_tests/server_errors_test.dart index 8ff9e76429a..5e48a38dc10 100644 --- a/packages/aws_common/test/http/client_conformance_tests/server_errors_test.dart +++ b/packages/aws_common/test/http/client_conformance_tests/server_errors_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import '../http_common.dart'; import 'server_errors_server_vm.dart' - if (dart.library.js) 'server_errors_server_web.dart'; + if (dart.library.js_interop) 'server_errors_server_web.dart'; /// Tests that the [AWSHttpClient] correctly handles server errors. void main() { diff --git a/packages/aws_common/test/http/downgrade_test.dart b/packages/aws_common/test/http/downgrade_test.dart index 0eac7511b25..cf5264f2203 100644 --- a/packages/aws_common/test/http/downgrade_test.dart +++ b/packages/aws_common/test/http/downgrade_test.dart @@ -7,7 +7,7 @@ import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; import 'downgrade_server_vm.dart' - if (dart.library.html) 'downgrade_server_web.dart'; + if (dart.library.js_interop) 'downgrade_server_web.dart'; import 'http_common.dart'; void main() { diff --git a/packages/aws_common/test/io/aws_file_html_test.dart b/packages/aws_common/test/io/aws_file_html_test.dart index 1122c79c91e..d2ad6da3ae5 100644 --- a/packages/aws_common/test/io/aws_file_html_test.dart +++ b/packages/aws_common/test/io/aws_file_html_test.dart @@ -6,13 +6,13 @@ library; import 'dart:async'; import 'dart:convert'; -//ignore: deprecated_member_use -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:aws_common/aws_common.dart'; import 'package:aws_common/web.dart'; import 'package:test/test.dart'; +import 'package:web/web.dart'; import 'utils.dart'; @@ -22,13 +22,16 @@ void main() { const testContentType = 'text/plain'; final testBytes = utf8.encode(testStringContent); final testBytesUtf16 = testStringContent.codeUnits; - final testBlob = html.Blob([testBytes], testContentType); - final testFile = html.File( - [testBlob], + final testBlob = Blob( + [testBytes.toJS].toJS, + BlobPropertyBag(type: testContentType), + ); + final testFile = File( + [testBlob].toJS, 'test_file.txt', - {'type': testBlob.type}, + FilePropertyBag(type: testBlob.type), ); - final testFilePath = html.Url.createObjectUrl(testFile); + final testFilePath = URL.createObjectURL(testFile); group('getChunkedStreamReader() API', () { test('should return ChunkedStreamReader over html File', () async { diff --git a/packages/aws_common/test/js/readable_stream_test.dart b/packages/aws_common/test/js/readable_stream_test.dart index f7bd9593220..2520b44177b 100644 --- a/packages/aws_common/test/js/readable_stream_test.dart +++ b/packages/aws_common/test/js/readable_stream_test.dart @@ -5,23 +5,23 @@ library; import 'dart:async'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:aws_common/src/js/readable_stream.dart'; import 'package:test/test.dart'; +import 'package:web/web.dart'; void main() { ReadableStream createReadableStream() { - return ReadableStream( - UnderlyingSource( - start: (controller) { - controller - ..enqueue(Uint8List.fromList([1, 2, 3, 4, 5])) - ..enqueue(Uint8List.fromList([6, 7, 8, 9, 0])) - ..close(); - }, - ), - ); + Future start(ReadableStreamDefaultController controller) async { + controller + ..enqueue(Uint8List.fromList([1, 2, 3, 4, 5]).toJS) + ..enqueue(Uint8List.fromList([6, 7, 8, 9, 0]).toJS) + ..close(); + } + + return ReadableStream(UnderlyingSource(start: start)); } group('ReadableStreamWrapper', () { @@ -43,7 +43,7 @@ void main() { }); group('asReadableStream (async)', () { - test('', () { + test('no Errors', () { final stream = Stream.fromIterable([ [1, 2, 3, 4, 5], [6, 7, 8, 9, 0], diff --git a/packages/common/amplify_db_common_dart/lib/src/connect.dart b/packages/common/amplify_db_common_dart/lib/src/connect.dart index c795b8a84a0..56822f0db64 100644 --- a/packages/common/amplify_db_common_dart/lib/src/connect.dart +++ b/packages/common/amplify_db_common_dart/lib/src/connect.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:drift/backends.dart'; export 'connect_stub.dart' - if (dart.library.html) 'connect_html.dart' + if (dart.library.js_interop) 'connect_html.dart' if (dart.library.io) 'connect_io.dart'; /// Interface of the Drift DB connect function. diff --git a/packages/secure_storage/amplify_secure_storage/lib/amplify_secure_storage.dart b/packages/secure_storage/amplify_secure_storage/lib/amplify_secure_storage.dart index 9232f0af1f4..77fac35f5c2 100644 --- a/packages/secure_storage/amplify_secure_storage/lib/amplify_secure_storage.dart +++ b/packages/secure_storage/amplify_secure_storage/lib/amplify_secure_storage.dart @@ -6,4 +6,4 @@ library; export 'package:amplify_secure_storage_dart/amplify_secure_storage_dart.dart'; export 'src/amplify_secure_storage.vm.dart' - if (dart.library.html) 'src/amplify_secure_storage.web.dart'; + if (dart.library.js_interop) 'src/amplify_secure_storage.web.dart'; diff --git a/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.dart b/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.dart index 5ea3b3f2d68..3d4761be681 100644 --- a/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.dart +++ b/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.dart @@ -3,5 +3,5 @@ // wrapper around web and desktop mixins export 'amplify_secure_storage_mixin.stub.dart' - if (dart.library.html) 'amplify_secure_storage_mixin.web.dart' + if (dart.library.js_interop) 'amplify_secure_storage_mixin.web.dart' if (dart.library.io) 'amplify_secure_storage_mixin.vm.dart'; diff --git a/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.stub.dart b/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.stub.dart index f92f1d698e6..6e837f1596e 100644 --- a/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.stub.dart +++ b/packages/secure_storage/amplify_secure_storage_dart/lib/src/mixins/amplify_secure_storage_mixin.stub.dart @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Note: This mixin is a stub -// This is only used if both dart.library.html and dart.library.io are not available +// This is only used if both dart.library.js_interop and dart.library.io are not available // See the implementations in the web/desktop dir which will be used on those platforms import 'dart:async'; diff --git a/packages/secure_storage/amplify_secure_storage_dart/lib/src/platforms/amplify_secure_storage_web.dart b/packages/secure_storage/amplify_secure_storage_dart/lib/src/platforms/amplify_secure_storage_web.dart index 1ab958cecaa..0eda62e4045 100644 --- a/packages/secure_storage/amplify_secure_storage_dart/lib/src/platforms/amplify_secure_storage_web.dart +++ b/packages/secure_storage/amplify_secure_storage_dart/lib/src/platforms/amplify_secure_storage_web.dart @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'package:amplify_secure_storage_dart/amplify_secure_storage_dart.dart'; import 'package:amplify_secure_storage_dart/src/exception/not_available_exception.dart'; @@ -9,6 +11,7 @@ import 'package:amplify_secure_storage_dart/src/exception/secure_storage_excepti import 'package:amplify_secure_storage_dart/src/platforms/amplify_secure_storage_in_memory.dart'; // ignore: implementation_imports import 'package:aws_common/src/js/indexed_db.dart'; +import 'package:web/web.dart'; /// The web implementation of [SecureStorageInterface]. class AmplifySecureStorageWeb extends AmplifySecureStorageInterface { @@ -90,16 +93,25 @@ class _IndexedDBStorage extends AmplifySecureStorageInterface { recoverySuggestion: SecureStorageException.missingRecovery, ); } + + void onUpgradeNeeded(IDBVersionChangeEvent event) { + final database = event.target?.getProperty('result'.toJS); + final objectStoreNames = database?.objectStoreNames; + if (!(objectStoreNames?.contains(storeName) ?? false)) { + database?.createObjectStore(storeName); + } + } + final openRequest = indexedDB!.open(databaseName, 1) - ..onupgradeneeded = (event) { - final database = event.target.result; - final objectStoreNames = database.objectStoreNames; - if (!objectStoreNames.contains(storeName)) { - database.createObjectStore(storeName); - } - }; + ..onupgradeneeded = onUpgradeNeeded.toJS; + try { - return await openRequest.future; + final result = await openRequest.future; + if (result.isA()) { + return result as IDBDatabase; + } else { + throw Exception('IDBOpenDBRequest failed'); + } } on Object catch (e) { throw SecureStorageException(e.toString()); } @@ -110,7 +122,7 @@ class _IndexedDBStorage extends AmplifySecureStorageInterface { final database = await _databaseFuture; final store = database.getObjectStore(storeName); try { - await store.put(value, key).future; + await store.put(value.toJS, key.toJS).future; } on Object catch (e) { throw SecureStorageException(e.toString()); } @@ -121,8 +133,8 @@ class _IndexedDBStorage extends AmplifySecureStorageInterface { final database = await _databaseFuture; final store = database.getObjectStore(storeName); try { - final value = await store.getObject(key).future; - return value; + final result = store.get(key.toJS).future; + return result.then((value) => (value as JSString?)?.toDart); } on Object catch (e) { throw SecureStorageException(e.toString()); } @@ -133,7 +145,7 @@ class _IndexedDBStorage extends AmplifySecureStorageInterface { final database = await _databaseFuture; final store = database.getObjectStore(storeName); try { - await store.delete(key).future; + await store.delete(key.toJS).future; } on Object catch (e) { throw SecureStorageException(e.toString()); } diff --git a/packages/secure_storage/amplify_secure_storage_dart/lib/src/worker/secure_storage_worker.worker.dart b/packages/secure_storage/amplify_secure_storage_dart/lib/src/worker/secure_storage_worker.worker.dart index 154a047603c..3033ee6fe50 100644 --- a/packages/secure_storage/amplify_secure_storage_dart/lib/src/worker/secure_storage_worker.worker.dart +++ b/packages/secure_storage/amplify_secure_storage_dart/lib/src/worker/secure_storage_worker.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'secure_storage_worker.worker.vm.dart' - if (dart.library.js) 'secure_storage_worker.worker.js.dart'; + if (dart.library.js_interop) 'secure_storage_worker.worker.js.dart'; diff --git a/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml b/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml index ea7ad0a4cb5..f7c15a25a7d 100644 --- a/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml +++ b/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: js: ">=0.6.4 <0.8.0" meta: ^1.16.0 path: ">=1.8.0 <2.0.0" + web: ^1.1.1 win32: ">=4.1.2 <6.0.0" worker_bee: ">=0.3.5 <0.4.0" @@ -37,8 +38,8 @@ dev_dependencies: amplify_secure_storage_test: path: ../amplify_secure_storage_test build: ^2.3.0 - build_runner: ^2.4.9 - build_web_compilers: ^4.0.0 + build_runner: ^2.4.15 + build_web_compilers: ^4.1.4 built_value_generator: ^8.10.1 ffigen: ^9.0.0 test: ^1.22.1 diff --git a/packages/storage/amplify_storage_s3/example/integration_test/main_test.dart b/packages/storage/amplify_storage_s3/example/integration_test/main_test.dart index e392b409282..d61e4befcca 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/main_test.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/main_test.dart @@ -11,7 +11,7 @@ import 'get_properties_test.dart' as get_properties_test; import 'get_url_test.dart' as get_url_test; import 'list_test.dart' as list_tests; import 'platform_test_io.dart' - if (dart.library.html) 'platform_test_html.dart' + if (dart.library.js_interop) 'platform_test_html.dart' as platform_test; import 'remove_many_test.dart' as remove_many_test; import 'remove_test.dart' as remove_test; diff --git a/packages/storage/amplify_storage_s3/example/integration_test/platform_test_html.dart b/packages/storage/amplify_storage_s3/example/integration_test/platform_test_html.dart index 55e1398719b..568050bd414 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/platform_test_html.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/platform_test_html.dart @@ -23,7 +23,7 @@ void main() { final path = 'public/upload-file-from-html-file-$fileId'; const content = 'upload data'; final data = content.codeUnits; - final file = await createHtmlFile(path: fileId, content: content); + final file = await createHtmlFile(path: path, content: content); addTearDownPath(StoragePath.fromString(path)); final result = await Amplify.Storage.uploadFile( diff --git a/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file.dart b/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file.dart index 14a63be566a..c525d6e2b2b 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'create_file_stub.dart' - if (dart.library.html) 'create_file_html.dart' + if (dart.library.js_interop) 'create_file_html.dart' if (dart.library.io) 'create_file_io.dart'; diff --git a/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file_html.dart b/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file_html.dart index d3d8b9de6b7..6ce3a0faca4 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file_html.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file_html.dart @@ -2,27 +2,32 @@ // SPDX-License-Identifier: Apache-2.0 // ignore: deprecated_member_use, avoid_web_libraries_in_flutter -import 'dart:html' as html; +import 'dart:js_interop'; + +import 'package:web/web.dart'; Future createFile({ required String path, required String content, String contentType = 'text/plain', }) async { - final file = await createHtmlFile( - path: path, - content: content, - contentType: contentType, - ); - return html.Url.createObjectUrl(file); + await createHtmlFile(path: path, content: content, contentType: contentType); + return path; } -Future createHtmlFile({ +Future createHtmlFile({ required String path, required String content, String contentType = 'text/plain', }) async { - final fileBlob = html.Blob([content], contentType); - final file = html.File([fileBlob], path, {'type': fileBlob.type}); + final fileBlob = Blob( + [content.toJS].toJS, + BlobPropertyBag(type: contentType), + ); + final file = File( + [fileBlob].toJS, + path, + FilePropertyBag(type: fileBlob.type), + ); return file; } diff --git a/packages/storage/amplify_storage_s3/example/pubspec.yaml b/packages/storage/amplify_storage_s3/example/pubspec.yaml index dadbe1867ad..b042c44f2b4 100644 --- a/packages/storage/amplify_storage_s3/example/pubspec.yaml +++ b/packages/storage/amplify_storage_s3/example/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: sdk: flutter go_router: ^6.5.7 path_provider: any + web: ^1.1.1 dev_dependencies: amplify_integration_test: any diff --git a/packages/storage/amplify_storage_s3/lib/src/utils/app_path_provider/app_path_provider.dart b/packages/storage/amplify_storage_s3/lib/src/utils/app_path_provider/app_path_provider.dart index 646ebc61d79..a9d543d441f 100644 --- a/packages/storage/amplify_storage_s3/lib/src/utils/app_path_provider/app_path_provider.dart +++ b/packages/storage/amplify_storage_s3/lib/src/utils/app_path_provider/app_path_provider.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'app_path_provider_stub.dart' - if (dart.library.html) 'app_path_provider_html.dart' + if (dart.library.js_interop) 'app_path_provider_html.dart' if (dart.library.io) 'app_path_provider_io.dart'; diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/download_file.dart b/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/download_file.dart index 0df8b37e65a..efe58cd8d7e 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/download_file.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/download_file.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'download_file_stub.dart' - if (dart.library.html) 'download_file_html.dart' + if (dart.library.js_interop) 'download_file_html.dart' if (dart.library.io) 'download_file_io.dart'; diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/transfer.dart b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/transfer.dart index 12e9a9bb627..b86a0fa5221 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/transfer.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/transfer.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'database/database_stub.dart' - if (dart.library.html) 'database/database_html.dart' + if (dart.library.js_interop) 'database/database_html.dart' if (dart.library.io) 'database/database_io.dart'; diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/utils/app_path_provider/app_path_provider.dart b/packages/storage/amplify_storage_s3_dart/lib/src/utils/app_path_provider/app_path_provider.dart index 646ebc61d79..a9d543d441f 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/utils/app_path_provider/app_path_provider.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/utils/app_path_provider/app_path_provider.dart @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export 'app_path_provider_stub.dart' - if (dart.library.html) 'app_path_provider_html.dart' + if (dart.library.js_interop) 'app_path_provider_html.dart' if (dart.library.io) 'app_path_provider_io.dart'; diff --git a/packages/worker_bee/e2e/lib/e2e_worker.worker.dart b/packages/worker_bee/e2e/lib/e2e_worker.worker.dart index 91c679af346..5c53f44e6b4 100644 --- a/packages/worker_bee/e2e/lib/e2e_worker.worker.dart +++ b/packages/worker_bee/e2e/lib/e2e_worker.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'e2e_worker.worker.vm.dart' - if (dart.library.js) 'e2e_worker.worker.js.dart'; + if (dart.library.js_interop) 'e2e_worker.worker.js.dart'; diff --git a/packages/worker_bee/e2e/lib/e2e_worker_no_result.worker.dart b/packages/worker_bee/e2e/lib/e2e_worker_no_result.worker.dart index d0c39fa13d2..895142ee6aa 100644 --- a/packages/worker_bee/e2e/lib/e2e_worker_no_result.worker.dart +++ b/packages/worker_bee/e2e/lib/e2e_worker_no_result.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'e2e_worker_no_result.worker.vm.dart' - if (dart.library.js) 'e2e_worker_no_result.worker.js.dart'; + if (dart.library.js_interop) 'e2e_worker_no_result.worker.js.dart'; diff --git a/packages/worker_bee/e2e/lib/e2e_worker_null_result.worker.dart b/packages/worker_bee/e2e/lib/e2e_worker_null_result.worker.dart index ef9d98e8471..069daca36a5 100644 --- a/packages/worker_bee/e2e/lib/e2e_worker_null_result.worker.dart +++ b/packages/worker_bee/e2e/lib/e2e_worker_null_result.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'e2e_worker_null_result.worker.vm.dart' - if (dart.library.js) 'e2e_worker_null_result.worker.js.dart'; + if (dart.library.js_interop) 'e2e_worker_null_result.worker.js.dart'; diff --git a/packages/worker_bee/e2e/lib/e2e_worker_throws.worker.dart b/packages/worker_bee/e2e/lib/e2e_worker_throws.worker.dart index 33955325f89..cf474f39450 100644 --- a/packages/worker_bee/e2e/lib/e2e_worker_throws.worker.dart +++ b/packages/worker_bee/e2e/lib/e2e_worker_throws.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'e2e_worker_throws.worker.vm.dart' - if (dart.library.js) 'e2e_worker_throws.worker.js.dart'; + if (dart.library.js_interop) 'e2e_worker_throws.worker.js.dart'; diff --git a/packages/worker_bee/e2e/lib/e2e_worker_void_result.worker.dart b/packages/worker_bee/e2e/lib/e2e_worker_void_result.worker.dart index b4f06f78ab2..e9618b626f2 100644 --- a/packages/worker_bee/e2e/lib/e2e_worker_void_result.worker.dart +++ b/packages/worker_bee/e2e/lib/e2e_worker_void_result.worker.dart @@ -8,4 +8,4 @@ // Generated by worker_bee_builder. export 'e2e_worker_void_result.worker.vm.dart' - if (dart.library.js) 'e2e_worker_void_result.worker.js.dart'; + if (dart.library.js_interop) 'e2e_worker_void_result.worker.js.dart'; diff --git a/packages/worker_bee/worker_bee/lib/src/js/impl.dart b/packages/worker_bee/worker_bee/lib/src/js/impl.dart index 5d5cd23d2e9..327736ec48e 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/impl.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/impl.dart @@ -2,13 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; +import 'dart:js_interop'; // ignore: implementation_imports import 'package:aws_common/src/js/common.dart'; import 'package:built_value/serializer.dart'; import 'package:meta/meta.dart'; +import 'package:web/web.dart'; import 'package:worker_bee/src/common.dart'; import 'package:worker_bee/src/exception/worker_bee_exception.dart'; +import 'package:worker_bee/src/js/js_extensions.dart'; import 'package:worker_bee/src/js/message_port_channel.dart'; import 'package:worker_bee/src/preamble.dart'; import 'package:worker_bee/worker_bee.dart'; @@ -88,7 +91,10 @@ mixin WorkerBeeImpl if (isWebWorker) { final serialized = _serialize(error); error = serialized.value!; - self.postMessage(serialized.value, serialized.transfer); + self.postMessage( + serialized.value?.toJSBoxOrCast, + serialized.transfer.toJSBoxOrCast, + ); } super.completeError(error, stackTrace); } @@ -99,34 +105,43 @@ mixin WorkerBeeImpl return runTraced(() async { await super.connect(logsChannel: logsChannel); final channel = StreamChannelController(sync: true); + + void onMessage(Event event) { + event as MessageEvent; + logger.verbose('Got message: ${event.data}'); + final serialized = event.data; + final message = _deserialize(serialized); + channel.foreign.sink.add(message); + } + self.addEventListener( 'message', - Zone.current.bindUnaryCallback((Event event) { - event as MessageEvent; - logger.verbose('Got message: ${event.data}'); - final serialized = event.data; - final message = _deserialize(serialized); - channel.foreign.sink.add(message); - }), + Zone.current.bindUnaryCallback(onMessage).toJS, ); channel.foreign.stream.listen( Zone.current.bindUnaryCallback((message) { logger.verbose('Sending message: $message'); final serialized = _serialize(message); - self.postMessage(serialized.value, serialized.transfer); + self.postMessage( + serialized.value?.toJSBoxOrCast, + serialized.transfer.toJSBoxOrCast, + ); }), ); logger.verbose('Ready'); - self.postMessage('ready'); + self.postMessage('ready'.toJS); final result = await run( channel.local.stream.asBroadcastStream().cast(), channel.local.sink.cast(), ); logger.verbose('Finished'); - self.postMessage('done'); + self.postMessage('done'.toJS); final serializedResult = _serialize(result); - self.postMessage(serializedResult.value, serializedResult.transfer); + self.postMessage( + serializedResult.value?.toJSBoxOrCast, + serializedResult.transfer.toJSBoxOrCast, + ); // Allow streams to flush, then close underlying resources. await close(); @@ -146,7 +161,7 @@ mixin WorkerBeeImpl // Spawn the worker using the specified script. try { - _worker = Worker(entrypoint); + _worker = Worker(entrypoint.toJS); } on Object { logger.debug('Could not launch worker at $entrypoint'); continue; @@ -174,11 +189,7 @@ mixin WorkerBeeImpl }, ); - // Listen for error messages on the worker. - // - // Some browsers do not currently support the `messageerror` event: - // https://developer.mozilla.org/en-US/docs/Web/API/Worker/messageerror_event#browser_compatibility - _worker!.addEventListener('messageerror', (Event event) { + void onEvent(Event event) { event as MessageEvent; final error = WorkerBeeExceptionImpl( 'Could not serialize message: ${event.data}', @@ -188,10 +199,12 @@ mixin WorkerBeeImpl } else { errorBeforeReady.completeError(error); } - }); - _worker!.onError = (Event event) { + } + + void onError(Event event) { Object error; - if (event is ErrorEvent) { + if (event.isA()) { + event as ErrorEvent; final eventJson = JSON.stringify(event.error); error = WorkerBeeExceptionImpl('${event.message} ($eventJson)'); } else { @@ -202,36 +215,47 @@ mixin WorkerBeeImpl } else { errorBeforeReady.completeError(error); } - }; + } + + // Listen for error messages on the worker. + // + // Some browsers do not currently support the `messageerror` event: + // https://developer.mozilla.org/en-US/docs/Web/API/Worker/messageerror_event#browser_compatibility + _worker!.addEventListener('messageerror', onEvent.toJS); + _worker!.onerror = onError.toJS; // Passes outgoing messages to the worker instance. _controller!.stream.listen( Zone.current.bindUnaryCallback((message) { logger.verbose('Sending message: $message'); final serialized = _serialize(message); - _worker!.postMessage(serialized.value, serialized.transfer); + + _worker!.postMessage( + serialized.value?.toJSBoxOrCast, + serialized.transfer.toJSBoxOrCast, + ); }), ); - // Listen to worker - _incomingMessages = StreamController(sync: true); - _worker!.onMessage = Zone.current.bindUnaryCallback(( - MessageEvent event, - ) { - if (event.data is String) { - if (event.data == 'ready') { + void onMessage(MessageEvent event) { + final eventData = event.data; + + if (eventData.isA()) { + eventData as JSString; + final state = eventData.toDart; + + if (state == 'ready') { logger.verbose('Received ready event'); ready.complete(); return; } - if (event.data == 'done') { + if (state == 'done') { logger.verbose('Received done event'); done = true; return; } } - final serialized = event.data; - final message = _deserialize(serialized); + final message = _deserialize(eventData); logger.verbose('Got message: $message'); if (message is WorkerBeeException) { if (ready.isCompleted) { @@ -249,7 +273,11 @@ mixin WorkerBeeImpl if (done) { complete(message); } - }); + } + + // Listen to worker + _incomingMessages = StreamController(sync: true); + _worker!.onmessage = Zone.current.bindUnaryCallback(onMessage).toJS; // Send assignment and logs channel final jsLogsChannel = MessageChannel(); @@ -263,7 +291,8 @@ mixin WorkerBeeImpl logsController.add(message); }), ); - _worker!.postMessage(name, [jsLogsChannel.port2]); + + _worker!.postMessage(name.toJS, [jsLogsChannel.port2].toJS); await Future.any([ready.future, errorBeforeReady.future]); diff --git a/packages/worker_bee/worker_bee/lib/src/js/js_extensions.dart b/packages/worker_bee/worker_bee/lib/src/js/js_extensions.dart new file mode 100644 index 00000000000..958edb0ff84 --- /dev/null +++ b/packages/worker_bee/worker_bee/lib/src/js/js_extensions.dart @@ -0,0 +1,27 @@ +import 'dart:js_interop'; + +import 'package:meta/meta.dart'; + +@internal +extension JSBoxOrCastObject on Object { + JSAny get toJSBoxOrCast { + // ignore: invalid_runtime_check_with_js_interop_types + if (this is JSAny) return this as JSAny; + + return toJSBox; + } +} + +@internal +extension JSBoxOrCastList on List { + JSArray get toJSBoxOrCast { + final mapped = map((item) { + if (item is List) { + return item.toJSBoxOrCast; + } + return item.toJSBoxOrCast; + }); + + return mapped.toList().toJS; + } +} diff --git a/packages/worker_bee/worker_bee/lib/src/js/message_port_channel.dart b/packages/worker_bee/worker_bee/lib/src/js/message_port_channel.dart index 4f8fa367b4e..f5c833d9ec9 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/message_port_channel.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/message_port_channel.dart @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; +import 'dart:js_interop'; -// ignore: implementation_imports -import 'package:aws_common/src/js/common.dart'; import 'package:built_value/serializer.dart'; +import 'package:web/web.dart'; import 'package:worker_bee/src/exception/worker_bee_exception.dart'; +import 'package:worker_bee/src/js/js_extensions.dart'; import 'package:worker_bee/src/serializers/serializers.dart'; import 'package:worker_bee/worker_bee.dart'; @@ -22,7 +23,9 @@ class MessagePortChannel Serializers? serializers, FullType specifiedType = FullType.unspecified, }) : _serializers = serializers ?? workerBeeSerializers, - _specifiedType = specifiedType; + _specifiedType = specifiedType { + messagePort.onmessage = _addEvent.toJS; + } /// The message port to communicate over. final MessagePort messagePort; @@ -33,18 +36,29 @@ class MessagePortChannel @override StreamSink get sink => this; + final StreamController _streamController = + StreamController(); + void _addEvent(MessageEvent event) => _streamController.add(event); + @override - late final Stream stream = messagePort.onMessage + late final Stream stream = _streamController.stream .transform( StreamTransformer.fromHandlers( handleData: Zone.current.bindBinaryCallback((event, sink) { - if (event.data == 'done') { - sink.close(); - close(); - return; + final eventData = event.data; + + if (eventData.isA()) { + eventData as JSString; + final state = eventData.toDart; + + if (state == 'done') { + sink.close(); + close(); + return; + } } final data = _serializers.deserialize( - event.data, + eventData, specifiedType: _specifiedType, ); if (data is WorkerBeeException || data is! T) { @@ -71,17 +85,17 @@ class MessagePortChannel () => _serializers.serialize(event, specifiedType: _specifiedType), zoneValues: {#transfer: transfer}, ); - messagePort.postMessage(serialized, transfer); + messagePort.postMessage(serialized?.toJSBoxOrCast, transfer.toJSBoxOrCast); } @override void addError(Object error, [StackTrace? stackTrace]) { - messagePort.postMessage( - _serializers.serialize( - WorkerBeeExceptionImpl(error, stackTrace), - specifiedType: FullType.unspecified, - ), + final serialized = _serializers.serialize( + WorkerBeeExceptionImpl(error, stackTrace), + specifiedType: FullType.unspecified, ); + + messagePort.postMessage(serialized?.toJSBoxOrCast); close(); } @@ -98,9 +112,11 @@ class MessagePortChannel Future close() async { if (_done.isCompleted) return; messagePort - ..postMessage('done') + ..postMessage('done'.toJS) ..close(); + _done.complete(); + await _streamController.close(); } @override diff --git a/packages/worker_bee/worker_bee/lib/src/js/preamble.dart b/packages/worker_bee/worker_bee/lib/src/js/preamble.dart index 06fb297afe8..6ba668b4a15 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/preamble.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/preamble.dart @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; +import 'dart:js_interop'; // ignore: implementation_imports import 'package:aws_common/src/js/common.dart'; import 'package:built_value/serializer.dart'; -import 'package:collection/collection.dart'; +import 'package:web/web.dart'; +import 'package:worker_bee/src/js/js_extensions.dart'; import 'package:worker_bee/src/js/message_port_channel.dart'; import 'package:worker_bee/src/preamble.dart'; import 'package:worker_bee/src/serializers/serializers.dart'; @@ -25,39 +27,43 @@ Future getWorkerAssignment() async { // Errors in the preamble should be reported to the parent thread. void onError(Object e, StackTrace st) { self.postMessage( - workerBeeSerializers.serialize(e, specifiedType: FullType.unspecified), + workerBeeSerializers + .serialize(e, specifiedType: FullType.unspecified) + ?.toJSBoxOrCast, ); } return runTraced(() async { final assignmentCompleter = Completer.sync(); - late void Function(Event) eventListener; - self.addEventListener( - 'message', - eventListener = Zone.current.bindUnaryCallback(( - Event event, - ) { - event as MessageEvent; - final message = event.data; - final messagePort = event.ports.firstOrNull; - if (message is String && messagePort is MessagePort) { - self.removeEventListener('message', eventListener); - assignmentCompleter.complete( - WorkerAssignment( - message, - MessagePortChannel(messagePort), - ), - ); - } else { - assignmentCompleter.completeError( - StateError( - 'Invalid worker assignment: ' - '${workerBeeSerializers.serialize(message)}', - ), - ); - } - }), - ); + late final JSExportedDartFunction jsOnMessageCallback; + + void onMessage(MessageEvent event) { + final eventData = event.data; + + final messagePort = event.ports.toDart.firstOrNull; + if (eventData.isA() && messagePort is MessagePort) { + eventData as JSString; + final state = eventData.toDart; + + self.removeEventListener('message', jsOnMessageCallback); + assignmentCompleter.complete( + WorkerAssignment(state, MessagePortChannel(messagePort)), + ); + } else { + assignmentCompleter.completeError( + StateError( + 'Invalid worker assignment: ' + '${workerBeeSerializers.serialize(eventData)}', + ), + ); + } + } + + final onMessageCallback = Zone.current + .bindUnaryCallback(onMessage); + jsOnMessageCallback = onMessageCallback.toJS; + + self.addEventListener('message', jsOnMessageCallback); return assignmentCompleter.future; }, onError: onError); } diff --git a/packages/worker_bee/worker_bee/lib/src/preamble.dart b/packages/worker_bee/worker_bee/lib/src/preamble.dart index 41294fbf820..40805817117 100644 --- a/packages/worker_bee/worker_bee/lib/src/preamble.dart +++ b/packages/worker_bee/worker_bee/lib/src/preamble.dart @@ -9,7 +9,7 @@ import 'package:stack_trace/stack_trace.dart'; import 'package:worker_bee/src/exception/worker_bee_exception.dart'; import 'package:worker_bee/worker_bee.dart'; -export 'vm/preamble.dart' if (dart.library.js) 'js/preamble.dart'; +export 'vm/preamble.dart' if (dart.library.js_interop) 'js/preamble.dart'; /// {@template worker_bee.worker_assignment} /// The worker bee assignment sent from the main thread. diff --git a/packages/worker_bee/worker_bee/lib/worker_bee.dart b/packages/worker_bee/worker_bee/lib/worker_bee.dart index 0075dd3cfcc..ae0db918b3a 100644 --- a/packages/worker_bee/worker_bee/lib/worker_bee.dart +++ b/packages/worker_bee/worker_bee/lib/worker_bee.dart @@ -10,7 +10,7 @@ import 'package:async/async.dart'; import 'package:worker_bee/src/common.dart'; import 'package:worker_bee/src/preamble.dart'; import 'package:worker_bee/src/vm/impl.dart' - if (dart.library.js) 'package:worker_bee/src/js/impl.dart'; + if (dart.library.js_interop) 'package:worker_bee/src/js/impl.dart'; export 'package:async/async.dart'; export 'package:aws_common/aws_common.dart' @@ -21,7 +21,7 @@ export 'package:worker_bee/src/exception/worker_bee_exception.dart' export 'package:worker_bee/src/logging/worker_log_entry.dart'; export 'package:worker_bee/src/preamble.dart' hide runTraced; export 'package:worker_bee/src/worker_bee_vm.dart' - if (dart.library.js) 'package:worker_bee/src/worker_bee_js.dart'; + if (dart.library.js_interop) 'package:worker_bee/src/worker_bee_js.dart'; /// {@template worker_bee.worker_bee} /// Annotation class for marking worker bees. diff --git a/packages/worker_bee/worker_bee/pubspec.yaml b/packages/worker_bee/worker_bee/pubspec.yaml index dd5e52a1dce..9be05020a51 100644 --- a/packages/worker_bee/worker_bee/pubspec.yaml +++ b/packages/worker_bee/worker_bee/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: stack_trace: ^1.10.0 stream_channel: ^2.1.0 stream_transform: ^2.0.0 + web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.2 <3.2.0" diff --git a/packages/worker_bee/worker_bee_builder/lib/src/worker_generator.dart b/packages/worker_bee/worker_bee_builder/lib/src/worker_generator.dart index 2b28f3bad9a..6f3fa642882 100644 --- a/packages/worker_bee/worker_bee_builder/lib/src/worker_generator.dart +++ b/packages/worker_bee/worker_bee_builder/lib/src/worker_generator.dart @@ -101,7 +101,7 @@ ${workerImpl.impl}'''); $generatedHeader export '${libraries[Target.vm]}' - if (dart.library.js) '${libraries[Target.js]}'; + if (dart.library.js_interop) '${libraries[Target.js]}'; '''; }