//################################################################################################
/** @file Networking mixin for ish.js
* @mixin ish.io.net
* @author Nick Campbell
* @license MIT
* @copyright 2014-2023, Nick Campbell
*/ //############################################################################################
/*global module, define, require, Promise */ //# Enable Node globals for JSHint
/*jshint maxcomplexity:9 */ //# Enable max complexity warnings for JSHint
(function () {
'use strict'; //<MIXIN>
function init(core, fetch, FormData) {
var fnNetInterfaceFactory;
//#########
/** Provides an interface that retries up to the passed definition before returning an unsuccessful result.
* @function ish.io.net.!
* @param {object} [oOptions] Value representing the desired options:
* @param {integer|function} [oOptions.retry=500] Value representing the number of milliseconds (1/1000ths of a second) or function called per attempt that returns the number of milliseconds between each call; <code>iWaitMilliseconds = oOptions.retry(iAttemptCount)</code>.
* @param {integer|function} [oOptions.wait=500] Value representing the number of milliseconds (1/1000ths of a second) or function called per attempt that returns the number of milliseconds between each call; <code>iWaitMilliseconds = oOptions.wait(iAttemptCount)</code> (DEPRECATED).
* @param {integer} [oOptions.maxAttempts=5] Value representing the maximum number of attempts before returning an unsuccessful result.
* @returns {object} =interface Value representing the following properties:
* @returns {object} =interface.get {@link: ish.io.net.get}.
* @returns {object} =interface.put {@link: ish.io.net.put}.
* @returns {object} =interface.post {@link: ish.io.net.post}.
* @returns {object} =interface.delete {@link: ish.io.net.delete}.
* @returns {object} =interface.head {@link: ish.io.net.head}.
* @returns {object} =interface.crud {@link: ish.io.net.crud}.
*/ //#####
core.io.net = function (oOptions) {
//# Setup the wrapper for fnNetInterfaceFactory so that we can .partial core.io.net below
return fnNetInterfaceFactory(oOptions);
}; //# core.io.net
//################################################################################################
/** Collection of Networking-based functionality.
* @namespace ish.io.net
* @ignore
*/ //############################################################################################
core.oop.partial(core.io.net, function (oProtected) {
var _undefined /*= undefined*/,
oCache = {}
;
//# Set the fnNetInterfaceFactory into the top-level variable
fnNetInterfaceFactory = function (vBaseOptions) {
return {
//#########
/** Represents the HTTP Request Verbs as a CRUD (Create, Read, Update, Delete) interface.
* @function ish.io.net.crud
* @$asProperty
* @ignore
*/ //#####
crud: {
//#########
/** Calls the passed URL to create a new entity (via <code>PUT</code>).
* @function ish.io.net.crud:create
* @param {string} sURL Value representing the URL to interrogate.
* @param {object} [oBody] Value representing the body of the request.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
create: oProtected.verbs.put(vBaseOptions),
//#########
/** Calls the passed URL to read an entity (via <code>GET</code>).
* @function ish.io.net.crud:read
* @param {string} sURL Value representing the URL to interrogate.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
read: oProtected.verbs.get(vBaseOptions),
//#########
/** Calls the passed URL to update an entity (via <code>POST</code>).
* @function ish.io.net.crud:update
* @param {string} sURL Value representing the URL to interrogate.
* @param {object} [oBody] Value representing the body of the request.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
update: oProtected.verbs.post(vBaseOptions),
//#########
/** Calls the passed URL to remove an entity (via <code>DELETE</code>).
* @function ish.io.net.crud:delete
* @param {string} sURL Value representing the URL to interrogate.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
'delete': oProtected.verbs.delete(vBaseOptions)
}, //# io.net.async.crud
//#########
/** Calls the passed URL via the <code>GET</code> HTTP Verb.
* @function ish.io.net.get
* @param {string} sURL Value representing the URL to interrogate.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
get: oProtected.verbs.get(vBaseOptions),
//#########
/** Calls the passed URL via the <code>PUT</code> HTTP Verb.
* @function ish.io.net.put
* @param {string} sURL Value representing the URL to interrogate.
* @param {object} [oBody] Value representing the body of the request.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
put: oProtected.verbs.put(vBaseOptions),
//#########
/** Calls the passed URL via the <code>POST</code> HTTP Verb.
* @function ish.io.net.post
* @param {string} sURL Value representing the URL to interrogate.
* @param {object} [oBody] Value representing the body of the request.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
post: oProtected.verbs.post(vBaseOptions),
//#########
/** Calls the passed URL via the <code>DELETE</code> HTTP Verb.
* @function ish.io.net.delete
* @param {string} sURL Value representing the URL to interrogate.
* @param {function|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {function} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
'delete': oProtected.verbs.delete(vBaseOptions),
//#########
/** Calls the passed URL via the <code>HEAD</code> HTTP Verb.
* @function ish.io.net.head
* @param {string} sURL Value representing the URL to interrogate.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
head: oProtected.verbs.head(vBaseOptions),
//#########
/** Calls the passed URL simulating a ping via the <code>HEAD</code> HTTP Verb.
* @function ish.io.net.ping
* @param {string} sURL Value representing the URL to interrogate.
* @param {fnIshIoNetCallback|object} [vCallback] Value representing the function to be called when the request returns or the desired options:
* @param {fnIshIoNetCallback} [vCallback.fn] Value representing the function to be called when the request returns; <code>vCallback.fn(bSuccess, oResponse, vArg, $xhr)</code>.
* @param {variant} [vCallback.arg] Value representing the argument that will be passed to the callback function.
* @param {object} [vCallback.headers] Value representing the HTTP headers of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).
* @param {string} [vCallback.mimeType] Value representing the MIME Type of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType|Mozilla.org}).
* @param {string} [vCallback.contentType] Value representing the Content Type HTTP Header of the request (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader|Mozilla.org}).<note>When <code>vCallback.contentType</code> is set, its value will override any value set in <code>vCallback.headers['content-type']</code>.</note>
* @param {string} [vCallback.responseType='text'] Value representing the type of data contained in the response (see: {@link: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType|Mozilla.org}).
* @param {boolean} [vCallback.cache=false] Value representing if the response is to be cached.
* @param {boolean} [vCallback.useCache=false] Value representing if the response is to be sourced from the cache if it's available.<note>When <code>!vCallback.useCache</code>, the HTTP Header <code>Cache-Control</code> is set to <code>no-cache, max-age=0</code>.</note>
*/ //#####
ping: function (sUrl /*, vCallback*/) {
oProtected.verbs.head(vBaseOptions)(sUrl); //, vCallback || function (/* bSuccess, oData, vArg, $xhr */) {});
}
};
}; //# fnNetInterfaceFactory
//# .extend our oProtected interfaces
core.extend(oProtected, {
//#
verbs: { //# GET, POST, PUT, PATCH, DELETE, HEAD + TRACE, CONNECT, OPTIONS - https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
get: function (vBaseOptions) {
return function (sUrl, vCallOptions) {
return oProtected.doFetch("GET", sUrl, _undefined, oProtected.processOptions(vBaseOptions, vCallOptions));
};
},
post: function (vBaseOptions) {
return function (sUrl, oBody, vCallOptions) {
return oProtected.doFetch("POST", sUrl, oBody, oProtected.processOptions(vBaseOptions, vCallOptions));
};
},
put: function (vBaseOptions) {
return function (sUrl, oBody, vCallOptions) {
return oProtected.doFetch("PUT", sUrl, oBody, oProtected.processOptions(vBaseOptions, vCallOptions));
};
},
"delete": function (vBaseOptions) {
return function (sUrl, vCallOptions) {
return oProtected.doFetch("DELETE", sUrl, _undefined, oProtected.processOptions(vBaseOptions, vCallOptions));
};
},
head: function (vBaseOptions) {
return function (sUrl, vCallOptions) {
return oProtected.doFetch("HEAD", sUrl, _undefined, oProtected.processOptions(vBaseOptions, vCallOptions));
};
}
}, //# verbs
//#
netInterfaceFactory: fnNetInterfaceFactory,
//# Processes the incoming options
processOptions: function (vBaseOptions, vCallOptions) {
var iWait,
oOptions = core.extend({}, vBaseOptions, vCallOptions)
;
//# If a retry or wait interface is defined
//# TODO: Remove .wait
if (oOptions.hasOwnProperty("retry") || oOptions.hasOwnProperty("wait")) {
oOptions.retry = oOptions.retry || oOptions.wait;
iWait = (core.type.int.mk(oOptions.retry, 500));
oOptions.maxAttempts = core.type.int.mk(oOptions.maxAttempts, 5);
oOptions.attempts = 1;
oOptions.retry = (core.type.fn.is(oOptions.retry) ? oOptions.retry : function (iAttemptCount) {
return (
iAttemptCount < oOptions.maxAttempts ?
iWait :
null
);
});
}
//#
if (core.type.fn.is(vBaseOptions)) {
oOptions = vBaseOptions(oOptions);
}
return oOptions;
}, //# processOptions
//# Wrapper for a Fetch call
doFetch: async function (sVerb, sUrl, oBody, oOptions) {
//# init
//# SEE: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters
//# SEE: https://javascript.info/fetch-api
/*var oInit = {
//# fetch init options
method: "GET", //# GET, POST, PUT, PATCH, DELETE, HEAD + TRACE, CONNECT, OPTIONS - https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
body: undefined, //# String, FormData, Blob, BufferSource, URLSearchParams - NOTE: Not allowed on GET and HEAD
headers: {}, //# { "Content-Type": "text/plain;charset=UTF-8" }
mode: "cors", //# "cors", "no-cors", "same-origin",
credentials: "same-origin", //# "omit", "same-origin", "include", FederatedCredential, PasswordCredential
cache: "default", //# "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached", SEE: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache
redirect: "follow", //# "redirect", "error", "manual"
referrer: "", //# same-origin URL, "same-origin", "", USVString
referrerPolicy: "", //# "", "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url", SEE: https://w3c.github.io/webappsec-referrer-policy/#referrer-policies
integrity: undefined, //# e.g., sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=
keepalive: false, //# false/true
signal: undefined, //# AbortSignal
//window: window, //# Non-standard?
//# ish.io.net additional oOptions
forceContentTypeOnJsonBody: true //# Forces the header's Content-Type to application/json if body is .stringify'd JSON
returnType: "jsonOrText", //# "arrayBuffer", "blob", "formData", "json", "text", "jsonOrText"
retry: 500, //# integer, function
maxAttempts: 5, //# integer
//# xhr-specific oOptions
async: false, //# false/true
useCache: false, //# false/true
//cache: false, //# false/true
fn: undefined, //# function callback
arg: undefined, //# variant
//headers: {}, //# { "Content-Type": "text/plain;charset=UTF-8" }
mimeType: undefined, //# string
contentType: undefined, //# string
responseType: "text" //# "text", "response"
};*/
var oInit, iMS, bRequestHasBody, bBodyIsFormData,
bJsonOrText = false,
syRetrying = core.type.symbol.get(),
oData = {
ok: false,
//status: _undefined,
url: sUrl,
verb: sVerb,
//async: bAsync,
//aborted: bAbort,
//response: _undefined,
text: _undefined,
json: _undefined,
retries: 0
}
;
//# Ensure the passed oOptions is an .obj and validate it's .returnType
oOptions = core.type.obj.mk(oOptions);
oOptions.returnType = (["arrayBuffer", "blob", "formData", "json", "text", "jsonOrText"].indexOf(oOptions.returnType) === -1 ?
"jsonOrText" :
oOptions.returnType
);
if (oOptions.returnType === "jsonOrText") {
oOptions.returnType = "text";
bJsonOrText = true;
}
//# If there's a oOptions.contentType, copy it into the .headers Content-Type
if (core.type.str.is(oOptions.contentType, true)) {
oOptions.headers = core.type.obj.mk(oOptions.headers);
oOptions.headers["Content-Type"] = oOptions.contentType;
}
//#
sVerb = core.type.str.mk(sVerb).toUpperCase();
sVerb = (["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"].indexOf(sVerb) === -1 ?
"GET" :
sVerb
);
bRequestHasBody = (core.type.is.value(oBody) && sVerb !== "GET" && sVerb !== "HEAD");
bBodyIsFormData = bRequestHasBody &&
core.type.fn.is(oBody.append) && //# see: https://developer.mozilla.org/en-US/docs/Web/API/FormData
core.type.fn.is(oBody.delete) && //# TODO: Neek
core.type.fn.is(oBody.entries) &&
core.type.fn.is(oBody.get) &&
core.type.fn.is(oBody.getAll) &&
core.type.fn.is(oBody.has) &&
core.type.fn.is(oBody.keys) &&
core.type.fn.is(oBody.set) &&
core.type.fn.is(oBody.values)
;
//# If this bRequestHasBody, it's oBody .is .obj, we're to .forceContentTypeOnJsonBody or we don't already have a Content-Type
if (bRequestHasBody &&
core.type.obj.is(oBody) && (
oOptions.forceContentTypeOnJsonBody ||
!core.type.str.is(core.resolve(oOptions, ["headers", "Content-Type"]), true)
)
) {
//# Set the .headers Content-Type to application/json
oOptions.headers = core.type.obj.mk(oOptions.headers);
oOptions.headers["Content-Type"] = "application/json";
}
//# Set the required .method and .body elements while removing the ish-specific .returnType, .retry and .attempts into .fetch's oInit
oInit = core.extend(oOptions, {
method: sVerb,
body: (bRequestHasBody ?
(core.type.obj.is(oBody) /*&& bBodyIsFormData*/ ? JSON.stringify(oBody) : oBody) :
_undefined
),
//mode: "cors",
//returnType: _undefined,
retry: _undefined,
attempts: _undefined
});
//#
return new Promise(async function callFetch(fnResolve /*, fnReject*/) {
//# .fetch the sURL
fetch(sUrl, oInit)
.then(async function /*fetchPromise*/(oResponse) {
var vReturnVal;
//# Set the oData based on the oResponse
oData.ok = oResponse.ok; // ((oResponse.status >= 200 && oResponse.status <= 299) || (oResponse.status === 0 && sUrl.substr(0, 7) === "file://"));
oData.status = oResponse.status;
oData.url = sUrl;
//oData.verb = sVerb;
//oData.async = true;
//oData.aborted = false;
oData.response = oResponse;
//oData.json = await oResponse.json();
//oData.text = await oResponse.text();
//# If the oData was not .ok and we have an oOptions.retry (calling it to calculate the iMS as we go)
if (
!oData.ok &&
core.type.fn.is(oOptions.retry) &&
core.type.int.is(iMS = oOptions.retry(oOptions.attempts))
) {
//# Recurse via setTimeout to run .callFetch again, passing in our fnResolve (ignoring fnReject as we never use it)
setTimeout(function () {
oData.retries = oOptions.attempts++;
callFetch(fnResolve /*, fnReject*/);
}, iMS);
//# Set our vReturnVal to .reject the fetchPromise so it's .catch'ed by chainedResponsePromise, passing syRetrying to signal it to be ignored
vReturnVal = Promise.reject(syRetrying);
}
//#
else {
//# Based on .ok, return the chainedResponsePromise
//# SEE: https://developer.mozilla.org/en-US/docs/Web/API/Response
vReturnVal = (oData.ok ?
await oResponse[oOptions.returnType]() :
Promise.reject(oResponse) //# Set our vReturnVal to .reject the fetchPromise so it's .catch'ed by chainedResponsePromise
);
//#
if (bJsonOrText && core.type.str.is.json(vReturnVal)) {
vReturnVal = JSON.parse(vReturnVal);
}
}
return vReturnVal;
})
.then(/*async*/ function /*chainedResponsePromise*/(vResponseData) {
oData.data = vResponseData;
//# If the vResponseData .is .obj or .arr, also set it into our .json
if (core.type.obj.is(vResponseData) || core.type.arr.is(vResponseData)) {
oData.json = vResponseData;
}
//# Else if the vResponseData .is .str, also set it into our .text
else if (core.type.str.is(vResponseData)) {
oData.text = vResponseData;
}
//# .fnResolve our oData
fnResolve(oData);
})
.catch(async function (oError) {
//# As long as oError isn't our syRetrying symbol (meaning that we're... syRetrying)
if (oError !== syRetrying) {
//# Set the oError into the .response and .fnResolve the Promise
oData.response = oError;
try {
oData.data = await oError[oOptions.returnType]();
} catch (e) {
oData.data = oError;
}
fnResolve(oData);
}
})
;
});
} //# oProtected.doFetch
});
//# Return the core.io.net functionality
//#########
/** Callback utilized by <code>ish.io.net</code> on completion of a XMLHttpRequest request.
* @callback fnIshIoNetCallback
* @param {boolean} bSuccess Value representing if the request returned successfully.
* @param {object} oResponse Value representing the results of the request:
* @param {boolean} oResponse.ok Value representing if the request returned successfully.
* @param {integer} oResponse.status Value representing the XMLHttpRequest's <code>status</code>.
* @param {string} oResponse.url Value representing the URL of the request.
* @param {string} oResponse.verb Value representing the HTTP Verb of the request.
* @param {boolean} oResponse.async Value representing if the request was asynchronous.
* @param {boolean} oResponse.aborted Value representing if the request was aborted prior to completion.
* @param {boolean} oResponse.loaded Value representing if the URL was successfully loaded (DEPRECATED).
* @param {variant} oResponse.response Value representing the XMLHttpRequest's <code>responseText</code> or <code>response</code>.
* @param {string} oResponse.text Value representing the XMLHttpRequest's <code>responseText</code>.
* @param {object} oResponse.json Value representing the XMLHttpRequest's <code>responseText</code> as parsed JSON.
* @param {variant} vArg Value representing the value passed in the original call as <code>vCallback.arg</code>.
* @param {object} $xhr Value representing the underlying <code>XMLHttpRequest</code> management object.
*/ //#####
return core.extend(
//# Setup the default io.net.* interfaces
fnNetInterfaceFactory(/*undefined*/),
{
//#########
/** Provides access to the request cache.
* @function ish.io.net.cache
* @param {object} oImportCache Value representing the cached entries to import into the current cache as <code>verb.url.data</code>.
* @returns {object} Value representing the request cache.
*/ //#####
cache: function (oImportCache) {
core.extend(oCache, oImportCache);
return oCache;
}, //# io.net.cache
//#########
/** IP-based type functionality.
* @function ish.io.net.ip
* @$asProperty
* @ignore
*/ //#####
ip: {
//#########
/** Determines if the passed value represents an IP.
* @function ish.io.net.ip:is
* @param {variant} x Value to interrogate.
* @returns {integer} Value representing the type of IP; <code>4</code> representing an IPv4, <code>6</code> representing an IPv6 and <code>0</code> (<code>falsey</code>) representing a non-IP.
*/ //#####
is: core.extend(
function (x) {
return (
core.io.net.ip.is.v4(x) ?
4 :
core.io.net.ip.is.v6(x) ?
6 :
0
);
}, function () {
//# Create the reIPv4 and mega reIPv6 RegEx based on the various valid patters |'ed together
//# NOTE: The RegEx's are set as RegEx's below for intellisense/highlighting/etc and not for any other reason
var reIPv4 = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
reIPv6 = new RegExp('^(?:' +
/(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}/ .source + '|' +
/(?:[0-9a-fA-F]{1,4}:){1,7}:/ .source + '|' +
/(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}/ .source + '|' +
/(?:[0-9a-fA-F]{1,4}:){1,5}:(?:[0-9a-fA-F]{1,4}:)[0-9a-fA-F]{1,4}/ .source + '|' +
/(?:[0-9a-fA-F]{1,4}:){1,4}:(?:[0-9a-fA-F]{1,4}:){2}[0-9a-fA-F]{1,4}/ .source + '|' +
/(?:[0-9a-fA-F]{1,4}:){1,3}:(?:[0-9a-fA-F]{1,4}:){3}[0-9a-fA-F]{1,4}/ .source + '|' +
/(?:[0-9a-fA-F]{1,4}:){1,2}:(?:[0-9a-fA-F]{1,4}:){4}[0-9a-fA-F]{1,4}/ .source + '|' +
/(?:[0-9a-fA-F]{1,4}:){1,1}:(?:[0-9a-fA-F]{1,4}:){5}[0-9a-fA-F]{1,4}/ .source + '|' +
/::(?:(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?/ .source +
')$'
);
return {
//#########
/** Determines if the passed value represents an IPv4.
* @function ish.io.net.ip:is:v4
* @param {variant} x Value to interrogate.
* @returns {boolean} Value representing if the passed value represents an IPv4.
*/ //#####
v4: function (x) {
return reIPv4.test(x + "");
}, //# core.io.net.ip.is.v4
//#########
/** Determines if the passed value represents an IPv6.
* @function ish.io.net.ip:is:v6
* @param {variant} x Value to interrogate.
* @returns {boolean} Value representing if the passed value represents an IPv6.
*/ //#####
v6: function (x) {
return reIPv6.test(x + "");
} //# core.io.net.ip.is.v6
};
}()
) //# core.io.net.ip.is
}, //# core.io.net.ip
//#########
/** Converts an object of key/value pairs into a FormData representation for use with multipart form bodies.
* @function ish.io.net.multipartFormData
* @param {object} oKeyValuePairs Value representing the key/value pairs to be sent in the request body.
* @returns {object} Value representing the key/value pairs in a FormData object.
*/ //#####
multipartFormData: function (oKeyValuePairs) {
var i,
a_sKeys = core.type.obj.ownKeys(oKeyValuePairs),
oReturnVal = new FormData()
;
//# If the passed oKeyValuePairs resulted in .ownKeys, traverse them
if (core.type.arr.is(a_sKeys, true)) {
for (i = 0; i < a_sKeys.length; i++) {
//# .append each key/value pair into our oReturnVal
//# See: https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
oReturnVal.append(a_sKeys[i], oKeyValuePairs[a_sKeys[i]]);
}
}
return oReturnVal;
}, //# core.io.net.multipartFormData
//#########
/** Enumeration of HTTP Status Codes.
* @function ish.io.net.status
* @$asProperty
* @returns {object} =status Value representing the following properties:
* @returns {object} =status.info Value representing 1xx HTTP status codes.
* @returns {object} =status.success Value representing 2xx HTTP status codes.
* @returns {object} =status.redirection Value representing 3xx HTTP status codes.
* @returns {object} =status.clientError Value representing 4xx HTTP status codes.
* @returns {object} =status.serverError Value representing 5xx HTTP status codes.
*/ //#####
status: {
info: {
continue: 100,
switchingProtocols: 101,
processing: 102,
earlyHints: 103
},
success: {
ok: 200,
created: 201,
accepted: 202,
nonAuthInfo: 203,
noContent: 204,
resetContent: 205,
partialContent: 206,
multiStatus: 207,
alreadyReported: 208,
imUsed: 226
},
redirection: {
multipleChoices: 300,
movedPermanently: 301,
found: 302,
seeOther: 303,
notModified: 304,
useProxy: 305,
switchProxy: 306,
temporaryRedirect: 307,
permanentRedirect: 30
},
clientError: {
badRequest: 400,
unauthorized: 401,
paymentRequired: 402,
forbidden: 403,
notFound: 404,
methodNotAllowed: 405,
notAcceptable: 406,
proxyAuthRequired: 407,
requestTimeout: 408,
conflict: 409,
gone: 410,
lengthRequired: 411,
preconditionFailed: 412,
payloadTooLarge: 413,
uriTooLong: 414,
unsupportedMediaType: 415,
rangeNotSatisfiable: 416,
expectationFailed: 417,
imATeapot: 418,
misdirectedRequest: 421,
unprocessableEntity: 422,
locked: 423,
failedDependency: 424,
upgradeRequired: 426,
preconditionRequired: 428,
tooManyRequests: 429,
requestHeaderFieldsTooLarge: 431,
unavailableForLegalReasons: 451
},
serverError: {
internalServerError: 500,
notImplemented: 501,
badGateway: 502,
serviceUnavailable: 503,
gatewayTimeout: 504,
httpVersionNotSupported: 505,
variantAlsoNegotiates: 506,
insufficientStorage: 507,
loopDetected: 508,
notExtended: 510,
networkAuthRequired: 511
}
} //# io.net.status
}
);
}); //# core.io.net
//# .fire the plugin's loaded event
core.io.event.fire("ish.io.net");
//# Return core to allow for chaining
return core;
}
//# If we are running server-side
//# NOTE: Compliant with UMD, see: https://github.com/umdjs/umd/blob/master/templates/returnExports.js
//# NOTE: Does not work with strict CommonJS, but only CommonJS-like environments that support module.exports, like Node.
if (typeof module === 'object' && module.exports) { //if (typeof module !== 'undefined' && this.module !== module && module.exports) {
module.exports = function (core) {
//# NOTE: FormData is included in NodeJS v18+ but the form-data npm package is required for versions below that
try {
return init(core, require('node-fetch-commonjs'), FormData);
} catch (e) {
return init(core, require('node-fetch-commonjs'), require('form-data'));
}
};
}
//# Else if we are running in an .amd environment, register as an anonymous module
else if (typeof define === 'function' && define.amd) {
define([], function (core) {
return init(core, window.fetch, window.FormData);
});
}
//# Else we are running in the browser, so we need to setup the _document-based features
else {
return init(window.head.ish || document.querySelector("SCRIPT[ish]").ish, window.fetch, window.FormData);
}
//</MIXIN>
}());