From 3cb77d63674952c7f52582394cb6a3506206fffe Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 11:14:43 -0700 Subject: [PATCH 01/36] Migrated Sigv4 Example Project --- .../aws_signature_v4/example/pubspec.yaml | 1 + .../js_interop/file_upload_input_element.dart | 18 +++++++++ .../web/js_interop/input_element_base.dart | 35 +++++++++++++++++ .../web/js_interop/text_input_element.dart | 12 ++++++ .../js_interop/text_input_element_base.dart | 39 +++++++++++++++++++ .../aws_signature_v4/example/web/main.dart | 30 +++++++------- 6 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 packages/aws_signature_v4/example/web/js_interop/file_upload_input_element.dart create mode 100644 packages/aws_signature_v4/example/web/js_interop/input_element_base.dart create mode 100644 packages/aws_signature_v4/example/web/js_interop/text_input_element.dart create mode 100644 packages/aws_signature_v4/example/web/js_interop/text_input_element_base.dart diff --git a/packages/aws_signature_v4/example/pubspec.yaml b/packages/aws_signature_v4/example/pubspec.yaml index 137ae663ae..2a49ebeabc 100644 --- a/packages/aws_signature_v4/example/pubspec.yaml +++ b/packages/aws_signature_v4/example/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: aws_signature_v4: ">=0.6.3 <0.7.0" collection: ^1.15.0 path: ">=1.8.0 <2.0.0" + web: ^1.1.1 dependency_overrides: aws_common: diff --git a/packages/aws_signature_v4/example/web/js_interop/file_upload_input_element.dart b/packages/aws_signature_v4/example/web/js_interop/file_upload_input_element.dart new file mode 100644 index 0000000000..fd3c2e033d --- /dev/null +++ b/packages/aws_signature_v4/example/web/js_interop/file_upload_input_element.dart @@ -0,0 +1,18 @@ +import 'dart:js_interop'; + +import 'package:web/web.dart'; + +import './input_element_base.dart'; + +extension type FileUploadInputElement(JSObject _) implements InputElementBase { + external String? get accept; + external set accept(String? value); + + external bool? get multiple; + external set multiple(bool? value); + + external bool get required; + external set required(bool value); + + external List? files; +} diff --git a/packages/aws_signature_v4/example/web/js_interop/input_element_base.dart b/packages/aws_signature_v4/example/web/js_interop/input_element_base.dart new file mode 100644 index 0000000000..728ab9b713 --- /dev/null +++ b/packages/aws_signature_v4/example/web/js_interop/input_element_base.dart @@ -0,0 +1,35 @@ +import 'dart:js_interop'; + +import 'package:web/web.dart'; + +extension type InputElementBase(JSObject _) implements Element { + external bool get autofocus; + external set autofocus(bool value); + + external bool? get disabled; + external set disabled(bool? value); + + external bool? get incremental; + external set incremental(bool? value); + + external bool? get indeterminate; + external set indeterminate(bool? value); + + external String? get name; + external set name(String? value); + + external String? get value; + external set value(String? value); + + external List? get labels; + + external String get validationMessage; + + external ValidityState get validity; + + external bool get willValidate; + + external bool checkValidity(); + + external void setCustomValidity(String error); +} diff --git a/packages/aws_signature_v4/example/web/js_interop/text_input_element.dart b/packages/aws_signature_v4/example/web/js_interop/text_input_element.dart new file mode 100644 index 0000000000..c27685bba4 --- /dev/null +++ b/packages/aws_signature_v4/example/web/js_interop/text_input_element.dart @@ -0,0 +1,12 @@ +import 'dart:js_interop'; + +import 'package:web/web.dart'; + +import './text_input_element_base.dart'; + +extension type TextInputElement(JSObject _) implements TextInputElementBase { + external String? get dirName; + external set dirName(String? value); + + external Element? get list; +} diff --git a/packages/aws_signature_v4/example/web/js_interop/text_input_element_base.dart b/packages/aws_signature_v4/example/web/js_interop/text_input_element_base.dart new file mode 100644 index 0000000000..b3a5e2baed --- /dev/null +++ b/packages/aws_signature_v4/example/web/js_interop/text_input_element_base.dart @@ -0,0 +1,39 @@ +import 'dart:js_interop'; + +import './input_element_base.dart'; + +extension type TextInputElementBase(JSObject _) implements InputElementBase { + external String get autocomplete; + external set autocomplete(String value); + + external int? get maxLength; + external set maxLength(int? value); + + external String get pattern; + external set pattern(String value); + + external String get placeholder; + external set placeholder(String value); + + external bool? get readOnly; + external set readOnly(bool? value); + + external bool get required; + external set required(bool value); + + external int? get size; + external set size(int? value); + + external void select(); + + external String? get selectionDirection; + external set selectionDirection(String? value); + + external int? get selectionEnd; + external set selectionEnd(int? value); + + external int? get selectionStart; + external set selectionStart(int? value); + + external void setSelectionRange(int start, int end, [String? direction]); +} diff --git a/packages/aws_signature_v4/example/web/main.dart b/packages/aws_signature_v4/example/web/main.dart index 05263e408d..49fec6cb70 100644 --- a/packages/aws_signature_v4/example/web/main.dart +++ b/packages/aws_signature_v4/example/web/main.dart @@ -4,24 +4,22 @@ // ignore_for_file: omit_local_variable_types import 'dart:async'; -//ignore: deprecated_member_use -import 'dart:html'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:aws_common/aws_common.dart'; import 'package:aws_signature_v4/aws_signature_v4.dart'; import 'package:path/path.dart' as p; +import 'package:web/web.dart'; -final TextInputElement bucketNameEl = - document.getElementById('bucket-name') as TextInputElement; -final TextInputElement regionEl = - document.getElementById('region') as TextInputElement; -final FileUploadInputElement fileEl = - document.getElementById('file') as FileUploadInputElement; -final ButtonElement uploadBtnEl = - document.getElementById('upload') as ButtonElement; -final AnchorElement downloadBtnEl = - document.getElementById('download') as AnchorElement; +import './js_interop/file_upload_input_element.dart'; +import './js_interop/text_input_element.dart'; + +final bucketNameEl = document.getElementById('bucket-name') as TextInputElement; +final regionEl = document.getElementById('region') as TextInputElement; +final fileEl = document.getElementById('file') as FileUploadInputElement; +final uploadBtnEl = document.getElementById('upload') as HTMLButtonElement; +final downloadBtnEl = document.getElementById('download') as HTMLAnchorElement; void main() { bucketNameEl.onChange.listen((e) { @@ -45,7 +43,7 @@ void main() { try { await upload(bucketUpload); } on Exception catch (e) { - window.console.error(e); + console.error(e.toString().toJS); } finally { uploadBtnEl.setBusy(false); } @@ -146,16 +144,16 @@ void updateState() { uploadBtnEl.disabled = !uploadEnabled; } -extension on Element { +extension on HTMLElement { void show() { style.display = 'block'; } } -extension on ButtonElement { +extension on HTMLButtonElement { void setBusy(bool busy) { if (busy) { - setAttribute('aria-busy', true); + setAttribute('aria-busy', 'true'); text = 'Uploading...'; } else { removeAttribute('aria-busy'); From 07a95166fb50c3fa34bd778b8b1dd0c1379a8bdc Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 11:17:33 -0700 Subject: [PATCH 02/36] Migrate Amplify DB Common Dart --- .../common/amplify_db_common_dart/lib/src/connect_html.dart | 3 +-- packages/common/amplify_db_common_dart/pubspec.yaml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/amplify_db_common_dart/lib/src/connect_html.dart b/packages/common/amplify_db_common_dart/lib/src/connect_html.dart index 50e95ca4be..d1ac4adc4e 100644 --- a/packages/common/amplify_db_common_dart/lib/src/connect_html.dart +++ b/packages/common/amplify_db_common_dart/lib/src/connect_html.dart @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; -//ignore: deprecated_member_use -import 'dart:html'; import 'package:amplify_core/amplify_core.dart'; import 'package:async/async.dart'; @@ -12,6 +10,7 @@ import 'package:drift/drift.dart'; import 'package:drift/wasm.dart'; import 'package:meta/meta.dart'; import 'package:sqlite3/wasm.dart'; +import 'package:web/web.dart'; final _sqlite3Memo = AsyncMemoizer(); diff --git a/packages/common/amplify_db_common_dart/pubspec.yaml b/packages/common/amplify_db_common_dart/pubspec.yaml index 1daeed0cfb..124bbd8199 100644 --- a/packages/common/amplify_db_common_dart/pubspec.yaml +++ b/packages/common/amplify_db_common_dart/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: meta: ^1.16.0 path: ">=1.8.0 <2.0.0" sqlite3: ">=2.0.0 <2.7.0" + web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.1 <3.2.0" From 939a198298416242151c497279ad8ff7488d5f3b Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 12:45:32 -0700 Subject: [PATCH 03/36] Migrate Example Common --- .../lib/src/components/button_component.dart | 8 +++++--- .../lib/src/components/center_component.dart | 6 ++---- .../lib/src/components/component.dart | 13 ++++++------- .../lib/src/components/container_component.dart | 6 ++---- .../lib/src/components/flex_component.dart | 6 ++---- .../lib/src/components/form_component.dart | 6 ++---- .../lib/src/components/table_component.dart | 10 ++++------ .../lib/src/components/text_component.dart | 6 +++--- .../src/components/text_form_field_component.dart | 10 +++++----- .../example_common/lib/src/utils/render_app.dart | 8 +++----- packages/example_common/pubspec.yaml | 1 + .../example_common/test/example_common_test.dart | 4 +--- 12 files changed, 36 insertions(+), 48 deletions(-) diff --git a/packages/example_common/lib/src/components/button_component.dart b/packages/example_common/lib/src/components/button_component.dart index cb64170b48..86efb30d7b 100644 --- a/packages/example_common/lib/src/components/button_component.dart +++ b/packages/example_common/lib/src/components/button_component.dart @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -//ignore: deprecated_member_use -import 'dart:html'; +import 'dart:js_interop'; import 'package:example_common/src/components/component.dart'; import 'package:example_common/src/utils/component_edge_insets.dart'; +import 'package:web/web.dart'; /// {@template example_common.button_component} /// A component that renders an html button element. @@ -43,7 +43,9 @@ class ButtonComponent extends Component { final bool loading; late final _buttonElement = () { - final el = ButtonElement()..innerHtml = loading ? 'Loading ...' : innerHtml; + final el = + HTMLButtonElement() + ..innerHTML = loading ? 'Loading ...'.toJS : innerHtml.toJS; if (id != null) { el.id = id!; } diff --git a/packages/example_common/lib/src/components/center_component.dart b/packages/example_common/lib/src/components/center_component.dart index 50219e2a7e..fe1204b8c3 100644 --- a/packages/example_common/lib/src/components/center_component.dart +++ b/packages/example_common/lib/src/components/center_component.dart @@ -1,10 +1,8 @@ // 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:example_common/src/components/component.dart'; +import 'package:web/web.dart'; /// {@template example_common.center_component} /// A component that centers the provided child @@ -18,7 +16,7 @@ class CenterComponent extends Component { @override Component render() { - final container = Element.div(); + final container = HTMLDivElement(); container.style.display = 'flex'; container.style.alignItems = 'center'; container.style.justifyContent = 'center'; diff --git a/packages/example_common/lib/src/components/component.dart b/packages/example_common/lib/src/components/component.dart index 3575d1562f..6499dadb36 100644 --- a/packages/example_common/lib/src/components/component.dart +++ b/packages/example_common/lib/src/components/component.dart @@ -2,10 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; -//ignore: deprecated_member_use -import 'dart:html'; import 'package:meta/meta.dart'; +import 'package:web/web.dart'; /// {@template example_common.component} /// A base component class that other components should extend @@ -21,13 +20,13 @@ abstract class Component { late Component _component = render(); // The element for this component - Element get _element => _component._element; + HTMLElement get _element => _component._element; // Wether or not the component is mounted (current rendered in the DOM) bool _isMounted = false; /// The style of the component's element - CssStyleDeclaration get style => _element.style; + CSSStyleDeclaration get style => _element.style; /// Calls [render], schedules the [componentDidMount] callback, and returns the [_element]. /// @@ -50,7 +49,7 @@ abstract class Component { } /// Creates a component from an html [Element]. - static Component fromElement(Element element) { + static Component fromElement(HTMLElement element) { return _ElementComponent(element); } } @@ -89,10 +88,10 @@ abstract class StatefulComponent extends Component { /// Useful for creating primitive components, but generally /// shouldn't be used directly class _ElementComponent extends Component { - _ElementComponent(Element element) : _element = element; + _ElementComponent(HTMLElement element) : _element = element; @override - final Element _element; + final HTMLElement _element; @override Component render() { diff --git a/packages/example_common/lib/src/components/container_component.dart b/packages/example_common/lib/src/components/container_component.dart index 17ed19ea42..8d971272be 100644 --- a/packages/example_common/lib/src/components/container_component.dart +++ b/packages/example_common/lib/src/components/container_component.dart @@ -1,11 +1,9 @@ // 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:example_common/src/components/component.dart'; import 'package:example_common/src/utils/component_edge_insets.dart'; +import 'package:web/web.dart'; /// {@template example_common.container_component} /// A component that renders a container with an optional child @@ -37,7 +35,7 @@ class ContainerComponent extends Component { @override Component render() { - final div = Element.div(); + final div = HTMLDivElement(); if (margin != null) div.style.margin = margin!.toCssString(); if (padding != null) div.style.padding = padding!.toCssString(); if (height != null) div.style.height = '${height}px'; diff --git a/packages/example_common/lib/src/components/flex_component.dart b/packages/example_common/lib/src/components/flex_component.dart index 908c22de28..c0bc92d29e 100644 --- a/packages/example_common/lib/src/components/flex_component.dart +++ b/packages/example_common/lib/src/components/flex_component.dart @@ -1,11 +1,9 @@ // 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:example_common/src/components/component.dart'; import 'package:example_common/src/enums.dart'; +import 'package:web/web.dart'; /// {@template example_common.flex_component} /// a component that displays a set of [children] in either a row or column @@ -29,7 +27,7 @@ class FlexComponent extends Component { @override Component render() { - final div = Element.div(); + final div = HTMLDivElement(); div.style.display = 'flex'; div.style.flexDirection = direction.flexDirection; div.style.alignItems = alignItems.name; diff --git a/packages/example_common/lib/src/components/form_component.dart b/packages/example_common/lib/src/components/form_component.dart index 613186c41d..713482c66d 100644 --- a/packages/example_common/lib/src/components/form_component.dart +++ b/packages/example_common/lib/src/components/form_component.dart @@ -1,10 +1,8 @@ // 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:example_common/src/components/component.dart'; +import 'package:web/web.dart'; /// {@template example_common.form_component} /// a component that displays a form @@ -21,7 +19,7 @@ class FormComponent extends Component { @override Component render() { - final formElement = FormElement(); + final formElement = HTMLFormElement(); if (id != null) formElement.id = id!; for (final child in children) { formElement.appendComponent(child); diff --git a/packages/example_common/lib/src/components/table_component.dart b/packages/example_common/lib/src/components/table_component.dart index 25d0a83676..49e627ae73 100644 --- a/packages/example_common/lib/src/components/table_component.dart +++ b/packages/example_common/lib/src/components/table_component.dart @@ -1,10 +1,8 @@ // 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:example_common/src/components/component.dart'; +import 'package:web/web.dart'; /// {@template example_common.table_component} /// A component that displays a set of rows in a table. @@ -18,20 +16,20 @@ class TableComponent extends Component { @override Component render() { - final table = TableElement()..createTHead(); + final table = HTMLTableElement()..createTHead(); final tbody = table.createTBody(); final headerRow = table.tHead!.insertRow(-1); for (var h = 0; h < tableDefinition.headers.length; h++) { - headerRow.insertCell(h).text = tableDefinition.headers[h]; + headerRow.insertCell(h).textContent = tableDefinition.headers[h]; } for (final row in tableDefinition.rows) { final newRow = tbody.insertRow(-1)..id = 'wrappedTableRow'; newRow.style.border = '1px solid black'; // add at the end for (var r = 0; r < row.length; r++) { - newRow.insertCell(r).text = row[r]; + newRow.insertCell(r).textContent = row[r]; } } return Component.fromElement(table); diff --git a/packages/example_common/lib/src/components/text_component.dart b/packages/example_common/lib/src/components/text_component.dart index 06f2be0dee..f81d891ba6 100644 --- a/packages/example_common/lib/src/components/text_component.dart +++ b/packages/example_common/lib/src/components/text_component.dart @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -//ignore: deprecated_member_use -import 'dart:html'; +import 'dart:js_interop'; import 'package:example_common/src/components/component.dart'; +import 'package:web/web.dart'; /// {@template example_common.text_component} /// a component that displays text @@ -18,6 +18,6 @@ class TextComponent extends Component { @override Component render() { - return Component.fromElement(Element.p()..innerHtml = text); + return Component.fromElement(HTMLParagraphElement()..innerHTML = text.toJS); } } diff --git a/packages/example_common/lib/src/components/text_form_field_component.dart b/packages/example_common/lib/src/components/text_form_field_component.dart index 237414e11f..b9148c8f74 100644 --- a/packages/example_common/lib/src/components/text_form_field_component.dart +++ b/packages/example_common/lib/src/components/text_form_field_component.dart @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -//ignore: deprecated_member_use -import 'dart:html'; +import 'dart:js_interop'; import 'package:example_common/src/components/component.dart'; import 'package:example_common/src/components/container_component.dart'; import 'package:example_common/src/components/flex_component.dart'; import 'package:example_common/src/utils/component_edge_insets.dart'; +import 'package:web/web.dart'; /// {@template example_common.text_form_field_component} /// a component that displays a form field - an input and a label. @@ -49,15 +49,15 @@ class TextFormFieldComponent extends Component { /// A callback that will run when the value has changed. final void Function(String? value) onChanged; - late final _labelElement = LabelElement()..innerHtml = labelText; + late final _labelElement = HTMLLabelElement()..innerHTML = labelText.toJS; late final _inputElement = - InputElement() + HTMLInputElement() ..type = type ..required = required ..id = id ..style.width = '100%' ..style.boxSizing = 'border-box' - ..value = initialValue; + ..value = initialValue ?? ''; @override Component render() { diff --git a/packages/example_common/lib/src/utils/render_app.dart b/packages/example_common/lib/src/utils/render_app.dart index 75168c8dee..bc6da2b9bb 100644 --- a/packages/example_common/lib/src/utils/render_app.dart +++ b/packages/example_common/lib/src/utils/render_app.dart @@ -1,10 +1,8 @@ // 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:example_common/example_common.dart'; +import 'package:web/web.dart'; const _rootComponentId = 'root-component'; @@ -15,7 +13,7 @@ void renderApp(Component component) { var rootElement = document.getElementById(_rootComponentId); if (rootElement == null) { - rootElement = Element.div()..id = _rootComponentId; + rootElement = HTMLDivElement()..id = _rootComponentId; final body = document.body; if (body == null) { throw Exception('The DOM must have a body element.'); @@ -27,6 +25,6 @@ void renderApp(Component component) { if (app == null) { rootElement.appendComponent(component); } else { - app.replaceWith(component.renderElement()); + app.parentNode?.replaceChild(app, component.renderElement()); } } diff --git a/packages/example_common/pubspec.yaml b/packages/example_common/pubspec.yaml index 26604ea574..bd8b88066c 100644 --- a/packages/example_common/pubspec.yaml +++ b/packages/example_common/pubspec.yaml @@ -8,6 +8,7 @@ environment: dependencies: meta: ^1.16.0 + web: ^1.1.1 dev_dependencies: amplify_lints: ">=2.0.2 <2.1.0" diff --git a/packages/example_common/test/example_common_test.dart b/packages/example_common/test/example_common_test.dart index 75c08de6c5..6a89321525 100644 --- a/packages/example_common/test/example_common_test.dart +++ b/packages/example_common/test/example_common_test.dart @@ -4,11 +4,9 @@ @TestOn('browser') library; -//ignore: deprecated_member_use -import 'dart:html'; - import 'package:example_common/example_common.dart'; import 'package:test/test.dart'; +import 'package:web/web.dart'; void main() { test('Can create a component and insert it into the DOM', () { From 6b7f0d815c2e24292871fce2c090628ae729d061 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 15:46:14 -0700 Subject: [PATCH 04/36] Migrated Amplify Storage S3 Dart --- .../platform_impl/download_file/dom_helper.dart | 16 ++++++++-------- .../html_amplify_storage_downloader.dart | 12 ++++++++++++ .../transfer/database/database_html.dart | 17 +++++++++-------- .../amplify_storage_s3_dart/pubspec.yaml | 1 + 4 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/html_amplify_storage_downloader.dart diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/dom_helper.dart b/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/dom_helper.dart index dbf10c895b..30827ac2ea 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/dom_helper.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/dom_helper.dart @@ -1,8 +1,8 @@ // 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:amplify_storage_s3_dart/src/platform_impl/download_file/html_amplify_storage_downloader.dart'; +import 'package:web/web.dart'; /// {@template amplify_storage_s3_dart.dom_helper} /// Helper to insert a hidden DOM element into the Web page to trigger @@ -22,23 +22,23 @@ class DomHelper { void _initializeContainerElement(String containerId) { final container = - Element.tag('amplify_storage_downloader') + HTMLAmplifyStorageDownloader() ..id = containerId ..style.display = 'none'; - querySelector('body')!.children.add(container); + document.querySelector('body')!.appendChild(container); _container = container; } /// Triggers browser download for the `url` with `name`. void download({required String url, String? name = ''}) { final anchor = - AnchorElement() + HTMLAnchorElement() ..href = url - ..download = name; + ..download = name ?? ''; - _container.children.add(anchor); + _container.appendChild(anchor); anchor.click(); - _container.children.remove(anchor); + _container.removeChild(anchor); } } diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/html_amplify_storage_downloader.dart b/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/html_amplify_storage_downloader.dart new file mode 100644 index 0000000000..9a8440f86a --- /dev/null +++ b/packages/storage/amplify_storage_s3_dart/lib/src/platform_impl/download_file/html_amplify_storage_downloader.dart @@ -0,0 +1,12 @@ +import 'dart:js_interop'; + +import 'package:meta/meta.dart'; +import 'package:web/web.dart'; + +@internal +extension type HTMLAmplifyStorageDownloader._(JSObject _) + implements HTMLElement, JSObject { + /// Creates an [HTMLAmplifyStorageDownloader] using the tag 'amplify_storage_downloader'. + HTMLAmplifyStorageDownloader() + : _ = document.createElement('amplify_storage_downloader'); +} diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/database/database_html.dart b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/database/database_html.dart index e8a7a3fcca..3b7682e6f9 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/database/database_html.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/transfer/database/database_html.dart @@ -1,13 +1,11 @@ // 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:amplify_core/amplify_core.dart'; import 'package:amplify_storage_s3_dart/src/storage_s3_service/transfer/database/database_stub.dart' as stub; import 'package:amplify_storage_s3_dart/src/storage_s3_service/transfer/database/transfer_record.dart'; +import 'package:web/web.dart'; /// a key prefix used by [TransferDatabase] when adding a key value pair into local storage. /// it is an identifier to differentiate the data stored by [TransferDatabase] into local storage from others. @@ -18,9 +16,12 @@ class TransferDatabase implements stub.TransferDatabase { /// {@macro amplify_storage_s3_dart.transfer_database} // ignore: avoid_unused_constructor_parameters TransferDatabase(DependencyManager dependencies); - - Iterable> get _entries => window.localStorage.entries - .where((element) => element.key.startsWith(_localStorageKeyPrefix)); + Iterable> get _entries => + List.generate(window.localStorage.length, (index) { + final key = window.localStorage.key(index) ?? ''; + final value = window.localStorage.getItem(key) ?? ''; + return MapEntry(key, value); + }).where((element) => element.key.startsWith(_localStorageKeyPrefix)); @override Future deleteTransferRecords(String uploadId) async { @@ -28,7 +29,7 @@ class TransferDatabase implements stub.TransferDatabase { for (final element in _entries) { final transferRecord = TransferRecord.fromJsonString(element.value); if (transferRecord.uploadId == uploadId) { - window.localStorage.remove(element.key); + window.localStorage.removeItem(element.key); result++; } } @@ -54,7 +55,7 @@ class TransferDatabase implements stub.TransferDatabase { @override Future insertTransferRecord(TransferRecord data) async { final key = _localStorageKeyPrefix + uuid(); - window.localStorage[key] = data.toJsonString(); + window.localStorage.setItem(key, data.toJsonString()); return key; } } diff --git a/packages/storage/amplify_storage_s3_dart/pubspec.yaml b/packages/storage/amplify_storage_s3_dart/pubspec.yaml index a1dcd13b14..cca4db3fb9 100644 --- a/packages/storage/amplify_storage_s3_dart/pubspec.yaml +++ b/packages/storage/amplify_storage_s3_dart/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: path: ">=1.8.0 <2.0.0" smithy: ">=0.7.4 <0.8.0" smithy_aws: ">=0.7.4 <0.8.0" + web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.1 <3.2.0" From ca8bb922afd3b59ec5f43f372d5dcee8190ab2be Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 15:46:36 -0700 Subject: [PATCH 05/36] Migrated Amplify Storage S3 Dart Example Project --- .../storage/amplify_storage_s3_dart/example/pubspec.yaml | 1 + .../storage/amplify_storage_s3_dart/example/web/main.dart | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/storage/amplify_storage_s3_dart/example/pubspec.yaml b/packages/storage/amplify_storage_s3_dart/example/pubspec.yaml index d4b508c888..3e692105ab 100644 --- a/packages/storage/amplify_storage_s3_dart/example/pubspec.yaml +++ b/packages/storage/amplify_storage_s3_dart/example/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: amplify_storage_s3_dart: any example_common: path: ../../../example_common + web: ^1.1.1 dev_dependencies: amplify_lints: diff --git a/packages/storage/amplify_storage_s3_dart/example/web/main.dart b/packages/storage/amplify_storage_s3_dart/example/web/main.dart index 1f5f77907a..1a594cd35f 100644 --- a/packages/storage/amplify_storage_s3_dart/example/web/main.dart +++ b/packages/storage/amplify_storage_s3_dart/example/web/main.dart @@ -1,10 +1,9 @@ // 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'; // TODO(HuiSF): Add example Web App void main() { - querySelector('#output')?.text = 'Your Dart app is running.'; + document.querySelector('#output')?.textContent = 'Your Dart app is running.'; } From 7992b1aed19255907259e6656076544378ad1427 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 15:55:06 -0700 Subject: [PATCH 06/36] Migrated Amplify Auth Cognito Dart --- .../lib/src/asf/asf_device_info_collector.js.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 6930a256c1..21bc63414b 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 @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// TODO(dnys1): Migrate to `js_interop`. library; import 'dart:convert'; +import 'dart:js_interop'; import 'package:amplify_auth_cognito_dart/src/asf/asf_device_info_collector.dart'; import 'package:amplify_auth_cognito_dart/src/asf/package_info.dart'; @@ -12,8 +12,6 @@ import 'package:async/async.dart'; import 'package:aws_common/aws_common.dart'; // ignore: implementation_imports import 'package:aws_common/src/js/common.dart'; -//ignore: deprecated_member_use -import 'package:js/js.dart'; import 'package:path/path.dart'; /// {@template amplify_auth_cognito_dart.asf.asf_device_info_js} From 308c463ebc4c0328e4509291143d11305db764f1 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 15:55:32 -0700 Subject: [PATCH 07/36] Migrated Amplify Auth Cognito Dart Example Project --- packages/auth/amplify_auth_cognito_dart/example/pubspec.yaml | 1 + .../example/web/components/user_component.dart | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/auth/amplify_auth_cognito_dart/example/pubspec.yaml b/packages/auth/amplify_auth_cognito_dart/example/pubspec.yaml index c67ccccc37..f82b149c20 100644 --- a/packages/auth/amplify_auth_cognito_dart/example/pubspec.yaml +++ b/packages/auth/amplify_auth_cognito_dart/example/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: example_common: path: ../../../example_common qr: ^3.0.1 + web: ^1.1.1 dev_dependencies: amplify_api_dart: any diff --git a/packages/auth/amplify_auth_cognito_dart/example/web/components/user_component.dart b/packages/auth/amplify_auth_cognito_dart/example/web/components/user_component.dart index cadcf09a26..382017b8ff 100644 --- a/packages/auth/amplify_auth_cognito_dart/example/web/components/user_component.dart +++ b/packages/auth/amplify_auth_cognito_dart/example/web/components/user_component.dart @@ -1,13 +1,11 @@ // 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:amplify_auth_cognito_dart/amplify_auth_cognito_dart.dart'; import 'package:amplify_core/amplify_core.dart'; import 'package:cognito_example/common.dart'; import 'package:example_common/example_common.dart'; +import 'package:web/web.dart'; import 'app_component.dart'; From d62c288573314e21fe0234fe3f7d3c0197ee4309 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 16:06:55 -0700 Subject: [PATCH 08/36] Migrated Amplify Core --- packages/amplify_core/lib/src/platform/platform_html.dart | 3 +-- packages/amplify_core/pubspec.yaml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amplify_core/lib/src/platform/platform_html.dart b/packages/amplify_core/lib/src/platform/platform_html.dart index a692ea8d3d..cceac8873b 100644 --- a/packages/amplify_core/lib/src/platform/platform_html.dart +++ b/packages/amplify_core/lib/src/platform/platform_html.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'; final RegExp _edgeRegex = RegExp(r'Edg/[\d\.]+'); final RegExp _operaRegex = RegExp(r'OPR/[\d\.]+'); diff --git a/packages/amplify_core/pubspec.yaml b/packages/amplify_core/pubspec.yaml index 3eb14e19d2..d15eee35de 100644 --- a/packages/amplify_core/pubspec.yaml +++ b/packages/amplify_core/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: retry: ^3.1.0 stack_trace: ^1.10.0 uuid: ">=3.0.6 <5.0.0" + web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.1 <3.2.0" From 85e3274dc5af57a95d7b29fec7880dfd81dbd8a3 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 16:15:36 -0700 Subject: [PATCH 09/36] Migrated Actions --- actions/lib/src/node/actions/exec.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/actions/lib/src/node/actions/exec.dart b/actions/lib/src/node/actions/exec.dart index e365231c2d..b639c2b156 100644 --- a/actions/lib/src/node/actions/exec.dart +++ b/actions/lib/src/node/actions/exec.dart @@ -3,8 +3,6 @@ import 'dart:convert'; import 'dart:js_interop'; -//ignore: deprecated_member_use -import 'dart:js_util'; @JS() external Exec get exec; @@ -46,9 +44,14 @@ extension type Exec._(JSObject it) { ignoreReturnCode: !failOnNonZeroExit, ); try { - final exitCode = await promiseToFuture( - _exec(commandLine, args.map((arg) => arg.toJS).toList().toJS, options), - ); + final jsExitCode = + await _exec( + commandLine, + args.map((arg) => arg.toJS).toList().toJS, + options, + ).toDart; + + final exitCode = (jsExitCode as JSNumber).toDartInt; return ExecResult( exitCode: exitCode, stdout: stdout.toString(), From d2a8415b804940ea9bcf199c5fe158d87883cb5b Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 16:53:59 -0700 Subject: [PATCH 10/36] Migrated Worker Bee E2E --- packages/worker_bee/e2e_test/pubspec.yaml | 3 +++ .../e2e_test/test/preamble_test.dart | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/worker_bee/e2e_test/pubspec.yaml b/packages/worker_bee/e2e_test/pubspec.yaml index be47965950..caf4f78525 100644 --- a/packages/worker_bee/e2e_test/pubspec.yaml +++ b/packages/worker_bee/e2e_test/pubspec.yaml @@ -5,6 +5,9 @@ publish_to: none environment: sdk: ^3.7.0 +dependencies: + web: ^1.1.1 + dependency_overrides: aws_common: path: ../../aws_common diff --git a/packages/worker_bee/e2e_test/test/preamble_test.dart b/packages/worker_bee/e2e_test/test/preamble_test.dart index 13eb9abad0..a262b92059 100644 --- a/packages/worker_bee/e2e_test/test/preamble_test.dart +++ b/packages/worker_bee/e2e_test/test/preamble_test.dart @@ -4,22 +4,28 @@ @TestOn('browser') library; -//ignore: deprecated_member_use -import 'dart:html'; +import 'dart:async'; +import 'dart:js_interop'; import 'package:test/test.dart'; +import 'package:web/web.dart'; void main() { Future badAssignmentTest(String jsEntrypoint) async { - final worker = Worker(jsEntrypoint); - worker.postMessage('NoWorker'); + final worker = Worker(jsEntrypoint.toJS); + worker.postMessage('NoWorker'.toJS); final errors = []; - worker.onError.listen(errors.add); + final Function errorCallback = errors.add; + worker.onerror = errorCallback.toJS; + + final firstMessageCompleter = Completer(); + final Function messageCallback = firstMessageCompleter.complete; + worker.onmessage = messageCallback.toJS; late dynamic data; await expectLater( - worker.onMessage.first, + firstMessageCompleter.future, completion( isA().having( (message) => data = message.data, From 963fd633fa29f15095c479df769d12a28479ecb4 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 16:57:47 -0700 Subject: [PATCH 11/36] Migrated Worker Bee --- packages/worker_bee/worker_bee/lib/src/worker_bee_js.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/worker_bee/worker_bee/lib/src/worker_bee_js.dart b/packages/worker_bee/worker_bee/lib/src/worker_bee_js.dart index 3651d8cf54..6752deca9e 100644 --- a/packages/worker_bee/worker_bee/lib/src/worker_bee_js.dart +++ b/packages/worker_bee/worker_bee/lib/src/worker_bee_js.dart @@ -1,6 +1,4 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -//ignore: deprecated_member_use -export 'package:js/js.dart'; export 'package:stream_channel/stream_channel.dart'; From 44d705507310db8b48d2f7d29b582375ec988b41 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 16:59:51 -0700 Subject: [PATCH 12/36] Migrated Template --- templates/dart-package/__brick__/example/web/main.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/templates/dart-package/__brick__/example/web/main.dart b/templates/dart-package/__brick__/example/web/main.dart index 6ef3c9fc2d..7f3a4e9d2c 100644 --- a/templates/dart-package/__brick__/example/web/main.dart +++ b/templates/dart-package/__brick__/example/web/main.dart @@ -1,6 +1,5 @@ -//ignore: deprecated_member_use -import 'dart:html'; +import 'package:web/web.dart'; void main() { - querySelector('#output')?.text = 'Your Amplify app is running.'; + document.querySelector('#output')?.text = 'Your Amplify app is running.'; } From 95d8188e244481e4a7de12e05a3a4bff2ad7241a Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 17:20:54 -0700 Subject: [PATCH 13/36] Pinned analyzer to 7.3.X Other dependencies do not support ^7.4.0 yet --- packages/worker_bee/worker_bee_builder/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/worker_bee/worker_bee_builder/pubspec.yaml b/packages/worker_bee/worker_bee_builder/pubspec.yaml index aef5122385..68997ba675 100644 --- a/packages/worker_bee/worker_bee_builder/pubspec.yaml +++ b/packages/worker_bee/worker_bee_builder/pubspec.yaml @@ -9,7 +9,7 @@ environment: sdk: ^3.7.0 dependencies: - analyzer: ^7.3.0 + analyzer: ">=7.3.0 <7.4.0" async: ^2.10.0 build: ^2.2.1 code_builder: ^4.10.1 From 1108c947533c650ccbc5bd8c73a971cf07cf9f66 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 28 Apr 2025 17:21:12 -0700 Subject: [PATCH 14/36] Fixed Sigv4 Analyzer errors --- packages/aws_signature_v4/example/web/main.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/aws_signature_v4/example/web/main.dart b/packages/aws_signature_v4/example/web/main.dart index 49fec6cb70..e8a3b935da 100644 --- a/packages/aws_signature_v4/example/web/main.dart +++ b/packages/aws_signature_v4/example/web/main.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:js_interop'; -import 'dart:typed_data'; import 'package:aws_common/aws_common.dart'; import 'package:aws_signature_v4/aws_signature_v4.dart'; @@ -68,7 +67,7 @@ Future upload(BucketUpload bucketUpload) async { final fileBlob = file.slice(); final reader = FileReader()..readAsArrayBuffer(fileBlob); await reader.onLoadEnd.first; - final fileBytes = reader.result as Uint8List?; + final fileBytes = (reader.result as JSUint8Array?)?.toDart; if (fileBytes == null) { throw Exception('Cannot read bytes from Blob.'); } @@ -154,10 +153,10 @@ extension on HTMLButtonElement { void setBusy(bool busy) { if (busy) { setAttribute('aria-busy', 'true'); - text = 'Uploading...'; + textContent = 'Uploading...'; } else { removeAttribute('aria-busy'); - text = 'Upload'; + textContent = 'Upload'; } } } From a9e3aa2b376842116da9f272ed5b5f9a9ebbe4c8 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 2 May 2025 16:11:39 -0700 Subject: [PATCH 15/36] Migrated aws_common --- .../lib/src/http/aws_http_client_js.dart | 52 ++- .../lib/src/io/aws_file_platform_html.dart | 40 +- packages/aws_common/lib/src/js/abort.dart | 55 --- packages/aws_common/lib/src/js/common.dart | 333 +---------------- packages/aws_common/lib/src/js/fetch.dart | 349 +----------------- .../aws_common/lib/src/js/indexed_db.dart | 258 +------------ packages/aws_common/lib/src/js/promise.dart | 50 --- .../lib/src/js/readable_stream.dart | 175 ++------- .../util/get_base_element_href_from_dom.dart | 3 +- packages/aws_common/pubspec.yaml | 2 + .../test/io/aws_file_html_test.dart | 17 +- .../test/js/readable_stream_test.dart | 1 + 12 files changed, 139 insertions(+), 1196 deletions(-) delete mode 100644 packages/aws_common/lib/src/js/abort.dart delete mode 100644 packages/aws_common/lib/src/js/promise.dart 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 e9a2d72967..6d1c2d38d4 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,16 @@ // SPDX-License-Identifier: Apache-2.0 import 'dart:async'; +import 'dart:js_interop'; 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 +48,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,21 +75,32 @@ class AWSHttpClientImpl extends AWSHttpClient { }, ) .takeUntil(cancelTrigger.future); - final body = Uint8List.fromList(await collectBytes(stream)); + JSAny body; + + 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 streamView = resp.body; + final resp = + await window + .fetch( + request.uri.toString().toJS, + RequestInit( + method: request.method.name, + headers: request.headers.jsify() as HeadersInit, + body: body, + signal: abortController.signal, + redirect: redirect, + ), + ) + .toDart; + + final streamView = resp.body!; final bodyController = StreamController>( sync: true, // In downstream operations, we may only have access to the body stream @@ -107,16 +120,19 @@ class AWSHttpClientImpl extends AWSHttpClient { } responseProgressController.close(); }; + unawaited( streamView.progress.forward( responseProgressController, cancelOnError: true, ), ); - unawaited(streamView.forward(bodyController, cancelOnError: true)); + unawaited( + streamView.stream.forward(bodyController, cancelOnError: true), + ); final streamedResponse = AWSStreamedHttpResponse( statusCode: resp.status, - headers: resp.headers, + headers: resp.headers.dartify() as Map, body: bodyController.stream.tap( null, onDone: () { 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 ac8113a3ca..bec02c3f00 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,8 @@ 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.map((item) => item.toJS).toList().toJS; + final retrievedBlob = Blob(blobParts); _size = retrievedBlob.size; @@ -221,9 +216,22 @@ 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 JSArray; + + final result = jsResult.toDart.map((item) => item.toDartInt).toList(); + yield result; 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 9e45abeb7c..0000000000 --- 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 b9ccc5fded..36f2c6804c 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,184 +56,13 @@ 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()], + void postMessage(Object? o, [List? transfer]) => callMethod( + 'postMessage'.toJS, + o.jsify(), + transfer?.map((item) => item.jsify()).toList().toJS, ); } -/// {@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; -} - /// Browser-based JSON utilities. @JS() @staticInterop @@ -367,21 +70,3 @@ 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; -} diff --git a/packages/aws_common/lib/src/js/fetch.dart b/packages/aws_common/lib/src/js/fetch.dart index f7a8c4979a..fa6ce7428c 100644 --- a/packages/aws_common/lib/src/js/fetch.dart +++ b/packages/aws_common/lib/src/js/fetch.dart @@ -1,92 +1,13 @@ // 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, -} +import 'dart:js_interop'; -/// 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:aws_common/src/js/common.dart'; +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$, @@ -100,270 +21,12 @@ enum RequestRedirect with JSEnum { 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]); +external JSPromise _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; + return _fetch(url, init).toDart; } diff --git a/packages/aws_common/lib/src/js/indexed_db.dart b/packages/aws_common/lib/src/js/indexed_db.dart index b62e2eb163..cb372451a2 100644 --- a/packages/aws_common/lib/src/js/indexed_db.dart +++ b/packages/aws_common/lib/src/js/indexed_db.dart @@ -2,272 +2,57 @@ // 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(_) => completer.complete(result); + final Function onSuccessCallback = onSuccess; - /// Fired when an error caused a request to fail. - set onerror(EventHandler newValue) { - js_util.setProperty(this, 'onerror', allowInterop(newValue)); - } + void onError(_) => completer.completeError('Could not complete IDBRequest'.toJS); + final Function onErrorCallback = onError; - /// 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 = onSuccessCallback.toJS; + onerror = onErrorCallback.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 +70,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 0313d01db7..0000000000 --- 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 679b21bad7..4fce1c06c6 100644 --- a/packages/aws_common/lib/src/js/readable_stream.dart +++ b/packages/aws_common/lib/src/js/readable_stream.dart @@ -2,24 +2,19 @@ // 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. @@ -79,18 +74,18 @@ abstract class UnderlyingSource { start == null ? undefined : start is Future Function(ReadableStreamController) - ? allowInterop((ReadableStreamController controller) { - return Promise.fromFuture(start(controller)); - }) - : allowInterop(start); + ? (ReadableStreamController controller) { + return start(controller).toJS; + } + : start; final pullFn = pull == null ? undefined : pull is Future Function(ReadableStreamController) - ? allowInterop((ReadableStreamController controller) { - return Promise.fromFuture(pull(controller)); - }) - : allowInterop(pull); + ? (ReadableStreamController controller) { + return pull(controller).toJS; + } + : pull; final cancelFn = cancel == null ? undefined @@ -99,28 +94,25 @@ abstract class UnderlyingSource { 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, + ? (String? reason, ReadableStreamController? controller) { + return cancel(reason, controller).toJS; + } + : cancel; + return UnderlyingSource.__( + start: startFn?.toExternalReference, + pull: pullFn?.toExternalReference, + cancel: cancelFn?.toJS, + type: type.jsValue?.toJS, + autoAllocateChunkSize: autoAllocateChunkSize?.toJS ?? undefined, ); } - external factory UnderlyingSource._({ - Object? start, - Object? pull, - Object? cancel, - String? type, - int? autoAllocateChunkSize, + external factory UnderlyingSource.__({ + ExternalDartReference? start, + ExternalDartReference? pull, + JSFunction? cancel, + JSString? type, + JSNumber? autoAllocateChunkSize, }); } @@ -139,13 +131,7 @@ enum ReadableStreamType with JSEnum { /// /// Similar to a Dart [StreamController]. /// {@endtemplate} -@JS() -@anonymous -@staticInterop -abstract class ReadableStreamController {} - -/// {@macro aws_common.js.readable_stream_controller} -extension PropsReadableStreamController on ReadableStreamController { +extension type ReadableStreamController._(JSObject _) implements JSObject { /// The desired size required to fill the stream's internal queue. external int get desiredSize; @@ -156,35 +142,6 @@ extension PropsReadableStreamController on ReadableStreamController { 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 +150,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 +158,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 +165,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,63 +173,29 @@ 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`. 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 9b754526f0..7d2e80f9fa 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 a2601bae4d..0f8a250d24 100644 --- a/packages/aws_common/pubspec.yaml +++ b/packages/aws_common/pubspec.yaml @@ -23,6 +23,8 @@ dependencies: path: ">=1.8.0 <2.0.0" stream_transform: ^2.0.0 uuid: ">=3.0.6 <5.0.0" + web: ^1.1.1 + http: ^1.3.0 dev_dependencies: amplify_lints: ">=3.1.1 <3.2.0" 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 1122c79c91..d2ad6da3ae 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 f7bd959322..9fe68f79e2 100644 --- a/packages/aws_common/test/js/readable_stream_test.dart +++ b/packages/aws_common/test/js/readable_stream_test.dart @@ -9,6 +9,7 @@ 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() { From dddac4d92904db23e955f02e3998ec57ed7e9927 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 2 May 2025 16:11:56 -0700 Subject: [PATCH 16/36] Migrated worker_bee --- .../worker_bee/lib/src/js/impl.dart | 76 ++++++++++++------- .../lib/src/js/message_port_channel.dart | 44 ++++++++--- .../worker_bee/lib/src/js/preamble.dart | 60 ++++++++------- packages/worker_bee/worker_bee/pubspec.yaml | 1 + 4 files changed, 113 insertions(+), 68 deletions(-) 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 5d5cd23d2e..41366b8633 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/impl.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/impl.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: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/message_port_channel.dart'; @@ -99,15 +101,18 @@ 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) { @@ -146,7 +151,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 +179,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 +189,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,29 +205,42 @@ 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?.toJSBox, + serialized.transfer.map((item) => item.toJSBox).toList().toJS, + ); }), ); - // 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 jsEventData = event.data; + String? eventData; + if (jsEventData?.isA() ?? false) { + jsEventData as JSString; + eventData = jsEventData.toDart; + } + + if (eventData is String) { + if (eventData == 'ready') { logger.verbose('Received ready event'); ready.complete(); return; } - if (event.data == 'done') { + if (eventData == 'done') { logger.verbose('Received done event'); done = true; return; @@ -249,7 +265,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 +283,7 @@ mixin WorkerBeeImpl logsController.add(message); }), ); - _worker!.postMessage(name, [jsLogsChannel.port2]); + _worker!.postMessage(name.toJS, jsLogsChannel.port2); await Future.any([ready.future, errorBeforeReady.future]); 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 4f8fa367b4..7ed3c32bed 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,10 +2,10 @@ // 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/serializers/serializers.dart'; import 'package:worker_bee/worker_bee.dart'; @@ -22,7 +22,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 +35,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') { + final jsEventData = event.data; + String? eventData; + if (jsEventData?.isA() ?? false) { + jsEventData as JSString; + eventData = jsEventData.toDart; + } + + if (eventData == 'done') { sink.close(); close(); return; } final data = _serializers.deserialize( - event.data, + eventData, specifiedType: _specifiedType, ); if (data is WorkerBeeException || data is! T) { @@ -71,16 +84,21 @@ class MessagePortChannel () => _serializers.serialize(event, specifiedType: _specifiedType), zoneValues: {#transfer: transfer}, ); - messagePort.postMessage(serialized, transfer); + messagePort.postMessage( + serialized?.toJSBox, + transfer.map((item) => item.toJSBox).toList().toJS, + ); } @override void addError(Object error, [StackTrace? stackTrace]) { messagePort.postMessage( - _serializers.serialize( - WorkerBeeExceptionImpl(error, stackTrace), - specifiedType: FullType.unspecified, - ), + _serializers + .serialize( + WorkerBeeExceptionImpl(error, stackTrace), + specifiedType: FullType.unspecified, + ) + ?.toJSBox, ); close(); } @@ -98,8 +116,10 @@ class MessagePortChannel Future close() async { if (_done.isCompleted) return; messagePort - ..postMessage('done') + ..postMessage('done'.toJS) ..close(); + + await _streamController.close(); _done.complete(); } 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 06fb297afe..d7de87ee96 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,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:collection/collection.dart'; +import 'package:web/web.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'; @@ -31,33 +32,36 @@ Future getWorkerAssignment() async { 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 Function onMessageCallback; + + void onMessage(Event event) { + event as MessageEvent; + final jsMessage = event.data; + String? message; + if (jsMessage.isA()) { + jsMessage as JSString; + message = jsMessage.toDart; + } + + final messagePort = event.ports.toDart.firstOrNull; + if (message is String && messagePort is MessagePort) { + self.removeEventListener('message', onMessageCallback.toJS); + assignmentCompleter.complete( + WorkerAssignment(message, MessagePortChannel(messagePort)), + ); + } else { + assignmentCompleter.completeError( + StateError( + 'Invalid worker assignment: ' + '${workerBeeSerializers.serialize(message)}', + ), + ); + } + } + + onMessageCallback = Zone.current.bindUnaryCallback(onMessage); + + self.addEventListener('message', onMessageCallback.toJS); return assignmentCompleter.future; }, onError: onError); } diff --git a/packages/worker_bee/worker_bee/pubspec.yaml b/packages/worker_bee/worker_bee/pubspec.yaml index 2fdf02a791..e867ca6888 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.1 <3.2.0" From 96aaec144a5f10b7548fc20856dddf4263257f9e Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 2 May 2025 16:12:08 -0700 Subject: [PATCH 17/36] Migrated amplify_analytics_pinpoint_dart --- .../index_db/indexed_db_adapter.dart | 74 ++++++++++++------- .../pubspec.yaml | 1 + 2 files changed, 47 insertions(+), 28 deletions(-) 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 fafc340478..c70e1f3c6e 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,35 @@ 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 Function onUpgradeNeededCallback = onUpgradeNeeded; + 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 = onUpgradeNeededCallback.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 +76,7 @@ class IndexedDbAdapter implements QueuedItemStore { @override Future addItem(String string) async { await _databaseOpenEvent; - await _getObjectStore().push({'value': string}).future; + await _getObjectStore().add(string.toJS).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 43a98ed7fe..66bfa65731 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.4 <0.8.0" smithy_aws: ">=0.7.4 <0.8.0" uuid: ">=3.0.6 <5.0.0" + web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.1 <3.2.0" From 98e68cc995a0c32a75dfb6b4262f2756e59d7ef2 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 2 May 2025 17:01:11 -0700 Subject: [PATCH 18/36] Migrated amplify_secure_storage_dart --- .../platforms/amplify_secure_storage_web.dart | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) 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 1ab958ceca..7dad3af3b5 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,30 @@ 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, + IDBObjectStoreParameters(keyPath: 'id'.toJS, autoIncrement: true), + ); + } + } + + final Function onUpgradeNeededCallback = onUpgradeNeeded; + 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 = onUpgradeNeededCallback.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 +127,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 +138,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 +150,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()); } From ee9cba51c3162d098829727d1c394e0e8967817a Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 2 May 2025 17:01:26 -0700 Subject: [PATCH 19/36] Fixed compile time errors --- packages/aws_common/lib/src/js/common.dart | 2 +- packages/aws_common/lib/src/js/readable_stream.dart | 13 +++++++------ .../amplify_secure_storage_dart/pubspec.yaml | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/aws_common/lib/src/js/common.dart b/packages/aws_common/lib/src/js/common.dart index 36f2c6804c..9d96b5920f 100644 --- a/packages/aws_common/lib/src/js/common.dart +++ b/packages/aws_common/lib/src/js/common.dart @@ -68,5 +68,5 @@ extension type GlobalScope._(JSObject _) implements EventTarget, JSObject { @staticInterop abstract class JSON { /// Stringifies a JSON-like object. - external static String stringify(Object? object); + external static String stringify(JSAny? object); } diff --git a/packages/aws_common/lib/src/js/readable_stream.dart b/packages/aws_common/lib/src/js/readable_stream.dart index 4fce1c06c6..dba7bfa667 100644 --- a/packages/aws_common/lib/src/js/readable_stream.dart +++ b/packages/aws_common/lib/src/js/readable_stream.dart @@ -94,7 +94,7 @@ extension type UnderlyingSource._(JSObject _) implements JSObject { String? reason, ReadableStreamController? controller, ]) - ? (String? reason, ReadableStreamController? controller) { + ? ([String? reason, ReadableStreamController? controller]) { return cancel(reason, controller).toJS; } : cancel; @@ -139,7 +139,7 @@ extension type ReadableStreamController._(JSObject _) implements JSObject { external void close(); /// Enqueues a given chunk in the associated stream. - external void enqueue(Uint8List chunk); + external void enqueue(JSUint8Array chunk); } /// Used to expand [ReadableStream] and treat `ReadableStream.stream` as a @@ -199,7 +199,7 @@ 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; @@ -241,10 +241,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); @@ -277,7 +278,7 @@ extension StreamToReadableStream on Stream> { } try { final chunk = await queue.next; - controller.enqueue(Uint8List.fromList(chunk)); + controller.enqueue(Uint8List.fromList(chunk).toJS); } on Object catch (e, st) { await queue.cancel(); // Allow error to propagate before closing. diff --git a/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml b/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml index 1f44361d96..875b445f1d 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.4 <0.4.0" From 8ed83063c079610b4ed8f281c19cfebaf179d475 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 2 May 2025 17:11:02 -0700 Subject: [PATCH 20/36] Fixed compiler errors --- .../lib/src/js/readable_stream.dart | 21 +++---------------- .../test/js/readable_stream_test.dart | 5 +++-- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/packages/aws_common/lib/src/js/readable_stream.dart b/packages/aws_common/lib/src/js/readable_stream.dart index dba7bfa667..7fa2f440bc 100644 --- a/packages/aws_common/lib/src/js/readable_stream.dart +++ b/packages/aws_common/lib/src/js/readable_stream.dart @@ -52,11 +52,7 @@ extension type UnderlyingSource._(JSObject _) implements JSObject { /// 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, + FutureOr Function()? cancel, /// This property controls what type of readable stream is being dealt with. ReadableStreamType type = ReadableStreamType.default$, @@ -86,22 +82,11 @@ extension type UnderlyingSource._(JSObject _) implements JSObject { return pull(controller).toJS; } : pull; - final cancelFn = - cancel == null - ? undefined - : cancel - is Future Function([ - String? reason, - ReadableStreamController? controller, - ]) - ? ([String? reason, ReadableStreamController? controller]) { - return cancel(reason, controller).toJS; - } - : cancel; + return UnderlyingSource.__( start: startFn?.toExternalReference, pull: pullFn?.toExternalReference, - cancel: cancelFn?.toJS, + cancel: cancel?.toJS, type: type.jsValue?.toJS, autoAllocateChunkSize: autoAllocateChunkSize?.toJS ?? undefined, ); diff --git a/packages/aws_common/test/js/readable_stream_test.dart b/packages/aws_common/test/js/readable_stream_test.dart index 9fe68f79e2..4c66f0fbe6 100644 --- a/packages/aws_common/test/js/readable_stream_test.dart +++ b/packages/aws_common/test/js/readable_stream_test.dart @@ -5,6 +5,7 @@ library; import 'dart:async'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:aws_common/src/js/readable_stream.dart'; @@ -17,8 +18,8 @@ void main() { UnderlyingSource( start: (controller) { controller - ..enqueue(Uint8List.fromList([1, 2, 3, 4, 5])) - ..enqueue(Uint8List.fromList([6, 7, 8, 9, 0])) + ..enqueue(Uint8List.fromList([1, 2, 3, 4, 5]).toJS) + ..enqueue(Uint8List.fromList([6, 7, 8, 9, 0]).toJS) ..close(); }, ), From cd4edb04868b4b042ca4e65cb9b32cb209ece499 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 2 May 2025 17:13:47 -0700 Subject: [PATCH 21/36] fixed compiler errors --- packages/aws_common/lib/src/js/readable_stream.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws_common/lib/src/js/readable_stream.dart b/packages/aws_common/lib/src/js/readable_stream.dart index 7fa2f440bc..766725b056 100644 --- a/packages/aws_common/lib/src/js/readable_stream.dart +++ b/packages/aws_common/lib/src/js/readable_stream.dart @@ -52,7 +52,7 @@ extension type UnderlyingSource._(JSObject _) implements JSObject { /// 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()? cancel, + JSPromise Function()? cancel, /// This property controls what type of readable stream is being dealt with. ReadableStreamType type = ReadableStreamType.default$, From 3ff6f629edc9bedd709d9fa51aea2a5ef82703ae Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 5 May 2025 01:38:13 -0700 Subject: [PATCH 22/36] Migrated amplify_auth_cognito_dart --- .../src/asf/asf_device_info_collector.js.dart | 34 +++---------------- .../hosted_ui/hosted_ui_platform_html.dart | 3 +- .../hosted_ui/initial_parameters_html.dart | 3 +- .../amplify_auth_cognito_dart/pubspec.yaml | 1 + 4 files changed, 8 insertions(+), 33 deletions(-) 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 21bc63414b..3f92b0c477 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/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 2112676d35..6a4d30f067 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 793f3b46dc..0c36d4cdcf 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/pubspec.yaml b/packages/auth/amplify_auth_cognito_dart/pubspec.yaml index c763d5e191..0591a31ac3 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.4 <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.4 <0.4.0" From 0826b04b3132096596ebd5fbe9eb02a0e4926084 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 5 May 2025 01:38:29 -0700 Subject: [PATCH 23/36] fixed compiler errors --- packages/aws_common/lib/src/js/indexed_db.dart | 6 ++---- .../lib/src/platforms/amplify_secure_storage_web.dart | 4 +--- packages/worker_bee/worker_bee/lib/src/js/preamble.dart | 9 +++++---- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/aws_common/lib/src/js/indexed_db.dart b/packages/aws_common/lib/src/js/indexed_db.dart index cb372451a2..7580df00be 100644 --- a/packages/aws_common/lib/src/js/indexed_db.dart +++ b/packages/aws_common/lib/src/js/indexed_db.dart @@ -26,13 +26,11 @@ extension PropsIDBRequest on IDBRequest { final completer = Completer.sync(); void onSuccess(_) => completer.complete(result); - final Function onSuccessCallback = onSuccess; void onError(_) => completer.completeError('Could not complete IDBRequest'.toJS); - final Function onErrorCallback = onError; - onsuccess = onSuccessCallback.toJS; - onerror = onErrorCallback.toJS; + onsuccess = onSuccess.toJS; + onerror = onError.toJS; return completer.future; } } 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 7dad3af3b5..d68367fdbd 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 @@ -105,10 +105,8 @@ class _IndexedDBStorage extends AmplifySecureStorageInterface { } } - final Function onUpgradeNeededCallback = onUpgradeNeeded; - final openRequest = indexedDB!.open(databaseName, 1) - ..onupgradeneeded = onUpgradeNeededCallback.toJS; + ..onupgradeneeded = onUpgradeNeeded.toJS; try { final result = await openRequest.future; 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 d7de87ee96..9083bae86e 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/preamble.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/preamble.dart @@ -32,7 +32,7 @@ Future getWorkerAssignment() async { return runTraced(() async { final assignmentCompleter = Completer.sync(); - late final Function onMessageCallback; + late final JSExportedDartFunction jsOnMessageCallback; void onMessage(Event event) { event as MessageEvent; @@ -45,7 +45,7 @@ Future getWorkerAssignment() async { final messagePort = event.ports.toDart.firstOrNull; if (message is String && messagePort is MessagePort) { - self.removeEventListener('message', onMessageCallback.toJS); + self.removeEventListener('message', jsOnMessageCallback); assignmentCompleter.complete( WorkerAssignment(message, MessagePortChannel(messagePort)), ); @@ -59,9 +59,10 @@ Future getWorkerAssignment() async { } } - onMessageCallback = Zone.current.bindUnaryCallback(onMessage); + final onMessageCallback = Zone.current.bindUnaryCallback(onMessage); + jsOnMessageCallback = onMessageCallback.toJS; - self.addEventListener('message', onMessageCallback.toJS); + self.addEventListener('message', jsOnMessageCallback); return assignmentCompleter.future; }, onError: onError); } From 7f194d2aa3e8bae09425fda0880fa2936c8a8021 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Mon, 5 May 2025 01:45:30 -0700 Subject: [PATCH 24/36] fixed compiler errors --- packages/aws_common/lib/src/js/indexed_db.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aws_common/lib/src/js/indexed_db.dart b/packages/aws_common/lib/src/js/indexed_db.dart index 7580df00be..1c1c2dc71d 100644 --- a/packages/aws_common/lib/src/js/indexed_db.dart +++ b/packages/aws_common/lib/src/js/indexed_db.dart @@ -18,16 +18,16 @@ extension PropsIDBVersionChangeEvent on IDBVersionChangeEvent { IDBOpenDBRequest get target => getProperty('target'.toJS); } - /// {@macro amplify_secure_storage_dart.idb_request} extension PropsIDBRequest on IDBRequest { /// Returns a [Future] which completes with the [result] of this request. Future get future { final completer = Completer.sync(); - void onSuccess(_) => completer.complete(result); + void onSuccess(Event _) => completer.complete(result); - void onError(_) => completer.completeError('Could not complete IDBRequest'.toJS); + void onError(Event _) => + completer.completeError('Could not complete IDBRequest'.toJS); onsuccess = onSuccess.toJS; onerror = onError.toJS; From e1680a5d6cbc1c07c3fb15874dd750a606653be6 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Wed, 7 May 2025 10:21:07 -0700 Subject: [PATCH 25/36] fixed compiler errors --- actions/lib/src/node/actions/http_request.dart | 4 +++- actions/pubspec.yaml | 1 + .../queued_item_store/index_db/indexed_db_adapter.dart | 4 +--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/actions/lib/src/node/actions/http_request.dart b/actions/lib/src/node/actions/http_request.dart index 959637604b..3db39096ae 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,7 @@ extension type HttpClient._(JSObject it) { String requestUrl, { Map headers = const {}, }) async { - final jsHeaders = headers.jsify() as JSObject; + final jsHeaders = headers.jsify() as HeadersInit; 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 1af428d06b..7a0bbaf535 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/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 c70e1f3c6e..0cb071bac6 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 @@ -51,10 +51,8 @@ class IndexedDbAdapter implements QueuedItemStore { } } - final Function onUpgradeNeededCallback = onUpgradeNeeded; - final openRequest = db.open(databaseName, 1) - ..onupgradeneeded = onUpgradeNeededCallback.toJS; + ..onupgradeneeded = onUpgradeNeeded.toJS; final result = await openRequest.future; if (result.isA()) { From 93c2d863b10e8448fd85ffd0044985a0750faa0a Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Thu, 8 May 2025 15:57:17 -0700 Subject: [PATCH 26/36] Migrated amplify_storage_s3 Example app --- .../integration_test/platform_test_html.dart | 2 +- .../utils/create_file/create_file_html.dart | 25 +++++++++++-------- .../amplify_storage_s3/example/pubspec.yaml | 1 + 3 files changed, 17 insertions(+), 11 deletions(-) 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 55e1398719..568050bd41 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_html.dart b/packages/storage/amplify_storage_s3/example/integration_test/utils/create_file/create_file_html.dart index d3d8b9de6b..6ce3a0faca 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 dadbe1867a..b042c44f2b 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 From 4d29a95dacbe8c5df3daeb4802a667410e9fceac Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Thu, 8 May 2025 15:59:38 -0700 Subject: [PATCH 27/36] Removed extra IDBObjectStore parameters --- .../lib/src/platforms/amplify_secure_storage_web.dart | 1 - .../secure_storage/amplify_secure_storage_dart/pubspec.yaml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) 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 d68367fdbd..fffbebb43e 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 @@ -100,7 +100,6 @@ class _IndexedDBStorage extends AmplifySecureStorageInterface { if (!(objectStoreNames?.contains(storeName) ?? false)) { database?.createObjectStore( storeName, - IDBObjectStoreParameters(keyPath: 'id'.toJS, autoIncrement: true), ); } } diff --git a/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml b/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml index 875b445f1d..3bae16a8a5 100644 --- a/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml +++ b/packages/secure_storage/amplify_secure_storage_dart/pubspec.yaml @@ -38,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.9.5 ffigen: ^9.0.0 test: ^1.22.1 From 17c8779124e94f54b0d376c69150ccd4e4d1cb39 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Thu, 8 May 2025 16:12:19 -0700 Subject: [PATCH 28/36] Fixed JS/Dart serialization for WebWorkers --- packages/aws_common/lib/src/js/common.dart | 7 +-- packages/aws_common/pubspec.yaml | 2 +- .../worker_bee/lib/src/js/impl.dart | 47 +++++++++++-------- .../worker_bee/lib/src/js/js_extensions.dart | 27 +++++++++++ .../lib/src/js/message_port_channel.dart | 40 +++++++--------- .../worker_bee/lib/src/js/preamble.dart | 27 ++++++----- 6 files changed, 90 insertions(+), 60 deletions(-) create mode 100644 packages/worker_bee/worker_bee/lib/src/js/js_extensions.dart diff --git a/packages/aws_common/lib/src/js/common.dart b/packages/aws_common/lib/src/js/common.dart index 9d96b5920f..5b0e833f6c 100644 --- a/packages/aws_common/lib/src/js/common.dart +++ b/packages/aws_common/lib/src/js/common.dart @@ -56,11 +56,8 @@ extension type GlobalScope._(JSObject _) implements EventTarget, JSObject { /// /// When called on a [Window], this sends a message to the parent window /// object. - void postMessage(Object? o, [List? transfer]) => callMethod( - 'postMessage'.toJS, - o.jsify(), - transfer?.map((item) => item.jsify()).toList().toJS, - ); + void postMessage(JSAny? o, [JSArray? transfer]) => + callMethod('postMessage'.toJS, o, transfer); } /// Browser-based JSON utilities. diff --git a/packages/aws_common/pubspec.yaml b/packages/aws_common/pubspec.yaml index 0f8a250d24..e6e43de556 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.6.0 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" @@ -24,7 +25,6 @@ dependencies: stream_transform: ^2.0.0 uuid: ">=3.0.6 <5.0.0" web: ^1.1.1 - http: ^1.3.0 dev_dependencies: amplify_lints: ">=3.1.1 <3.2.0" 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 41366b8633..f20b3b19e4 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/impl.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/impl.dart @@ -11,6 +11,7 @@ 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'; @@ -90,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); } @@ -118,20 +122,26 @@ mixin WorkerBeeImpl 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(); @@ -219,35 +229,33 @@ mixin WorkerBeeImpl Zone.current.bindUnaryCallback((message) { logger.verbose('Sending message: $message'); final serialized = _serialize(message); + _worker!.postMessage( - serialized.value?.toJSBox, - serialized.transfer.map((item) => item.toJSBox).toList().toJS, + serialized.value?.toJSBoxOrCast, + serialized.transfer.toJSBoxOrCast, ); }), ); void onMessage(MessageEvent event) { - final jsEventData = event.data; - String? eventData; - if (jsEventData?.isA() ?? false) { - jsEventData as JSString; - eventData = jsEventData.toDart; - } + final eventData = event.data; - if (eventData is String) { - if (eventData == 'ready') { + if (eventData.isA()) { + eventData as JSString; + final state = eventData.toDart; + + if (state == 'ready') { logger.verbose('Received ready event'); ready.complete(); return; } - if (eventData == '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) { @@ -283,7 +291,8 @@ mixin WorkerBeeImpl logsController.add(message); }), ); - _worker!.postMessage(name.toJS, 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 0000000000..958edb0ff8 --- /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 7ed3c32bed..f5c833d9ec 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 @@ -7,6 +7,7 @@ import 'dart:js_interop'; 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'; @@ -44,17 +45,17 @@ class MessagePortChannel .transform( StreamTransformer.fromHandlers( handleData: Zone.current.bindBinaryCallback((event, sink) { - final jsEventData = event.data; - String? eventData; - if (jsEventData?.isA() ?? false) { - jsEventData as JSString; - eventData = jsEventData.toDart; - } + final eventData = event.data; + + if (eventData.isA()) { + eventData as JSString; + final state = eventData.toDart; - if (eventData == 'done') { - sink.close(); - close(); - return; + if (state == 'done') { + sink.close(); + close(); + return; + } } final data = _serializers.deserialize( eventData, @@ -84,22 +85,17 @@ class MessagePortChannel () => _serializers.serialize(event, specifiedType: _specifiedType), zoneValues: {#transfer: transfer}, ); - messagePort.postMessage( - serialized?.toJSBox, - transfer.map((item) => item.toJSBox).toList().toJS, - ); + messagePort.postMessage(serialized?.toJSBoxOrCast, transfer.toJSBoxOrCast); } @override void addError(Object error, [StackTrace? stackTrace]) { - messagePort.postMessage( - _serializers - .serialize( - WorkerBeeExceptionImpl(error, stackTrace), - specifiedType: FullType.unspecified, - ) - ?.toJSBox, + final serialized = _serializers.serialize( + WorkerBeeExceptionImpl(error, stackTrace), + specifiedType: FullType.unspecified, ); + + messagePort.postMessage(serialized?.toJSBoxOrCast); close(); } @@ -119,8 +115,8 @@ class MessagePortChannel ..postMessage('done'.toJS) ..close(); - await _streamController.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 9083bae86e..6ba668b4a1 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/preamble.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/preamble.dart @@ -8,6 +8,7 @@ import 'dart:js_interop'; import 'package:aws_common/src/js/common.dart'; import 'package:built_value/serializer.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'; @@ -26,7 +27,9 @@ 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, ); } @@ -34,32 +37,30 @@ Future getWorkerAssignment() async { final assignmentCompleter = Completer.sync(); late final JSExportedDartFunction jsOnMessageCallback; - void onMessage(Event event) { - event as MessageEvent; - final jsMessage = event.data; - String? message; - if (jsMessage.isA()) { - jsMessage as JSString; - message = jsMessage.toDart; - } + void onMessage(MessageEvent event) { + final eventData = event.data; final messagePort = event.ports.toDart.firstOrNull; - if (message is String && messagePort is MessagePort) { + if (eventData.isA() && messagePort is MessagePort) { + eventData as JSString; + final state = eventData.toDart; + self.removeEventListener('message', jsOnMessageCallback); assignmentCompleter.complete( - WorkerAssignment(message, MessagePortChannel(messagePort)), + WorkerAssignment(state, MessagePortChannel(messagePort)), ); } else { assignmentCompleter.completeError( StateError( 'Invalid worker assignment: ' - '${workerBeeSerializers.serialize(message)}', + '${workerBeeSerializers.serialize(eventData)}', ), ); } } - final onMessageCallback = Zone.current.bindUnaryCallback(onMessage); + final onMessageCallback = Zone.current + .bindUnaryCallback(onMessage); jsOnMessageCallback = onMessageCallback.toJS; self.addEventListener('message', jsOnMessageCallback); From ffbb6c40d15c7f9c97f21c7946c4c0112c2fe69c Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Thu, 8 May 2025 16:16:35 -0700 Subject: [PATCH 29/36] chore(): fixed formatting errors --- packages/aws_common/lib/src/http/aws_http_client_js.dart | 4 +--- .../lib/src/platforms/amplify_secure_storage_web.dart | 4 +--- packages/worker_bee/worker_bee/lib/src/js/impl.dart | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) 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 6d1c2d38d4..6a8d26457b 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 @@ -127,9 +127,7 @@ class AWSHttpClientImpl extends AWSHttpClient { cancelOnError: true, ), ); - unawaited( - streamView.stream.forward(bodyController, cancelOnError: true), - ); + unawaited(streamView.stream.forward(bodyController, cancelOnError: true)); final streamedResponse = AWSStreamedHttpResponse( statusCode: resp.status, headers: resp.headers.dartify() as Map, 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 fffbebb43e..0eda62e404 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 @@ -98,9 +98,7 @@ class _IndexedDBStorage extends AmplifySecureStorageInterface { final database = event.target?.getProperty('result'.toJS); final objectStoreNames = database?.objectStoreNames; if (!(objectStoreNames?.contains(storeName) ?? false)) { - database?.createObjectStore( - storeName, - ); + database?.createObjectStore(storeName); } } 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 f20b3b19e4..327736ec48 100644 --- a/packages/worker_bee/worker_bee/lib/src/js/impl.dart +++ b/packages/worker_bee/worker_bee/lib/src/js/impl.dart @@ -243,7 +243,7 @@ mixin WorkerBeeImpl if (eventData.isA()) { eventData as JSString; final state = eventData.toDart; - + if (state == 'ready') { logger.verbose('Received ready event'); ready.complete(); From ace676629abc6fef7786e5d6e0ca406a8ccf7ae8 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 9 May 2025 11:48:07 -0700 Subject: [PATCH 30/36] chore(analytics): re-added value property instead of using a JSString --- .../queued_item_store/index_db/indexed_db_adapter.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 0cb071bac6..8e353e5b9e 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 @@ -74,7 +74,9 @@ class IndexedDbAdapter implements QueuedItemStore { @override Future addItem(String string) async { await _databaseOpenEvent; - await _getObjectStore().add(string.toJS).future; + final item = JSObject()..setProperty('value'.toJS, string.toJS); + + await _getObjectStore().add(item).future; } @override From 509e0572d6125388aafc879d195ba408ea8bbf9a Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 9 May 2025 16:47:53 -0700 Subject: [PATCH 31/36] chore(): cleaned up header converting as jsify does not convert properly --- .../lib/src/node/actions/http_request.dart | 6 +++- .../lib/src/http/aws_http_client_js.dart | 34 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/actions/lib/src/node/actions/http_request.dart b/actions/lib/src/node/actions/http_request.dart index 3db39096ae..5bc4351636 100644 --- a/actions/lib/src/node/actions/http_request.dart +++ b/actions/lib/src/node/actions/http_request.dart @@ -20,7 +20,11 @@ extension type HttpClient._(JSObject it) { String requestUrl, { Map headers = const {}, }) async { - final jsHeaders = headers.jsify() as HeadersInit; + final jsHeaders = Headers(); + for(final entry in request.headers.entries) { + requestHeaders.append(entry.key, entry.value); + } + final response = await _getJson(requestUrl, jsHeaders).toDart; final result = response as TypedResponse; if (result.statusCode != 200) { 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 6a8d26457b..7cbc3add0a 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 @@ -3,6 +3,7 @@ import 'dart:async'; import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'dart:typed_data'; import 'package:async/async.dart'; @@ -75,24 +76,32 @@ class AWSHttpClientImpl extends AWSHttpClient { }, ) .takeUntil(cancelTrigger.future); - JSAny body; - - if (request.scheme == 'http' || - supportedProtocols.supports(AlpnProtocol.http1_1)) { - body = Uint8List.fromList(await collectBytes(stream)).toJS; - } else { - body = stream.asReadableStream(); + 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 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.name, - headers: request.headers.jsify() as HeadersInit, + headers: requestHeaders, body: body, signal: abortController.signal, redirect: redirect, @@ -128,9 +137,16 @@ class AWSHttpClientImpl extends AWSHttpClient { ), ); 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.dartify() as Map, + headers: responseHeaders, body: bodyController.stream.tap( null, onDone: () { From b3a1061dd83b6d1ac7b87b8a2495944c7c31d7b4 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 9 May 2025 17:04:41 -0700 Subject: [PATCH 32/36] chore(): fixed typo --- actions/lib/src/node/actions/http_request.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/lib/src/node/actions/http_request.dart b/actions/lib/src/node/actions/http_request.dart index 5bc4351636..0b69329265 100644 --- a/actions/lib/src/node/actions/http_request.dart +++ b/actions/lib/src/node/actions/http_request.dart @@ -21,8 +21,8 @@ extension type HttpClient._(JSObject it) { Map headers = const {}, }) async { final jsHeaders = Headers(); - for(final entry in request.headers.entries) { - requestHeaders.append(entry.key, entry.value); + for(final entry in headers.entries) { + jsHeaders.append(entry.key, entry.value); } final response = await _getJson(requestUrl, jsHeaders).toDart; From 9ef4cef34122cc84d8a1adac9d615da7e79882b7 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Fri, 9 May 2025 17:07:21 -0700 Subject: [PATCH 33/36] chore(): fixed formatting --- packages/aws_common/lib/src/http/aws_http_client_js.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 7cbc3add0a..46056bed16 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 @@ -91,7 +91,7 @@ class AWSHttpClientImpl extends AWSHttpClient { if (completer.isCanceled) return; final requestHeaders = Headers(); - for(final entry in request.headers.entries) { + for (final entry in request.headers.entries) { requestHeaders.append(entry.key, entry.value); } @@ -142,6 +142,7 @@ class AWSHttpClientImpl extends AWSHttpClient { void headerBuilder(JSString value, JSString key, JSAny object) { responseHeaders[key.toDart] = value.toDart; } + resp.headers.callMethod('forEach'.toJS, headerBuilder.toJS); final streamedResponse = AWSStreamedHttpResponse( From b317734f35d7be9b7869d029af4dbec7863539d0 Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Tue, 13 May 2025 16:16:50 -0700 Subject: [PATCH 34/36] Chore() updated HTTP Method to be upercase --- packages/aws_common/lib/src/http/aws_http_client_js.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 46056bed16..e41098be4d 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 @@ -100,7 +100,7 @@ class AWSHttpClientImpl extends AWSHttpClient { .fetch( request.uri.toString().toJS, RequestInit( - method: request.method.name, + method: request.method.value, headers: requestHeaders, body: body, signal: abortController.signal, From 36eaa2a3eb066da7f3f69a70fa563130fa24f31e Mon Sep 17 00:00:00 2001 From: Tyler-Larkin Date: Tue, 13 May 2025 16:18:20 -0700 Subject: [PATCH 35/36] chore(): removed unused functions. --- packages/aws_common/lib/src/js/fetch.dart | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/aws_common/lib/src/js/fetch.dart b/packages/aws_common/lib/src/js/fetch.dart index fa6ce7428c..a862cdca43 100644 --- a/packages/aws_common/lib/src/js/fetch.dart +++ b/packages/aws_common/lib/src/js/fetch.dart @@ -1,8 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'dart:js_interop'; - import 'package:aws_common/src/js/common.dart'; import 'package:web/web.dart'; @@ -20,13 +18,3 @@ enum RequestRedirectValues with JSEnum { /// Caller intends to process the response in another context. manual, } - -@JS('fetch') -external JSPromise _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).toDart; -} From 8b9ebfa67a99e6ecf45f7808a3aab370ae2bda58 Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Wed, 14 May 2025 11:11:48 -0700 Subject: [PATCH 36/36] chore(test): added emailmfa attribute to first user sign up in test (#6141) * changed mfa preference flow for verifying if we can set sms as preferred and forego selection of mfa type upon sign in * removed duplicate test and changed "because" clause of test * changed test text to properly reflect the flow of the test --- .../mfa_sms_email_required_test.dart | 56 ++++++++----------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/packages/auth/amplify_auth_cognito/example/integration_test/mfa_sms_email_required_test.dart b/packages/auth/amplify_auth_cognito/example/integration_test/mfa_sms_email_required_test.dart index f2fc213ee8..a787d2809d 100644 --- a/packages/auth/amplify_auth_cognito/example/integration_test/mfa_sms_email_required_test.dart +++ b/packages/auth/amplify_auth_cognito/example/integration_test/mfa_sms_email_required_test.dart @@ -62,6 +62,7 @@ void main() { username: username, password: password, ); + check( resignInRes.nextStep.signInStep, ).equals(AuthSignInStep.confirmSignInWithOtpCode); @@ -99,9 +100,18 @@ void main() { username: username, password: password, ); + check( signInRes.nextStep.signInStep, - because: 'MFA is required so Cognito automatically enables SMS MFA', + because: 'MFA is required so Cognito will ask for MFA type', + ).equals(AuthSignInStep.continueSignInWithMfaSelection); + + final selectMfaRes = await Amplify.Auth.confirmSignIn( + confirmationValue: 'SMS', + ); + + check( + selectMfaRes.nextStep.signInStep, ).equals(AuthSignInStep.confirmSignInWithSmsMfaCode); final confirmRes = await Amplify.Auth.confirmSignIn( @@ -215,9 +225,18 @@ void main() { username: username, password: password, ); + check( signInRes.nextStep.signInStep, - because: 'MFA is required so Cognito automatically enables SMS MFA', + because: 'MFA is required so Cognito prompts MFA type selection', + ).equals(AuthSignInStep.continueSignInWithMfaSelection); + + final selectMfaRes = await Amplify.Auth.confirmSignIn( + confirmationValue: 'SMS', + ); + + check( + selectMfaRes.nextStep.signInStep, ).equals(AuthSignInStep.confirmSignInWithSmsMfaCode); final confirmRes = await Amplify.Auth.confirmSignIn( @@ -227,7 +246,8 @@ void main() { check( await cognitoPlugin.fetchMfaPreference(), - because: 'MFA is required so Cognito automatically enables SMS MFA', + because: + 'SMS MFA has been selected so Cognito fetches SMS as preferred', ).equals( const UserMfaPreference( enabled: {MfaType.sms}, @@ -235,36 +255,6 @@ void main() { ), ); - // Verify we can set SMS as preferred and forego selection. - - { - await signOutUser(assertComplete: true); - - final mfaCode = await getOtpCode(UserAttribute.phone(phoneNumber)); - final signInRes = await Amplify.Auth.signIn( - username: username, - password: password, - ); - check( - signInRes.nextStep.signInStep, - because: 'Preference is SMS MFA now', - ).equals(AuthSignInStep.confirmSignInWithSmsMfaCode); - check(signInRes.nextStep.codeDeliveryDetails).isNotNull() - ..has( - (d) => d.deliveryMedium, - 'deliveryMedium', - ).equals(DeliveryMedium.sms) - ..has( - (d) => d.destination, - 'destination', - ).isNotNull().startsWith('+'); - - final confirmRes = await Amplify.Auth.confirmSignIn( - confirmationValue: await mfaCode.code, - ); - check(confirmRes.nextStep.signInStep).equals(AuthSignInStep.done); - } - // Verify we can switch to EMAIL as preferred. await cognitoPlugin.updateMfaPreference(email: MfaPreference.preferred);