Skip to content

Commit

Permalink
Merge pull request #7 from Amarok24/relook
Browse files Browse the repository at this point in the history
Relook
  • Loading branch information
Amarok24 authored Feb 11, 2022
2 parents e82f8fe + 6ed79b9 commit aa5fabb
Show file tree
Hide file tree
Showing 16 changed files with 636 additions and 266 deletions.
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ An object-oriented XHR (XMLHttpRequest) wrapper/library.
- Written in TypeScript
- No dependencies
- Promise-based (asynchronous)
- Tiny (3kb minified) and pretty simple
- Ready to use ES module `oxhr.min.js` for your JavaScript projects
- Tiny (under 5kb minified)
- Robust and simple
- An alternative to _XMLHttpRequest_ and _fetch_
- An alternative to feature-rich libraries like [_axios_](https://github.com/axios/axios)
- A small demo included, see `index.html`

A small demo is included, see `index.html`.

If you are not familiar with ES modules have a look [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules).

Expand All @@ -26,15 +27,14 @@ __Import modules and create a new instance in TypeScript__

```ts
import { Oxhr } from "./oxhr.js";
import type { IOxhrParams, IRequestHeader } from "./oxhr_types.js";
import type { IOxhrParams, IRequestHeader } from "./oxhr-types.js";

async function fetchDataExample()
{
let response: any;
const options: IOxhrParams = {
url: "https://...",
consoleInfo: "Establishing my connection...",
onLoadEnd: () => { console.log("Loading finished!") }
onLoadEnd: () => { alert("Finished!") }
};

// The shortest possible call if you don't care about the return type.
Expand Down Expand Up @@ -76,7 +76,7 @@ interface IOxhrParams
| data | Data to send | - | null | Blob, BufferSource, FormData, URLSearchParams, ReadableStream, string, Document, null |
| requestHeaders | Array of IRequestHeader | - | | IRequestHeader[] |
| timeoutMs | Timeout in milliseconds | - | 60'000 | number |
| consoleInfo | Description for console output | - | | string |
| consoleInfo | Description for console output after loading is done. | - | | string |
| debug | Additional console output | - | false | boolean |


Expand All @@ -99,24 +99,35 @@ Please note that progress in % must not always work because it depends on server
| abort | Aborts an open connection | -- |


### Properties (read-only)

| Name | Description |
| :-- | :-- |
| instanceId | A unique ID of the Oxhr instance (UUID). |
| readyState | The XHR readyState code. |
| status | The XHR status code. |
| success | Returns _true_ if transfer was successful (readyState is DONE and status is OK). |

There is also a simple getter for reading the "readyState" of current XMLHttpRequest connection.

## FAQs

__How can I stop a running connection?__
- Very easily, you don't need special constructs like the `AbortController()` in Fetch API. In Oxhr you simply call the `abort` method. See the included demo.
- Very easily, you don't need the `AbortController()` object which is needed for the Fetch API. In Oxhr you simply call the `abort` method. See the included demo.

__Can I open multiple connections at once?__
- Yes and no. It's not possible using one instance of Oxhr, but you can create multiple (independent) instances. Since Oxhr works with EcmaScript's Promise object you can also make use of Promise.all(), Promise.any() etc. Additionally, you can start multiple "await ... send()" requests using one class instance, they will be handled one after another.
- Yes and no. It's not possible using one instance of Oxhr, but you can create multiple (independent) instances (each instance will have its own unique UUID). Since Oxhr works with EcmaScript's Promise object you can also make use of Promise.all(), Promise.any() etc. Additionally, you can start multiple "await ... send()" requests using one class instance, they will be handled one after another.


## Changelog
### _v1.2.0_
- Previous version didn't compile in TypeScript 4.5.4 because of an error, this update fixes it
- Added new "debug" parameter for more verbose console log
- Bugfix for custom onLoadEnd method which didn't get triggered in TypeScript 4.5.4
- Demo in index.html changed to se an interface for incoming JSON data
- Other minor code changes and cleanup
- Several small bugfixes, one big bugfix for custom onLoadEnd method which didn't get triggered in TypeScript 4.5.4
- Added 3 new getters: instanceId, status, success
- Demo in index.html changed significantly
- Many code improvements, now the library is more robust and handles all kind of wrong usage in a nice way
- Major code refactoring

---

Expand Down
65 changes: 44 additions & 21 deletions dist/demo.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Oxhr } from './oxhr.js';
import { ResourcesType } from './swapi-schema.js';
import { OxhrError } from './oxhr-error.js';
import { XhrReadyState } from './xhr-codes.js';
const startButton = document.querySelector('#startButton');
const abortButton = document.querySelector('#abortButton');
const swButton = document.querySelector('#swButton');
Expand All @@ -8,21 +10,24 @@ const loadBytes = document.querySelector('#loadBytes');
async function fetchRandomStarWarsData() {
let peopleResponse;
const random = Math.floor(Math.random() * 10 + 1);
const mySimpleOptions = {
const swOptions = {
url: `https://swapi.dev/api/${ResourcesType.People}/${random}`,
consoleInfo: 'Establishing my simple test connection...',
consoleInfo: 'StarWars connection finished, some details below.',
onLoadEnd: () => {
console.info('Opening browser pop-up message with character data...');
alert(JSON.stringify(peopleResponse));
},
responseType: 'json',
debug: true
debug: false
};
const mySimpleConnection = new Oxhr(mySimpleOptions);
;
peopleResponse = await mySimpleConnection.send();
const mySwConnection = new Oxhr(swOptions);
console.log(`mySWConnection.instanceId = ${mySwConnection.instanceId}`);
peopleResponse = await mySwConnection.send();
console.log(`Character name: ${peopleResponse.name}`);
console.log(peopleResponse);
console.log(`mySwConnection.readyState = ${mySwConnection.readyState}`);
if (mySwConnection.success)
console.log('Success!');
}
fetchRandomStarWarsData();
const myRequestHeaders = [];
Expand All @@ -31,8 +36,9 @@ const myOptions = {
method: 'GET',
responseType: 'json',
requestHeaders: myRequestHeaders,
timeoutMs: 7000,
consoleInfo: 'Establishing my test connection...',
timeoutMs: 20000,
debug: true,
consoleInfo: 'My test connection finished, some details below.',
onLoadEnd: onLoadEnd,
onTimeOut: onTimeOut,
onProgress: onProgress,
Expand All @@ -43,9 +49,16 @@ async function tryToSendData() {
let response;
const myData = `{ "test": 123 }`;
try {
console.log('AWAITING DATA');
console.log('try-block of tryToSendData');
console.log(`myConnection.instanceId = ${myConnection.instanceId}`);
console.log(`XHR status of myConnection is ${myConnection.status}`);
if ((myConnection.readyState !== XhrReadyState.DONE) && (myConnection.readyState !== XhrReadyState.UNSENT)) {
console.log('You have probably clicked on that button while a connection is being processed.');
return;
}
console.log('Now we will await myConnection.send');
response = await myConnection.send(myData);
console.log('ALL DATA RECEIVED');
console.log('await myConnection.send is DONE, all data received!');
if (response && response.someFixedResponseData === 12345) {
console.log('We got someFixedResponseData 12345');
}
Expand All @@ -54,25 +67,35 @@ async function tryToSendData() {
}
}
catch (e) {
if (!(e instanceof Error))
if (!(e instanceof OxhrError))
throw e;
console.log(`An error occured, but we handled it. Error message: ${e.message}`);
console.log(`Oxhr error message: ${e.message}`);
console.log(e);
}
finally {
console.log(`finally-block, XHR status of myConnection is ${myConnection.status}`);
}
}
function onProgress(percent, bytes) {
if (loadProgress)
loadProgress.value = percent;
if (loadBytes)
loadBytes.innerText = bytes + " bytes";
loadBytes.innerText = bytes + ' bytes';
}
function onAbort() {
console.log("demo: custom abort handler");
console.log('demo: custom abort handler');
alert('Connection aborted by user');
}
function onLoadEnd() {
console.log(`demo: custom loadend handler, readyState = ${myConnection.readyState}`);
console.log(`XHR status of myConnection is ${myConnection.status}`);
if (myConnection.success) {
alert('Success!');
}
}
function onTimeOut() {
console.log("demo: custom timeout handler");
console.log('demo: custom timeout handler');
alert('Connection time out!');
}
function handleStartButtonClick() {
console.log(`start: handleStartButtonClick readyState = ${myConnection.readyState}`);
Expand All @@ -81,16 +104,16 @@ function handleStartButtonClick() {
if (loadBytes)
loadBytes.innerText = "0 bytes";
tryToSendData();
console.log("end: handleStartButtonClick");
console.log('end: handleStartButtonClick');
}
function handleAbortButtonClick() {
console.log("start: handleAbortButtonClick");
console.log('start: handleAbortButtonClick');
myConnection.abort();
console.log("end: handleAbortButtonClick");
console.log('end: handleAbortButtonClick');
}
if (startButton)
startButton.addEventListener("click", handleStartButtonClick);
startButton.addEventListener('click', handleStartButtonClick);
if (abortButton)
abortButton.addEventListener("click", handleAbortButtonClick);
abortButton.addEventListener('click', handleAbortButtonClick);
if (swButton)
swButton.addEventListener("click", fetchRandomStarWarsData);
swButton.addEventListener('click', fetchRandomStarWarsData);
13 changes: 13 additions & 0 deletions dist/generate-uuid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export { generateUUID };
function generateUUID() {
let cryptoRef = null;
let r = '';
if (typeof self.crypto !== 'undefined') {
cryptoRef = self.crypto;
r = cryptoRef.randomUUID?.();
}
if (!r) {
r = 'Crypto API or randomUUID method not supported.';
}
return r;
}
7 changes: 7 additions & 0 deletions dist/oxhr-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { OxhrError };
class OxhrError extends Error {
constructor(message) {
super(message);
this.name = 'OxhrError';
}
}
1 change: 0 additions & 1 deletion dist/oxhr.min.js

This file was deleted.

26 changes: 26 additions & 0 deletions dist/xhr-codes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export var XhrReadyState;
(function (XhrReadyState) {
XhrReadyState[XhrReadyState["UNSENT"] = 0] = "UNSENT";
XhrReadyState[XhrReadyState["OPENED"] = 1] = "OPENED";
XhrReadyState[XhrReadyState["HEADERS_RECEIVED"] = 2] = "HEADERS_RECEIVED";
XhrReadyState[XhrReadyState["LOADING"] = 3] = "LOADING";
XhrReadyState[XhrReadyState["DONE"] = 4] = "DONE";
})(XhrReadyState || (XhrReadyState = {}));
export var XhrStatus;
(function (XhrStatus) {
XhrStatus[XhrStatus["UNSENT"] = 0] = "UNSENT";
XhrStatus[XhrStatus["CONTINUE"] = 100] = "CONTINUE";
XhrStatus[XhrStatus["OK"] = 200] = "OK";
XhrStatus[XhrStatus["LOADING"] = 200] = "LOADING";
XhrStatus[XhrStatus["MULTIPLE_CHOICE"] = 300] = "MULTIPLE_CHOICE";
XhrStatus[XhrStatus["BAD_REQUEST"] = 400] = "BAD_REQUEST";
XhrStatus[XhrStatus["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
XhrStatus[XhrStatus["FORBIDDEN"] = 403] = "FORBIDDEN";
XhrStatus[XhrStatus["NOT_FOUND"] = 404] = "NOT_FOUND";
XhrStatus[XhrStatus["NOT_ALLOWED"] = 405] = "NOT_ALLOWED";
XhrStatus[XhrStatus["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
XhrStatus[XhrStatus["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
XhrStatus[XhrStatus["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
XhrStatus[XhrStatus["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
XhrStatus[XhrStatus["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
})(XhrStatus || (XhrStatus = {}));
Loading

0 comments on commit aa5fabb

Please sign in to comment.