From 114267d58c4338e86c4e6abc64968ec387396414 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 27 Jul 2024 12:51:08 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Flipped=20the=20switch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/web_adapter/lib/dio_web_adapter.dart | 20 +- plugins/web_adapter/lib/src/html/adapter.dart | 594 ++++++++-------- plugins/web_adapter/lib/src/html/compute.dart | 30 +- .../web_adapter/lib/src/html/dio_impl.dart | 68 +- .../lib/src/html/multipart_file.dart | 44 +- .../lib/src/html/progress_stream.dart | 82 ++- .../lib/src/js_interop/adapter.dart | 668 +++++++++--------- .../lib/src/js_interop/compute.dart | 28 +- .../lib/src/js_interop/dio_impl.dart | 66 +- .../lib/src/js_interop/multipart_file.dart | 42 +- .../lib/src/js_interop/progress_stream.dart | 80 +-- 11 files changed, 851 insertions(+), 871 deletions(-) diff --git a/plugins/web_adapter/lib/dio_web_adapter.dart b/plugins/web_adapter/lib/dio_web_adapter.dart index c0da78e80..da552a582 100644 --- a/plugins/web_adapter/lib/dio_web_adapter.dart +++ b/plugins/web_adapter/lib/dio_web_adapter.dart @@ -3,22 +3,22 @@ library dio_web_adapter; // We separate implementations as `html` and `js_interop` // only for convenient diffing. -export 'src/html/adapter.dart'; +// export 'src/html/adapter.dart'; -// export 'src/js_interop/adapter.dart'; +export 'src/js_interop/adapter.dart'; -export 'src/html/compute.dart'; +// export 'src/html/compute.dart'; -// export 'src/js_interop/compute.dart'; +export 'src/js_interop/compute.dart'; -export 'src/html/dio_impl.dart'; +// export 'src/html/dio_impl.dart'; -// export 'src/js_interop/dio_impl.dart'; +export 'src/js_interop/dio_impl.dart'; -export 'src/html/multipart_file.dart'; +// export 'src/html/multipart_file.dart'; -// export 'src/js_interop/multipart_file.dart'; +export 'src/js_interop/multipart_file.dart'; -export 'src/html/progress_stream.dart'; +// export 'src/html/progress_stream.dart'; -// export 'src/js_interop/progress_stream.dart'; +export 'src/js_interop/progress_stream.dart'; diff --git a/plugins/web_adapter/lib/src/html/adapter.dart b/plugins/web_adapter/lib/src/html/adapter.dart index 5dc1cc881..715fb5004 100644 --- a/plugins/web_adapter/lib/src/html/adapter.dart +++ b/plugins/web_adapter/lib/src/html/adapter.dart @@ -1,298 +1,296 @@ -// export '../js_interop/adapter.dart'; - -import 'dart:async'; -import 'dart:convert'; -import 'dart:html'; -import 'dart:typed_data'; - -import 'package:dio/dio.dart'; -import 'package:dio/src/utils.dart'; -import 'package:meta/meta.dart'; - -BrowserHttpClientAdapter createAdapter() => BrowserHttpClientAdapter(); - -/// The default [HttpClientAdapter] for Web platforms. -class BrowserHttpClientAdapter implements HttpClientAdapter { - BrowserHttpClientAdapter({this.withCredentials = false}); - - /// These are aborted if the client is closed. - @visibleForTesting - final xhrs = {}; - - /// Whether to send credentials such as cookies or authorization headers for - /// cross-site requests. - /// - /// Defaults to `false`. - /// - /// You can also override this value using `Options.extra['withCredentials']` - /// for each request. - bool withCredentials; - - @override - Future fetch( - RequestOptions options, - Stream? requestStream, - Future? cancelFuture, - ) async { - final xhr = HttpRequest(); - xhrs.add(xhr); - xhr - ..open(options.method, '${options.uri}') - ..responseType = 'arraybuffer'; - - final withCredentialsOption = options.extra['withCredentials']; - if (withCredentialsOption != null) { - xhr.withCredentials = withCredentialsOption == true; - } else { - xhr.withCredentials = withCredentials; - } - - options.headers.remove(Headers.contentLengthHeader); - options.headers.forEach((key, v) { - if (v is Iterable) { - xhr.setRequestHeader(key, v.join(', ')); - } else { - xhr.setRequestHeader(key, v.toString()); - } - }); - - final sendTimeout = options.sendTimeout ?? Duration.zero; - final connectTimeout = options.connectTimeout ?? Duration.zero; - final receiveTimeout = options.receiveTimeout ?? Duration.zero; - final xhrTimeout = (connectTimeout + receiveTimeout).inMilliseconds; - xhr.timeout = xhrTimeout; - - final completer = Completer(); - - xhr.onLoad.first.then((_) { - final Uint8List body = (xhr.response as ByteBuffer).asUint8List(); - completer.complete( - ResponseBody.fromBytes( - body, - xhr.status!, - headers: xhr.responseHeaders.map((k, v) => MapEntry(k, v.split(','))), - statusMessage: xhr.statusText, - isRedirect: xhr.status == 302 || - xhr.status == 301 || - options.uri.toString() != xhr.responseUrl, - ), - ); - }); - - Timer? connectTimeoutTimer; - if (connectTimeout > Duration.zero) { - connectTimeoutTimer = Timer( - connectTimeout, - () { - connectTimeoutTimer = null; - if (completer.isCompleted) { - // connectTimeout is triggered after the fetch has been completed. - return; - } - xhr.abort(); - completer.completeError( - DioException.connectionTimeout( - requestOptions: options, - timeout: connectTimeout, - ), - StackTrace.current, - ); - }, - ); - } - - // This code is structured to call `xhr.upload.onProgress.listen` only when - // absolutely necessary, because registering an xhr upload listener prevents - // the request from being classified as a "simple request" by the CORS spec. - // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests - // Upload progress events only get triggered if the request body exists, - // so we can check it beforehand. - if (requestStream != null) { - if (connectTimeoutTimer != null) { - xhr.upload.onProgress.listen((event) { - connectTimeoutTimer?.cancel(); - connectTimeoutTimer = null; - }); - } - - if (sendTimeout > Duration.zero) { - final uploadStopwatch = Stopwatch(); - xhr.upload.onProgress.listen((event) { - if (!uploadStopwatch.isRunning) { - uploadStopwatch.start(); - } - final duration = uploadStopwatch.elapsed; - if (duration > sendTimeout) { - uploadStopwatch.stop(); - completer.completeError( - DioException.sendTimeout( - timeout: sendTimeout, - requestOptions: options, - ), - StackTrace.current, - ); - xhr.abort(); - } - }); - } - - final onSendProgress = options.onSendProgress; - if (onSendProgress != null) { - xhr.upload.onProgress.listen((event) { - if (event.loaded != null && event.total != null) { - onSendProgress(event.loaded!, event.total!); - } - }); - } - } - - final receiveStopwatch = Stopwatch(); - Timer? receiveTimer; - - void stopWatchReceiveTimeout() { - receiveTimer?.cancel(); - receiveTimer = null; - receiveStopwatch.stop(); - } - - void watchReceiveTimeout() { - if (receiveTimeout <= Duration.zero) { - return; - } - receiveStopwatch.reset(); - if (!receiveStopwatch.isRunning) { - receiveStopwatch.start(); - } - receiveTimer?.cancel(); - receiveTimer = Timer(receiveTimeout, () { - if (!completer.isCompleted) { - xhr.abort(); - completer.completeError( - DioException.receiveTimeout( - timeout: receiveTimeout, - requestOptions: options, - ), - StackTrace.current, - ); - } - stopWatchReceiveTimeout(); - }); - } - - xhr.onProgress.listen( - (ProgressEvent event) { - if (connectTimeoutTimer != null) { - connectTimeoutTimer!.cancel(); - connectTimeoutTimer = null; - } - watchReceiveTimeout(); - if (options.onReceiveProgress != null && - event.loaded != null && - event.total != null) { - options.onReceiveProgress!(event.loaded!, event.total!); - } - }, - onDone: () => stopWatchReceiveTimeout(), - ); - - xhr.onError.first.then((_) { - connectTimeoutTimer?.cancel(); - // Unfortunately, the underlying XMLHttpRequest API doesn't expose any - // specific information about the error itself. - // See also: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onerror - completer.completeError( - DioException.connectionError( - requestOptions: options, - reason: 'The XMLHttpRequest onError callback was called. ' - 'This typically indicates an error on the network layer.', - ), - StackTrace.current, - ); - }); - - xhr.onTimeout.first.then((_) { - final isConnectTimeout = connectTimeoutTimer != null; - if (connectTimeoutTimer != null) { - connectTimeoutTimer?.cancel(); - } - if (!completer.isCompleted) { - if (isConnectTimeout) { - completer.completeError( - DioException.connectionTimeout( - timeout: connectTimeout, - requestOptions: options, - ), - ); - } else { - completer.completeError( - DioException.receiveTimeout( - timeout: Duration(milliseconds: xhrTimeout), - requestOptions: options, - ), - StackTrace.current, - ); - } - } - }); - - cancelFuture?.then((_) { - if (xhr.readyState < HttpRequest.DONE && - xhr.readyState > HttpRequest.UNSENT) { - connectTimeoutTimer?.cancel(); - try { - xhr.abort(); - } catch (_) {} - if (!completer.isCompleted) { - completer.completeError( - DioException.requestCancelled( - requestOptions: options, - reason: 'The XMLHttpRequest was aborted.', - ), - ); - } - } - }); - - if (requestStream != null) { - if (options.method == 'GET') { - warningLog( - 'GET request with a body data are not support on the ' - 'web platform. Use POST/PUT instead.', - StackTrace.current, - ); - } - final completer = Completer(); - final sink = ByteConversionSink.withCallback( - (bytes) => completer.complete( - bytes is Uint8List ? bytes : Uint8List.fromList(bytes), - ), - ); - requestStream.listen( - sink.add, - onError: (Object e, StackTrace s) => completer.completeError(e, s), - onDone: sink.close, - cancelOnError: true, - ); - final bytes = await completer.future; - xhr.send(bytes); - } else { - xhr.send(); - } - return completer.future.whenComplete(() { - xhrs.remove(xhr); - }); - } - - /// Closes the client. - /// - /// This terminates all active requests. - @override - void close({bool force = false}) { - if (force) { - for (final xhr in xhrs) { - xhr.abort(); - } - } - xhrs.clear(); - } -} +// import 'dart:async'; +// import 'dart:convert'; +// import 'dart:html'; +// import 'dart:typed_data'; +// +// import 'package:dio/dio.dart'; +// import 'package:dio/src/utils.dart'; +// import 'package:meta/meta.dart'; +// +// BrowserHttpClientAdapter createAdapter() => BrowserHttpClientAdapter(); +// +// /// The default [HttpClientAdapter] for Web platforms. +// class BrowserHttpClientAdapter implements HttpClientAdapter { +// BrowserHttpClientAdapter({this.withCredentials = false}); +// +// /// These are aborted if the client is closed. +// @visibleForTesting +// final xhrs = {}; +// +// /// Whether to send credentials such as cookies or authorization headers for +// /// cross-site requests. +// /// +// /// Defaults to `false`. +// /// +// /// You can also override this value using `Options.extra['withCredentials']` +// /// for each request. +// bool withCredentials; +// +// @override +// Future fetch( +// RequestOptions options, +// Stream? requestStream, +// Future? cancelFuture, +// ) async { +// final xhr = HttpRequest(); +// xhrs.add(xhr); +// xhr +// ..open(options.method, '${options.uri}') +// ..responseType = 'arraybuffer'; +// +// final withCredentialsOption = options.extra['withCredentials']; +// if (withCredentialsOption != null) { +// xhr.withCredentials = withCredentialsOption == true; +// } else { +// xhr.withCredentials = withCredentials; +// } +// +// options.headers.remove(Headers.contentLengthHeader); +// options.headers.forEach((key, v) { +// if (v is Iterable) { +// xhr.setRequestHeader(key, v.join(', ')); +// } else { +// xhr.setRequestHeader(key, v.toString()); +// } +// }); +// +// final sendTimeout = options.sendTimeout ?? Duration.zero; +// final connectTimeout = options.connectTimeout ?? Duration.zero; +// final receiveTimeout = options.receiveTimeout ?? Duration.zero; +// final xhrTimeout = (connectTimeout + receiveTimeout).inMilliseconds; +// xhr.timeout = xhrTimeout; +// +// final completer = Completer(); +// +// xhr.onLoad.first.then((_) { +// final Uint8List body = (xhr.response as ByteBuffer).asUint8List(); +// completer.complete( +// ResponseBody.fromBytes( +// body, +// xhr.status!, +// headers: xhr.responseHeaders.map((k, v) => MapEntry(k, v.split(','))), +// statusMessage: xhr.statusText, +// isRedirect: xhr.status == 302 || +// xhr.status == 301 || +// options.uri.toString() != xhr.responseUrl, +// ), +// ); +// }); +// +// Timer? connectTimeoutTimer; +// if (connectTimeout > Duration.zero) { +// connectTimeoutTimer = Timer( +// connectTimeout, +// () { +// connectTimeoutTimer = null; +// if (completer.isCompleted) { +// // connectTimeout is triggered after the fetch has been completed. +// return; +// } +// xhr.abort(); +// completer.completeError( +// DioException.connectionTimeout( +// requestOptions: options, +// timeout: connectTimeout, +// ), +// StackTrace.current, +// ); +// }, +// ); +// } +// +// // This code is structured to call `xhr.upload.onProgress.listen` only when +// // absolutely necessary, because registering an xhr upload listener prevents +// // the request from being classified as a "simple request" by the CORS spec. +// // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests +// // Upload progress events only get triggered if the request body exists, +// // so we can check it beforehand. +// if (requestStream != null) { +// if (connectTimeoutTimer != null) { +// xhr.upload.onProgress.listen((event) { +// connectTimeoutTimer?.cancel(); +// connectTimeoutTimer = null; +// }); +// } +// +// if (sendTimeout > Duration.zero) { +// final uploadStopwatch = Stopwatch(); +// xhr.upload.onProgress.listen((event) { +// if (!uploadStopwatch.isRunning) { +// uploadStopwatch.start(); +// } +// final duration = uploadStopwatch.elapsed; +// if (duration > sendTimeout) { +// uploadStopwatch.stop(); +// completer.completeError( +// DioException.sendTimeout( +// timeout: sendTimeout, +// requestOptions: options, +// ), +// StackTrace.current, +// ); +// xhr.abort(); +// } +// }); +// } +// +// final onSendProgress = options.onSendProgress; +// if (onSendProgress != null) { +// xhr.upload.onProgress.listen((event) { +// if (event.loaded != null && event.total != null) { +// onSendProgress(event.loaded!, event.total!); +// } +// }); +// } +// } +// +// final receiveStopwatch = Stopwatch(); +// Timer? receiveTimer; +// +// void stopWatchReceiveTimeout() { +// receiveTimer?.cancel(); +// receiveTimer = null; +// receiveStopwatch.stop(); +// } +// +// void watchReceiveTimeout() { +// if (receiveTimeout <= Duration.zero) { +// return; +// } +// receiveStopwatch.reset(); +// if (!receiveStopwatch.isRunning) { +// receiveStopwatch.start(); +// } +// receiveTimer?.cancel(); +// receiveTimer = Timer(receiveTimeout, () { +// if (!completer.isCompleted) { +// xhr.abort(); +// completer.completeError( +// DioException.receiveTimeout( +// timeout: receiveTimeout, +// requestOptions: options, +// ), +// StackTrace.current, +// ); +// } +// stopWatchReceiveTimeout(); +// }); +// } +// +// xhr.onProgress.listen( +// (ProgressEvent event) { +// if (connectTimeoutTimer != null) { +// connectTimeoutTimer!.cancel(); +// connectTimeoutTimer = null; +// } +// watchReceiveTimeout(); +// if (options.onReceiveProgress != null && +// event.loaded != null && +// event.total != null) { +// options.onReceiveProgress!(event.loaded!, event.total!); +// } +// }, +// onDone: () => stopWatchReceiveTimeout(), +// ); +// +// xhr.onError.first.then((_) { +// connectTimeoutTimer?.cancel(); +// // Unfortunately, the underlying XMLHttpRequest API doesn't expose any +// // specific information about the error itself. +// // See also: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onerror +// completer.completeError( +// DioException.connectionError( +// requestOptions: options, +// reason: 'The XMLHttpRequest onError callback was called. ' +// 'This typically indicates an error on the network layer.', +// ), +// StackTrace.current, +// ); +// }); +// +// xhr.onTimeout.first.then((_) { +// final isConnectTimeout = connectTimeoutTimer != null; +// if (connectTimeoutTimer != null) { +// connectTimeoutTimer?.cancel(); +// } +// if (!completer.isCompleted) { +// if (isConnectTimeout) { +// completer.completeError( +// DioException.connectionTimeout( +// timeout: connectTimeout, +// requestOptions: options, +// ), +// ); +// } else { +// completer.completeError( +// DioException.receiveTimeout( +// timeout: Duration(milliseconds: xhrTimeout), +// requestOptions: options, +// ), +// StackTrace.current, +// ); +// } +// } +// }); +// +// cancelFuture?.then((_) { +// if (xhr.readyState < HttpRequest.DONE && +// xhr.readyState > HttpRequest.UNSENT) { +// connectTimeoutTimer?.cancel(); +// try { +// xhr.abort(); +// } catch (_) {} +// if (!completer.isCompleted) { +// completer.completeError( +// DioException.requestCancelled( +// requestOptions: options, +// reason: 'The XMLHttpRequest was aborted.', +// ), +// ); +// } +// } +// }); +// +// if (requestStream != null) { +// if (options.method == 'GET') { +// warningLog( +// 'GET request with a body data are not support on the ' +// 'web platform. Use POST/PUT instead.', +// StackTrace.current, +// ); +// } +// final completer = Completer(); +// final sink = ByteConversionSink.withCallback( +// (bytes) => completer.complete( +// bytes is Uint8List ? bytes : Uint8List.fromList(bytes), +// ), +// ); +// requestStream.listen( +// sink.add, +// onError: (Object e, StackTrace s) => completer.completeError(e, s), +// onDone: sink.close, +// cancelOnError: true, +// ); +// final bytes = await completer.future; +// xhr.send(bytes); +// } else { +// xhr.send(); +// } +// return completer.future.whenComplete(() { +// xhrs.remove(xhr); +// }); +// } +// +// /// Closes the client. +// /// +// /// This terminates all active requests. +// @override +// void close({bool force = false}) { +// if (force) { +// for (final xhr in xhrs) { +// xhr.abort(); +// } +// } +// xhrs.clear(); +// } +// } diff --git a/plugins/web_adapter/lib/src/html/compute.dart b/plugins/web_adapter/lib/src/html/compute.dart index 8ef14d17c..fa9d6be3b 100644 --- a/plugins/web_adapter/lib/src/html/compute.dart +++ b/plugins/web_adapter/lib/src/html/compute.dart @@ -19,19 +19,17 @@ // The file is intentionally not refactored so that it is easier to keep the // compute package up to date with Flutter's implementation. -// export '../js_interop/compute.dart'; - -import 'package:dio/src/compute/compute.dart' as c; - -/// The dart:html implementation of [c.compute]. -Future compute( - c.ComputeCallback callback, - Q message, { - String? debugLabel, -}) async { - // To avoid blocking the UI immediately for an expensive function call, we - // pump a single frame to allow the framework to complete the current set - // of work. - await null; - return callback(message); -} +// import 'package:dio/src/compute/compute.dart' as c; +// +// /// The dart:html implementation of [c.compute]. +// Future compute( +// c.ComputeCallback callback, +// Q message, { +// String? debugLabel, +// }) async { +// // To avoid blocking the UI immediately for an expensive function call, we +// // pump a single frame to allow the framework to complete the current set +// // of work. +// await null; +// return callback(message); +// } diff --git a/plugins/web_adapter/lib/src/html/dio_impl.dart b/plugins/web_adapter/lib/src/html/dio_impl.dart index f5adc61bd..4aca66163 100644 --- a/plugins/web_adapter/lib/src/html/dio_impl.dart +++ b/plugins/web_adapter/lib/src/html/dio_impl.dart @@ -1,35 +1,33 @@ -// export '../js_interop/dio_impl.dart'; - -import 'package:dio/dio.dart'; - -import 'adapter.dart'; - -/// Create the [Dio] instance for Web platforms. -Dio createDio([BaseOptions? options]) => DioForBrowser(options); - -/// Implements features for [Dio] on Web platforms. -class DioForBrowser with DioMixin implements Dio { - /// Create Dio instance with default [Options]. - /// It's mostly just one Dio instance in your application. - DioForBrowser([BaseOptions? options]) { - this.options = options ?? BaseOptions(); - httpClientAdapter = BrowserHttpClientAdapter(); - } - - @override - Future download( - String urlPath, - dynamic savePath, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - bool deleteOnError = true, - String lengthHeader = Headers.contentLengthHeader, - Object? data, - Options? options, - }) { - throw UnsupportedError( - 'The download method is not available in the Web environment.', - ); - } -} +// import 'package:dio/dio.dart'; +// +// import 'adapter.dart'; +// +// /// Create the [Dio] instance for Web platforms. +// Dio createDio([BaseOptions? options]) => DioForBrowser(options); +// +// /// Implements features for [Dio] on Web platforms. +// class DioForBrowser with DioMixin implements Dio { +// /// Create Dio instance with default [Options]. +// /// It's mostly just one Dio instance in your application. +// DioForBrowser([BaseOptions? options]) { +// this.options = options ?? BaseOptions(); +// httpClientAdapter = BrowserHttpClientAdapter(); +// } +// +// @override +// Future download( +// String urlPath, +// dynamic savePath, { +// ProgressCallback? onReceiveProgress, +// Map? queryParameters, +// CancelToken? cancelToken, +// bool deleteOnError = true, +// String lengthHeader = Headers.contentLengthHeader, +// Object? data, +// Options? options, +// }) { +// throw UnsupportedError( +// 'The download method is not available in the Web environment.', +// ); +// } +// } diff --git a/plugins/web_adapter/lib/src/html/multipart_file.dart b/plugins/web_adapter/lib/src/html/multipart_file.dart index b75199ac4..ff848c7de 100644 --- a/plugins/web_adapter/lib/src/html/multipart_file.dart +++ b/plugins/web_adapter/lib/src/html/multipart_file.dart @@ -1,23 +1,21 @@ -// export '../js_interop/multipart_file.dart'; - -import 'package:http_parser/http_parser.dart'; - -final _err = UnsupportedError( - 'MultipartFile is only supported where dart:io is available.', -); - -Never multipartFileFromPath( - String filePath, { - String? filename, - MediaType? contentType, - final Map>? headers, -}) => - throw _err; - -Never multipartFileFromPathSync( - String filePath, { - String? filename, - MediaType? contentType, - final Map>? headers, -}) => - throw _err; +// import 'package:http_parser/http_parser.dart'; +// +// final _err = UnsupportedError( +// 'MultipartFile is only supported where dart:io is available.', +// ); +// +// Never multipartFileFromPath( +// String filePath, { +// String? filename, +// MediaType? contentType, +// final Map>? headers, +// }) => +// throw _err; +// +// Never multipartFileFromPathSync( +// String filePath, { +// String? filename, +// MediaType? contentType, +// final Map>? headers, +// }) => +// throw _err; diff --git a/plugins/web_adapter/lib/src/html/progress_stream.dart b/plugins/web_adapter/lib/src/html/progress_stream.dart index 5d97b8d8a..44e0312d3 100644 --- a/plugins/web_adapter/lib/src/html/progress_stream.dart +++ b/plugins/web_adapter/lib/src/html/progress_stream.dart @@ -1,42 +1,40 @@ -// export '../js_interop/progress_stream.dart'; - -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:dio/dio.dart'; - -Stream addProgress( - Stream> stream, - int? length, - RequestOptions options, -) { - if (stream is Stream) { - return stream; - } - final streamTransformer = _transform>(stream, length, options); - return stream.transform(streamTransformer); -} - -StreamTransformer _transform>( - Stream stream, - int? length, - RequestOptions options, -) { - return StreamTransformer.fromHandlers( - handleData: (S data, sink) { - final cancelToken = options.cancelToken; - if (cancelToken != null && cancelToken.isCancelled) { - cancelToken.requestOptions = options; - sink - ..addError(cancelToken.cancelError!) - ..close(); - } else { - if (data is Uint8List) { - sink.add(data); - } else { - sink.add(Uint8List.fromList(data)); - } - } - }, - ); -} +// import 'dart:async'; +// import 'dart:typed_data'; +// +// import 'package:dio/dio.dart'; +// +// Stream addProgress( +// Stream> stream, +// int? length, +// RequestOptions options, +// ) { +// if (stream is Stream) { +// return stream; +// } +// final streamTransformer = _transform>(stream, length, options); +// return stream.transform(streamTransformer); +// } +// +// StreamTransformer _transform>( +// Stream stream, +// int? length, +// RequestOptions options, +// ) { +// return StreamTransformer.fromHandlers( +// handleData: (S data, sink) { +// final cancelToken = options.cancelToken; +// if (cancelToken != null && cancelToken.isCancelled) { +// cancelToken.requestOptions = options; +// sink +// ..addError(cancelToken.cancelError!) +// ..close(); +// } else { +// if (data is Uint8List) { +// sink.add(data); +// } else { +// sink.add(Uint8List.fromList(data)); +// } +// } +// }, +// ); +// } diff --git a/plugins/web_adapter/lib/src/js_interop/adapter.dart b/plugins/web_adapter/lib/src/js_interop/adapter.dart index 08b50425c..cec2d2c47 100644 --- a/plugins/web_adapter/lib/src/js_interop/adapter.dart +++ b/plugins/web_adapter/lib/src/js_interop/adapter.dart @@ -1,336 +1,334 @@ -export '../html/adapter.dart'; +import 'dart:async'; +import 'dart:convert'; +import 'dart:js_interop'; +import 'dart:typed_data'; -// import 'dart:async'; -// import 'dart:convert'; -// import 'dart:js_interop'; -// import 'dart:typed_data'; -// -// import 'package:dio/dio.dart'; -// import 'package:dio/src/utils.dart'; -// import 'package:meta/meta.dart'; -// import 'package:web/web.dart' as web; -// -// BrowserHttpClientAdapter createAdapter() => BrowserHttpClientAdapter(); -// -// /// The default [HttpClientAdapter] for Web platforms. -// class BrowserHttpClientAdapter implements HttpClientAdapter { -// BrowserHttpClientAdapter({this.withCredentials = false}); -// -// /// These are aborted if the client is closed. -// @visibleForTesting -// final xhrs = {}; -// -// /// Whether to send credentials such as cookies or authorization headers for -// /// cross-site requests. -// /// -// /// Defaults to `false`. -// /// -// /// You can also override this value using `Options.extra['withCredentials']` -// /// for each request. -// bool withCredentials; -// -// @override -// Future fetch( -// RequestOptions options, -// Stream? requestStream, -// Future? cancelFuture, -// ) async { -// final xhr = web.XMLHttpRequest(); -// xhrs.add(xhr); -// xhr -// ..open(options.method, '${options.uri}') -// ..responseType = 'arraybuffer'; -// -// final withCredentialsOption = options.extra['withCredentials']; -// if (withCredentialsOption != null) { -// xhr.withCredentials = withCredentialsOption == true; -// } else { -// xhr.withCredentials = withCredentials; -// } -// -// options.headers.remove(Headers.contentLengthHeader); -// options.headers.forEach((key, v) { -// if (v is Iterable) { -// xhr.setRequestHeader(key, v.join(', ')); -// } else { -// xhr.setRequestHeader(key, v.toString()); -// } -// }); -// -// final sendTimeout = options.sendTimeout ?? Duration.zero; -// final connectTimeout = options.connectTimeout ?? Duration.zero; -// final receiveTimeout = options.receiveTimeout ?? Duration.zero; -// final xhrTimeout = (connectTimeout + receiveTimeout).inMilliseconds; -// xhr.timeout = xhrTimeout; -// -// final completer = Completer(); -// -// xhr.onLoad.first.then((_) { -// final ByteBuffer body = (xhr.response as JSArrayBuffer).toDart; -// completer.complete( -// ResponseBody.fromBytes( -// body.asUint8List(), -// xhr.status, -// headers: xhr.getResponseHeaders(), -// statusMessage: xhr.statusText, -// isRedirect: xhr.status == 302 || -// xhr.status == 301 || -// options.uri.toString() != xhr.responseURL, -// ), -// ); -// }); -// -// Timer? connectTimeoutTimer; -// if (connectTimeout > Duration.zero) { -// connectTimeoutTimer = Timer( -// connectTimeout, -// () { -// connectTimeoutTimer = null; -// if (completer.isCompleted) { -// // connectTimeout is triggered after the fetch has been completed. -// return; -// } -// xhr.abort(); -// completer.completeError( -// DioException.connectionTimeout( -// requestOptions: options, -// timeout: connectTimeout, -// ), -// StackTrace.current, -// ); -// }, -// ); -// } -// -// // This code is structured to call `xhr.upload.onProgress.listen` only when -// // absolutely necessary, because registering an xhr upload listener prevents -// // the request from being classified as a "simple request" by the CORS spec. -// // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests -// // Upload progress events only get triggered if the request body exists, -// // so we can check it beforehand. -// if (requestStream != null) { -// final xhrUploadProgressStream = -// web.EventStreamProviders.progressEvent.forTarget(xhr.upload); -// -// if (connectTimeoutTimer != null) { -// xhrUploadProgressStream.listen((_) { -// connectTimeoutTimer?.cancel(); -// connectTimeoutTimer = null; -// }); -// } -// -// if (sendTimeout > Duration.zero) { -// final uploadStopwatch = Stopwatch(); -// xhrUploadProgressStream.listen((_) { -// if (!uploadStopwatch.isRunning) { -// uploadStopwatch.start(); -// } -// final duration = uploadStopwatch.elapsed; -// if (duration > sendTimeout) { -// uploadStopwatch.stop(); -// completer.completeError( -// DioException.sendTimeout( -// timeout: sendTimeout, -// requestOptions: options, -// ), -// StackTrace.current, -// ); -// xhr.abort(); -// } -// }); -// } -// -// final onSendProgress = options.onSendProgress; -// if (onSendProgress != null) { -// xhrUploadProgressStream.listen((event) { -// onSendProgress(event.loaded, event.total); -// }); -// } -// } else { -// if (sendTimeout > Duration.zero) { -// debugLog( -// 'sendTimeout cannot be used without a request body to send', -// StackTrace.current, -// ); -// } -// if (options.onSendProgress != null) { -// debugLog( -// 'onSendProgress cannot be used without a request body to send', -// StackTrace.current, -// ); -// } -// } -// -// final receiveStopwatch = Stopwatch(); -// Timer? receiveTimer; -// -// void stopWatchReceiveTimeout() { -// receiveTimer?.cancel(); -// receiveTimer = null; -// receiveStopwatch.stop(); -// } -// -// void watchReceiveTimeout() { -// if (receiveTimeout <= Duration.zero) { -// return; -// } -// receiveStopwatch.reset(); -// if (!receiveStopwatch.isRunning) { -// receiveStopwatch.start(); -// } -// receiveTimer?.cancel(); -// receiveTimer = Timer(receiveTimeout, () { -// if (!completer.isCompleted) { -// xhr.abort(); -// completer.completeError( -// DioException.receiveTimeout( -// timeout: receiveTimeout, -// requestOptions: options, -// ), -// StackTrace.current, -// ); -// } -// stopWatchReceiveTimeout(); -// }); -// } -// -// xhr.onProgress.listen( -// (event) { -// if (connectTimeoutTimer != null) { -// connectTimeoutTimer!.cancel(); -// connectTimeoutTimer = null; -// } -// watchReceiveTimeout(); -// if (options.onReceiveProgress != null) { -// options.onReceiveProgress!(event.loaded, event.total); -// } -// }, -// onDone: () => stopWatchReceiveTimeout(), -// ); -// -// xhr.onError.first.then((_) { -// connectTimeoutTimer?.cancel(); -// // Unfortunately, the underlying XMLHttpRequest API doesn't expose any -// // specific information about the error itself. -// // See also: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onerror -// completer.completeError( -// DioException.connectionError( -// requestOptions: options, -// reason: 'The XMLHttpRequest onError callback was called. ' -// 'This typically indicates an error on the network layer.', -// ), -// StackTrace.current, -// ); -// }); -// -// web.EventStreamProviders.timeoutEvent.forTarget(xhr).first.then((_) { -// final isConnectTimeout = connectTimeoutTimer != null; -// if (connectTimeoutTimer != null) { -// connectTimeoutTimer?.cancel(); -// } -// if (!completer.isCompleted) { -// if (isConnectTimeout) { -// completer.completeError( -// DioException.connectionTimeout( -// timeout: connectTimeout, -// requestOptions: options, -// ), -// ); -// } else { -// completer.completeError( -// DioException.receiveTimeout( -// timeout: Duration(milliseconds: xhrTimeout), -// requestOptions: options, -// ), -// StackTrace.current, -// ); -// } -// } -// }); -// -// cancelFuture?.then((_) { -// if (xhr.readyState < web.XMLHttpRequest.DONE && -// xhr.readyState > web.XMLHttpRequest.UNSENT) { -// connectTimeoutTimer?.cancel(); -// try { -// xhr.abort(); -// } catch (_) {} -// if (!completer.isCompleted) { -// completer.completeError( -// DioException.requestCancelled( -// requestOptions: options, -// reason: 'The XMLHttpRequest was aborted.', -// ), -// ); -// } -// } -// }); -// -// if (requestStream != null) { -// if (options.method == 'GET') { -// debugLog( -// 'GET request with a body data are not support on the ' -// 'web platform. Use POST/PUT instead.', -// StackTrace.current, -// ); -// } -// final completer = Completer(); -// final sink = ByteConversionSink.withCallback( -// (bytes) => completer.complete( -// bytes is Uint8List ? bytes : Uint8List.fromList(bytes), -// ), -// ); -// requestStream.listen( -// sink.add, -// onError: (Object e, StackTrace s) => completer.completeError(e, s), -// onDone: sink.close, -// cancelOnError: true, -// ); -// final bytes = await completer.future; -// xhr.send(bytes.toJS); -// } else { -// xhr.send(); -// } -// return completer.future.whenComplete(() { -// xhrs.remove(xhr); -// }); -// } -// -// /// Closes the client. -// /// -// /// This terminates all active requests. -// @override -// void close({bool force = false}) { -// if (force) { -// for (final xhr in xhrs) { -// xhr.abort(); -// } -// } -// xhrs.clear(); -// } -// } -// -// extension on web.XMLHttpRequest { -// Map> getResponseHeaders() { -// final headersString = getAllResponseHeaders(); -// final headers = >{}; -// if (headersString.isEmpty) { -// return headers; -// } -// final headersList = headersString.split('\r\n'); -// for (final header in headersList) { -// if (header.isEmpty) { -// continue; -// } -// -// final splitIdx = header.indexOf(': '); -// if (splitIdx == -1) { -// continue; -// } -// final key = header.substring(0, splitIdx).toLowerCase(); -// final value = header.substring(splitIdx + 2); -// (headers[key] ??= []).add(value); -// } -// return headers; -// } -// } +import 'package:dio/dio.dart'; +import 'package:dio/src/utils.dart'; +import 'package:meta/meta.dart'; +import 'package:web/web.dart' as web; + +BrowserHttpClientAdapter createAdapter() => BrowserHttpClientAdapter(); + +/// The default [HttpClientAdapter] for Web platforms. +class BrowserHttpClientAdapter implements HttpClientAdapter { + BrowserHttpClientAdapter({this.withCredentials = false}); + + /// These are aborted if the client is closed. + @visibleForTesting + final xhrs = {}; + + /// Whether to send credentials such as cookies or authorization headers for + /// cross-site requests. + /// + /// Defaults to `false`. + /// + /// You can also override this value using `Options.extra['withCredentials']` + /// for each request. + bool withCredentials; + + @override + Future fetch( + RequestOptions options, + Stream? requestStream, + Future? cancelFuture, + ) async { + final xhr = web.XMLHttpRequest(); + xhrs.add(xhr); + xhr + ..open(options.method, '${options.uri}') + ..responseType = 'arraybuffer'; + + final withCredentialsOption = options.extra['withCredentials']; + if (withCredentialsOption != null) { + xhr.withCredentials = withCredentialsOption == true; + } else { + xhr.withCredentials = withCredentials; + } + + options.headers.remove(Headers.contentLengthHeader); + options.headers.forEach((key, v) { + if (v is Iterable) { + xhr.setRequestHeader(key, v.join(', ')); + } else { + xhr.setRequestHeader(key, v.toString()); + } + }); + + final sendTimeout = options.sendTimeout ?? Duration.zero; + final connectTimeout = options.connectTimeout ?? Duration.zero; + final receiveTimeout = options.receiveTimeout ?? Duration.zero; + final xhrTimeout = (connectTimeout + receiveTimeout).inMilliseconds; + xhr.timeout = xhrTimeout; + + final completer = Completer(); + + xhr.onLoad.first.then((_) { + final ByteBuffer body = (xhr.response as JSArrayBuffer).toDart; + completer.complete( + ResponseBody.fromBytes( + body.asUint8List(), + xhr.status, + headers: xhr.getResponseHeaders(), + statusMessage: xhr.statusText, + isRedirect: xhr.status == 302 || + xhr.status == 301 || + options.uri.toString() != xhr.responseURL, + ), + ); + }); + + Timer? connectTimeoutTimer; + if (connectTimeout > Duration.zero) { + connectTimeoutTimer = Timer( + connectTimeout, + () { + connectTimeoutTimer = null; + if (completer.isCompleted) { + // connectTimeout is triggered after the fetch has been completed. + return; + } + xhr.abort(); + completer.completeError( + DioException.connectionTimeout( + requestOptions: options, + timeout: connectTimeout, + ), + StackTrace.current, + ); + }, + ); + } + + // This code is structured to call `xhr.upload.onProgress.listen` only when + // absolutely necessary, because registering an xhr upload listener prevents + // the request from being classified as a "simple request" by the CORS spec. + // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests + // Upload progress events only get triggered if the request body exists, + // so we can check it beforehand. + if (requestStream != null) { + final xhrUploadProgressStream = + web.EventStreamProviders.progressEvent.forTarget(xhr.upload); + + if (connectTimeoutTimer != null) { + xhrUploadProgressStream.listen((_) { + connectTimeoutTimer?.cancel(); + connectTimeoutTimer = null; + }); + } + + if (sendTimeout > Duration.zero) { + final uploadStopwatch = Stopwatch(); + xhrUploadProgressStream.listen((_) { + if (!uploadStopwatch.isRunning) { + uploadStopwatch.start(); + } + final duration = uploadStopwatch.elapsed; + if (duration > sendTimeout) { + uploadStopwatch.stop(); + completer.completeError( + DioException.sendTimeout( + timeout: sendTimeout, + requestOptions: options, + ), + StackTrace.current, + ); + xhr.abort(); + } + }); + } + + final onSendProgress = options.onSendProgress; + if (onSendProgress != null) { + xhrUploadProgressStream.listen((event) { + onSendProgress(event.loaded, event.total); + }); + } + } else { + if (sendTimeout > Duration.zero) { + warningLog( + 'sendTimeout cannot be used without a request body to send', + StackTrace.current, + ); + } + if (options.onSendProgress != null) { + warningLog( + 'onSendProgress cannot be used without a request body to send', + StackTrace.current, + ); + } + } + + final receiveStopwatch = Stopwatch(); + Timer? receiveTimer; + + void stopWatchReceiveTimeout() { + receiveTimer?.cancel(); + receiveTimer = null; + receiveStopwatch.stop(); + } + + void watchReceiveTimeout() { + if (receiveTimeout <= Duration.zero) { + return; + } + receiveStopwatch.reset(); + if (!receiveStopwatch.isRunning) { + receiveStopwatch.start(); + } + receiveTimer?.cancel(); + receiveTimer = Timer(receiveTimeout, () { + if (!completer.isCompleted) { + xhr.abort(); + completer.completeError( + DioException.receiveTimeout( + timeout: receiveTimeout, + requestOptions: options, + ), + StackTrace.current, + ); + } + stopWatchReceiveTimeout(); + }); + } + + xhr.onProgress.listen( + (event) { + if (connectTimeoutTimer != null) { + connectTimeoutTimer!.cancel(); + connectTimeoutTimer = null; + } + watchReceiveTimeout(); + if (options.onReceiveProgress != null) { + options.onReceiveProgress!(event.loaded, event.total); + } + }, + onDone: () => stopWatchReceiveTimeout(), + ); + + xhr.onError.first.then((_) { + connectTimeoutTimer?.cancel(); + // Unfortunately, the underlying XMLHttpRequest API doesn't expose any + // specific information about the error itself. + // See also: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onerror + completer.completeError( + DioException.connectionError( + requestOptions: options, + reason: 'The XMLHttpRequest onError callback was called. ' + 'This typically indicates an error on the network layer.', + ), + StackTrace.current, + ); + }); + + web.EventStreamProviders.timeoutEvent.forTarget(xhr).first.then((_) { + final isConnectTimeout = connectTimeoutTimer != null; + if (connectTimeoutTimer != null) { + connectTimeoutTimer?.cancel(); + } + if (!completer.isCompleted) { + if (isConnectTimeout) { + completer.completeError( + DioException.connectionTimeout( + timeout: connectTimeout, + requestOptions: options, + ), + ); + } else { + completer.completeError( + DioException.receiveTimeout( + timeout: Duration(milliseconds: xhrTimeout), + requestOptions: options, + ), + StackTrace.current, + ); + } + } + }); + + cancelFuture?.then((_) { + if (xhr.readyState < web.XMLHttpRequest.DONE && + xhr.readyState > web.XMLHttpRequest.UNSENT) { + connectTimeoutTimer?.cancel(); + try { + xhr.abort(); + } catch (_) {} + if (!completer.isCompleted) { + completer.completeError( + DioException.requestCancelled( + requestOptions: options, + reason: 'The XMLHttpRequest was aborted.', + ), + ); + } + } + }); + + if (requestStream != null) { + if (options.method == 'GET') { + warningLog( + 'GET request with a body data are not support on the ' + 'web platform. Use POST/PUT instead.', + StackTrace.current, + ); + } + final completer = Completer(); + final sink = ByteConversionSink.withCallback( + (bytes) => completer.complete( + bytes is Uint8List ? bytes : Uint8List.fromList(bytes), + ), + ); + requestStream.listen( + sink.add, + onError: (Object e, StackTrace s) => completer.completeError(e, s), + onDone: sink.close, + cancelOnError: true, + ); + final bytes = await completer.future; + xhr.send(bytes.toJS); + } else { + xhr.send(); + } + return completer.future.whenComplete(() { + xhrs.remove(xhr); + }); + } + + /// Closes the client. + /// + /// This terminates all active requests. + @override + void close({bool force = false}) { + if (force) { + for (final xhr in xhrs) { + xhr.abort(); + } + } + xhrs.clear(); + } +} + +extension on web.XMLHttpRequest { + Map> getResponseHeaders() { + final headersString = getAllResponseHeaders(); + final headers = >{}; + if (headersString.isEmpty) { + return headers; + } + final headersList = headersString.split('\r\n'); + for (final header in headersList) { + if (header.isEmpty) { + continue; + } + + final splitIdx = header.indexOf(': '); + if (splitIdx == -1) { + continue; + } + final key = header.substring(0, splitIdx).toLowerCase(); + final value = header.substring(splitIdx + 2); + (headers[key] ??= []).add(value); + } + return headers; + } +} diff --git a/plugins/web_adapter/lib/src/js_interop/compute.dart b/plugins/web_adapter/lib/src/js_interop/compute.dart index 2740a6986..c54adbd58 100644 --- a/plugins/web_adapter/lib/src/js_interop/compute.dart +++ b/plugins/web_adapter/lib/src/js_interop/compute.dart @@ -19,19 +19,17 @@ // The file is intentionally not refactored so that it is easier to keep the // compute package up to date with Flutter's implementation. -export '../html/compute.dart'; +import 'package:dio/src/compute/compute.dart' as c; -// import 'package:dio/src/compute/compute.dart' as c; -// -// /// The dart:html implementation of [c.compute]. -// Future compute( -// c.ComputeCallback callback, -// Q message, { -// String? debugLabel, -// }) async { -// // To avoid blocking the UI immediately for an expensive function call, we -// // pump a single frame to allow the framework to complete the current set -// // of work. -// await null; -// return callback(message); -// } +/// The dart:html implementation of [c.compute]. +Future compute( + c.ComputeCallback callback, + Q message, { + String? debugLabel, +}) async { + // To avoid blocking the UI immediately for an expensive function call, we + // pump a single frame to allow the framework to complete the current set + // of work. + await null; + return callback(message); +} diff --git a/plugins/web_adapter/lib/src/js_interop/dio_impl.dart b/plugins/web_adapter/lib/src/js_interop/dio_impl.dart index 8bb08cb6b..a9b777f6f 100644 --- a/plugins/web_adapter/lib/src/js_interop/dio_impl.dart +++ b/plugins/web_adapter/lib/src/js_interop/dio_impl.dart @@ -1,35 +1,33 @@ -export '../html/dio_impl.dart'; +import 'package:dio/dio.dart'; -// import 'package:dio/dio.dart'; -// -// import 'adapter.dart'; -// -// /// Create the [Dio] instance for Web platforms. -// Dio createDio([BaseOptions? options]) => DioForBrowser(options); -// -// /// Implements features for [Dio] on Web platforms. -// class DioForBrowser with DioMixin implements Dio { -// /// Create Dio instance with default [Options]. -// /// It's mostly just one Dio instance in your application. -// DioForBrowser([BaseOptions? options]) { -// this.options = options ?? BaseOptions(); -// httpClientAdapter = BrowserHttpClientAdapter(); -// } -// -// @override -// Future download( -// String urlPath, -// dynamic savePath, { -// ProgressCallback? onReceiveProgress, -// Map? queryParameters, -// CancelToken? cancelToken, -// bool deleteOnError = true, -// String lengthHeader = Headers.contentLengthHeader, -// Object? data, -// Options? options, -// }) { -// throw UnsupportedError( -// 'The download method is not available in the Web environment.', -// ); -// } -// } +import 'adapter.dart'; + +/// Create the [Dio] instance for Web platforms. +Dio createDio([BaseOptions? options]) => DioForBrowser(options); + +/// Implements features for [Dio] on Web platforms. +class DioForBrowser with DioMixin implements Dio { + /// Create Dio instance with default [Options]. + /// It's mostly just one Dio instance in your application. + DioForBrowser([BaseOptions? options]) { + this.options = options ?? BaseOptions(); + httpClientAdapter = BrowserHttpClientAdapter(); + } + + @override + Future download( + String urlPath, + dynamic savePath, { + ProgressCallback? onReceiveProgress, + Map? queryParameters, + CancelToken? cancelToken, + bool deleteOnError = true, + String lengthHeader = Headers.contentLengthHeader, + Object? data, + Options? options, + }) { + throw UnsupportedError( + 'The download method is not available in the Web environment.', + ); + } +} diff --git a/plugins/web_adapter/lib/src/js_interop/multipart_file.dart b/plugins/web_adapter/lib/src/js_interop/multipart_file.dart index 47829b703..64d946803 100644 --- a/plugins/web_adapter/lib/src/js_interop/multipart_file.dart +++ b/plugins/web_adapter/lib/src/js_interop/multipart_file.dart @@ -1,23 +1,21 @@ -export '../html/multipart_file.dart'; +import 'package:http_parser/http_parser.dart'; -// import 'package:http_parser/http_parser.dart'; -// -// final _err = UnsupportedError( -// 'MultipartFile is only supported where dart:io is available.', -// ); -// -// Never multipartFileFromPath( -// String filePath, { -// String? filename, -// MediaType? contentType, -// final Map>? headers, -// }) => -// throw _err; -// -// Never multipartFileFromPathSync( -// String filePath, { -// String? filename, -// MediaType? contentType, -// final Map>? headers, -// }) => -// throw _err; +final _err = UnsupportedError( + 'MultipartFile is only supported where dart:io is available.', +); + +Never multipartFileFromPath( + String filePath, { + String? filename, + MediaType? contentType, + final Map>? headers, +}) => + throw _err; + +Never multipartFileFromPathSync( + String filePath, { + String? filename, + MediaType? contentType, + final Map>? headers, +}) => + throw _err; diff --git a/plugins/web_adapter/lib/src/js_interop/progress_stream.dart b/plugins/web_adapter/lib/src/js_interop/progress_stream.dart index b8a008a0f..43a4e6f22 100644 --- a/plugins/web_adapter/lib/src/js_interop/progress_stream.dart +++ b/plugins/web_adapter/lib/src/js_interop/progress_stream.dart @@ -1,42 +1,40 @@ -export '../html/progress_stream.dart'; +import 'dart:async'; +import 'dart:typed_data'; -// import 'dart:async'; -// import 'dart:typed_data'; -// -// import 'package:dio/dio.dart'; -// -// Stream addProgress( -// Stream> stream, -// int? length, -// RequestOptions options, -// ) { -// if (stream is Stream) { -// return stream; -// } -// final streamTransformer = _transform>(stream, length, options); -// return stream.transform(streamTransformer); -// } -// -// StreamTransformer _transform>( -// Stream stream, -// int? length, -// RequestOptions options, -// ) { -// return StreamTransformer.fromHandlers( -// handleData: (S data, sink) { -// final cancelToken = options.cancelToken; -// if (cancelToken != null && cancelToken.isCancelled) { -// cancelToken.requestOptions = options; -// sink -// ..addError(cancelToken.cancelError!) -// ..close(); -// } else { -// if (data is Uint8List) { -// sink.add(data); -// } else { -// sink.add(Uint8List.fromList(data)); -// } -// } -// }, -// ); -// } +import 'package:dio/dio.dart'; + +Stream addProgress( + Stream> stream, + int? length, + RequestOptions options, +) { + if (stream is Stream) { + return stream; + } + final streamTransformer = _transform>(stream, length, options); + return stream.transform(streamTransformer); +} + +StreamTransformer _transform>( + Stream stream, + int? length, + RequestOptions options, +) { + return StreamTransformer.fromHandlers( + handleData: (S data, sink) { + final cancelToken = options.cancelToken; + if (cancelToken != null && cancelToken.isCancelled) { + cancelToken.requestOptions = options; + sink + ..addError(cancelToken.cancelError!) + ..close(); + } else { + if (data is Uint8List) { + sink.add(data); + } else { + sink.add(Uint8List.fromList(data)); + } + } + }, + ); +} From 17218b1953f65ae839fb18009eb6f86281f76ed4 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 27 Jul 2024 12:52:30 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Allows=20`package:web`?= =?UTF-8?q?=200.5.x~1.x.x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/web_adapter/pubspec.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/web_adapter/pubspec.yaml b/plugins/web_adapter/pubspec.yaml index f1dd94957..75580bb4d 100644 --- a/plugins/web_adapter/pubspec.yaml +++ b/plugins/web_adapter/pubspec.yaml @@ -13,14 +13,13 @@ repository: https://github.com/cfug/dio/blob/main/plugins/web_adapter issue_tracker: https://github.com/cfug/dio/issues environment: - sdk: '>=2.18.0 <4.0.0' -# sdk: '>=3.3.0 <4.0.0' + sdk: ^3.3.0 dependencies: dio: ^5.0.0 http_parser: ^4.0.0 meta: ^1.5.0 -# web: '>=0.5.0 <0.6.0' + web: '>=0.5.0 <2.0.0' dev_dependencies: lints: any From 2d2662106bcd27de539ca1af39dcb083909b229f Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 27 Jul 2024 12:53:12 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=93=9D=20CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/web_adapter/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/web_adapter/CHANGELOG.md b/plugins/web_adapter/CHANGELOG.md index 34a999b08..47954b85c 100644 --- a/plugins/web_adapter/CHANGELOG.md +++ b/plugins/web_adapter/CHANGELOG.md @@ -1,6 +1,6 @@ ## Unreleased -*None.* +- Supports the WASM environment. ## 1.0.1 From e247bd03d42817e519e8b94a63c9bdcf0ac10ea5 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 27 Jul 2024 12:57:35 +0800 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=9A=80=20Adds=20`js=5Finterop`=20cond?= =?UTF-8?q?itional=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dio/lib/src/adapter.dart | 1 + dio/lib/src/compute/compute.dart | 4 +++- dio/lib/src/dio.dart | 1 + dio/lib/src/dio_mixin.dart | 1 + dio/lib/src/multipart_file.dart | 1 + 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dio/lib/src/adapter.dart b/dio/lib/src/adapter.dart index 96bf2149d..1f88a3fc1 100644 --- a/dio/lib/src/adapter.dart +++ b/dio/lib/src/adapter.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'adapters/io_adapter.dart' + if (dart.library.js_interop) 'adapters/browser_adapter.dart' if (dart.library.html) 'adapters/browser_adapter.dart' as adapter; import 'headers.dart'; import 'options.dart'; diff --git a/dio/lib/src/compute/compute.dart b/dio/lib/src/compute/compute.dart index a1c1aef4d..291b46466 100644 --- a/dio/lib/src/compute/compute.dart +++ b/dio/lib/src/compute/compute.dart @@ -25,7 +25,9 @@ import 'dart:async'; -import 'compute_io.dart' if (dart.library.html) 'compute_web.dart' as _c; +import 'compute_io.dart' + if (dart.library.js_interop) 'compute_web.dart' + if (dart.library.html) 'compute_web.dart' as _c; /// Signature for the callback passed to [compute]. /// diff --git a/dio/lib/src/dio.dart b/dio/lib/src/dio.dart index 0cbd18529..321398745 100644 --- a/dio/lib/src/dio.dart +++ b/dio/lib/src/dio.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'adapter.dart'; import 'cancel_token.dart'; import 'dio/dio_for_native.dart' + if (dart.library.js_interop) 'dio/dio_for_browser.dart' if (dart.library.html) 'dio/dio_for_browser.dart'; import 'dio_mixin.dart'; import 'headers.dart'; diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart index 87c04e17c..dbc1bd96b 100644 --- a/dio/lib/src/dio_mixin.dart +++ b/dio/lib/src/dio_mixin.dart @@ -16,6 +16,7 @@ import 'headers.dart'; import 'interceptors/imply_content_type.dart'; import 'options.dart'; import 'progress_stream/io_progress_stream.dart' + if (dart.library.js_interop) 'progress_stream/browser_progress_stream.dart' if (dart.library.html) 'progress_stream/browser_progress_stream.dart'; import 'response.dart'; import 'response/response_stream_handler.dart'; diff --git a/dio/lib/src/multipart_file.dart b/dio/lib/src/multipart_file.dart index f40e050cf..8d451a4d3 100644 --- a/dio/lib/src/multipart_file.dart +++ b/dio/lib/src/multipart_file.dart @@ -4,6 +4,7 @@ import 'dart:typed_data' show Uint8List; import 'package:http_parser/http_parser.dart' show MediaType; import 'multipart_file/io_multipart_file.dart' + if (dart.library.js_interop) 'multipart_file/browser_multipart_file.dart' if (dart.library.html) 'multipart_file/browser_multipart_file.dart'; import 'utils.dart'; From aac9ba23b1fd8c5dd09d1f87df3b10105c3deb15 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Thu, 8 Aug 2024 21:35:16 +0800 Subject: [PATCH 5/9] Update tests.yml Signed-off-by: Alex Li --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1fcccb2d3..d72c45fa0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,7 +51,7 @@ jobs: dart ./scripts/melos_packages.dart echo $(cat .melos_packages) >> $GITHUB_ENV - name: Melos Bootstrap - run: melos bootstrap + run: melos bootstrap ${{ matrix.sdk == 'min' && '--ignore="dio_web_adapter"' || '' }} - name: '[Verify step] Format' if: ${{ matrix.sdk == 'stable' }} run: melos run format From 6becc217c190a69b072e163df8ac93bec360d14b Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 9 Aug 2024 12:34:28 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=92=9A=20++?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/tests.yml | 5 ++++- example_flutter_app/android/app/build.gradle | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d72c45fa0..2362b6889 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,12 +46,15 @@ jobs: - uses: bluefireteam/melos-action@v3 with: run-bootstrap: false + - name: Remove dio_web_adapter overrides + if: ${{ matrix.sdk == 'min' }} + run: rm -rf plugins/web_adapter - name: Check satisfied packages run: | dart ./scripts/melos_packages.dart echo $(cat .melos_packages) >> $GITHUB_ENV - name: Melos Bootstrap - run: melos bootstrap ${{ matrix.sdk == 'min' && '--ignore="dio_web_adapter"' || '' }} + run: melos bootstrap - name: '[Verify step] Format' if: ${{ matrix.sdk == 'stable' }} run: melos run format diff --git a/example_flutter_app/android/app/build.gradle b/example_flutter_app/android/app/build.gradle index 2f633af9b..b224408f8 100644 --- a/example_flutter_app/android/app/build.gradle +++ b/example_flutter_app/android/app/build.gradle @@ -25,12 +25,12 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 33 + compileSdkVersion flutter.compileSdkVersion defaultConfig { applicationId "com.example.flutterApp" - minSdkVersion 16 - targetSdkVersion 33 + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } From 5c61aad134bc4862293382db7e61e10f573c6427 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sun, 11 Aug 2024 16:57:50 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=94=A5=20Remove=20`html`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/web_adapter/lib/dio_web_adapter.dart | 27 +- .../lib/src/{js_interop => }/adapter.dart | 0 .../lib/src/{js_interop => }/compute.dart | 0 .../lib/src/{js_interop => }/dio_impl.dart | 0 plugins/web_adapter/lib/src/html/adapter.dart | 296 ------------------ plugins/web_adapter/lib/src/html/compute.dart | 35 --- .../web_adapter/lib/src/html/dio_impl.dart | 33 -- .../lib/src/html/multipart_file.dart | 21 -- .../lib/src/html/progress_stream.dart | 40 --- .../src/{js_interop => }/multipart_file.dart | 0 .../src/{js_interop => }/progress_stream.dart | 0 11 files changed, 5 insertions(+), 447 deletions(-) rename plugins/web_adapter/lib/src/{js_interop => }/adapter.dart (100%) rename plugins/web_adapter/lib/src/{js_interop => }/compute.dart (100%) rename plugins/web_adapter/lib/src/{js_interop => }/dio_impl.dart (100%) delete mode 100644 plugins/web_adapter/lib/src/html/adapter.dart delete mode 100644 plugins/web_adapter/lib/src/html/compute.dart delete mode 100644 plugins/web_adapter/lib/src/html/dio_impl.dart delete mode 100644 plugins/web_adapter/lib/src/html/multipart_file.dart delete mode 100644 plugins/web_adapter/lib/src/html/progress_stream.dart rename plugins/web_adapter/lib/src/{js_interop => }/multipart_file.dart (100%) rename plugins/web_adapter/lib/src/{js_interop => }/progress_stream.dart (100%) diff --git a/plugins/web_adapter/lib/dio_web_adapter.dart b/plugins/web_adapter/lib/dio_web_adapter.dart index da552a582..64e8178f9 100644 --- a/plugins/web_adapter/lib/dio_web_adapter.dart +++ b/plugins/web_adapter/lib/dio_web_adapter.dart @@ -1,24 +1,7 @@ library dio_web_adapter; -// We separate implementations as `html` and `js_interop` -// only for convenient diffing. - -// export 'src/html/adapter.dart'; - -export 'src/js_interop/adapter.dart'; - -// export 'src/html/compute.dart'; - -export 'src/js_interop/compute.dart'; - -// export 'src/html/dio_impl.dart'; - -export 'src/js_interop/dio_impl.dart'; - -// export 'src/html/multipart_file.dart'; - -export 'src/js_interop/multipart_file.dart'; - -// export 'src/html/progress_stream.dart'; - -export 'src/js_interop/progress_stream.dart'; +export 'src/adapter.dart'; +export 'src/compute.dart'; +export 'src/dio_impl.dart'; +export 'src/multipart_file.dart'; +export 'src/progress_stream.dart'; diff --git a/plugins/web_adapter/lib/src/js_interop/adapter.dart b/plugins/web_adapter/lib/src/adapter.dart similarity index 100% rename from plugins/web_adapter/lib/src/js_interop/adapter.dart rename to plugins/web_adapter/lib/src/adapter.dart diff --git a/plugins/web_adapter/lib/src/js_interop/compute.dart b/plugins/web_adapter/lib/src/compute.dart similarity index 100% rename from plugins/web_adapter/lib/src/js_interop/compute.dart rename to plugins/web_adapter/lib/src/compute.dart diff --git a/plugins/web_adapter/lib/src/js_interop/dio_impl.dart b/plugins/web_adapter/lib/src/dio_impl.dart similarity index 100% rename from plugins/web_adapter/lib/src/js_interop/dio_impl.dart rename to plugins/web_adapter/lib/src/dio_impl.dart diff --git a/plugins/web_adapter/lib/src/html/adapter.dart b/plugins/web_adapter/lib/src/html/adapter.dart deleted file mode 100644 index 715fb5004..000000000 --- a/plugins/web_adapter/lib/src/html/adapter.dart +++ /dev/null @@ -1,296 +0,0 @@ -// import 'dart:async'; -// import 'dart:convert'; -// import 'dart:html'; -// import 'dart:typed_data'; -// -// import 'package:dio/dio.dart'; -// import 'package:dio/src/utils.dart'; -// import 'package:meta/meta.dart'; -// -// BrowserHttpClientAdapter createAdapter() => BrowserHttpClientAdapter(); -// -// /// The default [HttpClientAdapter] for Web platforms. -// class BrowserHttpClientAdapter implements HttpClientAdapter { -// BrowserHttpClientAdapter({this.withCredentials = false}); -// -// /// These are aborted if the client is closed. -// @visibleForTesting -// final xhrs = {}; -// -// /// Whether to send credentials such as cookies or authorization headers for -// /// cross-site requests. -// /// -// /// Defaults to `false`. -// /// -// /// You can also override this value using `Options.extra['withCredentials']` -// /// for each request. -// bool withCredentials; -// -// @override -// Future fetch( -// RequestOptions options, -// Stream? requestStream, -// Future? cancelFuture, -// ) async { -// final xhr = HttpRequest(); -// xhrs.add(xhr); -// xhr -// ..open(options.method, '${options.uri}') -// ..responseType = 'arraybuffer'; -// -// final withCredentialsOption = options.extra['withCredentials']; -// if (withCredentialsOption != null) { -// xhr.withCredentials = withCredentialsOption == true; -// } else { -// xhr.withCredentials = withCredentials; -// } -// -// options.headers.remove(Headers.contentLengthHeader); -// options.headers.forEach((key, v) { -// if (v is Iterable) { -// xhr.setRequestHeader(key, v.join(', ')); -// } else { -// xhr.setRequestHeader(key, v.toString()); -// } -// }); -// -// final sendTimeout = options.sendTimeout ?? Duration.zero; -// final connectTimeout = options.connectTimeout ?? Duration.zero; -// final receiveTimeout = options.receiveTimeout ?? Duration.zero; -// final xhrTimeout = (connectTimeout + receiveTimeout).inMilliseconds; -// xhr.timeout = xhrTimeout; -// -// final completer = Completer(); -// -// xhr.onLoad.first.then((_) { -// final Uint8List body = (xhr.response as ByteBuffer).asUint8List(); -// completer.complete( -// ResponseBody.fromBytes( -// body, -// xhr.status!, -// headers: xhr.responseHeaders.map((k, v) => MapEntry(k, v.split(','))), -// statusMessage: xhr.statusText, -// isRedirect: xhr.status == 302 || -// xhr.status == 301 || -// options.uri.toString() != xhr.responseUrl, -// ), -// ); -// }); -// -// Timer? connectTimeoutTimer; -// if (connectTimeout > Duration.zero) { -// connectTimeoutTimer = Timer( -// connectTimeout, -// () { -// connectTimeoutTimer = null; -// if (completer.isCompleted) { -// // connectTimeout is triggered after the fetch has been completed. -// return; -// } -// xhr.abort(); -// completer.completeError( -// DioException.connectionTimeout( -// requestOptions: options, -// timeout: connectTimeout, -// ), -// StackTrace.current, -// ); -// }, -// ); -// } -// -// // This code is structured to call `xhr.upload.onProgress.listen` only when -// // absolutely necessary, because registering an xhr upload listener prevents -// // the request from being classified as a "simple request" by the CORS spec. -// // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests -// // Upload progress events only get triggered if the request body exists, -// // so we can check it beforehand. -// if (requestStream != null) { -// if (connectTimeoutTimer != null) { -// xhr.upload.onProgress.listen((event) { -// connectTimeoutTimer?.cancel(); -// connectTimeoutTimer = null; -// }); -// } -// -// if (sendTimeout > Duration.zero) { -// final uploadStopwatch = Stopwatch(); -// xhr.upload.onProgress.listen((event) { -// if (!uploadStopwatch.isRunning) { -// uploadStopwatch.start(); -// } -// final duration = uploadStopwatch.elapsed; -// if (duration > sendTimeout) { -// uploadStopwatch.stop(); -// completer.completeError( -// DioException.sendTimeout( -// timeout: sendTimeout, -// requestOptions: options, -// ), -// StackTrace.current, -// ); -// xhr.abort(); -// } -// }); -// } -// -// final onSendProgress = options.onSendProgress; -// if (onSendProgress != null) { -// xhr.upload.onProgress.listen((event) { -// if (event.loaded != null && event.total != null) { -// onSendProgress(event.loaded!, event.total!); -// } -// }); -// } -// } -// -// final receiveStopwatch = Stopwatch(); -// Timer? receiveTimer; -// -// void stopWatchReceiveTimeout() { -// receiveTimer?.cancel(); -// receiveTimer = null; -// receiveStopwatch.stop(); -// } -// -// void watchReceiveTimeout() { -// if (receiveTimeout <= Duration.zero) { -// return; -// } -// receiveStopwatch.reset(); -// if (!receiveStopwatch.isRunning) { -// receiveStopwatch.start(); -// } -// receiveTimer?.cancel(); -// receiveTimer = Timer(receiveTimeout, () { -// if (!completer.isCompleted) { -// xhr.abort(); -// completer.completeError( -// DioException.receiveTimeout( -// timeout: receiveTimeout, -// requestOptions: options, -// ), -// StackTrace.current, -// ); -// } -// stopWatchReceiveTimeout(); -// }); -// } -// -// xhr.onProgress.listen( -// (ProgressEvent event) { -// if (connectTimeoutTimer != null) { -// connectTimeoutTimer!.cancel(); -// connectTimeoutTimer = null; -// } -// watchReceiveTimeout(); -// if (options.onReceiveProgress != null && -// event.loaded != null && -// event.total != null) { -// options.onReceiveProgress!(event.loaded!, event.total!); -// } -// }, -// onDone: () => stopWatchReceiveTimeout(), -// ); -// -// xhr.onError.first.then((_) { -// connectTimeoutTimer?.cancel(); -// // Unfortunately, the underlying XMLHttpRequest API doesn't expose any -// // specific information about the error itself. -// // See also: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onerror -// completer.completeError( -// DioException.connectionError( -// requestOptions: options, -// reason: 'The XMLHttpRequest onError callback was called. ' -// 'This typically indicates an error on the network layer.', -// ), -// StackTrace.current, -// ); -// }); -// -// xhr.onTimeout.first.then((_) { -// final isConnectTimeout = connectTimeoutTimer != null; -// if (connectTimeoutTimer != null) { -// connectTimeoutTimer?.cancel(); -// } -// if (!completer.isCompleted) { -// if (isConnectTimeout) { -// completer.completeError( -// DioException.connectionTimeout( -// timeout: connectTimeout, -// requestOptions: options, -// ), -// ); -// } else { -// completer.completeError( -// DioException.receiveTimeout( -// timeout: Duration(milliseconds: xhrTimeout), -// requestOptions: options, -// ), -// StackTrace.current, -// ); -// } -// } -// }); -// -// cancelFuture?.then((_) { -// if (xhr.readyState < HttpRequest.DONE && -// xhr.readyState > HttpRequest.UNSENT) { -// connectTimeoutTimer?.cancel(); -// try { -// xhr.abort(); -// } catch (_) {} -// if (!completer.isCompleted) { -// completer.completeError( -// DioException.requestCancelled( -// requestOptions: options, -// reason: 'The XMLHttpRequest was aborted.', -// ), -// ); -// } -// } -// }); -// -// if (requestStream != null) { -// if (options.method == 'GET') { -// warningLog( -// 'GET request with a body data are not support on the ' -// 'web platform. Use POST/PUT instead.', -// StackTrace.current, -// ); -// } -// final completer = Completer(); -// final sink = ByteConversionSink.withCallback( -// (bytes) => completer.complete( -// bytes is Uint8List ? bytes : Uint8List.fromList(bytes), -// ), -// ); -// requestStream.listen( -// sink.add, -// onError: (Object e, StackTrace s) => completer.completeError(e, s), -// onDone: sink.close, -// cancelOnError: true, -// ); -// final bytes = await completer.future; -// xhr.send(bytes); -// } else { -// xhr.send(); -// } -// return completer.future.whenComplete(() { -// xhrs.remove(xhr); -// }); -// } -// -// /// Closes the client. -// /// -// /// This terminates all active requests. -// @override -// void close({bool force = false}) { -// if (force) { -// for (final xhr in xhrs) { -// xhr.abort(); -// } -// } -// xhrs.clear(); -// } -// } diff --git a/plugins/web_adapter/lib/src/html/compute.dart b/plugins/web_adapter/lib/src/html/compute.dart deleted file mode 100644 index fa9d6be3b..000000000 --- a/plugins/web_adapter/lib/src/html/compute.dart +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file corresponds to Flutter's -// [`foundation/_isolates_web.dart`](https://github.com/flutter/flutter/blob/stable/packages/flutter/lib/src/foundation/_isolates_web.dart). -// -// Changes are only synced with the `stable` branch. -// -// Last synced commit: -// [978a2e7](https://github.com/flutter/flutter/commit/978a2e7bf6a2ed287130af8dbd94cef019fb7bef) -// -// The changes are currently manually synced. If you noticed that the Flutter's -// original `compute` function (and any of the related files) have changed -// on the `stable` branch and you would like to see those changes in the `compute` package -// please open an [issue](https://github.com/dartsidedev/compute/issues), -// and I'll try my best to "merge". -// -// The file is intentionally not refactored so that it is easier to keep the -// compute package up to date with Flutter's implementation. - -// import 'package:dio/src/compute/compute.dart' as c; -// -// /// The dart:html implementation of [c.compute]. -// Future compute( -// c.ComputeCallback callback, -// Q message, { -// String? debugLabel, -// }) async { -// // To avoid blocking the UI immediately for an expensive function call, we -// // pump a single frame to allow the framework to complete the current set -// // of work. -// await null; -// return callback(message); -// } diff --git a/plugins/web_adapter/lib/src/html/dio_impl.dart b/plugins/web_adapter/lib/src/html/dio_impl.dart deleted file mode 100644 index 4aca66163..000000000 --- a/plugins/web_adapter/lib/src/html/dio_impl.dart +++ /dev/null @@ -1,33 +0,0 @@ -// import 'package:dio/dio.dart'; -// -// import 'adapter.dart'; -// -// /// Create the [Dio] instance for Web platforms. -// Dio createDio([BaseOptions? options]) => DioForBrowser(options); -// -// /// Implements features for [Dio] on Web platforms. -// class DioForBrowser with DioMixin implements Dio { -// /// Create Dio instance with default [Options]. -// /// It's mostly just one Dio instance in your application. -// DioForBrowser([BaseOptions? options]) { -// this.options = options ?? BaseOptions(); -// httpClientAdapter = BrowserHttpClientAdapter(); -// } -// -// @override -// Future download( -// String urlPath, -// dynamic savePath, { -// ProgressCallback? onReceiveProgress, -// Map? queryParameters, -// CancelToken? cancelToken, -// bool deleteOnError = true, -// String lengthHeader = Headers.contentLengthHeader, -// Object? data, -// Options? options, -// }) { -// throw UnsupportedError( -// 'The download method is not available in the Web environment.', -// ); -// } -// } diff --git a/plugins/web_adapter/lib/src/html/multipart_file.dart b/plugins/web_adapter/lib/src/html/multipart_file.dart deleted file mode 100644 index ff848c7de..000000000 --- a/plugins/web_adapter/lib/src/html/multipart_file.dart +++ /dev/null @@ -1,21 +0,0 @@ -// import 'package:http_parser/http_parser.dart'; -// -// final _err = UnsupportedError( -// 'MultipartFile is only supported where dart:io is available.', -// ); -// -// Never multipartFileFromPath( -// String filePath, { -// String? filename, -// MediaType? contentType, -// final Map>? headers, -// }) => -// throw _err; -// -// Never multipartFileFromPathSync( -// String filePath, { -// String? filename, -// MediaType? contentType, -// final Map>? headers, -// }) => -// throw _err; diff --git a/plugins/web_adapter/lib/src/html/progress_stream.dart b/plugins/web_adapter/lib/src/html/progress_stream.dart deleted file mode 100644 index 44e0312d3..000000000 --- a/plugins/web_adapter/lib/src/html/progress_stream.dart +++ /dev/null @@ -1,40 +0,0 @@ -// import 'dart:async'; -// import 'dart:typed_data'; -// -// import 'package:dio/dio.dart'; -// -// Stream addProgress( -// Stream> stream, -// int? length, -// RequestOptions options, -// ) { -// if (stream is Stream) { -// return stream; -// } -// final streamTransformer = _transform>(stream, length, options); -// return stream.transform(streamTransformer); -// } -// -// StreamTransformer _transform>( -// Stream stream, -// int? length, -// RequestOptions options, -// ) { -// return StreamTransformer.fromHandlers( -// handleData: (S data, sink) { -// final cancelToken = options.cancelToken; -// if (cancelToken != null && cancelToken.isCancelled) { -// cancelToken.requestOptions = options; -// sink -// ..addError(cancelToken.cancelError!) -// ..close(); -// } else { -// if (data is Uint8List) { -// sink.add(data); -// } else { -// sink.add(Uint8List.fromList(data)); -// } -// } -// }, -// ); -// } diff --git a/plugins/web_adapter/lib/src/js_interop/multipart_file.dart b/plugins/web_adapter/lib/src/multipart_file.dart similarity index 100% rename from plugins/web_adapter/lib/src/js_interop/multipart_file.dart rename to plugins/web_adapter/lib/src/multipart_file.dart diff --git a/plugins/web_adapter/lib/src/js_interop/progress_stream.dart b/plugins/web_adapter/lib/src/progress_stream.dart similarity index 100% rename from plugins/web_adapter/lib/src/js_interop/progress_stream.dart rename to plugins/web_adapter/lib/src/progress_stream.dart From 991fcacad42d876b233ac61fc92c62414eac07f6 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sun, 11 Aug 2024 16:58:49 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=93=9D=20CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/web_adapter/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/web_adapter/CHANGELOG.md b/plugins/web_adapter/CHANGELOG.md index 47954b85c..c7c1e456e 100644 --- a/plugins/web_adapter/CHANGELOG.md +++ b/plugins/web_adapter/CHANGELOG.md @@ -1,6 +1,7 @@ ## Unreleased -- Supports the WASM environment. +- Supports the WASM environment. Users should upgrade the adapter with + `dart pub upgrade` or `flutter pub upgrade` to use the WASM-supported version. ## 1.0.1 From d274fc500e6c151d5792ff5c89e4497c5642934b Mon Sep 17 00:00:00 2001 From: Alex Li Date: Tue, 13 Aug 2024 17:30:29 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E2=9C=85=20Try=20to=20run=20tests=20with?= =?UTF-8?q?=20WASM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- melos.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/melos.yaml b/melos.yaml index fa7c611a3..1e939a3b5 100644 --- a/melos.yaml +++ b/melos.yaml @@ -79,11 +79,13 @@ scripts: run: melos run test:web:single env: TEST_PLATFORM: chrome + WITH_WASM: true test:web:firefox: name: Dart Web tests in firefox run: melos run test:web:single env: TEST_PLATFORM: firefox + WITH_WASM: false test:web:single: name: Dart Web tests in a browser exec: | @@ -91,6 +93,9 @@ scripts: dart test --platform ${TEST_PLATFORM} --preset=${TEST_PRESET:-default},${TARGET_DART_SDK:-stable} --chain-stack-traces else dart test --platform ${TEST_PLATFORM} --coverage=coverage/${TEST_PLATFORM} --preset=${TEST_PRESET:-default},${TARGET_DART_SDK:-stable} --chain-stack-traces + if [ "$WITH_WASM" = "true" ]; then + dart test --platform ${TEST_PLATFORM} --coverage=coverage/${TEST_PLATFORM} --preset=${TEST_PRESET:-default},${TARGET_DART_SDK:-stable} --chain-stack-traces --compiler=dart2wasm + fi fi packageFilters: flutter: false