diff --git a/.gitignore b/.gitignore index edc627e..3b20552 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ dist lib node_modules +release +private diff --git a/README.md b/README.md index 738d44e..f69abb3 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,21 @@ The [Fetch Web API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U AxleJS supercharges fetch, with better error handling, easier to use options, can automatically follow redirects, an easier way to manage and view headers and search queries, custom response and request classes and methods, and a lot more. +## [Documentation](https://github.com/ksplatdev/AxleJS/wiki/Documenation) + ## Features -1. All HTTP Restful Methods. -2. Very small, 9KB Minified and 15KB Unminified. -3. Fast and easy to use. -4. Makes fetch easier to use. -5. Asynchronous. -6. Error handling. -7. Adds cancellation to fetch api. -8. Custom Response and Request classes. -9. Written in TypeScript. -10. Built-in TypeDefs. +1. Functions for all HTTP Restful Methods. +2. Use built-in middleware or make custom middleware. +3. Very small, ~10KB Minified and ~17KB Unminified. +4. Fast and easy to use. +5. [Documentation](https://github.com/ksplatdev/AxleJS/wiki/Documenation). +6. Makes fetch easier to use. +7. Better Error handling. +8. Adds cancellation to fetch api. +9. Custom and Extended Response and Request classes. +10. Written in TypeScript. +11. Built-in TypeDefs. ## How to Download @@ -33,18 +36,18 @@ AxleJS supercharges fetch, with better error handling, easier to use options, ca 1. Download the [latest release](https://github.com/ksplatdev/AxleJS/releases/latest) from GitHub. 2. Unzip and move contents into your project. 3. Import AxleJS with the [ES6 Import Syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import). -4. Read the documentation. +4. Read the [documentation](https://github.com/ksplatdev/AxleJS/wiki/Documenation). ### NPM 1. Run `npm i axlejs`. 2. Import AxleJS with the [ES6 Import Syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import). -3. Read the documentation. +3. Read the [documentation](https://github.com/ksplatdev/AxleJS/wiki/Documenation). ### CDN -1. Import from or for minified version . -2. Read the documentation. +1. Import from or for minified version . +2. Read the [documentation](https://github.com/ksplatdev/AxleJS/wiki/Documenation). ## Quick Start @@ -175,8 +178,9 @@ For more info, read the documentation. 9. Test your changes on codesandbox or any other platform. 10. Stage, Commit, and Push your changes on your branch. 11. Create a pull request to merge onto the staging branch. -12. Wait for your pull request to be reviewed and possibly merged. -13. Thanks for contributing! +12. State all changes as a changelog in the pull request. +13. Wait for your pull request to be reviewed and possibly merged. +14. Thanks for contributing! ### File Structure diff --git a/examples/cancelRequest.ts b/examples/cancelRequest.ts new file mode 100644 index 0000000..a281df8 --- /dev/null +++ b/examples/cancelRequest.ts @@ -0,0 +1,18 @@ +import Axle from '../dist/index'; + +// setup cancel +const cancelMark = new Axle.cancelMark(); +const cancelSignal = cancelMark.signal; + +(async function test() { + const json = await ( + await Axle.get('https://dog.ceo/api/breeds/image/random', { + signal: cancelSignal, + }) + ).json(); + + console.log(json.message); // dog image url + + // cancel with message, optional + cancelMark.cancel('Canceled Request'); +})(); diff --git a/examples/useMiddleware.ts b/examples/useMiddleware.ts new file mode 100644 index 0000000..c9f9713 --- /dev/null +++ b/examples/useMiddleware.ts @@ -0,0 +1,26 @@ +import Axle from '../dist/index'; + +// custom middleware +Axle.use((req, res) => { + if (res.query['id']) { + console.log(`ID: ${res.queryParam('id')}`); + } else { + console.log('Different URL, no ID found'); + } +}); + +// built-in middleware +Axle.use(Axle.middleware.timeTook()); + +// built-in middleware options +Axle.useOptions({ + ...Axle.middlewareOptions.cors(), + ...Axle.middlewareOptions.kneepads(), +}); + +(async function test() { + const res = await Axle.get('https://dog.ceo/api/breeds/image/random'); + const json = await res.json(); + + console.log(json.message); // random dog url +})(); diff --git a/package-lock.json b/package-lock.json index cde5441..82ba611 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "axlejs", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -198,12 +198,6 @@ "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, - "@types/html-minifier-terser": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", - "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==", - "dev": true - }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -630,12 +624,6 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -680,24 +668,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, "camelcase": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", @@ -762,15 +732,6 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - } - }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -838,25 +799,6 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, - "css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" - } - }, - "css-what": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", - "integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==", - "dev": true - }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -942,70 +884,6 @@ "esutils": "^2.0.2" } }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "requires": { - "utila": "~0.4" - } - }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", - "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, "electron-to-chromium": { "version": "1.3.822", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz", @@ -1037,12 +915,6 @@ "ansi-colors": "^4.1.1" } }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - }, "envinfo": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", @@ -1497,12 +1369,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, "hosted-git-info": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", @@ -1512,73 +1378,6 @@ "lru-cache": "^6.0.0" } }, - "html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", - "dev": true, - "requires": { - "camel-case": "^4.1.1", - "clean-css": "^4.2.3", - "commander": "^4.1.1", - "he": "^1.2.0", - "param-case": "^3.0.3", - "relateurl": "^0.2.7", - "terser": "^4.6.3" - }, - "dependencies": { - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } - } - } - } - }, - "html-webpack-plugin": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.2.tgz", - "integrity": "sha512-HvB33boVNCz2lTyBsSiMffsJ+m0YLIQ+pskblXgN9fnjS1BgEcuAfdInfXfGrkdXV406k9FiDi86eVCDBgJOyQ==", - "dev": true, - "requires": { - "@types/html-minifier-terser": "^5.0.0", - "html-minifier-terser": "^5.0.1", - "lodash": "^4.17.21", - "pretty-error": "^3.0.4", - "tapable": "^2.0.0" - } - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -1861,12 +1660,6 @@ "p-locate": "^4.1.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -1885,23 +1678,6 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "requires": { - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2032,24 +1808,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, "node-releases": { "version": "1.1.75", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", @@ -2083,15 +1841,6 @@ "path-key": "^3.0.0" } }, - "nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2215,24 +1964,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2254,24 +1985,6 @@ "lines-and-columns": "^1.1.6" } }, - "pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2344,16 +2057,6 @@ "fast-diff": "^1.1.2" } }, - "pretty-error": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-3.0.4.tgz", - "integrity": "sha512-ytLFLfv1So4AO1UkoBF6GXQgJRaKbiSiGFICaOPNwQ3CMvBvXpLRubeQWyPGnsbV/t9ml9qto6IeCsho0aEvwQ==", - "dev": true, - "requires": { - "lodash": "^4.17.20", - "renderkid": "^2.0.6" - } - }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -2508,42 +2211,6 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "renderkid": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", - "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", - "dev": true, - "requires": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3015,12 +2682,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/package.json b/package.json index 122bc65..fa0b2a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "axlejs", - "version": "1.0.0", + "version": "1.1.0", "description": "Fetch, supercharged.", "main": "dist/index.js", "typings": "dist/index.d.ts", @@ -40,7 +40,6 @@ "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^3.4.1", - "html-webpack-plugin": "^5.3.2", "onchange": "^7.1.0", "open-cli": "^7.0.0", "prettier": "^2.3.2", diff --git a/src/core/delete.ts b/src/core/delete.ts index d973811..0dd4794 100644 --- a/src/core/delete.ts +++ b/src/core/delete.ts @@ -1,8 +1,9 @@ import { AxleTypes } from '../index'; import AxleRequest from '../models/request'; +import AxleResponse from '../models/response'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export default async function deleteReq>( +export default async function deleteReq | FormData>( url: string, data?: t | undefined | null, options: AxleTypes.AxleOptions = { @@ -13,12 +14,8 @@ export default async function deleteReq>( 'Content-Type': 'application/json', }, } -) { - try { - const req = new AxleRequest('DELETE', url, data, options); - const res = await req.run(); - return res; - } catch (error) { - console.error(error); - } +): Promise { + const req = new AxleRequest('DELETE', url, data, options); + const res = await req.run(); + return res; } diff --git a/src/core/get.ts b/src/core/get.ts index 02c180e..4511231 100644 --- a/src/core/get.ts +++ b/src/core/get.ts @@ -1,5 +1,6 @@ import { AxleTypes } from '../index'; import AxleRequest from '../models/request'; +import AxleResponse from '../models/response'; export default async function get( url: string, @@ -7,12 +8,8 @@ export default async function get( mode: 'cors', cache: 'default', } -) { - try { - const req = new AxleRequest('GET', url, undefined, options); - const res = await req.run(); - return res; - } catch (error) { - console.error(error); - } +): Promise { + const req = new AxleRequest('GET', url, undefined, options); + const res = await req.run(); + return res; } diff --git a/src/core/head.ts b/src/core/head.ts new file mode 100644 index 0000000..a134ee4 --- /dev/null +++ b/src/core/head.ts @@ -0,0 +1,15 @@ +import { AxleTypes } from '../index'; +import AxleRequest from '../models/request'; +import AxleResponse from '../models/response'; + +export default async function head( + url: string, + options: AxleTypes.AxleOptions = { + mode: 'cors', + cache: 'default', + } +): Promise { + const req = new AxleRequest('HEAD', url, undefined, options); + const res = await req.run(); + return res; +} diff --git a/src/core/patch.ts b/src/core/patch.ts index 3b78e0d..4a84560 100644 --- a/src/core/patch.ts +++ b/src/core/patch.ts @@ -1,5 +1,6 @@ import { AxleTypes } from '../index'; import AxleRequest from '../models/request'; +import AxleResponse from '../models/response'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export default async function patch>( @@ -13,12 +14,8 @@ export default async function patch>( 'Content-Type': 'application/json', }, } -) { - try { - const req = new AxleRequest('PATCH', url, data, options); - const res = await req.run(); - return res; - } catch (error) { - console.error(error); - } +): Promise { + const req = new AxleRequest('PATCH', url, data, options); + const res = await req.run(); + return res; } diff --git a/src/core/post.ts b/src/core/post.ts index 77f4ddb..3ba1fe4 100644 --- a/src/core/post.ts +++ b/src/core/post.ts @@ -1,5 +1,6 @@ import { AxleTypes } from '../index'; import AxleRequest from '../models/request'; +import AxleResponse from '../models/response'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export default async function post>( @@ -13,12 +14,8 @@ export default async function post>( 'Content-Type': 'application/json', }, } -) { - try { - const req = new AxleRequest('POST', url, data, options); - const res = await req.run(); - return res; - } catch (error) { - console.error(error); - } +): Promise { + const req = new AxleRequest('POST', url, data, options); + const res = await req.run(); + return res; } diff --git a/src/core/put.ts b/src/core/put.ts index 756e523..d7b32d9 100644 --- a/src/core/put.ts +++ b/src/core/put.ts @@ -1,5 +1,6 @@ import { AxleTypes } from '../index'; import AxleRequest from '../models/request'; +import AxleResponse from '../models/response'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export default async function put>( @@ -13,10 +14,8 @@ export default async function put>( 'Content-Type': 'application/json', }, } -) { - try { - return new AxleRequest('PUT', url, data, options).response; - } catch (error) { - console.error(error); - } +): Promise { + const req = new AxleRequest('PUT', url, data, options); + const res = await req.run(); + return res; } diff --git a/src/core/use.ts b/src/core/use.ts new file mode 100644 index 0000000..4e3b0f4 --- /dev/null +++ b/src/core/use.ts @@ -0,0 +1,13 @@ +import { AxleTypes } from '../index'; + +const __axle_js_middleware: AxleTypes.AxleMiddleware[] = []; + +// use middleware for AxleJS, such as certain settings for all requests, etc.. +export default function use(cb: AxleTypes.AxleMiddleware) { + return __axle_js_middleware.push(cb); +} + +// gets the updated array of middleware(s) +export function __getMiddleware() { + return __axle_js_middleware; +} diff --git a/src/core/useOptions.ts b/src/core/useOptions.ts new file mode 100644 index 0000000..8f72f2e --- /dev/null +++ b/src/core/useOptions.ts @@ -0,0 +1,13 @@ +import { AxleTypes } from '../index'; + +const __axle_js_middleware_options: AxleTypes.AxleOptions[] = []; + +// use / extend all of your requests' options +export default function useOptions(options: AxleTypes.AxleOptions) { + return __axle_js_middleware_options.push(options); +} + +// retrieve options +export function __getMiddlewareOptions() { + return __axle_js_middleware_options; +} diff --git a/src/index.ts b/src/index.ts index 23bfeb0..1fc7558 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,16 @@ import deleteReq from './core/delete'; import get from './core/get'; +import head from './core/head'; import patch from './core/patch'; import post from './core/post'; import put from './core/put'; +import use from './core/use'; +import useOptions from './core/useOptions'; +import cors from './middleware/cors'; +import kneepads from './middleware/kneepads'; +import timeTook from './middleware/timeTook'; import AxleCancelMark from './models/cancelMark'; +import AxleRequest from './models/request'; import AxleResponse from './models/response'; export namespace AxleTypes { @@ -24,6 +31,7 @@ export namespace AxleTypes { credentials?: RequestCredentials; headers?: AxleHeaders; redirect?: 'manual' | 'follow' | 'error'; + referrer?: string; referrerPolicy?: | 'no-referrer' | 'no-referrer-when-downgrade' @@ -33,12 +41,25 @@ export namespace AxleTypes { | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url'; - body?: string; + body?: string | FormData; keepalive?: boolean; signal?: AbortSignal | null; // eslint-disable-next-line @typescript-eslint/no-explicit-any window?: any; integrity?: string; + handleStatus?: (status: number, statusMessage: string) => boolean; + } + + export type AxleMiddleware = ( + req: AxleRequest, + res: AxleResponse + ) => unknown; + + export interface AxleError { + status: number; + message: string; + response: AxleResponse; + request: AxleRequest; } } @@ -48,10 +69,20 @@ const Axle = { delete: deleteReq, put: put, patch: patch, - all: (promises: Promise[]) => { + head: head, + all: (promises: Promise[]) => { return Promise.all(promises); }, cancelMark: AxleCancelMark, + use: use, + useOptions: useOptions, + middleware: { + timeTook: timeTook, + }, + middlewareOptions: { + cors: cors, + kneepads: kneepads, + }, }; export default Axle; diff --git a/src/middleware/cors.ts b/src/middleware/cors.ts new file mode 100644 index 0000000..42142c5 --- /dev/null +++ b/src/middleware/cors.ts @@ -0,0 +1,9 @@ +import { AxleTypes } from '../index'; + +// Built-in AxleMiddleware Options +export default function cors(): AxleTypes.AxleOptions { + return { + mode: 'cors', + cache: 'default', + }; +} diff --git a/src/middleware/kneepads.ts b/src/middleware/kneepads.ts new file mode 100644 index 0000000..3e0690f --- /dev/null +++ b/src/middleware/kneepads.ts @@ -0,0 +1,10 @@ +import { AxleTypes } from '../index'; + +// middleware options for protecting against accidentally sending credentials +export default function kneepads(): AxleTypes.AxleOptions { + return { + credentials: 'omit', + referrer: '', + referrerPolicy: 'no-referrer', + }; +} diff --git a/src/middleware/timeTook.ts b/src/middleware/timeTook.ts new file mode 100644 index 0000000..961e3f0 --- /dev/null +++ b/src/middleware/timeTook.ts @@ -0,0 +1,10 @@ +import { AxleTypes } from '../index'; +import AxleRequest from '../models/request'; +import AxleResponse from '../models/response'; + +// Built-in AxleMiddleware +export default function timeTook(): AxleTypes.AxleMiddleware { + return (req: AxleRequest, res: AxleResponse) => { + console.log('Time Took: ' + res.timeTook + 'ms'); + }; +} diff --git a/src/models/headers.ts b/src/models/headers.ts index db31786..3207269 100644 --- a/src/models/headers.ts +++ b/src/models/headers.ts @@ -1,3 +1,4 @@ +// Class extended from Fetch Headers, has more features to help make headers simpler and easier export default class AxleHeaders { private headers: Headers; @@ -103,10 +104,27 @@ export default class AxleHeaders { return [...this.values()]; } + // converts to object public object() { return Object.fromEntries(this.entries); } + public is(name: string, value: string) { + if (this.has(name)) { + return this.get(name) === value; + } else { + return null; + } + } + + public includesValue(name: string, value: string) { + if (this.has(name)) { + return this.get(name)?.includes(value) || false; + } else { + return null; + } + } + public get basic() { return { ...this.headers }; } diff --git a/src/models/request.ts b/src/models/request.ts index 8b82b49..bbd68a2 100644 --- a/src/models/request.ts +++ b/src/models/request.ts @@ -1,13 +1,18 @@ +/* Custom Request class / for code reusability */ + +import { __getMiddleware } from '../core/use'; +import { __getMiddlewareOptions } from '../core/useOptions'; import { AxleTypes } from '../index'; import handleStatus from '../utils/handleStatus'; +import isJSON from '../utils/isJSON'; import AxleResponse from './response'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export default class AxleRequest> { - private method: string; - private url: string; - private body: t | undefined | null; - private options: AxleTypes.AxleOptions; + public method: string; + public url: string; + public body: t | FormData | undefined | null; + public options: AxleTypes.AxleOptions; public response: AxleResponse | undefined; @@ -25,19 +30,127 @@ export default class AxleRequest> { this.response; } - public async run() { - const res = await fetch(this.url, { - ...this.options, - method: this.method, - body: JSON.stringify(this.body), + // Fetches, returns Promise + public async run(): Promise { + // get any middleware options + + // merge into one object + let middlewareOptions: AxleTypes.AxleOptions = {}; + __getMiddlewareOptions().forEach((option) => { + middlewareOptions = { ...middlewareOptions, ...option }; }); - if (handleStatus(this.method, res.status, res.statusText)) { - this.response = new AxleResponse(res); - return this.response; + // fetch body + let fetchBody: t | FormData | string | undefined | null = this.body; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (isJSON(fetchBody as Record)) { + fetchBody = JSON.stringify(this.body) as string; + } else { + fetchBody = this.body as FormData; } - this.response = new AxleResponse(res); - return this.response; + // fetch options + const fetchOptions = { + ...this.options, + method: this.method, + body: fetchBody, + ...middlewareOptions, + }; + + const timeStart = performance.now(); + + // fetch + const res = await fetch(this.url, fetchOptions); + + const timeEnd = performance.now(); + + // validate status + // check if user passed in a way to handle statuses + if (fetchOptions.handleStatus) { + // TRUE = ERROR + if (fetchOptions.handleStatus(res.status, res.statusText)) { + // check if statusText is empty + if (res.statusText.trim() !== '') { + this.response = new AxleResponse(res, timeStart, timeEnd); + + console.error( + `${this.method.toUpperCase()}: Fetch returned with error code ${ + res.status + }. Status Message: "${res.statusText}"` + ); + + // reject + return Promise.reject({ + status: res.status, + message: `${this.method.toUpperCase()}: Fetch returned with error code ${ + res.status + }. Status Message: "${res.statusText}"`, + response: this.response, + request: this, + }); + } else { + this.response = new AxleResponse(res, timeStart, timeEnd); + + console.error( + `${this.method.toUpperCase()}: Fetch returned with error code ${ + res.status + }.` + ); + + // reject + return Promise.reject({ + status: res.status, + message: `${this.method.toUpperCase()}: Fetch returned with error code ${ + res.status + }.`, + response: this.response, + request: this, + }); + } + } else { + // NO ERROR + // normal w/ user passed status check + this.response = new AxleResponse(res, timeStart, timeEnd); + + // run middleware + __getMiddleware().forEach((cb) => { + cb(this, this.response as AxleResponse); + }); + + return this.response; + } + } else { + // check if status is 400+, throws error and rejects promise if so + if (handleStatus(this.method, res.status, res.statusText)) { + this.response = new AxleResponse(res, timeStart, timeEnd); + + console.error( + handleStatus(this.method, res.status, res.statusText) + ); + + // reason is returned from handleStatus if not false + return Promise.reject({ + status: res.status, + message: handleStatus( + this.method, + res.status, + res.statusText + ), + response: this.response, + request: this, + }); + } else { + // normal + this.response = new AxleResponse(res, timeStart, timeEnd); + + // run middleware + __getMiddleware().forEach((cb) => { + cb(this, this.response as AxleResponse); + }); + + return this.response; + } + } } } diff --git a/src/models/response.ts b/src/models/response.ts index 7ab019f..12b3ab1 100644 --- a/src/models/response.ts +++ b/src/models/response.ts @@ -4,11 +4,16 @@ import getQuery from '../utils/getQuery'; import queryParam from '../utils/queryParam'; import AxleHeaders from './headers'; +// custom, extended Response class, extended from Fetch Response export default class AxleResponse { private res: Response; + private timeStart: number; + private timeEnd: number; - constructor(res: Response) { + constructor(res: Response, timeStart: number, timeEnd: number) { this.res = res; + this.timeStart = timeStart; + this.timeEnd = timeEnd; } public async json>(): Promise { @@ -22,7 +27,11 @@ export default class AxleResponse { return text; } - public get body() { + public async body() { + return (await this.res.body?.getReader().read())?.value; + } + + public get bodyReader() { return this.res.body; } @@ -40,6 +49,7 @@ export default class AxleResponse { } } + // automatically redirect public finishRedirect() { if (this.res.headers.has('Location')) { const url = this.res.headers.get('Location'); @@ -89,6 +99,10 @@ export default class AxleResponse { return new AxleHeaders(this.res.headers); } + public get timeTook() { + return this.timeEnd - this.timeStart; + } + public get type() { return this.res.type; } diff --git a/src/utils/getQuery.ts b/src/utils/getQuery.ts index 0b9d1c0..c703645 100644 --- a/src/utils/getQuery.ts +++ b/src/utils/getQuery.ts @@ -1,4 +1,5 @@ export default function getQuery(url: string) { + // gets the search query from url const urlSearchParams = new URLSearchParams(url); const params = Object.fromEntries(urlSearchParams.entries()); diff --git a/src/utils/handleStatus.ts b/src/utils/handleStatus.ts index bf071a8..f7b365f 100644 --- a/src/utils/handleStatus.ts +++ b/src/utils/handleStatus.ts @@ -3,15 +3,14 @@ export default function handleStatus( status: number, statusMessage?: string ) { + // throw error if status is 400+ + // returns error message if true, returns false if false if (status >= 400) { if (statusMessage) { - console.error( - `${method.toUpperCase()}: Fetch return with error code ${status}. Status Message: "${statusMessage}"` - ); + return `${method.toUpperCase()}: Fetch returned with error code ${status}. Status Message: "${statusMessage}"`; } else { - console.error(`Fetch return with error ${status}`); + return `Fetch returned with error ${status}`; } - return true; } else { return false; } diff --git a/src/utils/isJSON.ts b/src/utils/isJSON.ts new file mode 100644 index 0000000..0969ba3 --- /dev/null +++ b/src/utils/isJSON.ts @@ -0,0 +1,10 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default function isJSON(json: Record) { + try { + const jsonStr = JSON.stringify(json); + JSON.parse(jsonStr); + return true; + } catch (error) { + return false; + } +} diff --git a/src/utils/queryParam.ts b/src/utils/queryParam.ts index bbde61e..2047639 100644 --- a/src/utils/queryParam.ts +++ b/src/utils/queryParam.ts @@ -1,4 +1,5 @@ export default function queryParam(name: string, url: string) { + // gets a search query by name from a url const urlSearchParams = new URLSearchParams(url); if (urlSearchParams.has(name)) {