Skip to content

Commit

Permalink
Rewrite module to mimic fetch interface
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed May 19, 2019
1 parent 90c0166 commit 09db827
Show file tree
Hide file tree
Showing 36 changed files with 8,004 additions and 2,958 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ coverage/
node_modules/
npm-debug.log
dist/
/browser.js
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ notifications:
on_failure: change

node_js:
- '6'
- '8'
- stable

after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"
257 changes: 99 additions & 158 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# ![Servie](https://cdn.rawgit.com/serviejs/servie/master/logo.svg)

[![NPM version](https://img.shields.io/npm/v/servie.svg?style=flat)](https://npmjs.org/package/servie)
[![NPM downloads](https://img.shields.io/npm/dm/servie.svg?style=flat)](https://npmjs.org/package/servie)
[![Build status](https://img.shields.io/travis/serviejs/servie.svg?style=flat)](https://travis-ci.org/serviejs/servie)
[![Test coverage](https://img.shields.io/coveralls/serviejs/servie.svg?style=flat)](https://coveralls.io/r/serviejs/servie?branch=master)
[![NPM version](https://img.shields.io/npm/v/servie.svg)](https://npmjs.org/package/servie)
[![NPM downloads](https://img.shields.io/npm/dm/servie.svg)](https://npmjs.org/package/servie)
[![Build status](https://img.shields.io/travis/serviejs/servie.svg)](https://travis-ci.org/serviejs/servie)
[![Test coverage](https://img.shields.io/coveralls/serviejs/servie.svg)](https://coveralls.io/r/serviejs/servie?branch=master)
[![Bundle size](https://img.shields.io/bundlephobia/minzip/servie.svg)](https://bundlephobia.com/result?p=servie)

> Standard, framework-agnostic HTTP interfaces for JavaScript servers and clients.
Expand All @@ -15,221 +16,161 @@ npm install servie --save

## Usage

* [`throwback`](https://github.com/serviejs/throwback) Compose middleware functions into a single function
* [`servie-lambda`](https://github.com/serviejs/servie-lambda) Servie transport layer for AWS Lambda
* [`servie-http`](https://github.com/serviejs/servie-http) Servie transport layer for node.js HTTP
* [`busboy`](https://www.npmjs.com/package/busboy) A streaming parser for HTML form data
* [`qs`](https://github.com/ljharb/qs) and [`querystring`](https://nodejs.org/api/querystring.html) Parse HTTP query string to object
* [`get-body`](https://github.com/serviejs/get-body) General body parser for forms, JSON and text
* [`servie-cors`](https://github.com/serviejs/servie-cors) CORS middleware for Servie
* [`servie-route`](https://github.com/serviejs/servie-route) Routing middleware for Servie
* [`servie-mount`](https://github.com/serviejs/servie-mount) Mount Servie middleware on a path prefix
* [`servie-compat-http`](https://github.com/serviejs/servie-compat-http) Mimic node.js HTTP using Servie
* [`servie-redirect`](https://github.com/serviejs/servie-redirect) Create response objects for redirection
* [`servie-cookie-store`](https://github.com/serviejs/servie-cookie-store) API for managing client-side cookies
* [`servie-errorhandler`](https://github.com/serviejs/servie-errorhandler) Standard error handler for transport layers
* [`servie-finalhandler`](https://github.com/serviejs/servie-finalhandler) Standard final handler for transport layers
* [`http-errors`](https://github.com/jshttp/http-errors) Create HTTP errors
* [`boom`](https://github.com/hapijs/boom) HTTP-friendly error objects
* [`consolidate`](https://github.com/tj/consolidate.js) Template rendering

### `Servie`

> Base HTTP class for common request and response logic.
- [`throwback`](https://github.com/serviejs/throwback) Compose middleware functions into a single function
- [`popsicle`](https://github.com/serviejs/popsicle) HTTP request library for node.js and browsers
- [`servie-lambda`](https://github.com/serviejs/servie-lambda) Servie transport layer for AWS Lambda
- [`servie-http`](https://github.com/serviejs/servie-http) Servie transport layer for node.js HTTP
- [`get-body`](https://github.com/serviejs/get-body) General body parser for forms, JSON and text
- [`servie-cors`](https://github.com/serviejs/servie-cors) CORS middleware for Servie
- [`servie-route`](https://github.com/serviejs/servie-route) Routing middleware for Servie
- [`servie-mount`](https://github.com/serviejs/servie-mount) Mount Servie middleware on a path prefix
- [`servie-compat-http`](https://github.com/serviejs/servie-compat-http) Mimic node.js HTTP using Servie
- [`servie-redirect`](https://github.com/serviejs/servie-redirect) Create response objects for redirection
- [`servie-cookie-store`](https://github.com/serviejs/servie-cookie-store) API for managing client-side cookies
- [`servie-errorhandler`](https://github.com/serviejs/servie-errorhandler) Standard error handler for transport layers
- [`servie-finalhandler`](https://github.com/serviejs/servie-finalhandler) Standard final handler for transport layers
- [`http-errors`](https://github.com/jshttp/http-errors) Create HTTP errors
- [`boom`](https://github.com/hapijs/boom) HTTP-friendly error objects
- [`consolidate`](https://github.com/tj/consolidate.js) Template rendering

```ts
import { Servie } from 'servie'
import { Body, Request, Response, Headers, AbortController } from "servie";
```

#### Options
> Servie is a universal package, meaning node.js and browsers are supported without needing configuration. This means the primary endpoint requires some `dom` types in TypeScript. When in a node.js, or browser, only environment, prefer importing `servie/dist/{node,browser}` instead.
* `events?` An instance of `EventEmitter`
* `headers?` An instance of `Headers`
* `trailer?` An instance of `Promise<Headers>`
* `body?` An instance of `Body`

#### Properties
### `Body`

* `events` An event emitter for listening to the request and response lifecycle
* `headers` The headers as a `Headers` instance
* `trailer` A promise that resolves to a `Headers` instance
* `body` The request or response payload
* `started` Boolean indicating if a request/response has started
* `finished` Boolean indicating if a request/response has finished
* `bytesTransferred` The number of bytes sent in the HTTP request/response
* `allHeaders` Combined `Request` and `Body` headers instance
> Base HTTP class shared between `Request` and `Response`.
#### Methods
```ts
new Body(body, headers);
```

* `abort(): boolean` Aborts the HTTP connection
* `clone(): Servie` Abstract method implemented by `Request` and `Response` to clone new instance (throws `TypeError` when `started == true`)
#### Body

#### Events
Supported body types, depending on environment:

* `started` when `started == true`
* `finished` when `finished == true`
* `progress` when `bytesTransferred` increments
* `connection` when `Request` connection is available
* `abort` when `Request` or `Response` is aborting
* `aborted` when `Request` or `Response` sets `aborted == true`
- Node.js - `string | Buffer | ArrayBuffer | Readable | null | undefined`
- Browser - `string | ArrayBuffer | ReadableStream | null | undefined`

### `Request`
#### Properties and Methods

> HTTP class for encapsulating a `Request`, extends `Servie`.
- `bodyUsed` Boolean whether the body is consumed
- `rawBody` Returns the raw body if not consumed
- `text()` Returns the body as a `Promise<string>`
- `json()` Returns the body with `JSON.parse` as `Promise<any>`
- `arrayBuffer()` Returns the body as a `Promise<ArrayBuffer>`
- `clone()` Clones an unconsumed body

```ts
import { Request } from 'servie'
```
### `Request`

#### Options
> HTTP class for modelling a `Request`, extends `Body`.
```ts
const request = new Request({
url: '/',
method: 'GET'
})
new Request(input [, init]);
```

> Extends `Servie` options.
#### Input

* `url` The HTTP request url (`string`)
* `method?` The HTTP request method (`string`, default: `GET`)
* `connection?` Connection information (`{ remoteAddress?: string, remotePort?: number, localAddress?: string, localPort?: number, encrypted?: boolean }`)
A URL string or another `Request` instance to clone from.

#### Properties
#### Init

* `url` Requested url (`string`)
* `method` Requested method (`string`)
* `Url` Request url parsed into individual parts (`object`)
* `connection` HTTP connection information when available (`object`)
- `method?` HTTP request method.
- `body?` Any support body types.
- `signal?` A signal from an `AbortController`.
- `headers?` A map, list of tuples, or `Header` instance to initialize from.
- `trailer?` A promise resolve to a support header initialization types (above).

#### Events
#### Properties and Methods

* `abort` Request aborted and transport _MUST_ handle
* `error` An out-of-band error occurred and transport _MUST_ handle
* `response` The corresponding `Response` has started
* `connection` Emitted when connection information becomes available
- `url` Requested url string
- `method` Requested method string
- `signal` Signal event emitter
- `headers` A `Header` instance
- `trailer` A `Promise<Header>` instance
- `clone()` Clones the request into a new instance

### `Response`

> HTTP class for encapsulating a `Response`, extends `Servie`.
> HTTP class for modelling a `Response`, extends `Body`.
```ts
import { Response } from 'servie'
new Response([body [, init]]);
```

#### Options
#### Body

```ts
const response = new Response({})
```
One of the support body types (above).

> Extends `Servie` options.
#### Init

* `statusCode?` The HTTP response status code (`number`)
* `statusMessage?` The HTTP response status message (`string`)
- `status?` The numeric HTTP response status code
- `statusText?` The HTTP response status text

#### Properties
#### Properties and Methods

* `statusCode` The HTTP response status code (`number`)
* `statusMessage?` The HTTP response status message (`string`)
* `ok` Returns whether response was successful (status in range 200-299) (`boolean`)
- `status` The numeric HTTP response status code
- `statusText` The HTTP response status text
- `ok` Boolean indicates successful response (`status` between 200 and 299)
- `headers` A `Header` instance
- `trailer` A `Promise<Header>` instance
- `clone()` Clones the response into a new instance

### `Headers`

> Used by `Servie` for `Request`, `Response` and `Body` objects.
```ts
import { Headers, createHeaders } from 'servie'
```

#### Options
> Map representation of HTTP headers.
```ts
const headers = createHeaders(...) // new Headers([...])
new Headers([init]);
```

Create `Headers` instance from raw value (e.g. `HeadersObject | string[] | null`).

#### Properties
#### Init

* `rawHeaders` The raw HTTP headers list (`string[]`)
Initialize headers from `Iterable<HeaderTuple>`, a `HeadersObject` or an existing `Headers` instance.

#### Methods

* `set(name: string, value: string | string[]): this` Set a HTTP header by overriding case-insensitive headers of the same name
* `append(name: string, value: string | string[]): this` Append a HTTP header
* `get(name: string): string | undefined` Retrieve a case-insensitive HTTP header
* `getAll(name: string): string[]` Retrieve a list of matching case-insensitive HTTP headers
* `has(name: string): boolean` Check if a case-insensitive header is already set
* `delete(name: string): this` Delete a case-insensitive header
* `asObject(toLower?: boolean): HeadersObject` Return the headers as a plain object
* `extend(obj: HeadersObject): this` Extends the current headers with an object
* `keys()` Iterable of the available header names
* `values()` Iterable of header values
* `entries()` Iterable of headers as `[key, value]`
* `clear()` Clears the headers instance
* `clone()` Clones the `Headers` instance
- `set(name: string, value: string | string[]): void` Set a HTTP header by overriding case-insensitive headers of the same name
- `append(name: string, value: string | string[]): void` Append a HTTP header
- `get(name: string): string | undefined` Retrieve a case-insensitive HTTP header
- `getAll(name: string): string[]` Retrieve a list of matching case-insensitive HTTP headers
- `has(name: string): boolean` Check if a case-insensitive header is already set
- `delete(name: string): void` Delete a case-insensitive header
- `asObject(): HeadersObject` Return the lower-cased headers as a plain object
- `extend(obj: HeadersInit): this` Extends the current headers with an object
- `keys()` Iterable of the available header names
- `values()` Iterable of header values
- `entries()` Iterable of headers as `[key, value]`
- `clear()` Clears the headers instance
- `clone()` Clones the `Headers` instance

#### Static Methods
### `AbortController`

* `is(obj: any): boolean` Checks if an object is `Headers`

### `Body`

> Immutable representation of the body used by `Request` and `Response`.
> Simple controller for aborting a `Request` instance.
```ts
import { Body, createBody } from 'servie/dist/body/{node,browser,universal}'
new AbortController();
```

> `Body` is a complex part of Servie due to support for browsers and node.js together. TypeScript is also [missing a good story](https://github.com/Microsoft/TypeScript/issues/7753) for universal modules with code paths offering different features (e.g. streams in node.js, native `ReadableStream` in browsers), so it's required that you import a specific version for your environment.
**Note:** Each endpoint (`body/node`, `body/browser`, `body/universal`) offers the same basic API (`Body`, `createBody`). Universal relies on module bundlers (e.g. webpack) and [`package.json#browser`](package.json) to switch the node.js API with the browser API at bundle time. TypeScript _will_ require `dom` types since the interface returns a union of possible bodies (`node` and `browser`).

#### Options

```ts
const body = createBody(...) // new Body({ rawBody: ... })
```

Create a `Body` instance from raw data (e.g. `Readable | ReadableStream | Buffer | ArrayBuffer | object | string | null`).

#### Properties

* `buffered` Indicates the raw body is entirely in memory (`boolean`)
* `bodyUsed` Indicates the body has been read (`boolean`)
* `hasBody` Indicates the body is not empty (`boolean`)
* `headers` Instance of body-related HTTP headers (`Headers`)

#### Methods

* `text(): Promise<string>` Returns body as a UTF-8 string
* `json(): Promise<any>` Returns body parsed as JSON
* `arrayBuffer(): Promise<ArrayBuffer>` Returns the body as an `ArrayBuffer` instance
* `buffer(): Promise<Buffer>` Returns the body as a `Buffer` instance (node.js)
* `stream(): Readable` Returns a readable node.js stream (node.js)
* `readableStream(): ReadableStream` Returns a readable WHATWG stream (browsers)
* `clone(): this` Clones the `Body` instance (allowing body re-use, throws `TypeError` when `bodyUsed == true`)

#### Static Methods
#### Properties and Methods

* `is(obj: any): boolean` Checks if an object is `Body`
- `signal` A `Signal` instance to pass to a `Request` instance
- `abort()` Used to abort any listening requests through the `signal`

## Implementers
## Implementation

If you're building the transports for Servie, there are some life cycle events you need to be aware of:

1. Listen to the `error` event on `Request` and `Response` for errors
2. Listen to the `abort` event on `Request` to destroy the connection and, when destroyed, set `req.aborted = true`
1. Listen to the `error` event on `signal` for errors
2. Listen to the `abort` event on `signal` to destroy the connection
3. Resolve `trailer` promise and append to HTTP request or response
4. Set `started = true` and `finished = true` on `Request` and `Response` (as appropriate)
5. Set `bytesTransferred` on `Request` and `Response` with transfer progress
6. Set `req.closed = true` when the connection has finished
4. There are some existing built-in type-safe events in `SignalEvents` you can support

## JavaScript

This module is designed for ES2015 environments and published with [TypeScript](https://github.com/Microsoft/TypeScript) definitions on NPM.
This module is designed for ES2017 environments and published with [TypeScript](https://github.com/Microsoft/TypeScript) definitions on NPM.

## License

Expand Down
Loading

0 comments on commit 09db827

Please sign in to comment.