Skip to content

Commit

Permalink
feat!: 1.0.0-dev.3
Browse files Browse the repository at this point in the history
  • Loading branch information
Zekfad committed Jan 23, 2023
1 parent 334d17c commit 5cb7445
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 46 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
## 1.0.0-dev.3
- **BREAKING**: Renamed extensions from `<Class>Extension` to `<Class>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`.
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
10 changes: 8 additions & 2 deletions lib/src/abort_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ 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 {
/// Creates a new [AbortController] object instance.
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;

Expand Down
31 changes: 30 additions & 1 deletion lib/src/abort_signal.dart
Original file line number Diff line number Diff line change
@@ -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();
}
20 changes: 20 additions & 0 deletions lib/src/fetch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,25 @@ import 'response.dart';
@JS('fetch')
external Promise<Response> _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<Response> fetch(dynamic resource, [RequestInit? options]) =>
promiseToFuture(_fetch(resource, options));
86 changes: 58 additions & 28 deletions lib/src/headers.dart
Original file line number Diff line number Diff line change
@@ -1,49 +1,63 @@
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<String, String> init) =>
Headers._init(init.toJsObject());

/// Creates [Headers] from array of 2 items arrays.
factory Headers.fromArray(List<List<String>> init) {
final _init = JsArray<JsArray<dynamic>>();
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<String, String> init) =>
headersFromMap(init);

/// Warning: available only with Dart 3.0 or higher.
/// Creates [Headers] from array of 2 items arrays.
factory Headers.fromArray(List<List<String>> 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);

/// 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<List<String>> _entries();

Expand All @@ -57,16 +71,16 @@ 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<String> _keys();

/// Sets a new value for an existing header inside a [Headers] object,
/// or adds the header if it does not already exist.
external void set(String name, String value);

/// 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<String> _values();
Expand All @@ -83,3 +97,19 @@ extension HeadersExtension on Headers {
/// the key/value pairs contained in this object.
IteratorWrapper<String> values() => IteratorWrapper(_values());
}

/// Creates [Headers] from [Map].
Headers headersFromMap(Map<String, String> init) =>
Headers._init(init.toJsObject());

/// Creates [Headers] from array of 2 items arrays.
Headers headersFromArray(List<List<String>> init) {
final _init = JsArray<JsArray<dynamic>>();
for (final header in init) {
if (header.length != 2)
throw Exception('Bad argument');

_init.add(header.toJsArray());
}
return Headers._init(_init);
}
13 changes: 10 additions & 3 deletions lib/src/iterator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
/// 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<T> next();
}
7 changes: 7 additions & 0 deletions lib/src/iterator_result.dart
Original file line number Diff line number Diff line change
@@ -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<T> {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/iterator_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> extends IterableBase<T> implements core.Iterator<T> {
IteratorWrapper(this._iterator);

Expand Down
6 changes: 5 additions & 1 deletion lib/src/readable_stream.dart
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion lib/src/readable_stream_default_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ 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 {
/// Creates and returns a [ReadableStreamDefaultReader] object instance.
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
Expand Down
8 changes: 8 additions & 0 deletions lib/src/readable_stream_default_reader_chunk.dart
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/request_init/request_init.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) =>
Expand Down
Loading

0 comments on commit 5cb7445

Please sign in to comment.