Skip to content

Commit 4c4fbd1

Browse files
committed
Version 2.1.0
1 parent 059ff2e commit 4c4fbd1

3 files changed

Lines changed: 228 additions & 9 deletions

File tree

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
The MIT License (MIT)
2-
Copyright (c) 2019 Aldwin Vlasblom
2+
Copyright (c) 2020 Aldwin Vlasblom
33

44
Permission is hereby granted, free of charge, to any person obtaining a copy of
55
this software and associated documentation files (the "Software"), to deal in

README.md

Lines changed: 226 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
# Fluture Node
22

3-
Common Node API's wrapped to return [Fluture][] Futures.
3+
FP-style HTTP and streaming utils for Node based on [Fluture][].
4+
5+
```console
6+
$ npm install fluture fluture-node
7+
```
48

59
## API
610

7-
#### <a name="once" href="https://github.com/fluture-js/fluture-node/blob/v2.0.0/index.js#L9">`once :: String -⁠> EventEmitter -⁠> Future Error a`</a>
11+
### EventEmitter
12+
13+
#### <a name="once" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L29">`once :: String -⁠> EventEmitter -⁠> Future Error a`</a>
814

915
Resolve a Future with the first event emitted over
1016
the given event emitter under the given event name.
@@ -19,9 +25,38 @@ itself from the event emitter.
1925
Future.of (42);
2026
```
2127

22-
#### <a name="buffer" href="https://github.com/fluture-js/fluture-node/blob/v2.0.0/index.js#L41">`buffer :: ReadableStream a -⁠> Future Error (Array a)`</a>
28+
### Buffer
29+
30+
#### <a name="encode" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L63">`encode :: Charset -⁠> Buffer -⁠> Future Error String`</a>
31+
32+
Given an encoding and a [Buffer][], returns a Future of the result of
33+
encoding said buffer using the given encoding. The Future will reject
34+
with an Error if the encoding is unknown.
35+
36+
```js
37+
> encode ('utf8') (Buffer.from ('Hello world!'));
38+
'Hello world!'
39+
```
40+
41+
### Stream
42+
43+
#### <a name="streamOf" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L80">`streamOf :: Buffer -⁠> Future a (Readable Buffer)`</a>
44+
45+
Given a [Buffer][], returns a Future of a [Readable][] stream which will
46+
emit the given Buffer before ending.
2347

24-
Buffer all data on a Stream into a Future of an Array.
48+
The stream is wrapped in a Future because creation of a stream causes
49+
side-effects if it's not consumed in time, making it safer to pass it
50+
around wrapped in a Future.
51+
52+
#### <a name="emptyStream" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L96">`emptyStream :: Future a (Readable Buffer)`</a>
53+
54+
A [Readable][] stream which ends after emiting zero bytes. Can be useful
55+
as an empty [`request`](#request) body, for example.
56+
57+
#### <a name="buffer" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L102">`buffer :: Readable a -⁠> Future Error (Array a)`</a>
58+
59+
Buffer all data on a [Readable][] stream into a Future of an Array.
2560

2661
When the Future is cancelled, it removes any trace of
2762
itself from the Stream.
@@ -37,7 +72,16 @@ itself from the Stream.
3772
Future.of ([Buffer.from ('hello'), Buffer.from ('world')]);
3873
```
3974

