Compare commits

...

114 Commits

Author SHA1 Message Date
0a83d8b476 2.0.14 2023-04-19 14:24:43 +02:00
8e7c730d86 fix(core): update 2023-04-19 14:24:43 +02:00
fe50adb1ff 2.0.13 2023-04-19 14:13:35 +02:00
cd75f7acd8 fix(core): update 2023-04-19 14:13:34 +02:00
bb0dd6426a 2.0.12 2023-04-19 04:07:44 +02:00
d471376681 fix(core): update 2023-04-19 04:07:44 +02:00
b882922f2b 2.0.11 2022-08-21 14:03:18 +02:00
9a0d35c325 fix(core): update 2022-08-21 14:03:18 +02:00
7b49bba0d2 2.0.10 2022-08-06 22:29:12 +02:00
6600a23a00 fix(core): update 2022-08-06 22:29:12 +02:00
e2845c9992 2.0.9 2022-08-01 17:15:53 +02:00
5e6f2c6fbf fix(core): update 2022-08-01 17:15:52 +02:00
d3d0649b73 2.0.8 2022-08-01 17:10:22 +02:00
fba43df3c4 fix(core): update 2022-08-01 17:10:22 +02:00
c6fa540543 2.0.7 2022-08-01 17:10:11 +02:00
1891b54389 fix(core): update 2022-08-01 17:10:11 +02:00
fee8443af1 2.0.6 2022-07-30 18:43:11 +02:00
c48f956ae3 fix(core): update 2022-07-30 18:43:10 +02:00
4a4b64a2c4 2.0.5 2022-07-30 02:01:20 +02:00
43d4b47782 fix(core): update 2022-07-30 02:01:20 +02:00
6d970cb925 2.0.4 2022-07-30 01:52:04 +02:00
43710c930e fix(core): update 2022-07-30 01:52:04 +02:00
306dd7c366 2.0.3 2022-07-29 15:45:04 +02:00
3d69d97891 fix(core): update 2022-07-29 15:45:04 +02:00
a6d52702fd 2.0.2 2022-07-29 15:43:56 +02:00
de31ee6093 fix(core): update 2022-07-29 15:43:55 +02:00
cd2d7b2525 2.0.1 2022-07-29 15:41:33 +02:00
2d4a75c9cd fix(core): update 2022-07-29 15:41:32 +02:00
557fec0386 2.0.0 2022-07-29 01:20:25 +02:00
e803f9e48d BREAKING CHANGE(core): switch to esm 2022-07-29 01:20:24 +02:00
76c714a931 1.1.57 2022-07-29 01:19:50 +02:00
e8669f0420 fix(core): update 2022-07-29 01:19:50 +02:00
d9e6214a7e 1.1.56 2022-02-15 23:09:15 +01:00
7c4227bfc6 fix(core): update 2022-02-15 23:09:15 +01:00
e55a521395 1.1.55 2022-02-15 19:02:44 +01:00
06fc279caf fix(core): update 2022-02-15 19:02:43 +01:00
e89e317bbc 1.1.54 2022-02-15 18:57:42 +01:00
d182832e47 fix(core): update 2022-02-15 18:57:42 +01:00
92059a50de 1.1.53 2022-02-15 18:53:02 +01:00
db80f2df7e fix(core): update 2022-02-15 18:53:02 +01:00
145505b891 1.1.52 2021-05-16 23:39:26 +00:00
f4f50c6a94 fix(core): update 2021-05-16 23:39:25 +00:00
d204059313 1.1.51 2020-09-29 15:22:25 +00:00
00210566d5 fix(core): update 2020-09-29 15:22:25 +00:00
14245b2521 1.1.50 2020-09-29 15:20:41 +00:00
f0fa91e2db fix(core): update 2020-09-29 15:20:40 +00:00
19a1fe1524 1.1.49 2020-08-24 12:04:11 +00:00
27770a8ad1 fix(core): update 2020-08-24 12:04:10 +00:00
ab48f11e83 1.1.48 2020-08-24 12:01:39 +00:00
a0a9e3f824 fix(core): update 2020-08-24 12:01:38 +00:00
c829b06169 1.1.47 2020-01-13 08:00:40 +00:00
80fa40baf4 fix(core): update 2020-01-13 08:00:39 +00:00
3659b80e1e 1.1.46 2020-01-13 07:58:54 +00:00
770e7d46ea fix(core): update 2020-01-13 07:58:54 +00:00
2a46f2a306 1.1.45 2020-01-12 19:36:58 +00:00
eae4d09664 fix(core): update 2020-01-12 19:36:58 +00:00
23a2f597fc 1.1.44 2020-01-12 19:32:20 +00:00
c278249c32 fix(core): update 2020-01-12 19:32:20 +00:00
a32c372374 1.1.43 2019-11-21 17:32:31 +00:00
f98972d9fe fix(core): update 2019-11-21 17:32:30 +00:00
acebe6a381 1.1.42 2019-10-28 16:18:15 +01:00
7031504852 1.1.41 2019-10-27 14:41:49 +01:00
3010a1da9a fix(core): update 2019-10-27 14:41:48 +01:00
cdead33be4 1.1.40 2019-10-27 14:39:07 +01:00
5e23649702 fix(core): update 2019-10-27 14:39:06 +01:00
cc6bd5726a 1.1.39 2019-10-27 14:38:13 +01:00
f487584e80 fix(core): update 2019-10-27 14:38:12 +01:00
443bccd4c9 1.1.38 2019-10-27 14:36:18 +01:00
f359856b1d fix(core): update 2019-10-27 14:36:17 +01:00
bda04f124b 1.1.37 2019-10-27 14:32:27 +01:00
466187fd52 fix(core): update 2019-10-27 14:32:27 +01:00
d22504317e 1.1.36 2019-09-29 16:42:56 +02:00
6e31d84798 fix(core): update 2019-09-29 16:42:56 +02:00
36472b7306 1.1.35 2019-09-29 00:56:56 +02:00
e86f14b8d8 fix(core): update 2019-09-29 00:56:56 +02:00
2b9e7f6dd2 1.1.34 2019-09-29 00:43:37 +02:00
5e4afccf9c fix(core): update 2019-09-29 00:43:37 +02:00
3de7a1a210 1.1.33 2019-09-29 00:42:51 +02:00
bd2a5eedff fix(core): update 2019-09-29 00:42:51 +02:00
aa18357d75 1.1.32 2019-09-28 22:50:35 +02:00
9960aff219 fix(core): update 2019-09-28 22:50:35 +02:00
03d884ed59 1.1.31 2019-09-28 22:27:10 +02:00
9a0ac6fc62 fix(core): update 2019-09-28 22:27:09 +02:00
ad35ea4eb8 1.1.30 2019-09-28 21:40:05 +02:00
ffb0195f04 fix(core): update 2019-09-28 21:40:04 +02:00
78737c24df 1.1.29 2019-09-28 21:34:30 +02:00
6e276eda00 1.1.28 2019-09-28 21:33:14 +02:00
021d26a23a fix(core): update 2019-09-28 21:33:13 +02:00
c9c8a1234c 1.1.27 2019-09-08 18:00:49 +02:00
dffabd905f fix(core): update 2019-09-08 18:00:48 +02:00
36f2707141 1.1.26 2019-09-08 17:57:28 +02:00
b00d674b6f fix(core): update 2019-09-08 17:57:28 +02:00
b09598d465 1.1.25 2019-09-08 17:49:33 +02:00
acc7b2d46f fix(core): update 2019-09-08 17:49:32 +02:00
16a97a420c 1.1.24 2019-09-08 17:47:31 +02:00
a73c78e54b fix(core): update 2019-09-08 17:47:30 +02:00
1f408b5123 1.1.23 2019-08-22 12:40:19 +02:00
284f4967f4 fix(core): update 2019-08-22 12:40:19 +02:00
55c80c1403 1.1.22 2019-08-22 12:38:55 +02:00
7a3e565dbb fix(core): update 2019-08-22 12:38:55 +02:00
6f5d10ccd3 1.1.21 2019-08-22 12:38:12 +02:00
f1ddab72f6 fix(core): update 2019-08-22 12:38:11 +02:00
376225888c 1.1.20 2019-08-21 12:55:20 +02:00
63e8660f6c fix(core): update 2019-08-21 12:55:19 +02:00
2358b1d48f 1.1.19 2019-08-16 22:00:01 +02:00
9db29bacc2 fix(core): update 2019-08-16 22:00:01 +02:00
5f27b6834c 1.1.18 2019-08-16 21:43:10 +02:00
6717ecf80c fix(core): update 2019-08-16 21:43:09 +02:00
7784f99878 1.1.17 2019-08-16 21:38:50 +02:00
54a0521f9e fix(core): update 2019-08-16 21:38:50 +02:00
ef25315d59 1.1.16 2019-06-12 15:16:28 +02:00
74b6bf230f fix(core): update 2019-06-12 15:16:27 +02:00
fe693e6383 1.1.15 2019-04-23 00:14:31 +02:00
6014e94ee0 fix(core): update 2019-04-23 00:14:30 +02:00
21 changed files with 18313 additions and 731 deletions

