diff --git a/CHANGELOG.md b/CHANGELOG.md index 704b77b..63980e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## 1.0.0-dev.3 +- **BREAKING**: Renamed extensions from `Extension` to `InstanceMembers`. +- `AbortSignal` + - Added `timeout` constructor-like method. + - Added `abort` constructor-like method. + - Added `aborted` property. + - Added `reason` property. + - Added `throwIfAborted` method. +- `AbortController` + - Added docs. +- `fetch()` added docs. +- `Response` + - Fixed internal definition for `_formData` method. + - Added docs. +- `Headers` + - Added docs. + - Add methods `headersFromMap` and `headersFromArray` to support Dart 2.19. +- `Iterator` and `IteratorResult` + - Added docs. +- `ReadableStreamDefaultReader` and `ReadableStreamDefaultReaderChunk` + - Added docs. + ## 1.0.0-dev.2 - Downgraded `js` dependency to `^0.6.5`. diff --git a/README.md b/README.md index 30318db..d463a12 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ # Fetch API. +> ⚠️ Requires Dart 2.19 or higher. + This package provides JavaScript bindings to [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). ## Features * Full request parameters coverage -* Abort requests +* Support canceling requests * Read response * As text - * As Blob - * As Stream of Uint8List + * As `Blob` + * As `Stream` of `Uint8List` * Support streaming of data * Get access to redirect status -* Support non-200 responses +* Support non-`200` responses diff --git a/lib/src/abort_controller.dart b/lib/src/abort_controller.dart index a2e195c..b639fc0 100644 --- a/lib/src/abort_controller.dart +++ b/lib/src/abort_controller.dart @@ -2,6 +2,12 @@ import '_js.dart'; import 'abort_signal.dart'; +/// The [AbortController] interface represents a controller object that allows +/// you to abort one or more Web requests as and when desired. +/// +/// You can create a new [AbortController] object using the `AbortController()` +/// constructor. Communicating with a DOM request is done using an [AbortSignal] +/// object. @JS() @staticInterop class AbortController { @@ -9,8 +15,8 @@ class AbortController { external factory AbortController(); } -extension AbortControllerExtension on AbortController { - /// Returns an `AbortSignal` object instance, which can be used to +extension AbortControllerInstanceMembers on AbortController { + /// Returns an [AbortSignal] object instance, which can be used to /// communicate with, or to abort, a DOM request. external final AbortSignal signal; diff --git a/lib/src/abort_signal.dart b/lib/src/abort_signal.dart index 603ee0d..d726f7c 100644 --- a/lib/src/abort_signal.dart +++ b/lib/src/abort_signal.dart @@ -1,8 +1,37 @@ import '_js.dart'; +import 'abort_controller.dart'; +/// The [AbortSignal] interface represents 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. @JS() @staticInterop class AbortSignal { - external factory AbortSignal(); + /// Returns an [AbortSignal] instance that will automatically abort + /// after a specified [time]. + factory AbortSignal.timeout(Duration time) + => AbortSignal._timeout(time.inMilliseconds); + + /// Returns an [AbortSignal] instance that is already set as aborted. + external static AbortSignal abort([dynamic reason]); + + /// Returns an [AbortSignal] instance that will automatically abort + /// after a specified [time] in milliseconds. + @JS('timeout') + external static AbortSignal _timeout(int time); +} + + +extension AbortSignalInstanceMembers on AbortSignal { + /// A [bool] that indicates whether the request(s) the signal is + /// communicating with is/are aborted (`true`) or not (`false`). + external final bool aborted; + + /// A JavaScript value providing the abort reason, once the signal has aborted. + external final dynamic reason; + + /// Throws the signal's abort [reason] if the signal has been aborted; + /// otherwise it does nothing. + external void throwIfAborted(); } diff --git a/lib/src/fetch.dart b/lib/src/fetch.dart index d5f2bad..1dbf2d8 100644 --- a/lib/src/fetch.dart +++ b/lib/src/fetch.dart @@ -6,5 +6,25 @@ import 'response.dart'; @JS('fetch') external Promise _fetch(dynamic resource, RequestInit? options); +/// 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. +/// +/// The promise resolves to the [Response] object representing the response +/// to your request. +/// +/// A `fetch()` promise only rejects when a network error is encountered +/// (which is usually when there's a permissions issue or similar). +/// A `fetch()` promise does not reject on HTTP errors (`404`, etc.). +/// Instead, a then() handler must check the [ResponseInstanceMembers.ok] and/or +/// [ResponseInstanceMembers.status] properties. +/// +/// `WindowOrWorkerGlobalScope` is implemented by both `Window` and +/// `WorkerGlobalScope`, which means that the `fetch()` method is available +/// in pretty much any context in which you might want to fetch resources. +/// +/// The `fetch()` method is controlled by the `connect-src` directive of +/// Content Security Policy rather than the directive of the resources +/// it's retrieving. Future fetch(dynamic resource, [RequestInit? options]) => promiseToFuture(_fetch(resource, options)); diff --git a/lib/src/headers.dart b/lib/src/headers.dart index 63412b3..d8a076e 100644 --- a/lib/src/headers.dart +++ b/lib/src/headers.dart @@ -1,40 +1,54 @@ import '_js.dart'; import 'iterator.dart' as js; import 'iterator_wrapper.dart'; - - +import 'response.dart'; + + +/// 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. +/// +/// A [Headers] object has an associated header list, which is initially empty +/// and consists of zero or more name and value pairs. You can add to this using +/// methods like `append()`. In all methods of this interface, header names are +/// matched by case-insensitive byte sequence. +/// +/// For security reasons, some headers can only be controlled by the user agent. +/// These headers include the forbidden header names and forbidden response +/// header names. +/// +/// A [Headers] object also has an associated guard, which takes +/// a value of `immutable`, `request`, `request-no-cors`, `response`, or `none`. +/// This affects whether the `set()`, `delete()`, and `append()` methods will +/// mutate the header. +/// +/// You can retrieve a [Headers] object via the `Request.headers` and +/// [ResponseInstanceMembers.headers] properties, and create +/// a new [Headers] object using the `Headers()` constructor. @JS() @staticInterop class Headers { - /// Creates empty [Headers]. - factory Headers() => Headers._(); - - /// Creates [Headers] from [Map]. - factory Headers.fromMap(Map init) => - Headers._init(init.toJsObject()); - - /// Creates [Headers] from array of 2 items arrays. - factory Headers.fromArray(List> init) { - final _init = JsArray>(); - for (final header in init) { - if (header.length != 2) - throw Exception('Bad argument'); - - _init.add(header.toJsArray()); - } - return Headers._init(_init); - } - /// Creates a new [Headers] object. @JS('Headers') - external factory Headers._(); + external factory Headers(); /// Creates a new [Headers] object. @JS('Headers') external factory Headers._init(dynamic init); + + /// Warning: available only with Dart 3.0 or higher. + /// Creates [Headers] from [Map]. + factory Headers.fromMap(Map init) => + headersFromMap(init); + + /// Warning: available only with Dart 3.0 or higher. + /// Creates [Headers] from array of 2 items arrays. + factory Headers.fromArray(List> init) => + headersFromArray(init); } -extension HeadersExtension on Headers { +extension HeadersInstanceMembers on Headers { /// 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); @@ -42,8 +56,8 @@ extension HeadersExtension on Headers { /// Deletes a header from a [Headers] object. external void delete(String name); - /// Returns an `iterator` allowing to go through all key/value pairs contained - /// in this object. + /// Returns an [js.Iterator] allowing to go through all key/value pairs + /// contained in this object. @JS('entries') external js.Iterator> _entries(); @@ -57,8 +71,8 @@ extension HeadersExtension on Headers { /// a certain header. external bool has(String name); - /// Returns an `iterator` allowing you to go through all keys of the key/value - /// pairs contained in this object. + /// Returns an [js.Iterator] allowing you to go through all keys of + /// the key/value pairs contained in this object. @JS('keys') external js.Iterator _keys(); @@ -66,7 +80,7 @@ extension HeadersExtension on Headers { /// or adds the header if it does not already exist. external void set(String name, String value); - /// Returns an `iterator` allowing you to go through all values of + /// Returns an [js.Iterator] allowing you to go through all values of /// the key/value pairs contained in this object. @JS('values') external js.Iterator _values(); @@ -83,3 +97,19 @@ extension HeadersExtension on Headers { /// the key/value pairs contained in this object. IteratorWrapper values() => IteratorWrapper(_values()); } + +/// Creates [Headers] from [Map]. +Headers headersFromMap(Map init) => + Headers._init(init.toJsObject()); + +/// Creates [Headers] from array of 2 items arrays. +Headers headersFromArray(List> init) { + final _init = JsArray>(); + for (final header in init) { + if (header.length != 2) + throw Exception('Bad argument'); + + _init.add(header.toJsArray()); + } + return Headers._init(_init); +} diff --git a/lib/src/iterator.dart b/lib/src/iterator.dart index efa3e73..591ec54 100644 --- a/lib/src/iterator.dart +++ b/lib/src/iterator.dart @@ -2,14 +2,21 @@ import '_js.dart'; import 'iterator_result.dart'; +/// The iterator protocol defines a standard way to produce a sequence of values +/// (either finite or infinite), and potentially a return value when all values +/// have been generated. +/// +/// All iterator protocol methods (`next()`, `return()`, and `throw()`) are +/// expected to return an object implementing the [IteratorResult] interface. @JS() @anonymous class Iterator { /// A function that accepts zero or one argument and returns an object /// conforming to the [IteratorResult] interface. /// - /// If a non-object value gets returned (such as false or undefined) - /// when a built-in language feature (such as for...of) is using the iterator, - /// a TypeError ("iterator.next() returned a non-object value") will be thrown. + /// If a non-object value gets returned (such as `false` or `undefined`) + /// when a built-in language feature (such as `for...of`) is using + /// the iterator, a `TypeError` (`"iterator.next() returned a non-object + /// value"`) will be thrown. external IteratorResult next(); } diff --git a/lib/src/iterator_result.dart b/lib/src/iterator_result.dart index fa83cc9..4360018 100644 --- a/lib/src/iterator_result.dart +++ b/lib/src/iterator_result.dart @@ -1,6 +1,13 @@ import '_js.dart'; +/// In practice, neither property is strictly required; if an object without +/// either property is returned, it's effectively equivalent to +/// `{ done: false, value: undefined }`. +/// +/// If an iterator returns a result with `done: true`, any subsequent calls +/// to `next()` are expected to return `done: true` as well, although +/// this is not enforced on the language level. @JS() @anonymous class IteratorResult { diff --git a/lib/src/iterator_wrapper.dart b/lib/src/iterator_wrapper.dart index f5afd97..c4ce249 100644 --- a/lib/src/iterator_wrapper.dart +++ b/lib/src/iterator_wrapper.dart @@ -4,7 +4,7 @@ import 'dart:core' as core; import 'iterator.dart' as js; -/// Wrapper on top of JS iterator, providing [Iterable] and [Iterator] APIs. +/// Wrapper on top of [js.Iterator], implementing [Iterable] and [Iterator] APIs. class IteratorWrapper extends IterableBase implements core.Iterator { IteratorWrapper(this._iterator); diff --git a/lib/src/readable_stream.dart b/lib/src/readable_stream.dart index 039d53f..a6e218b 100644 --- a/lib/src/readable_stream.dart +++ b/lib/src/readable_stream.dart @@ -1,7 +1,11 @@ import '_js.dart'; import 'readable_stream_default_reader.dart'; +import 'response.dart'; +/// The [ReadableStream] interface of the Streams API represents a readable +/// stream of byte data. The Fetch API offers a concrete instance of +/// a [ReadableStream] through the body property of a [Response] object. @JS() @staticInterop class ReadableStream { @@ -12,7 +16,7 @@ class ReadableStream { ]); } -extension ReadableStreamExtension on ReadableStream { +extension ReadableStreamInstanceMembers on ReadableStream { /// Returns a [bool] indicating whether or not the readable stream /// is locked to a reader. external final bool locked; diff --git a/lib/src/readable_stream_default_reader.dart b/lib/src/readable_stream_default_reader.dart index 190267e..5b6ab3f 100644 --- a/lib/src/readable_stream_default_reader.dart +++ b/lib/src/readable_stream_default_reader.dart @@ -5,6 +5,12 @@ import 'readable_stream.dart'; import 'readable_stream_default_reader_chunk.dart'; +/// The [ReadableStreamDefaultReader] interface of the Streams API represents +/// a default reader that can be used to read stream data supplied from +/// a network (such as a fetch request). +/// +/// A [ReadableStreamDefaultReader] can be used to read from a [ReadableStream] +/// that has an underlying source of any type. @JS() @staticInterop class ReadableStreamDefaultReader { @@ -12,7 +18,7 @@ class ReadableStreamDefaultReader { external factory ReadableStreamDefaultReader(ReadableStream stream); } -extension ReadableStreamDefaultReaderExtension on ReadableStreamDefaultReader { +extension ReadableStreamDefaultReaderInstanceMembers on ReadableStreamDefaultReader { /// Returns a `Promise` that fulfills when the stream closes, /// or rejects if the stream throws an error or the reader's lock is released. /// This property enables you to write code that responds to an end to diff --git a/lib/src/readable_stream_default_reader_chunk.dart b/lib/src/readable_stream_default_reader_chunk.dart index 71fe44f..046a0ba 100644 --- a/lib/src/readable_stream_default_reader_chunk.dart +++ b/lib/src/readable_stream_default_reader_chunk.dart @@ -1,7 +1,15 @@ import 'dart:typed_data'; import '_js.dart'; +import 'readable_stream_default_reader.dart'; +/// Describes chunk from [ReadableStreamDefaultReaderInstanceMembers.read]. +/// +/// If a chunk is available, then object will be of the form +/// `{ value: theChunk, done: false }`. +/// +/// If the stream becomes closed, the object will be of the form +/// `{ value: undefined, done: true }`. @JS() @anonymous class ReadableStreamDefaultReaderChunk { diff --git a/lib/src/request_init/request_init.dart b/lib/src/request_init/request_init.dart index e46ffea..466447e 100644 --- a/lib/src/request_init/request_init.dart +++ b/lib/src/request_init/request_init.dart @@ -113,7 +113,7 @@ class RequestInit { external AbortSignal? signal; } -extension RequestInitExtension on RequestInit { +extension RequestInitInstanceMembers on RequestInit { /// The mode you want to use for the request RequestMode get requestMode => RequestMode.from(mode); set requestMode(RequestMode requestMode) => diff --git a/lib/src/response/response.dart b/lib/src/response/response.dart index cb273fa..de92672 100644 --- a/lib/src/response/response.dart +++ b/lib/src/response/response.dart @@ -8,15 +8,27 @@ import 'response_options.dart'; import 'response_type.dart'; +/// The [Response] interface of the Fetch API represents the response +/// to a request. +/// +/// You can create a new [Response] object using the `Response()` constructor, +/// but you are more likely to encounter a [Response] object being returned +/// as the result of another API operation —for example, a service worker +/// `FetchEvent.respondWith`, or a simple `fetch()`. @JS() @staticInterop class Response { + /// Creates a new [Response] object. external factory Response([dynamic body, ResponseOptions? options]); + + /// Returns a new [Response] object associated with a network error. external static Response error(); + + /// Creates a new response with a different URL. external static Response redirect(String url, [int status = 302]); } -extension ResponseExtension on Response { +extension ResponseInstanceMembers on Response { /// A [ReadableStream] of the body contents. external final ReadableStream? body; @@ -67,7 +79,7 @@ extension ResponseExtension on Response { /// Returns a promise that resolves with a [FormData] representation of /// the response body. @JS('formData') - external Promise _formData(); + external Promise _formData(); /// Returns a promise that resolves with the result of parsing the response /// body text as JSON. diff --git a/pubspec.yaml b/pubspec.yaml index 15dbd5e..473329e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fetch_api description: JavaScript bindings for Fetch API, allowing a flexible HTTP requests. -version: 1.0.0-dev.2 +version: 1.0.0-dev.3 homepage: https://github.com/Zekfad/fetch_api repository: https://github.com/Zekfad/fetch_api issue_tracker: https://github.com/Zekfad/fetch_api/issues @@ -10,7 +10,7 @@ platforms: web: environment: - sdk: '>=2.18.0 <4.0.0' + sdk: '>=2.19.0-0 <4.0.0' dependencies: js: ^0.6.5