From 119cd6e0af2e37822f0a635cf53429c5f3521422 Mon Sep 17 00:00:00 2001 From: Miroslav Popov <info@forexsb.com> Date: Sun, 21 Apr 2024 19:41:20 +0300 Subject: [PATCH] Major project reorganization. --- .gitignore | 2 + HttpRequest.ts | 122 ---------------------------- README.md | 81 ++++++++++++++----- index.html | 110 ++++++++++++++++++++++++++ index.js | 193 +++++++++++++++++++++++++++++++++++++++++++++ index.js.map | 1 + index.tsbuildinfo | 1 + lib/Application.ts | 99 +++++++++++++++++++++++ lib/HttpRequest.ts | 144 +++++++++++++++++++++++++++++++++ package-lock.json | 28 +++++++ package.json | 17 ++++ tsconfig.json | 13 +++ 12 files changed, 670 insertions(+), 141 deletions(-) create mode 100644 .gitignore delete mode 100644 HttpRequest.ts create mode 100644 index.html create mode 100644 index.js create mode 100644 index.js.map create mode 100644 index.tsbuildinfo create mode 100644 lib/Application.ts create mode 100644 lib/HttpRequest.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb79dd5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +.idea diff --git a/HttpRequest.ts b/HttpRequest.ts deleted file mode 100644 index 11a6de5..0000000 --- a/HttpRequest.ts +++ /dev/null @@ -1,122 +0,0 @@ -/** - * HttpRequest - * - * A simple XMLHttpRequest helper - * https://github.com/PopovMP/http-request - * - * Copyright @ 2022 Miroslav Popov - * - * v1.1 2023.06.11 - */ - -interface HttpRequestOptions { - headers : object - responseType: XMLHttpRequestResponseType -} - -interface HttpRequestResponse { - readyState : number - response : ArrayBuffer | Blob | Document | Object | string | undefined - responseText: string | undefined - responseType: XMLHttpRequestResponseType - responseURL : string - status : number - statusText : string - headers : Record<string, string> -} - -type HttpRequestCallback = (res: HttpRequestResponse) => void - -/** - * Provides get and post methods - * @class HttpRequest - */ -class HttpRequest { - - /** - * Make a GET request - */ - public static get(url: string, options: HttpRequestOptions, callback: HttpRequestCallback): void { - HttpRequest.request("GET", url, null, options, callback); - } - - /** - * Make a POST request - */ - public static post(url: string, body: any, options: HttpRequestOptions, callback: HttpRequestCallback): void { - HttpRequest.request("POST", url, body, options, callback); - } - - /** - * Make a request - */ - private static request(method: string, url: string, body: any, options: HttpRequestOptions, - callback: HttpRequestCallback): void { - const req: XMLHttpRequest = new XMLHttpRequest(); - - req.open(method, url, true); - - if (typeof options.headers === "object") { - for (const name of Object.keys(options.headers)) { - // @ts-ignore - req.setRequestHeader(name, options.headers[name]); - } - } - - if (typeof options.responseType === "string") { - req.responseType = options.responseType; - } - - req.onreadystatechange = HttpRequest.req_readyStateChange.bind(this, req, callback); - req.onerror = HttpRequest.req_error.bind(this, callback); - req.send(body); - } - - /** - * Handles XMLHttpRequest :: onreadystatechange - * Calls the callback when the XMLHttpRequest is DONE - */ - static req_readyStateChange(req: XMLHttpRequest, callback: HttpRequestCallback): void { - if (req.readyState !== XMLHttpRequest.DONE) { - return; - } - - const headers: {[header: string]: string} = {}; - const reqResponseHeaders: string[] = req.getAllResponseHeaders().trim().split(/[\r\n]+/); - for (const header of reqResponseHeaders) { - const parts: string[] = header.split(": "); - // @ts-ignore - headers[parts[0]] = parts[1]; - } - - const isResponseText: boolean = req.responseType === "text" || req.responseType === ""; - - callback({ - readyState : req.readyState, - response : isResponseText ? undefined : req.response, - responseText: isResponseText ? req.responseText : undefined, - responseType: req.responseType, - responseURL : req.responseURL, - status : req.status, - statusText : req.statusText, - headers : headers, - }); - } - - /** - * Handles XMLHttpRequest :: onerror - * Calls the callback when the XMLHttpRequest rises an error - */ - static req_error(callback: HttpRequestCallback): void { - callback({ - readyState : 4, - response : undefined, - responseText: undefined, - responseType: "", - responseURL : "", - status : 400, - statusText : "An error occurred during the transaction", - headers : {}, - }); - } -} diff --git a/README.md b/README.md index d2b9594..81e907d 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,77 @@ # HttpRequest -Simple HTTP Request helper for the browser +Simple HTTP Request helper for the browser. -## Get JSON +HttpRequest provides three methods: `get`, `post`, and `form`. + +## Acquire + +Get the `HttpRequest` class in TypeScript from the `lib` subdirectory. + +Get `HttpRequest` compiled to a JavaScript class from the `application.js` file. + + +## Tests + +See examples and tests: https://popovmp.github.io/http-request/ + +## Get Text ```TypeScript -const url = "https://example.com/foo.json"; -const oprtions = {responseType: "json"}; +const url:string = "https://httpbin.org/get"; +const options: HttpRequestOptions = {}; +HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + console.log(JSON.stringify(res)); +}); +``` -HttpRequest.get(url, options, (res: HttpRequestResponse) => { - const foo = res.response; -}) +## Get Json + +```TypeScript +const url:string = "https://httpbin.org/get"; +const options: HttpRequestOptions = {responseType: "json"}; +HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + console.log(JSON.stringify(res)); +}); ``` + ## Get binary data ```TypeScript const url = "https://exmaple.com/buffer.bin"; -const options = { - headers : {}, - responseType: "arraybuffer" as XMLHttpRequestResponseType, -}; +const options = {headers: {}, responseType: "arraybuffer"}; HttpRequest.get(url, options, (res: HttpRequestResponse) => { - if ( (res.status === 200 || res.status === 304) && res.response !== undefined) { - const buffer: ArrayBuffer = res.response; - // do somethign with the buffer - } - else { - console.error(`Status: ${res.status}`); - } -}) + if ((res.status === 200 || res.status === 304) && res.response !== undefined) { + const buffer: ArrayBuffer = res.response; + // Do somethign with the buffer + } + else { + console.error(`Status: ${res.status}`); + } +}); +``` + +## Post JSON + +```TypeScript +const url = "https://exmaple.com/api/user"; +const body = {username: "John", email: "john@example.com", pasword: "12343"}; +const options = {headers: {}, responseType: "json"}; + +HttpRequest.post(url, body, options, (res: HttpRequestResponse) => { + const user: User = res.response; +}); +``` + +## Post Form + +```TypeScript + const url: string = "https://httpbin.org/post"; +const options: HttpRequestOptions = {responseType: "json"}; +const form: {[param: string]: string|number} = {foo: "bar", baz: 42}; +HttpRequest.form(url, form, options, (res: HttpRequestResponse): void => { + console.log(JSON.stringify(res)); +}); ``` diff --git a/index.html b/index.html new file mode 100644 index 0000000..f7acf44 --- /dev/null +++ b/index.html @@ -0,0 +1,110 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>HttpRequest Lib</title> +</head> +<body> + +<h1>HttpRequest Test Page</h1> + +<p>Test requests against <a href="https://httpbin.org/">https://httpbin.org/</a></p> + +<h2>GET Text</h2> +<pre> +const url:string = "https://httpbin.org/get"; +const options: HttpRequestOptions = {}; +HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); +}); +</pre> +<button type="button" id="get-text-btn">GET JSON</button> +<pre id="get-text-res"></pre> + + +<h2>GET JSON</h2> +<pre> +const url:string = "https://httpbin.org/get"; +const options: HttpRequestOptions = {responseType: "json"}; +HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); +}); +</pre> +<button type="button" id="get-json-btn">GET JSON</button> +<pre id="get-json-res"></pre> + + +<h2>POST JSON Text</h2> +<h3>Post JSON object as a string</h3> +<pre> +const url: string = "https://httpbin.org/post"; +const options: HttpRequestOptions = {responseType: "json", headers: {"X-Custom": "custom"}}; +const body: any = {foo: "bar", baz: 42}; +const bodyTxt: string = JSON.stringify(body); +HttpRequest.post(url, bodyTxt, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); +}); +</pre> +<button type="button" id="post-json-text-btn">POST JSON</button> +<pre id="post-json-text-res"></pre> + + +<h2>POST JSON</h2> +<h3>Post JSON</h3> +<pre> +const url: string = "https://httpbin.org/anything"; +const options: HttpRequestOptions = {responseType: "json", headers: {"Content-Type": "application/json"}}; +const body: any = {foo: "bar", baz: 42}; +HttpRequest.post(url, body, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); +}); +</pre> +<button type="button" id="post-json-btn">POST JSON</button> +<pre id="post-json-res"></pre> + + +<h2>POST Form</h2> +<h3>Post form</h3> +<pre> +const url: string = "https://httpbin.org/post"; +const options: HttpRequestOptions = {responseType: "json"}; +const form: {[param: string]: string|number} = {foo: "bar", baz: 42}; +HttpRequest.form(url, form, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); +}); +</pre> +<button type="button" id="post-form-btn">POST Form</button> +<pre id="post-form-res"></pre> + + +<h2>Timeout</h2> +<pre> +const url: string = "https://httpbin.org/delay/10"; +const options: HttpRequestOptions = {timeout: 5 * 1000}; +HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); +}); +</pre> +<button type="button" id="post-timeout-btn">POST Form</button> +<pre id="post-timeout-res"></pre> + + +<h2>Resources not found (404)</h2> +<pre> +const url: string = "https://httpbin.org/status/404"; +const options: HttpRequestOptions = {}; +HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); +}); +</pre> +<button type="button" id="post-404-btn">POST Form</button> +<pre id="post-404-res"></pre> + + +<script src="index.js"></script> +<script> + const app = new Application(); + app.initialize(); +</script> +</body> +</html> diff --git a/index.js b/index.js new file mode 100644 index 0000000..a57f3cb --- /dev/null +++ b/index.js @@ -0,0 +1,193 @@ +"use strict"; +class Application { + initialize() { + document.getElementById("get-text-btn") + .addEventListener("click", this.getTextTest.bind(this)); + document.getElementById("get-json-btn") + .addEventListener("click", this.getJsonTest.bind(this)); + document.getElementById("post-json-text-btn") + .addEventListener("click", this.postJsonTextTest.bind(this)); + document.getElementById("post-json-btn") + .addEventListener("click", this.postJsonTest.bind(this)); + document.getElementById("post-form-btn") + .addEventListener("click", this.postFormTest.bind(this)); + document.getElementById("post-timeout-btn") + .addEventListener("click", this.postTimeoutTest.bind(this)); + document.getElementById("post-404-btn") + .addEventListener("click", this.post404Test.bind(this)); + } + getTextTest(event) { + event.preventDefault(); + const resField = document.getElementById("get-text-res"); + const url = "https://httpbin.org/get"; + const options = {}; + HttpRequest.get(url, options, (res) => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + getJsonTest(event) { + event.preventDefault(); + const resField = document.getElementById("get-json-res"); + const url = "https://httpbin.org/get"; + const options = { responseType: "json" }; + HttpRequest.get(url, options, (res) => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + postJsonTextTest(event) { + event.preventDefault(); + const resField = document.getElementById("post-json-text-res"); + const url = "https://httpbin.org/post"; + const options = { responseType: "json", headers: { "X-Custom": "custom" } }; + const body = { foo: "bar", baz: 42 }; + const bodyTxt = JSON.stringify(body); + HttpRequest.post(url, bodyTxt, options, (res) => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + postJsonTest(event) { + event.preventDefault(); + const resField = document.getElementById("post-json-res"); + const url = "https://httpbin.org/anything"; + const options = { responseType: "json", headers: { "Content-Type": "application/json" } }; + const body = { foo: "bar", baz: 42 }; + HttpRequest.post(url, body, options, (res) => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + postFormTest(event) { + event.preventDefault(); + const resField = document.getElementById("post-form-res"); + const url = "https://httpbin.org/post"; + const options = { responseType: "json" }; + const form = { foo: "bar", baz: 42 }; + HttpRequest.form(url, form, options, (res) => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + postTimeoutTest(event) { + event.preventDefault(); + const resField = document.getElementById("post-timeout-res"); + const url = "https://httpbin.org/delay/10"; + const options = { timeout: 5 * 1000 }; + HttpRequest.get(url, options, (res) => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + post404Test(event) { + event.preventDefault(); + const resField = document.getElementById("post-404-res"); + const url = "https://httpbin.org/status/404"; + const options = {}; + HttpRequest.get(url, options, (res) => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } +} +/** + * HttpRequest + * + * A simple XMLHttpRequest helper + * https://github.com/PopovMP/http-request + * + * Copyright @ 2024 Miroslav Popov + * + * v1.4 2024.04.21 + */ +/** + * Provides `get` and `post` methods + * @class HttpRequest + */ +class HttpRequest { + /** + * Make a GET request + */ + static get(url, options, callback) { + HttpRequest.request("GET", url, null, options, callback); + } + /** + * Make a POST request + */ + static post(url, body, options, callback) { + HttpRequest.request("POST", url, body, options, callback); + } + /** + * Make POST request encoded as "application/x-www-form-urlencoded" + */ + static form(url, formData, options, callback) { + const parameters = []; + for (const param of Object.keys(formData)) + parameters.push(`${param}=${encodeURIComponent(formData[param])}`); + const body = parameters.join("&"); + if (!options.headers) + options.headers = {}; + options.headers["Content-Type"] = "application/x-www-form-urlencoded"; + HttpRequest.request("POST", url, body, options, callback); + } + /** + * Make a request + */ + static request(method, url, body, options, callback) { + let isCompleted = false; + const req = new XMLHttpRequest(); + req.open(method, url, true); + if (typeof options.headers === "object") { + for (const name of Object.keys(options.headers)) + req.setRequestHeader(name, options.headers[name]); + } + req.timeout = typeof options.timeout === "number" ? options.timeout : 20 * 1000; + req.responseType = options.responseType || ""; + req.onreadystatechange = req_readyStateChange; + req.onerror = req_error; + req.ontimeout = req_timeout; + req.onabort = req_abort; + req.send(body); + function req_readyStateChange() { + if (req.readyState !== XMLHttpRequest.DONE || req.status === 0) + return; + if (isCompleted) + return; + const headers = {}; + const resHeaders = req.getAllResponseHeaders().trim().split(/[\r\n]+/); + for (const header of resHeaders) { + const parts = header.split(": "); + headers[parts[0]] = parts[1]; + } + const isResponseText = req.responseType === "text" || req.responseType === ""; + isCompleted = true; + callback({ + response: isResponseText ? undefined : req.response, + responseText: isResponseText ? req.responseText : undefined, + responseType: req.responseType, + responseURL: req.responseURL, + status: req.status, + statusText: req.statusText, + headers: headers, + }); + } + function req_abort() { + resError("Request aborted"); + } + function req_timeout() { + resError("Request timeout"); + } + function req_error() { + resError("An error occurred during the transaction"); + } + function resError(message) { + if (isCompleted) + return; + isCompleted = true; + callback({ + response: undefined, + responseText: undefined, + responseType: req.responseType, + responseURL: url, + status: req.status, + statusText: message, + headers: {}, + }); + } + } +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/index.js.map b/index.js.map new file mode 100644 index 0000000..67f38d9 --- /dev/null +++ b/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["lib/Application.ts","lib/HttpRequest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/index.tsbuildinfo b/index.tsbuildinfo new file mode 100644 index 0000000..1407f3c --- /dev/null +++ b/index.tsbuildinfo @@ -0,0 +1 @@ +{"bundle":{"commonSourceDirectory":"./lib","sourceFiles":["./lib/Application.ts","./lib/HttpRequest.ts"],"js":{"sections":[{"pos":0,"end":13,"kind":"prologue","data":"use strict"},{"pos":14,"end":7202,"kind":"text"}],"sources":{"prologues":[{"file":0,"text":"","directives":[{"pos":-1,"end":-1,"expression":{"pos":-1,"end":-1,"text":"use strict"}}]}]},"mapHash":"988bcaea8df7201dc98cc185d7d5754fc42fab9d9b06c6ab682cef0c3f49b3df","hash":"8d6c5e3e22301c10ac79710470f473ba2a5cce64d82cbf5720d30adeab30c424"}},"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./lib/application.ts","./lib/httprequest.ts"],"fileInfos":["824cb491a40f7e8fdeb56f1df5edf91b23f3e3ee6b4cde84d4a99be32338faee","45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","9a68c0c07ae2fa71b44384a839b7b8d81662a236d4b9ac30916718f7510b1b2d","5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","5514e54f17d6d74ecefedc73c504eadffdeda79c7ea205cf9febead32d45c4bc","87d693a4920d794a73384b3c779cadcb8548ac6945aa7a925832fe2418c9527a","138fb588d26538783b78d1e3b2c2cc12d55840b97bf5e08bca7f7a174fbe2f17","dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","4443e68b35f3332f753eacc66a04ac1d2053b8b035a0e0ac1d455392b5e243b3","bc47685641087c015972a3f072480889f0d6c65515f12bd85222f49a98952ed7","0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","93495ff27b8746f55d19fcbcdbaccc99fd95f19d057aed1bd2c0cafe1335fbf0","6fc23bb8c3965964be8c597310a2878b53a0306edb71d4b5a4dfe760186bcc01","ea011c76963fb15ef1cdd7ce6a6808b46322c527de2077b6cfdf23ae6f5f9ec7","38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","bb42a7797d996412ecdc5b2787720de477103a0b2e53058569069a0e2bae6c7e","4738f2420687fd85629c9efb470793bb753709c2379e5f85bc1815d875ceadcd","2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","9fc46429fbe091ac5ad2608c657201eb68b6f1b8341bd6d670047d32ed0a88fa","61c37c1de663cf4171e1192466e52c7a382afa58da01b1dc75058f032ddf0839","b541a838a13f9234aba650a825393ffc2292dc0fc87681a5d81ef0c96d281e7a","b20fe0eca9a4e405f1a5ae24a2b3290b37cf7f21eba6cbe4fc3fab979237d4f3","811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","49ed889be54031e1044af0ad2c603d627b8bda8b50c1a68435fe85583901d072","e93d098658ce4f0c8a0779e6cab91d0259efb88a318137f686ad76f8410ca270","063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","bf14a426dbbf1022d11bd08d6b8e709a2e9d246f0c6c1032f3b2edb9a902adbe","5e07ed3809d48205d5b985642a59f2eba47c402374a7cf8006b686f79efadcbd","2b72d528b2e2fe3c57889ca7baef5e13a56c957b946906d03767c642f386bbc3","8073890e29d2f46fdbc19b8d6d2eb9ea58db9a2052f8640af20baff9afbc8640","368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","51e547984877a62227042850456de71a5c45e7fe86b7c975c6e68896c86fa23b","956d27abdea9652e8368ce029bb1e0b9174e9678a273529f426df4b3d90abd60","4fa6ed14e98aa80b91f61b9805c653ee82af3502dc21c9da5268d3857772ca05","e6633e05da3ff36e6da2ec170d0d03ccf33de50ca4dc6f5aeecb572cedd162fb","d8670852241d4c6e03f2b89d67497a4bbefe29ecaa5a444e2c11a9b05e6fccc6","8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","caccc56c72713969e1cfe5c3d44e5bab151544d9d2b373d7dbe5a1e4166652be","3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","50d53ccd31f6667aff66e3d62adf948879a3a16f05d89882d1188084ee415bbc","33358442698bb565130f52ba79bfd3d4d484ac85fe33f3cb1759c54d18201393","782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","23a55a365892166010e710a0d48b253bf8b4031a68d5d18bfdd461ca9955db5b","bb73ff13acd5e322caf39bbd813546674150e136994949b5a611adbf47af28c1"],"root":[59,60],"options":{"outFile":"./index.js","sourceMap":true,"strict":true,"target":2}},"version":"5.4.5"} \ No newline at end of file diff --git a/lib/Application.ts b/lib/Application.ts new file mode 100644 index 0000000..9a76b72 --- /dev/null +++ b/lib/Application.ts @@ -0,0 +1,99 @@ +class Application { + public initialize(): void { + (document.getElementById("get-text-btn") as HTMLElement) + .addEventListener("click", this.getTextTest.bind(this)); + (document.getElementById("get-json-btn") as HTMLElement) + .addEventListener("click", this.getJsonTest.bind(this)); + (document.getElementById("post-json-text-btn") as HTMLElement) + .addEventListener("click", this.postJsonTextTest.bind(this)); + (document.getElementById("post-json-btn") as HTMLElement) + .addEventListener("click", this.postJsonTest.bind(this)); + (document.getElementById("post-form-btn") as HTMLElement) + .addEventListener("click", this.postFormTest.bind(this)); + (document.getElementById("post-timeout-btn") as HTMLElement) + .addEventListener("click", this.postTimeoutTest.bind(this)); + (document.getElementById("post-404-btn") as HTMLElement) + .addEventListener("click", this.post404Test.bind(this)); + } + + public getTextTest(event: Event): void { + event.preventDefault(); + const resField: HTMLElement = document.getElementById("get-text-res") as HTMLElement; + + const url:string = "https://httpbin.org/get"; + const options: HttpRequestOptions = {}; + HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + + public getJsonTest(event: Event): void { + event.preventDefault(); + const resField: HTMLElement = document.getElementById("get-json-res") as HTMLElement; + + const url:string = "https://httpbin.org/get"; + const options: HttpRequestOptions = {responseType: "json"}; + HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + + public postJsonTextTest(event: Event): void { + event.preventDefault(); + const resField: HTMLElement = document.getElementById("post-json-text-res") as HTMLElement; + + const url: string = "https://httpbin.org/post"; + const options: HttpRequestOptions = {responseType: "json", headers: {"X-Custom": "custom"}}; + const body: any = {foo: "bar", baz: 42}; + const bodyTxt: string = JSON.stringify(body); + HttpRequest.post(url, bodyTxt, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + + public postJsonTest(event: Event): void { + event.preventDefault(); + const resField: HTMLElement = document.getElementById("post-json-res") as HTMLElement; + + const url: string = "https://httpbin.org/anything"; + const options: HttpRequestOptions = {responseType: "json", headers: {"Content-Type": "application/json"}}; + const body: any = {foo: "bar", baz: 42}; + HttpRequest.post(url, body, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + + public postFormTest(event: Event): void { + event.preventDefault(); + const resField: HTMLElement = document.getElementById("post-form-res") as HTMLElement; + + const url: string = "https://httpbin.org/post"; + const options: HttpRequestOptions = {responseType: "json"}; + const form: {[param: string]: string|number} = {foo: "bar", baz: 42}; + HttpRequest.form(url, form, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + + public postTimeoutTest(event: Event): void { + event.preventDefault(); + const resField: HTMLElement = document.getElementById("post-timeout-res") as HTMLElement; + + const url: string = "https://httpbin.org/delay/10"; + const options: HttpRequestOptions = {timeout: 5 * 1000}; + HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } + + public post404Test(event: Event): void { + event.preventDefault(); + const resField: HTMLElement = document.getElementById("post-404-res") as HTMLElement; + + const url: string = "https://httpbin.org/status/404"; + const options: HttpRequestOptions = {}; + HttpRequest.get(url, options, (res: HttpRequestResponse): void => { + resField.innerText = JSON.stringify(res, null, 2); + }); + } +} diff --git a/lib/HttpRequest.ts b/lib/HttpRequest.ts new file mode 100644 index 0000000..194e135 --- /dev/null +++ b/lib/HttpRequest.ts @@ -0,0 +1,144 @@ +/** + * HttpRequest + * + * A simple XMLHttpRequest helper + * https://github.com/PopovMP/http-request + * + * Copyright @ 2024 Miroslav Popov + * + * v1.4 2024.04.21 + */ + +interface HttpRequestOptions { + headers ?: Record<string, string> + responseType?: "" | "arraybuffer" | "blob" | "document" | "json" | "text" + timeout ?: number // a number of milliseconds +} + +interface HttpRequestResponse { + response : ArrayBuffer | Blob | Document | Object | string | undefined + responseText: string | undefined + responseType: "" | "arraybuffer" | "blob" | "document" | "json" | "text" + responseURL : string + status : number + statusText : string + headers : Record<string, string> +} + +type HttpRequestMethod = "GET" | "POST" | "HEAD" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" +type HttpRequestCallback = (res: HttpRequestResponse) => void + +/** + * Provides `get` and `post` methods + * @class HttpRequest + */ +class HttpRequest { + + /** + * Make a GET request + */ + public static get(url: string, options: HttpRequestOptions, callback: HttpRequestCallback): void { + HttpRequest.request("GET", url, null, options, callback); + } + + /** + * Make a POST request + */ + public static post(url: string, body: any, options: HttpRequestOptions, + callback: HttpRequestCallback): void { + HttpRequest.request("POST", url, body, options, callback); + } + + /** + * Make POST request encoded as "application/x-www-form-urlencoded" + */ + public static form(url: string, formData: Record<string, string|number>, + options: HttpRequestOptions, callback: HttpRequestCallback): void { + + const parameters: string[] = []; + for (const param of Object.keys(formData)) + parameters.push(`${param}=${encodeURIComponent(formData[param])}`); + const body: string = parameters.join("&"); + + if (!options.headers) + options.headers = {}; + options.headers["Content-Type"] = "application/x-www-form-urlencoded"; + + HttpRequest.request("POST", url, body, options, callback); + } + + /** + * Make a request + */ + public static request(method: HttpRequestMethod, url: string, body: any, + options: HttpRequestOptions, callback: HttpRequestCallback): void { + let isCompleted: boolean = false; + + const req: XMLHttpRequest = new XMLHttpRequest(); + req.open(method, url, true); + + if (typeof options.headers === "object") { + for (const name of Object.keys(options.headers)) + req.setRequestHeader(name, options.headers[name]); + } + + req.timeout = typeof options.timeout === "number" ? options.timeout : 20 * 1000; + req.responseType = options.responseType || ""; + req.onreadystatechange = req_readyStateChange; + req.onerror = req_error; + req.ontimeout = req_timeout; + req.onabort = req_abort; + req.send(body); + + function req_readyStateChange(): void { + if (req.readyState !== XMLHttpRequest.DONE || req.status === 0) return; + if (isCompleted) return; + + const headers : Record<string, string> = {}; + const resHeaders: string[] = req.getAllResponseHeaders().trim().split(/[\r\n]+/); + for (const header of resHeaders) { + const parts: string[] = header.split(": "); + headers[parts[0]] = parts[1]; + } + + const isResponseText: boolean = req.responseType === "text" || req.responseType === ""; + + isCompleted = true; + callback({ + response : isResponseText ? undefined : req.response, + responseText: isResponseText ? req.responseText : undefined, + responseType: req.responseType, + responseURL : req.responseURL, + status : req.status, + statusText : req.statusText, + headers : headers, + }); + } + + function req_abort(): void { + resError("Request aborted"); + } + + function req_timeout(): void { + resError("Request timeout"); + } + + function req_error(): void { + resError("An error occurred during the transaction"); + } + + function resError(message: string): void { + if (isCompleted) return; + isCompleted = true; + callback({ + response : undefined, + responseText: undefined, + responseType: req.responseType, + responseURL : url, + status : req.status, + statusText : message, + headers : {}, + }); + } + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0f29a65 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "http-request", + "version": "1.4.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "http-request", + "version": "1.4.0", + "license": "MIT", + "dependencies": { + "typescript": "^5.4.5" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..69f05eb --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "http-request", + "version": "1.4.0", + "description": "Simple HTTP Request helper for the browser", + "main": "index.js", + "directories": { + "lib": "lib" + }, + "dependencies": { + "typescript": "^5.4.5" + }, + "scripts": { + "build": "tsc -p tsconfig.json" + }, + "author": "Popov", + "license": "MIT" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9b13238 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "incremental": true, + "lib": ["ES2022", "DOM"], + "target": "ES6", + "strict": true, + "outFile": "./index.js", + "sourceMap": true + }, + "include": [ + "./lib/*.ts" + ] +}