40-
#### <a name="instant" href="https://github.com/fluture-js/fluture-node/blob/v2.0.0/index.js#L80">`instant :: b -⁠> Future a b`</a>
75+
#### <a name="bufferString" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L141">`bufferString :: Charset -⁠> Readable Buffer -⁠> Future Error String`</a>
76+
77+
A version of [`buffer`](#buffer) specialized in Strings.
78+
79+
Takes a charset and a [Readable][] stream of [Buffer][]s, and returns
80+
a Future containing a String with the fully buffered and encoded result.
81+
82+
### Event Loop
83+
84+
#### <a name="instant" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L153">`instant :: b -⁠> Future a b`</a>
4185

4286
Resolves a Future with the given value in the next tick,
4387
using [`process.nextTick`][]. The scheduled job cannot be
@@ -49,7 +93,7 @@ blocking the event loop until it's completed.
4993
Future.of ('noodles')
5094
```
5195

52-
#### <a name="immediate" href="https://github.com/fluture-js/fluture-node/blob/v2.0.0/index.js#L96">`immediate :: b -⁠> Future a b`</a>
96+
#### <a name="immediate" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L169">`immediate :: b -⁠> Future a b`</a>
5397

5498
Resolves a Future with the given value in the next tick,
5599
using [`setImmediate`][]. This job will run as soon as all
@@ -61,6 +105,181 @@ job is unscheduled.
61105
Future.of ('results')
62106
```
63107

64-
[Fluture]: https://github.com/fluture-js/Fluture
108+
### Http
109+
110+
The functions below are to be used in compositions such as the one shown
111+
below, in order to cover a wide variety of HTTP-related use cases.
112+
113+
```js
114+
import {map, chain, chainRej, encase, fork} from 'fluture/index.js';
115+
import {retrieve,
116+
acceptStatus,
117+
autoBufferResponse,
118+
responseToError} from './index.js';
119+
120+
const rejectUnacceptable = res => (
121+
acceptStatus (200) (res)
122+
.pipe (chainRej (responseToError))
123+
);
124+
125+
retrieve ('https://api.github.com/users/Avaq') ({'User-Agent': 'Avaq'})
126+
.pipe (chain (rejectUnacceptable))
127+
.pipe (chain (autoBufferResponse))
128+
.pipe (chain (encase (JSON.parse)))
129+
.pipe (map (avaq => avaq.name))
130+
.pipe (fork (console.error) (console.log));
131+
```
132+
133+
The example above will either:
134+
135+
1. log `"Aldwin Vlasblom"` to the terminal if nothing weird happens; or
136+
2. log an error to the console if:
137+
* a network error occurs;
138+
* the response code is not 200; or
139+
* the JSON is malformed.
140+
141+
Note that we were in control of how an unexpected status was treated,
142+
how an erroneous response would be formatted as an error message,
143+
whether the response would be parsed as JSON, and how a failure of parsing
144+
the JSON would have been treated.
145+
146+
The goal of the functions below us to give you as much control over HTTP
147+
requests as possible, while still keeping boilerplate low by leveraging
148+
function composition.
149+
150+
This contrasts with many of the popular HTTP client libraries out there,
151+
which often make decisions for you, taking away control in an attempt to
152+
provide a smoother usage experience.
153+
154+
#### <a name="request" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L255">`request :: Object -⁠> Url -⁠> Readable Buffer -⁠> Future Error IncomingMessage`</a>
155+
156+
This is the "lowest level" function for making HTTP requests. It does not
157+
handle buffering, encoding, content negotiation, or anything really.
158+
For most use cases, you can use one of the more specialized functions:
159+
160+
* [`send`](#send): Make a generic HTTP request.
161+
* [`retrieve`](#retrieve): Make a GET request.
162+
163+
Given an Object of [http options][], a String containing the request URL,
164+
and a [Readable][] stream of [Buffer][]s to be sent as the request body,
165+
returns a Future which makes an HTTP request and resolves with its
166+
[IncomingMessage][]. If the Future is cancelled, the request is aborted.
167+
168+
```js
169+
import {attempt, chain} from 'fluture/index.js';
170+
import {createReadStream} from 'fs';
171+
172+
const sendBinary = request ({
173+
method: 'POST',
174+
headers: {'Transfer-Encoding': 'chunked'},
175+
});
176+
177+
const eventualBody = attempt (() => createReadStream ('./data.bin'));
178+
179+
eventualBody.pipe (chain (sendBinary ('https://example.com')));
180+
```
181+
182+
If you want to use this function to transfer a stream of data, don't forget
183+
to set the Transfer-Encoding header to "chunked".
184+
185+
#### <a name="retrieve" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L299">`retrieve :: Url -⁠> StrMap String -⁠> Future Error IncomingMessage`</a>
186+
187+
A version of [`request`](#request) specialized in the `GET` method.
188+
189+
Given a URL and a StrMap of request headers, returns a Future which
190+
makes a GET requests to the given resource.
191+
192+
```js
193+
retrieve ('https://api.github.com/users/Avaq') ({'User-Agent': 'Avaq'})
194+
```
195+
196+
#### <a name="send" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L313">`send :: Mimetype -⁠> Method -⁠> Url -⁠> StrMap String -⁠> Buffer -⁠> Future Error IncomingMessage`</a>
197+
198+
A version of [`request`](#request) for sending arbitrary data to a server.
199+
There's also more specific versions for sending common types of data:
200+
201+
* [`sendJson`](#sendJson) sends JSON stringified data.
202+
* [`sendForm`](#sendForm) sends form encoded data.
203+
204+
Given a MIME type, a request method, a URL, a StrMap of headers, and
205+
finally a Buffer, returns a Future which will send the Buffer to the
206+
server at the given URL using the given request method, telling it the
207+
buffer contains data of the given MIME type.
208+
209+
This function will always send the Content-Type and Content-Length headers,
210+
alongside the provided headers. Manually provoding either of these headers
211+
override those generated by this function.
212+
213+
#### <a name="sendJson" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L337">`sendJson :: Method -⁠> String -⁠> StrMap String -⁠> JsonValue -⁠> Future Error IncomingMessage`</a>
214+
215+
A version of [`send`](#send) specialized in sending JSON.
216+
217+
Given a request method, a URL, a StrMap of headers and a JavaScript plain
218+
object, returns a Future which sends the object to the server at the
219+
given URL after JSON-encoding it.
220+
221+
```js
222+
sendJson ('PUT')
223+
('https://example.com/users/bob')
224+
({Authorization: 'Bearer asd123'})
225+
({name: 'Bob', email: 'bob@example.com'});
226+
```
227+
228+
#### <a name="sendForm" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L356">`sendForm :: Method -⁠> String -⁠> StrMap String -⁠> JsonValue -⁠> Future Error IncomingMessage`</a>
229+
230+
A version of [`send`](#send) specialized in sending form data.
231+
232+
Given a request method, a URL, a StrMap of headers and a JavaScript plain
233+
object, returns a Future which sends the object to the server at the
234+
given URL after www-form-urlencoding it.
235+
236+
```js
237+
sendForm ('POST')
238+
('https://example.com/users/create')
239+
({})
240+
({name: 'Bob', email: 'bob@example.com'});
241+
```
242+
243+
#### <a name="acceptStatus" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L375">`acceptStatus :: Number -⁠> IncomingMessage -⁠> Future IncomingMessage IncomingMessage`</a>
244+
245+
This function "tags" an [IncomingMessage][] based on a given status code.
246+
If the response status matches the given status code, the returned Future
247+
will resolve. If it doesn't, the returned Future will reject.
248+
249+
The idea is that you can compose this function with one that returns an
250+
IncomingMessage, and reject any responses that don't meet the expected
251+
status code. In combination with [`responseToError`](#responseToError),
252+
you can then flatten it back into the outer Future.
253+
254+
The usage example under the [Http](#http) section shows this.
255+
256+
#### <a name="bufferResponse" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L391">`bufferResponse :: Charset -⁠> IncomingMessage -⁠> Future Error String`</a>
257+
258+
A version of [`buffer`](#buffer) specialized in [IncomingMessage][]s.
259+
260+
See also [`autoBufferResponse`](#autoBufferResponse).
261+
262+
Given a charset and an IncomingMessage, returns a Future with the buffered,
263+
encoded, message body.
264+
265+
#### <a name="autoBufferResponse" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L404">`autoBufferResponse :: IncomingMessage -⁠> Future Error String`</a>
266+
267+
Given an IncomingMessage, buffers and decodes the message body using the
268+
charset provided in the message headers. Falls back to UTF-8 if the
269+
charset was not provided.
270+
271+
Returns a Future with the buffered, encoded, message body.
272+
273+
#### <a name="responseToError" href="https://github.com/fluture-js/fluture-node/blob/v2.1.0/index.js#L418">`responseToError :: IncomingMessage -⁠> Future Error a`</a>
274+
275+
Given a response, returns a *rejected* Future of an instance of Error
276+
with a message based on the content of the response.
277+
65278
[`process.nextTick`]: https://nodejs.org/api/process.html#process_process_nexttick_callback_args
66279
[`setImmediate`]: https://nodejs.org/api/timers.html#timers_setimmediate_callback_args
280+
281+
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer
282+
[Fluture]: https://github.com/fluture-js/Fluture
283+
[http options]: https://nodejs.org/api/http.html#http_http_request_url_options_callback
284+
[IncomingMessage]: https://nodejs.org/api/http.html#http_class_http_incomingmessage
285+
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fluture-node",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"description": "FP-style HTTP and streaming utils for Node based on Fluture",
55
"tags": [
66
"buffer",

0 commit comments

Comments
 (0)