18
.gitignore vendored
View File

@ -1,4 +1,20 @@
node_modules/ .nogit/
# artifacts
coverage/ coverage/
public/ public/
pages/ pages/
# installs
node_modules/
# caches
.yarn/
.cache/
.rpt2_cache
# builds
dist/
dist_*/
# custom

View File

@ -1,74 +1,72 @@
# gitzone standard # gitzone ci_default
image: hosttoday/ht-docker-node:npmci image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache: cache:
paths: paths:
- .npmci_cache/ - .npmci_cache/
key: "$CI_BUILD_STAGE" key: '$CI_BUILD_STAGE'
stages: stages:
- security - security
- test - test
- release - release
- metadata - metadata
before_script:
- npm install -g @shipzone/npmci
# ==================== # ====================
# security stage # security stage
# ==================== # ====================
mirror: auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security stage: security
script: script:
- npmci git mirror - npmci npm prepare
- npmci command npm install --production --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=prod --production
tags: tags:
- docker - docker
- notpriv allow_failure: true
snyk: auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security stage: security
script: script:
- npmci command npm install -g snyk - npmci npm prepare
- npmci command npm install --ignore-scripts - npmci command npm install --ignore-scripts
- npmci command snyk test - npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=dev
tags: tags:
- docker - docker
- notpriv allow_failure: true
# ==================== # ====================
# test stage # test stage
# ==================== # ====================
testLEGACY:
stage: test
script:
- npmci node install legacy
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
allow_failure: true
testLTS: testStable:
stage: test
script:
- npmci node install lts
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
testSTABLE:
stage: test stage: test
script: script:
- npmci npm prepare
- npmci node install stable - npmci node install stable
- npmci npm install - npmci npm install
- npmci npm test - npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/ coverage: /\d+.?\d+?\%\s*coverage/
tags: tags:
- docker - docker
- notpriv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release: release:
stage: release stage: release
@ -78,6 +76,7 @@ release:
only: only:
- tags - tags
tags: tags:
- lossless
- docker - docker
- notpriv - notpriv
@ -86,20 +85,15 @@ release:
# ==================== # ====================
codequality: codequality:
stage: metadata stage: metadata
image: docker:stable
allow_failure: true allow_failure: true
services: only:
- docker:stable-dind - tags
script: script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - npmci command npm install -g typescript
- docker run - npmci npm prepare
--env SOURCE_CODE="$PWD" - npmci npm install
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths: [codeclimate.json]
tags: tags:
- lossless
- docker - docker
- priv - priv
@ -110,17 +104,19 @@ trigger:
only: only:
- tags - tags
tags: tags:
- lossless
- docker - docker
- notpriv - notpriv
pages: pages:
image: hosttoday/ht-docker-node:npmci
stage: metadata stage: metadata
script: script:
- npmci command npm install -g typedoc typescript - npmci node install stable
- npmci npm prepare
- npmci npm install - npmci npm install
- npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/ - npmci command npm run buildDocs
tags: tags:
- lossless
- docker - docker
- notpriv - notpriv
only: only:
@ -130,13 +126,3 @@ pages:
paths: paths:
- public - public
allow_failure: true allow_failure: true
windowsCompatibility:
image: stefanscherer/node-windows:10-build-tools
stage: metadata
script:
- npm install & npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- windows
allow_failure: true

