Comparing version 1.7.0 to 2.0.0
@@ -15,3 +15,3 @@ ## Description | ||
> | ||
> The `divide` helpers supports **only 2 numbers** - dividend and divisor. | ||
> The `divide` helper supports **only 2 numbers** - dividend and divisor. | ||
@@ -18,0 +18,0 @@ ## Usage |
@@ -23,3 +23,3 @@ ## Description | ||
```ts | ||
const response = await nuti.req.get('http://localhost:3000/user'); | ||
const response = await nuti.http.get('http://localhost:3000/user'); | ||
@@ -54,5 +54,5 @@ console.log(response); | ||
```ts | ||
const response = await nuti.req.post('http://localhost:3000/user', { | ||
name: 'some new user', | ||
}); | ||
const response = await nuti.http | ||
.post('http://localhost:3000/user') | ||
.body({ name: 'some new user' }); | ||
@@ -85,5 +85,5 @@ console.log(response); | ||
```ts | ||
const response = await nuti.req.put('http://localhost:3000/user', { | ||
name: 'some updated user', | ||
}); | ||
const response = await nuti.http | ||
.put('http://localhost:3000/user') | ||
.body({ name: 'some updated user' }); | ||
@@ -116,5 +116,5 @@ console.log(response); | ||
```ts | ||
const response = await nuti.req.delete('http://localhost:3000/user', { | ||
name: 'some user', | ||
}); | ||
const response = await nuti.http | ||
.delete('http://localhost:3000/user') | ||
.body({ name: 'some user' }); | ||
@@ -141,2 +141,48 @@ console.log(response); | ||
### Retry | ||
> **Note** | ||
> | ||
> If `retry` method was called - `pipe` method will be disregarded. | ||
**Does not mean to be used for 400+ status codes, but for connection errors and etc.** | ||
If troubles with API for performing requests to are possible - `retry` | ||
method can handle such requests and automatically perform new one. | ||
By default retries to do request in 10 seconds. Max interval time can be 60 seconds, and max amount | ||
of attempts - 15 times. If bigger value is passed - max value will be set. | ||
Following example will perform up to 3 additional requests, if main request has failed: | ||
```ts | ||
const response = await nuti.http | ||
.get('http://localhost:3000/possible-unavailable') | ||
.retry({ attempts: 3 }); | ||
``` | ||
Also, next request time can be specified (example - 25 seconds), and log can be added: | ||
```ts | ||
const response = await nuti.http | ||
.get('http://localhost:3000/possible-unavailable') | ||
.retry({ attempts: 3, interval: 25, logOnRetry: true }); | ||
``` | ||
### Pipe | ||
> **Note** | ||
> | ||
> If `retry` method was called - `pipe` method will be disregarded. | ||
Allows to pipe response to any `writable`/`duplex` stream. Also returns response as | ||
`nuti.http.get()` request: | ||
```ts | ||
const stream = fs.createWriteStream('out.json'); | ||
const response = await nuti.http | ||
.get('http://localhost:3000/users') | ||
.pipe(stream); | ||
``` | ||
### TS GENERIC TYPES | ||
@@ -156,3 +202,5 @@ | ||
const { json } = await nuti.req.get<UserResponse>('http://localhost:3000/user'); | ||
const { json } = await nuti.http.get<UserResponse>( | ||
'http://localhost:3000/user', | ||
); | ||
@@ -177,5 +225,5 @@ if (json != null) { | ||
```ts | ||
const response = await nuti.req.get('http://localhost:3000/', { | ||
'content-type': 'text/html', | ||
}); | ||
const response = await nuti.http | ||
.get('http://localhost:3000/') | ||
.headers({ 'content-type': 'text/html' }); | ||
@@ -205,8 +253,8 @@ console.log(response); | ||
### FAILED REQUEST | ||
### 400+ status code responses | ||
Lets pretend your server has responds with `json` content-type for `404 Not Found` cases: | ||
Lets pretend your server responds with `json` content-type for `404 Not Found` cases: | ||
```ts | ||
const response = await nuti.req.get('http://localhost:3000/not-found'); | ||
const response = await nuti.http.get('http://localhost:3000/not-found'); | ||
@@ -213,0 +261,0 @@ console.log(response); |
@@ -7,6 +7,5 @@ /*! | ||
*/ | ||
/// <reference types="node" /> | ||
import * as http from 'http'; | ||
import { Response } from './types'; | ||
import { Request } from './request'; | ||
export declare class HttpClient { | ||
private logger; | ||
/** | ||
@@ -16,3 +15,3 @@ * GET request. | ||
*/ | ||
get<T extends object>(url: string, headers?: http.IncomingHttpHeaders): Promise<Response<T>>; | ||
get<T extends object>(url: string): Request<T, "GET">; | ||
/** | ||
@@ -23,3 +22,3 @@ * POST request. | ||
*/ | ||
post<T extends object>(url: string, body: unknown, headers?: http.IncomingHttpHeaders): Promise<Response<T>>; | ||
post<T extends object>(url: string): Request<T, "POST">; | ||
/** | ||
@@ -30,3 +29,3 @@ * PUT request. | ||
*/ | ||
put<T extends object>(url: string, body: unknown, headers?: http.IncomingHttpHeaders): Promise<Response<T>>; | ||
put<T extends object>(url: string): Request<T, "PUT">; | ||
/** | ||
@@ -37,15 +36,4 @@ * DELETE request. | ||
*/ | ||
delete<T extends object>(url: string, body: unknown, headers?: http.IncomingHttpHeaders): Promise<Response<T>>; | ||
/** | ||
* Main request's handler. | ||
* @returns Promise which will be resolved **after** response stream is finished. | ||
*/ | ||
private request; | ||
/** | ||
* Function for validation request input. | ||
* @param payload options for request and url | ||
* @returns valid request options, raw request body and valid protocol (http(s)) | ||
*/ | ||
private validateReqInput; | ||
delete<T extends object>(url: string): Request<T, "DELETE">; | ||
} | ||
export declare const req: HttpClient; | ||
export declare const http: HttpClient; |
@@ -9,10 +9,9 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.req = exports.HttpClient = void 0; | ||
const http = require("http"); | ||
const https = require("https"); | ||
const protocols = { | ||
['http:']: http, | ||
['https:']: https, | ||
}; | ||
exports.http = exports.HttpClient = void 0; | ||
const logger_1 = require("../logger"); | ||
const request_1 = require("./request"); | ||
class HttpClient { | ||
constructor() { | ||
this.logger = new logger_1.Logger(); | ||
} | ||
/** | ||
@@ -22,7 +21,4 @@ * GET request. | ||
*/ | ||
async get(url, headers) { | ||
return this.request(url, { | ||
method: 'GET', | ||
headers, | ||
}); | ||
get(url) { | ||
return new request_1.Request(url, 'GET', this.logger); | ||
} | ||
@@ -34,8 +30,4 @@ /** | ||
*/ | ||
async post(url, body, headers) { | ||
return this.request(url, { | ||
method: 'POST', | ||
headers, | ||
body, | ||
}); | ||
post(url) { | ||
return new request_1.Request(url, 'POST', this.logger); | ||
} | ||
@@ -47,8 +39,4 @@ /** | ||
*/ | ||
async put(url, body, headers) { | ||
return this.request(url, { | ||
method: 'PUT', | ||
headers, | ||
body, | ||
}); | ||
put(url) { | ||
return new request_1.Request(url, 'PUT', this.logger); | ||
} | ||
@@ -60,82 +48,7 @@ /** | ||
*/ | ||
async delete(url, body, headers) { | ||
return this.request(url, { | ||
method: 'DELETE', | ||
headers, | ||
body, | ||
}); | ||
delete(url) { | ||
return new request_1.Request(url, 'DELETE', this.logger); | ||
} | ||
/** | ||
* Main request's handler. | ||
* @returns Promise which will be resolved **after** response stream is finished. | ||
*/ | ||
async request(url, options) { | ||
return new Promise((resolve, reject) => { | ||
const { opts, validProtocol, rawReqBody } = this.validateReqInput({ | ||
...options, | ||
url, | ||
}); | ||
const req = validProtocol.request(opts, (res) => { | ||
let rawData = ''; | ||
const status = res.statusCode; | ||
const isJSON = res.headers['content-type']?.includes('application/json') === true; | ||
const contentLengthRaw = Number(res.headers['content-length']); | ||
const contentLength = Number.isNaN(contentLengthRaw) | ||
? 0 | ||
: contentLengthRaw; | ||
res.on('data', (chunk) => { | ||
rawData += chunk.toString(); | ||
}); | ||
res.on('end', () => { | ||
resolve({ | ||
status, | ||
ok: status < 400, | ||
contentLength, | ||
headers: res.headers, | ||
json: isJSON && status !== 204 ? JSON.parse(rawData) : undefined, | ||
body: rawData, | ||
}); | ||
}); | ||
res.on('error', reject); | ||
}); | ||
req.on('error', reject); | ||
req.write(rawReqBody); | ||
req.end(); | ||
}); | ||
} | ||
/** | ||
* Function for validation request input. | ||
* @param payload options for request and url | ||
* @returns valid request options, raw request body and valid protocol (http(s)) | ||
*/ | ||
validateReqInput(payload) { | ||
const { headers = {}, body = {}, method, url } = payload; | ||
const { protocol, hostname, port, pathname, host } = new URL(url); | ||
const validProtocol = protocols[protocol]; | ||
if (validProtocol == null) { | ||
throw new Error(`Unsupported protocol: ${protocol.replace(':', '')}`); | ||
} | ||
const rawReqBody = JSON.stringify(body); | ||
if (headers['content-type'] == null) { | ||
Object.assign(headers, { | ||
'content-type': 'application/json', | ||
'content-length': rawReqBody.length, | ||
}); | ||
} | ||
return { | ||
opts: { | ||
protocol, | ||
hostname, | ||
host, | ||
port, | ||
path: pathname, | ||
headers, | ||
method, | ||
}, | ||
validProtocol, | ||
rawReqBody, | ||
}; | ||
} | ||
} | ||
exports.HttpClient = HttpClient; | ||
exports.req = new HttpClient(); | ||
exports.http = new HttpClient(); |
@@ -17,6 +17,16 @@ /*! | ||
} | ||
export interface RequestOptions { | ||
method: Method; | ||
headers?: http.IncomingHttpHeaders; | ||
body?: unknown; | ||
export interface RetryOptions { | ||
/** | ||
* Amount of attempts after failed request. | ||
*/ | ||
attempts: number; | ||
/** | ||
* Time in **seconds** to wait before perform new retry. | ||
* @default 10 seconds | ||
*/ | ||
interval?: number; | ||
/** | ||
* Specifies if log should be written, if request has failed. | ||
*/ | ||
logOnRetry?: boolean; | ||
} |
@@ -6,7 +6,5 @@ /*! | ||
*/ | ||
import * as types from './logger/types'; | ||
import { timeout } from './timeout'; | ||
import type * as LogTypes from './logger/types'; | ||
export type Types = { | ||
LoggerOptions: LogTypes.LoggerOptions; | ||
}; | ||
export * from './types'; | ||
export declare const nuti: { | ||
@@ -31,5 +29,5 @@ /** | ||
* A function which overrides `toString` method for Objects and Arrays. | ||
* @see [docs](../docs/prettyOut.md) | ||
* @see [docs](../docs/prettify.md) | ||
*/ | ||
prettyOut: (...objects: (object | unknown[])[]) => void; | ||
prettify: (...objects: (object | unknown[])[]) => void; | ||
/** | ||
@@ -39,5 +37,5 @@ * A function for creating a logger with ability to write file logs. | ||
* | ||
* @param options {@link LogTypes.LoggerOptions LoggerOptions} | ||
* @param options {@link types.LoggerOptions LoggerOptions} | ||
*/ | ||
makeLogger: (options?: LogTypes.LoggerOptions | undefined) => import("./logger").Logger; | ||
makeLogger: (options?: types.LoggerOptions | undefined) => import("./logger").Logger; | ||
/** | ||
@@ -59,3 +57,3 @@ * A function for creating flag instance. | ||
*/ | ||
req: import("./httpClient").HttpClient; | ||
http: import("./httpClient").HttpClient; | ||
/** | ||
@@ -62,0 +60,0 @@ * A function for creating deep clones of Objects or Arrays. |
@@ -7,5 +7,19 @@ "use strict"; | ||
*/ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.nuti = void 0; | ||
const prettyOut_1 = require("./prettyOut"); | ||
const prettify_1 = require("./prettify"); | ||
const timeout_1 = require("./timeout"); | ||
@@ -18,2 +32,3 @@ const logger_1 = require("./logger"); | ||
const floats_1 = require("./floats"); | ||
__exportStar(require("./types"), exports); | ||
exports.nuti = { | ||
@@ -38,5 +53,5 @@ /** | ||
* A function which overrides `toString` method for Objects and Arrays. | ||
* @see [docs](../docs/prettyOut.md) | ||
* @see [docs](../docs/prettify.md) | ||
*/ | ||
prettyOut: prettyOut_1.prettyOut, | ||
prettify: prettify_1.prettify, | ||
/** | ||
@@ -46,3 +61,3 @@ * A function for creating a logger with ability to write file logs. | ||
* | ||
* @param options {@link LogTypes.LoggerOptions LoggerOptions} | ||
* @param options {@link types.LoggerOptions LoggerOptions} | ||
*/ | ||
@@ -66,3 +81,3 @@ makeLogger: logger_1.makeLogger, | ||
*/ | ||
req: httpClient_1.req, | ||
http: httpClient_1.http, | ||
/** | ||
@@ -69,0 +84,0 @@ * A function for creating deep clones of Objects or Arrays. |
{ | ||
"name": "nuti", | ||
"version": "1.7.0", | ||
"version": "2.0.0", | ||
"author": "Andrii Lytovchenko <andr.lyt.dev@gmail.com>", | ||
@@ -48,9 +48,9 @@ "license": "MIT", | ||
"devDependencies": { | ||
"@types/jest": "^29.4.0", | ||
"@types/node": "^18.15.3", | ||
"@types/jest": "^29.5.0", | ||
"@types/node": "^18.15.5", | ||
"@typescript-eslint/eslint-plugin": "^5.55.0", | ||
"@typescript-eslint/parser": "^5.55.0", | ||
"cspell": "^6.29.3", | ||
"cspell": "^6.30.2", | ||
"eslint": "^8.36.0", | ||
"eslint-config-prettier": "^8.7.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-plugin-jest": "^27.2.1", | ||
@@ -60,7 +60,7 @@ "eslint-plugin-prettier": "^4.2.1", | ||
"npsh": "^2.0.0", | ||
"prettier": "^2.8.4", | ||
"prettier": "^2.8.6", | ||
"ts-jest": "^29.0.5", | ||
"ts-node": "^10.7.0", | ||
"typescript": "^4.9.5" | ||
"typescript": "^5.0.2" | ||
} | ||
} |
@@ -31,5 +31,5 @@ # nuti | ||
3. [flag](./docs/flag.md) | ||
4. [prettyOut](./docs/prettyOut.md) | ||
4. [prettify](./docs/prettify.md) | ||
5. [rand](./docs/rand.md) | ||
6. [req](./docs/httpClient.md) | ||
6. [http](./docs/httpClient.md) | ||
7. [clone](./docs/clone.md) | ||
@@ -36,0 +36,0 @@ 8. [floats](./docs/floats.md) |
58023
43
1213