diff --git a/LICENSE b/LICENSE index cd0171a..bcd4705 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ -MIT License -Copyright (c) 2019 Wessel T +The MIT License (MIT) + +Copyright (c) 2019-present Wessel "wesselgame" T Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..47114fc --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# Wumpfetch +> A lightweight and fast Node.js HTTP client which can be used in various ways + +> [GitHub](https://www.github.com/PassTheWessel/wumpfetch) **|** [NPM](https://www.npmjs.com/package/wumpfetch) + +## Installing +```sh +$ yarn add wumpfetch # Install w/ Yarn (the superior package manager) +$ npm i wumpfetch # Install w/ NPM +``` + +## Usage +##### Code +```js +const w = require( 'wumpfetch' ); + +;( async() => { + const r = await w( 'https://aws.random.cat/meow' ).send(); + + console.log( r.json() ); +}); +``` +##### Result +```sh +$ node test.js +{ file: 'https://purr.objects-us-east-1.dream.io/i/100_-_rURSo7L.gif' } +``` + +### Sending data in a JSON body to a server +```js +const w = require( 'wumpfetch' ); + +;( async() => { + const r = await w( 'https://my-site.com/postboi', 'POST' ) + .query( 'video', 'wumpboye' ) // Add a query + .header({ 'Authorization': 'Pablito' }) // Set a header + .post({ x: 'y', z: 1, beep: 'boop', chocolate: true }) // Send a json body + .timeout( 1000 ) // Set a 1s timeout + .send(); + + console.log( r.json() ); +})(); +``` +or +```js +const w = require( 'wumpfetch' ); + +;( async() => { + const r = await w({ + url: 'https://my-site.com/postboi', + method: 'GET', + headers: { + 'Authorization': 'Pablo' + } + }); + + console.log( r.json() ); +})(); +``` +or +```js +const w = require( 'wumpfetch' ); + +;( async() => { + const r = await w( 'https://my-site.com/postboi', { method: 'GET' }); + console.log( r.json() ); +})(); +``` + +### Why should i use wumpfetch? +Wumpfetch is a lightweight and fast request library comparing to other packages such as request and node-fetch which are both around 150kb in size \ No newline at end of file diff --git a/createRequest.js b/createRequest.js new file mode 100644 index 0000000..cab965a --- /dev/null +++ b/createRequest.js @@ -0,0 +1,3 @@ +const path = require( 'path' ); + +module.exports = ( url, method ) => { return new( require( path.join( __dirname, 'model', 'WumpRequest.js' ) ) )( url, method ); } \ No newline at end of file diff --git a/model/WumpRequest.js b/model/WumpRequest.js new file mode 100644 index 0000000..8778b80 --- /dev/null +++ b/model/WumpRequest.js @@ -0,0 +1,122 @@ +const qs = require( 'querystring' ); +const zlib = require( 'zlib' ); +const path = require( 'path' ); +const http = require( 'http' ); +const https = require( 'https' ); +const { URL } = require( 'url' ); + +const WumpResponse = require( path.join( __dirname, 'WumpResponse.js' ) ); + +const c = [ 'gzip', 'deflate' ]; + +module.exports = class WumpRequest { + constructor ( url = {}, m = {} ) { + const o = typeof url === 'string' ? m : url; + const obj = typeof o === 'object'; + + this.m = typeof o === 'string' ? o : obj && o.method ? o.method : 'GET'; + this.url = typeof url === 'string' ? new URL( url ) : obj && typeof o.url === 'string' ? new URL( o.url ) : url; + this.SDA = obj && typeof o.sendDataAs === 'string' ? o.sendDataAs : null; + this.data = obj && o.data ? o.data : null; + this.streamed = obj && o.streamed ? true : false; + this.compressed = obj && o.compressed ? true : false; + this.rHeaders = obj && typeof o.headers === 'object' ? o.headers : {}; + this.timeoutTime = obj && typeof o.timeout === 'number' ? o.timeout : null; + this.coreOptions = obj && typeof o.coreOptions === 'object' ? o.coreOptions : {}; + + return this; + } + + query ( a, b ) { + if ( typeof a === 'object' ) Object.keys( a ).forEach( ( v ) => this.url.searchParams.append( v, a[ v ] ) ); + else this.url.searchParams.append( a, ab ) + + return this; + } + + body ( data, SA ) { + this.SDA = typeof data === 'object' && !SA && !Buffer.isBuffer( data ) ? 'json' : ( SA ? SA.toLowerCase() : 'buffer' ); + this.data = this.SDA === 'form' ? qs.stringify( data ) : ( this.SDA === 'json' ? JSON.stringify( data ) : data ); + + return this; + } + + header ( a, b ) { + if (typeof a === 'object') Object.keys( a ).forEach( ( v ) => this.rHeaders[ v.toLowerCase() ] = a[ v ] ); + else this.rHeaders[ a.toLowerCase() ] = b; + + return this; + } + + compress () { + this.compressed = true + if ( !this.rHeaders[ 'accept-encoding' ] ) this.rHeaders[ 'accept-encoding' ] = c.join( ', ' ); + + return this; + } + + path ( p ) { this.url.pathname = path.join( this.url.pathname, p ); return this; } + stream () { this.streamed = true; return this; } + option ( n, v ) { this.coreOptions[ n ] = v; return this; } + timeout ( timeout ) { this.timeoutTime = timeout; return this; } + + send () { + return new Promise( ( resolve, reject ) => { + if ( this.data ) { + if ( !this.rHeaders.hasOwnProperty( 'user-agent' ) ) this.rHeaders[ 'User-Agent' ] = 'wumpfetch/0.0.1 (https://github.com/PassTheWessel/wumpfetch)'; + + if ( this.SDA === 'json' && !this.rHeaders.hasOwnProperty( 'content-type' ) ) this.rHeaders[ 'Content-Type' ] = 'application/json'; + if ( this.SDA === 'form') { + if ( !this.rHeaders.hasOwnProperty( 'content-type' ) ) this.rHeaders['Content-Type'] = 'application/x-www-form-urlencoded'; + if ( !this.rHeaders.hasOwnProperty( 'content-length' ) ) this.rHeaders[ 'Content-Length' ] = Buffer.byteLength( this.data ); + } + } + + let req; + const options = Object.assign({ + 'host' : this.url.hostname, + 'port' : this.url.port, + 'path' : this.url.pathname + this.url.search, + 'method' : this.m, + 'headers' : this.rHeaders, + 'protocol': this.url.protocol + }, this.coreOptions ); + + const resHandler = ( res ) => { + let stream = res; + + if ( this.compressed ) { + if ( res.headers[ 'content-encoding' ] === 'gzip' ) stream = res.pipe( zlib.createGunzip() ); + else if ( res.headers[ 'content-encoding' ] === 'deflate' ) stream = res.pipe( zlib.createInflate() ); + } + + let wumpRes; + + if ( this.streamed ) resolve( stream ); + else { + wumpRes = new WumpResponse( res ); + + stream.on( 'error', ( e ) => reject( e ) ); + stream.on( 'data', ( c ) => wumpRes._addChunk( c ) ) + stream.on( 'end', () => resolve( wumpRes ) ); + } + } + + if ( this.url.protocol === 'http:' ) req = http.request( options, resHandler ); + else if (this.url.protocol === 'https:') req = https.request( options, resHandler ); + else throw new Error( `Bad URL protocol: ${this.url.protocol}` ); + + if ( this.timeoutTime ) { + req.setTimeout( this.timeoutTime, () => { + req.abort(); + if ( !this.streamed ) reject( new Error( 'Timeout reached' ) ); + }); + } + + req.on( 'error', ( e ) => reject( e ) ); + if ( this.data ) req.write( this.data ); + + req.end(); + }); + } +} \ No newline at end of file diff --git a/model/WumpResponse.js b/model/WumpResponse.js new file mode 100644 index 0000000..f9bd99e --- /dev/null +++ b/model/WumpResponse.js @@ -0,0 +1,14 @@ +module.exports = class WumpResponse { + constructor ( res ) { + this.body = Buffer.alloc( 0 ); + this.coreRes = res + + this.headers = res.headers; + this.statusCode = res.statusCode; + } + + _addChunk ( chunk ) { this.body = Buffer.concat([ this.body, chunk ]); } + + text () { return this.body.toString(); } + json () { return JSON.parse( this.body ); } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..df00b64 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "wumpfetch", + "version": "0.0.1", + "description": "🚀 A lightweight and fast Node.js HTTP Client", + "author": "Wessel \"wesselgame\" T ", + "license": "MIT", + "main": "createRequest.js", + "bugs": { + "url": "https://www.github.com/PassTheWessel/wumpfetch/issues" + }, + "homepage": "https://www.github.com/PassTheWessel/wumpfetch#readme", + "repository": { + "type": "git", + "url": "https://www.github.com/PassTheWessel/wumpfetch" + }, + "keywords": [ + "http", + "https", + "request", + "fetch", + "url", + "get", + "post", + "patch", + "lightweight" + ], + "dependencies": {} +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..628c06b --- /dev/null +++ b/test.js @@ -0,0 +1,6 @@ +const w = require( './createRequest' ); + +;( async() => { + const r = await w( { url: 'https://aws.random.cat/meow' } ).send(); + console.log( r.json() ) +})();