11
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "npm test",
"name": "Run npm test",
"request": "launch",
"type": "node-terminal"
}
]
}

26
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"json.schemas": [
{
"fileMatch": ["/npmextra.json"],
"schema": {
"type": "object",
"properties": {
"npmci": {
"type": "object",
"description": "settings for npmci"
},
"gitzone": {
"type": "object",
"description": "settings for gitzone",
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}
}
}
}
]
}

View File

@ -1,69 +0,0 @@
# smartrequest
dropin replacement for request
## Availabililty
[![npm](https://push.rocks/assets/repo-button-npm.svg)](https://www.npmjs.com/package/smartrequest)
[![git](https://push.rocks/assets/repo-button-git.svg)](https://GitLab.com/pushrocks/smartrequest)
[![git](https://push.rocks/assets/repo-button-mirror.svg)](https://github.com/pushrocks/smartrequest)
[![docs](https://push.rocks/assets/repo-button-docs.svg)](https://pushrocks.gitlab.io/smartrequest/)
## Status for master
[![build status](https://GitLab.com/pushrocks/smartrequest/badges/master/build.svg)](https://GitLab.com/pushrocks/smartrequest/commits/master)
[![coverage report](https://GitLab.com/pushrocks/smartrequest/badges/master/coverage.svg)](https://GitLab.com/pushrocks/smartrequest/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/smartrequest.svg)](https://www.npmjs.com/package/smartrequest)
[![Dependency Status](https://david-dm.org/pushrocks/smartrequest.svg)](https://david-dm.org/pushrocks/smartrequest)
[![bitHound Dependencies](https://www.bithound.io/github/pushrocks/smartrequest/badges/dependencies.svg)](https://www.bithound.io/github/pushrocks/smartrequest/master/dependencies/npm)
[![bitHound Code](https://www.bithound.io/github/pushrocks/smartrequest/badges/code.svg)](https://www.bithound.io/github/pushrocks/smartrequest)
[![TypeScript](https://img.shields.io/badge/TypeScript-2.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%206.x.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
## Usage
Use TypeScript for best in class instellisense.
> note: smartrequest uses the **native** node request module under the hood (not the one from npm)
```javascript
import * as smartrequest from 'smartrequest'
// simple post
let options: smartrequest.ISmartRequestOptions = { // typed options
headers: {
"Content-Type": "application/json"
"Authorization": "Bearer token"
},
requestBody: {
key1: 'value1',
key2: 3
}
}
smartrequest.post('https://example.com', options).then(res => {
console.log(res.status)
console.log(res.body) // if json, body will be parsed automatically
}).catch(err => {
console.log(err)
})
// also available
smartrequest.get(...)
smartrequest.put(...)
smartrequest.del(...)
// streaming
smartrequest.get('https://example.com/bigfile.mp4', optionsArg, true).then(res => { // third arg = true signals streaming
console.log(res.status)
res.on('data', data => {
// do something with the data chunk here
}
res.on('end', () => {
// do something when things have ended
})
})
```
[![npm](https://push.rocks/assets/repo-header.svg)](https://push.rocks)

View File

@ -5,5 +5,16 @@
"npmci": { "npmci": {
"npmGlobalTools": [], "npmGlobalTools": [],
"npmAccessLevel": "public" "npmAccessLevel": "public"
},
"gitzone": {
"projectType": "npm",
"module": {
"githost": "gitlab.com",
"gitscope": "pushrocks",
"gitrepo": "smartrequest",
"description": "dropin replacement for request",
"npmPackagename": "@pushrocks/smartrequest",
"license": "MIT"
}
} }
} }

14077
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,15 @@
{ {
"name": "@pushrocks/smartrequest", "name": "@pushrocks/smartrequest",
"version": "1.1.14", "version": "2.0.14",
"private": false, "private": false,
"description": "dropin replacement for request", "description": "dropin replacement for request",
"main": "dist/index.js", "main": "dist_ts/index.js",
"typings": "dist/index.d.ts", "typings": "dist_ts/index.d.ts",
"type": "module",
"scripts": { "scripts": {
"test": "(tstest test/)", "test": "(tstest test/ --web)",
"build": "(tsbuild)" "build": "(tsbuild --web)",
"buildDocs": "tsdoc"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -23,15 +25,31 @@
}, },
"homepage": "https://gitlab.com/pushrocks/smartrequest#README", "homepage": "https://gitlab.com/pushrocks/smartrequest#README",
"dependencies": { "dependencies": {
"@pushrocks/smartpromise": "^2.0.5", "@pushrocks/smartpromise": "^4.0.2",
"@types/form-data": "^2.2.1", "@pushrocks/smarturl": "^3.0.6",
"form-data": "^2.3.2" "agentkeepalive": "^4.3.0",
"form-data": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.0.22", "@gitzone/tsbuild": "^2.1.65",
"@gitzone/tsrun": "^1.1.12", "@gitzone/tsrun": "^1.2.39",
"@gitzone/tstest": "^1.0.15", "@gitzone/tstest": "^1.0.74",
"@pushrocks/tapbundle": "^3.0.5", "@pushrocks/tapbundle": "^5.0.4",
"@types/node": "^10.5.8" "@types/node": "^18.15.11"
} },
"files": [
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"
],
"browserslist": [
"last 1 chrome versions"
]
} }

4431
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

96
readme.md Normal file
View File

@ -0,0 +1,96 @@
# @pushrocks/smartrequest
dropin replacement for request
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartrequest)
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartrequest)
* [github.com (source mirror)](https://github.com/pushrocks/smartrequest)
* [docs (typedoc)](https://pushrocks.gitlab.io/smartrequest/)
## Status for master
Status Category | Status Badge
-- | --
GitLab Pipelines | [![pipeline status](https://gitlab.com/pushrocks/smartrequest/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/pushrocks/smartrequest/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@pushrocks/smartrequest)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/pushrocks/smartrequest)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@pushrocks/smartrequest)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@pushrocks/smartrequest)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@pushrocks/smartrequest)](https://lossless.cloud)
Platform support | [![Supports Windows 10](https://badgen.net/badge/supports%20Windows%2010/yes/green?icon=windows)](https://lossless.cloud) [![Supports Mac OS X](https://badgen.net/badge/supports%20Mac%20OS%20X/yes/green?icon=apple)](https://lossless.cloud)
## Usage
Use TypeScript for best in class instellisense.
### Features
- supports http
- supports https
- supports unix socks
- supports formData
- supports file uploads
- supports best practice keepAlive
- dedicated functions for working with JSON request/response cycles
- written in TypeScript
- continuously updated
- uses node native http and https modules
- used in modules like @pushrocks/smartproxy and @apiglobal/typedrequest
- commercial support available at [https://lossless.support](https://lossless.support)
> note: smartrequest uses the **native** node http/https modules under the hood (not the bloated one from npm)
```javascript
import * as smartrequest from 'smartrequest'
// simple post
const options: smartrequest.ISmartRequestOptions = { // typed options
headers: {
"Content-Type": "application/json"
"Authorization": "Bearer token"
},
requestBody: JSON.stringify({
key1: 'value1',
key2: 3
})
}
smartrequest.request('https://example.com', options).then(res => {
console.log(res.status)
console.log(res.body) // if json, body will be parsed automatically
}).catch(err => {
console.log(err)
})
// dedicated JSON methods are available:
smartrequest.getJson(...)
smartrequest.postJson(...)
smartrequest.putJson(...)
smartrequest.delJson(...)
// streaming
smartrequest.get('https://example.com/bigfile.mp4', optionsArg, true).then(res => { // third arg = true signals streaming
console.log(res.status)
res.on('data', data => {
// do something with the data chunk here
}
res.on('end', () => {
// do something when things have ended
})
})
```
## Contribution
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
[![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)

View File

@ -1,25 +1,23 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect, expectAsync } from '@pushrocks/tapbundle';
import * as smartrequest from '../ts/index'; import * as smartrequest from '../ts/index.js';
tap.test('should request a html document over https', async () => { tap.test('should request a html document over https', async () => {
await expect(smartrequest.getJson('https://encrypted.google.com/')) await expectAsync(smartrequest.getJson('https://encrypted.google.com/')).toHaveProperty('body');
.to.eventually.property('body')
.be.a('string');
}); });
tap.test('should request a JSON document over https', async () => { tap.test('should request a JSON document over https', async () => {
await expect(smartrequest.getJson('https://jsonplaceholder.typicode.com/posts/1')) await expectAsync(smartrequest.getJson('https://jsonplaceholder.typicode.com/posts/1'))
.to.eventually.property('body') .property('body')
.property('id') .property('id')
.equal(1); .toEqual(1);
}); });
tap.test('should post a JSON document over http', async () => { tap.skip.test('should post a JSON document over http', async () => {
await expect(smartrequest.postJson('http://md5.jsontest.com/?text=example_text')) await expectAsync(smartrequest.postJson('http://md5.jsontest.com/?text=example_text'))
.to.eventually.property('body') .property('body')
.property('md5') .property('md5')
.equal('fa4c6baa0812e5b5c80ed8885e55a8a6'); .toEqual('fa4c6baa0812e5b5c80ed8885e55a8a6');
}); });
tap.skip.test('should deal with unix socks', async () => { tap.skip.test('should deal with unix socks', async () => {
@ -28,8 +26,8 @@ tap.skip.test('should deal with unix socks', async () => {
{ {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Host: 'docker.sock' Host: 'docker.sock',
} },
} }
); );
console.log(socketResponse.body); console.log(socketResponse.body);

8
ts/00_commitinfo_data.ts Normal file
View File

@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@pushrocks/smartrequest',
version: '2.0.14',
description: 'dropin replacement for request'
}

View File

@ -1,6 +1,7 @@
export { request, IExtendedIncomingMessage } from './smartrequest.request'; export { request, safeGet } from './smartrequest.request.js';
export { ISmartRequestOptions } from './smartrequest.interfaces'; export type { IExtendedIncomingMessage } from './smartrequest.request.js';
export type { ISmartRequestOptions } from './smartrequest.interfaces.js';
export * from './smartrequest.jsonrest'; export * from './smartrequest.jsonrest.js';
export * from './smartrequest.binaryrest'; export * from './smartrequest.binaryrest.js';
export * from './smartrequest.formdata'; export * from './smartrequest.formdata.js';

View File

@ -1,27 +1,31 @@
// this file implements methods to get and post binary data. // this file implements methods to get and post binary data.
import * as interfaces from './smartrequest.interfaces'; import * as interfaces from './smartrequest.interfaces.js';
import { request } from './smartrequest.request'; import { request } from './smartrequest.request.js';
import * as plugins from './smartrequest.plugins'; import * as plugins from './smartrequest.plugins.js';
export const getBinary = async ( export const getBinary = async (
domainArg: string, domainArg: string,
optionsArg: interfaces.ISmartRequestOptions = {} optionsArg: interfaces.ISmartRequestOptions = {}
) => { ) => {
optionsArg = {
...optionsArg,
autoJsonParse: false,
};
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
const response = await request(domainArg, optionsArg, true); const response = await request(domainArg, optionsArg, true);
const data = []; const data: Array<Buffer> = [];
response response
.on('data', function(chunk) { .on('data', function (chunk: Buffer) {
data.push(chunk); data.push(chunk);
}) })
.on('end', function() { .on('end', function () {
//at this point data is an array of Buffers //at this point data is an array of Buffers
//so Buffer.concat() can make us a new Buffer //so Buffer.concat() can make us a new Buffer
//of all of them together //of all of them together
const buffer = Buffer.concat(data); const buffer = Buffer.concat(data);
response.body = buffer.toString('binary'); response.body = buffer;
done.resolve(); done.resolve();
}); });
await done.promise; await done.promise;

View File

@ -1,6 +1,6 @@
import * as plugins from './smartrequest.plugins'; import * as plugins from './smartrequest.plugins.js';
import * as interfaces from './smartrequest.interfaces'; import * as interfaces from './smartrequest.interfaces.js';
import { request } from './smartrequest.request'; import { request } from './smartrequest.request.js';
/** /**
* the interfae for FormFieldData * the interfae for FormFieldData
@ -8,16 +8,38 @@ import { request } from './smartrequest.request';
export interface IFormField { export interface IFormField {
name: string; name: string;
type: 'string' | 'filePath' | 'Buffer'; type: 'string' | 'filePath' | 'Buffer';
payload: string; payload: string | Buffer;
fileName?: string;
contentType?: string;
} }
const appendFormField = async (formDataArg: plugins.formData, formDataField: IFormField) => { const appendFormField = async (formDataArg: plugins.formData, formDataField: IFormField) => {
if (formDataField.type === 'filePath') { switch (formDataField.type) {
let fileData = plugins.fs.readFileSync(plugins.path.join(process.cwd(), formDataField.payload)); case 'string':
formDataArg.append(formDataField.name, formDataField.payload);
break;
case 'filePath':
if (typeof formDataField.payload !== 'string') {
throw new Error(
`Payload for key ${
formDataField.name
} must be of type string. Got ${typeof formDataField.payload} instead.`
);
}
const fileData = plugins.fs.readFileSync(
plugins.path.join(process.cwd(), formDataField.payload)
);
formDataArg.append('file', fileData, { formDataArg.append('file', fileData, {
filename: 'upload.pdf', filename: formDataField.fileName ? formDataField.fileName : 'upload.pdf',
contentType: 'application/pdf' contentType: 'application/pdf',
}); });
break;
case 'Buffer':
formDataArg.append(formDataField.name, formDataField.payload, {
filename: formDataField.fileName ? formDataField.fileName : 'upload.pdf',
contentType: formDataField.contentType ? formDataField.contentType : 'application/pdf',
});
break;
} }
}; };
@ -30,14 +52,46 @@ export const postFormData = async (
for (const formField of payloadArg) { for (const formField of payloadArg) {
await appendFormField(form, formField); await appendFormField(form, formField);
} }
const requestOptions = Object.assign({}, optionsArg, { const requestOptions = {
...optionsArg,
method: 'POST', method: 'POST',
headers: { headers: {
...optionsArg.headers, ...optionsArg.headers,
...form.getHeaders() ...form.getHeaders(),
}, },
requestBody: form requestBody: form,
}); };
// lets fire the actual request for sending the formdata
const response = await request(urlArg, requestOptions);
return response;
};
export const postFormDataUrlEncoded = async (
urlArg: string,
optionsArg: interfaces.ISmartRequestOptions = {},
payloadArg: { key: string; content: string }[]
) => {
let resultString = '';
for (const keyContentPair of payloadArg) {
if (resultString) {
resultString += '&';
}
resultString += `${encodeURIComponent(keyContentPair.key)}=${encodeURIComponent(
keyContentPair.content
)}`;
}
const requestOptions: interfaces.ISmartRequestOptions = {
...optionsArg,
method: 'POST',
headers: {
...optionsArg.headers,
'content-type': 'application/x-www-form-urlencoded',
},
requestBody: resultString,
};
// lets fire the actual request for sending the formdata // lets fire the actual request for sending the formdata
const response = await request(urlArg, requestOptions); const response = await request(urlArg, requestOptions);

View File

@ -1,6 +1,9 @@
import * as plugins from './smartrequest.plugins'; import * as plugins from './smartrequest.plugins.js';
import * as https from 'https'; import * as https from 'https';
export interface ISmartRequestOptions extends https.RequestOptions { export interface ISmartRequestOptions extends https.RequestOptions {
keepAlive?: boolean;
requestBody?: any; requestBody?: any;
autoJsonParse?: boolean;
queryParams?: { [key: string]: string };
} }

View File

@ -1,7 +1,7 @@
// This file implements methods to get and post JSON in a simple manner. // This file implements methods to get and post JSON in a simple manner.
import * as interfaces from './smartrequest.interfaces'; import * as interfaces from './smartrequest.interfaces.js';
import { request } from './smartrequest.request'; import { request } from './smartrequest.request.js';
/** /**
* gets Json and puts the right headers + handles response aggregation * gets Json and puts the right headers + handles response aggregation
@ -14,7 +14,7 @@ export const getJson = async (
) => { ) => {
optionsArg.method = 'GET'; optionsArg.method = 'GET';
optionsArg.headers = { optionsArg.headers = {
...optionsArg.headers ...optionsArg.headers,
}; };
let response = await request(domainArg, optionsArg); let response = await request(domainArg, optionsArg);
return response; return response;
@ -37,7 +37,7 @@ export const postJson = async (
// assign the right Content-Type, leaving all other headers in place // assign the right Content-Type, leaving all other headers in place
optionsArg.headers = { optionsArg.headers = {
...optionsArg.headers, ...optionsArg.headers,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}; };
} }
let response = await request(domainArg, optionsArg); let response = await request(domainArg, optionsArg);

View File

@ -1,10 +1,19 @@
import * as formData from 'form-data'; // node native scope
import * as fs from 'fs'; import * as fs from 'fs';
import * as http from 'http'; import * as http from 'http';
import * as https from 'https'; import * as https from 'https';
import * as path from 'path'; import * as path from 'path';
import * as url from 'url';
export { http, https, fs, path };
// pushrocks scope
import * as smartpromise from '@pushrocks/smartpromise'; import * as smartpromise from '@pushrocks/smartpromise';
import * as smarturl from '@pushrocks/smarturl';
export { formData, http, https, fs, path, url, smartpromise }; export { smartpromise, smarturl };
// third party scope
import agentkeepalive from 'agentkeepalive';
import formData from 'form-data';
export { agentkeepalive, formData };

View File

@ -1,6 +1,5 @@
import * as https from 'https'; import * as plugins from './smartrequest.plugins.js';
import * as plugins from './smartrequest.plugins'; import * as interfaces from './smartrequest.interfaces.js';
import * as interfaces from './smartrequest.interfaces';
import { IncomingMessage } from 'http'; import { IncomingMessage } from 'http';
@ -9,21 +8,26 @@ export interface IExtendedIncomingMessage extends IncomingMessage {
} }
const buildUtf8Response = ( const buildUtf8Response = (
incomingMessageArg: IncomingMessage incomingMessageArg: IncomingMessage,
autoJsonParse = true
): Promise<IExtendedIncomingMessage> => { ): Promise<IExtendedIncomingMessage> => {
let done = plugins.smartpromise.defer<IExtendedIncomingMessage>(); const done = plugins.smartpromise.defer<IExtendedIncomingMessage>();
// Continuously update stream with data // Continuously update stream with data
let body = ''; let body = '';
incomingMessageArg.on('data', function(chunkArg) { incomingMessageArg.on('data', (chunkArg) => {
body += chunkArg; body += chunkArg;
}); });
incomingMessageArg.on('end', function() { incomingMessageArg.on('end', () => {
if (autoJsonParse) {
try { try {
(incomingMessageArg as IExtendedIncomingMessage).body = JSON.parse(body); (incomingMessageArg as IExtendedIncomingMessage).body = JSON.parse(body);
} catch (err) { } catch (err) {
(incomingMessageArg as IExtendedIncomingMessage).body = body; (incomingMessageArg as IExtendedIncomingMessage).body = body;
} }
} else {
(incomingMessageArg as IExtendedIncomingMessage).body = body;
}
done.resolve(incomingMessageArg as IExtendedIncomingMessage); done.resolve(incomingMessageArg as IExtendedIncomingMessage);
}); });
return done.promise; return done.promise;
@ -46,79 +50,176 @@ const parseSocketPathAndRoute = (stringToParseArg: string) => {
const result = parseRegex.exec(stringToParseArg); const result = parseRegex.exec(stringToParseArg);
return { return {
socketPath: result[1], socketPath: result[1],
path: result[2] path: result[2],
}; };
}; };
/**
* a custom http agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpAgent = new plugins.agentkeepalive({
keepAlive: true,
maxFreeSockets: 10,
maxSockets: 100,
maxTotalSockets: 1000,
timeout: 60000,
});
/**
* a custom http agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpAgentKeepAliveFalse = new plugins.agentkeepalive({
keepAlive: false,
timeout: 60000,
});
/**
* a custom https agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpsAgent = new plugins.agentkeepalive.HttpsAgent({
keepAlive: true,
maxFreeSockets: 10,
maxSockets: 100,
maxTotalSockets: 1000,
timeout: 60000,
});
/**
* a custom https agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpsAgentKeepAliveFalse = new plugins.agentkeepalive.HttpsAgent({
keepAlive: false,
timeout: 60000,
});
export let request = async ( export let request = async (
domainArg: string, urlArg: string,
optionsArg: interfaces.ISmartRequestOptions = {}, optionsArg: interfaces.ISmartRequestOptions = {},
streamArg: boolean = false responseStreamArg: boolean = false,
requestDataFunc: (req: plugins.http.ClientRequest) => void = null
): Promise<IExtendedIncomingMessage> => { ): Promise<IExtendedIncomingMessage> => {
let done = plugins.smartpromise.defer<any>(); const done = plugins.smartpromise.defer<IExtendedIncomingMessage>();
// merge options
const defaultOptions: interfaces.ISmartRequestOptions = {
// agent: agent,
autoJsonParse: true,
keepAlive: true,
};
optionsArg = {
...defaultOptions,
...optionsArg,
};
// parse url // parse url
let parsedUrl: plugins.url.Url; const parsedUrl = plugins.smarturl.Smarturl.createFromUrl(urlArg, {
parsedUrl = plugins.url.parse(domainArg); searchParams: optionsArg.queryParams || {},
});
optionsArg.hostname = parsedUrl.hostname; optionsArg.hostname = parsedUrl.hostname;
if (parsedUrl.port) { if (parsedUrl.port) {
optionsArg.port = parseInt(parsedUrl.port); optionsArg.port = parseInt(parsedUrl.port, 10);
} }
optionsArg.path = parsedUrl.path; optionsArg.path = parsedUrl.path;
optionsArg.queryParams = parsedUrl.searchParams;
// determine if unixsock // determine if unixsock
if (testForUnixSock(domainArg)) { if (testForUnixSock(urlArg)) {
const detailedUnixPath = parseSocketPathAndRoute(optionsArg.path); const detailedUnixPath = parseSocketPathAndRoute(optionsArg.path);
optionsArg.socketPath = detailedUnixPath.socketPath; optionsArg.socketPath = detailedUnixPath.socketPath;
optionsArg.path = detailedUnixPath.path; optionsArg.path = detailedUnixPath.path;
} }
// TODO: support tcp sockets
// lets determine agent
switch (true) {
case !!optionsArg.agent:
break;
case parsedUrl.protocol === 'https:' && optionsArg.keepAlive:
optionsArg.agent = httpsAgent;
break;
case parsedUrl.protocol === 'https:' && !optionsArg.keepAlive:
optionsArg.agent = httpsAgentKeepAliveFalse;
break;
case parsedUrl.protocol === 'http:' && optionsArg.keepAlive:
optionsArg.agent = httpAgent;
break;
case parsedUrl.protocol === 'http:' && !optionsArg.keepAlive:
optionsArg.agent = httpAgentKeepAliveFalse;
break;
}
// lets determine the request module to use // lets determine the request module to use
const requestModule = (() => { const requestModule = (() => {
if (parsedUrl.protocol === 'https:') { switch (true) {
case parsedUrl.protocol === 'https:':
return plugins.https; return plugins.https;
} else if (parsedUrl.protocol === 'http:') { case parsedUrl.protocol === 'http:':
return plugins.http; return plugins.http;
} else {
throw new Error(`unsupported protocol: ${parsedUrl.protocol}`);
} }
})() as typeof plugins.https; })() as typeof plugins.https;
if (!requestModule) {
console.error(`The request to ${urlArg} is missing a viable protocol. Must be http or https`);
return;
}
// lets perform the actual request // lets perform the actual request
let request = requestModule.request(optionsArg); const requestToFire = requestModule.request(optionsArg, async (response) => {
if (responseStreamArg) {
// lets write the requestBody done.resolve(response as IExtendedIncomingMessage);
if (optionsArg.requestBody) {
if (!(optionsArg.requestBody instanceof plugins.formData)) {
if (typeof optionsArg.requestBody !== 'string') {
optionsArg.requestBody = JSON.stringify(optionsArg.requestBody);
}
request.write(optionsArg.requestBody);
request.end();
} else if (optionsArg.requestBody instanceof plugins.formData) {
optionsArg.requestBody.pipe(request).on('finish', event => {
request.end();
});
}
} else { } else {
request.end(); const builtResponse = await buildUtf8Response(response, optionsArg.autoJsonParse);
}
// lets handle an error
request.on('error', e => {
console.error(e);
});
// lets handle the response
request.on('response', async response => {
if (streamArg) {
done.resolve(response);
} else {
const builtResponse = await buildUtf8Response(response);
done.resolve(builtResponse); done.resolve(builtResponse);
} }
}); });
const result = await done.promise; // lets write the requestBody
return result; if (optionsArg.requestBody) {
if (optionsArg.requestBody instanceof plugins.formData) {
optionsArg.requestBody.pipe(requestToFire).on('finish', (event: any) => {
requestToFire.end();
});
} else {
if (typeof optionsArg.requestBody !== 'string') {
optionsArg.requestBody = JSON.stringify(optionsArg.requestBody);
}
requestToFire.write(optionsArg.requestBody);
requestToFire.end();
}
} else if (requestDataFunc) {
requestDataFunc(requestToFire);
} else {
requestToFire.end();
}
// lets handle an error
requestToFire.on('error', (e) => {
console.error(e);
requestToFire.destroy();
});
const response = await done.promise;
response.on('error', (err) => {
console.log(err);
response.destroy();
});
return response;
};
export const safeGet = async (urlArg: string) => {
const agentToUse = urlArg.startsWith('http') ? plugins.http.globalAgent : plugins.https.globalAgent;
try {
const response = await request(urlArg, {
method: 'GET',
agent: agentToUse,
timeout: 5000,
autoJsonParse: false,
});
return response;
} catch (err) {
console.log(err);
return null;
}
}; };

10
tsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "nodenext",
"esModuleInterop": true
}
}

View File

@ -1,3 +0,0 @@
{
"extends": "tslint-config-standard"
}