traverson
Version:
Hypermedia API/HATEOAS client for Node.js and the browser
1,671 lines (1,427 loc) • 4.32 MB
JavaScript
// modules are defined as an array
// [ module function, map of requireuires ]
//
// map of requireuires is short require name -> numeric require
//
// anything defined in a previous bundle is accessed via the
// orig method which is the requireuire for previous bundles
(function outer (modules, cache, entry) {
// Save the require from previous bundle to this closure if any
var previousRequire = typeof require == "function" && require;
function findProxyquireifyName() {
var deps = Object.keys(modules)
.map(function (k) { return modules[k][1]; });
for (var i = 0; i < deps.length; i++) {
var pq = deps[i]['proxyquireify'];
if (pq) return pq;
}
}
var proxyquireifyName = findProxyquireifyName();
function newRequire(name, jumped){
// Find the proxyquireify module, if present
var pqify = (proxyquireifyName != null) && cache[proxyquireifyName];
// Proxyquireify provides a separate cache that is used when inside
// a proxyquire call, and is set to null outside a proxyquire call.
// This allows the regular caching semantics to work correctly both
// inside and outside proxyquire calls while keeping the cached
// modules isolated.
// When switching from one proxyquire call to another, it clears
// the cache to prevent contamination between different sets
// of stubs.
var currentCache = (pqify && pqify.exports._cache) || cache;
if(!currentCache[name]) {
if(!modules[name]) {
// if we cannot find the the module within our internal map or
// cache jump to the current global require ie. the last bundle
// that was added to the page.
var currentRequire = typeof require == "function" && require;
if (!jumped && currentRequire) return currentRequire(name, true);
// If there are other bundles on this page the require from the
// previous one is saved to 'previousRequire'. Repeat this as
// many times as there are bundles until the module is found or
// we exhaust the require chain.
if (previousRequire) return previousRequire(name, true);
var err = new Error('Cannot find module \'' + name + '\'');
err.code = 'MODULE_NOT_FOUND';
throw err;
}
var m = currentCache[name] = {exports:{}};
// The normal browserify require function
var req = function(x){
var id = modules[name][1][x];
return newRequire(id ? id : x);
};
// The require function substituted for proxyquireify
var moduleRequire = function(x){
var pqify = (proxyquireifyName != null) && cache[proxyquireifyName];
// Only try to use the proxyquireify version if it has been `require`d
if (pqify && pqify.exports._proxy) {
return pqify.exports._proxy(req, x);
} else {
return req(x);
}
};
modules[name][0].call(m.exports,moduleRequire,m,m.exports,outer,modules,currentCache,entry);
}
return currentCache[name].exports;
}
for(var i=0;i<entry.length;i++) newRequire(entry[i]);
// Override the current require with this new one
return newRequire;
})
({1:[function(require,module,exports){
'use strict';
// TODO Replace by a proper lightweight logging module, suited for the browser
var enabled = false;
function Logger(id) {
if (id == null) {
id = '';
}
this.id = id;
}
Logger.prototype.enable = function() {
this.enabled = true;
};
Logger.prototype.debug = function(message) {
if (enabled) {
console.log(this.id + '/debug: ' + message);
}
};
Logger.prototype.info = function(message) {
if (enabled) {
console.log(this.id + '/info: ' + message);
}
};
Logger.prototype.warn = function(message) {
if (enabled) {
console.log(this.id + '/warn: ' + message);
}
};
Logger.prototype.error = function(message) {
if (enabled) {
console.log(this.id + '/error: ' + message);
}
};
function minilog(id) {
return new Logger(id);
}
minilog.enable = function() {
enabled = true;
};
module.exports = minilog;
},{}],2:[function(require,module,exports){
'use strict';
module.exports = {
isArray: function(o) {
if (o == null) {
return false;
}
return Object.prototype.toString.call(o) === '[object Array]';
}
};
},{}],3:[function(require,module,exports){
'use strict';
/*
* Copied from underscore.string module. Just the functions we need, to reduce
* the browserified size.
*/
var _s = {
startsWith: function(str, starts) {
if (starts === '') return true;
if (str == null || starts == null) return false;
str = String(str); starts = String(starts);
return str.length >= starts.length && str.slice(0, starts.length) === starts;
},
endsWith: function(str, ends){
if (ends === '') return true;
if (str == null || ends == null) return false;
str = String(str); ends = String(ends);
return str.length >= ends.length &&
str.slice(str.length - ends.length) === ends;
},
splice: function(str, i, howmany, substr){
var arr = _s.chars(str);
arr.splice(~~i, ~~howmany, substr);
return arr.join('');
},
contains: function(str, needle){
if (needle === '') return true;
if (str == null) return false;
return String(str).indexOf(needle) !== -1;
},
chars: function(str) {
if (str == null) return [];
return String(str).split('');
}
};
module.exports = _s;
},{}],4:[function(require,module,exports){
'use strict';
var minilog = require('minilog')
, errorModule = require('./errors')
, errors = errorModule.errors
, createError = errorModule.createError
, log = minilog('traverson');
exports.abortTraversal = function abortTraversal() {
log.debug('aborting link traversal');
this.aborted = true;
if (this.currentRequest) {
log.debug('request in progress. trying to abort it, too.');
this.currentRequest.abort();
}
};
exports.registerAbortListener = function registerAbortListener(t, callback) {
if (t.currentRequest) {
t.currentRequest.on('abort', function() {
exports.callCallbackOnAbort(t);
});
}
};
exports.callCallbackOnAbort = function callCallbackOnAbort(t) {
log.debug('link traversal aborted');
if (!t.callbackHasBeenCalledAfterAbort) {
t.callbackHasBeenCalledAfterAbort = true;
t.callback(exports.abortError(), t);
}
};
exports.abortError = function abortError() {
var error = createError('Link traversal process has been aborted.',
errors.TraversalAbortedError);
error.aborted = true;
return error;
};
},{"./errors":5,"minilog":1}],5:[function(require,module,exports){
'use strict';
module.exports = {
errors: {
HTTPError: 'HTTPError',
InvalidArgumentError: 'InvalidArgumentError',
InvalidStateError: 'InvalidStateError',
JSONError: 'JSONError',
JSONPathError: 'JSONPathError',
LinkError: 'LinkError',
TraversalAbortedError: 'TraversalAbortedError',
UnsupportedMediaType: 'UnsupportedMediaTypeError',
},
createError: function(message, name, data) {
var error = new Error(message);
error.name = name;
if (data) {
error.data = data;
}
return error;
},
};
},{}],6:[function(require,module,exports){
(function (process){(function (){
'use strict';
var minilog = require('minilog')
, log = minilog('traverson')
, abortTraversal = require('./abort_traversal')
, detectContentType = require('./transforms/detect_content_type')
, errorModule = require('./errors')
, errors = errorModule.errors
, createError = errorModule.createError
, getOptionsForStep = require('./transforms/get_options_for_step');
var nextTickAvailable = process &&
Object.hasOwnProperty.call(process, 'nextTick');
/**
* Executes a HTTP GET request during the link traversal process.
*/
// This method is currently used for all intermediate GET requests during the
// link traversal process. Coincidentally, it is also used for the final request
// in a link traversal should this happen to be a GET request. Otherwise (POST/
// PUT/PATCH/DELETE), Traverson uses exectueHttpRequest.
exports.fetchResource = function fetchResource(t, callback) {
log.debug('fetching resource for next step');
if (t.step.url) {
log.debug('fetching resource from', t.step.url);
return executeHttpGet(t, callback);
} else if (t.step.doc) {
// The step already has an attached result document, so all is fine and we
// can call the callback immediately
log.debug('resource for next step has already been fetched, using ' +
'embedded');
if (nextTickAvailable) {
return process.nextTick(function() {
callback(null, t);
});
}
return callback(null, t);
} else {
var error = createError('Can not process step.', errors.InvalidStateError);
error.step = t.step;
if (nextTickAvailable) {
return process.nextTick(function() {
callback(error, t);
});
}
return callback(error, t);
}
};
function executeHttpGet(t, callback) {
var options = getOptionsForStep(t);
log.debug('HTTP GET request to', t.step.url);
log.debug('options', options);
t.mostRecentHttpMethodName = 'GET';
t.currentRequest =
t.requestModuleInstance.get(t.step.url, options,
function(err, response, body) {
log.debug('HTTP GET request to', t.step.url, 'returned');
t.currentRequest = null;
// workaround for cases where response body is empty but body comes in as
// the third argument
if (body && !response.body) {
response.body = body;
}
t.step.response = response;
if (err) {
return callback(err, t);
}
log.debug('request to', t.step.url, 'finished without error (',
response.statusCode, ')');
if (!detectContentType(t, callback)) return;
return callback(null, t);
});
abortTraversal.registerAbortListener(t, callback);
}
/**
* Executes an arbitrary HTTP request.
*/
// This method is currently used for POST/PUT/PATCH/DELETE at the end of a link
// traversal process. If the link traversal process requires a GET as the last
// request, Traverson uses exectueHttpGet.
exports.executeHttpRequest =
function(t, request, method, methodName, callback) {
var requestOptions = getOptionsForStep(t);
if (t.body !== null && typeof t.body !== 'undefined') {
requestOptions.body = (t.rawPayload || requestOptions.jsonReplacer) ?
t.body : JSON.stringify(t.body);
}
log.debug('HTTP', methodName, 'request to', t.step.url);
log.debug('options', requestOptions);
t.mostRecentHttpMethodName = methodName;
t.currentRequest =
method.call(request, t.step.url, requestOptions,
function(err, response, body) {
log.debug('HTTP', methodName, 'request to', t.step.url, 'returned');
t.currentRequest = null;
// workaround for cases where response body is empty but body comes in as
// the third argument
if (body && !response.body) {
response.body = body;
}
t.step.response = response;
if (err) {
return callback(err);
}
return callback(null, response);
});
abortTraversal.registerAbortListener(t, callback);
};
}).call(this)}).call(this,require('_process'))
},{"./abort_traversal":4,"./errors":5,"./transforms/detect_content_type":15,"./transforms/get_options_for_step":17,"_process":150,"minilog":1}],7:[function(require,module,exports){
'use strict';
module.exports = function isContinuation(t) {
return t.continuation && t.step && t.step.response;
};
},{}],8:[function(require,module,exports){
'use strict';
var minilog = require('minilog')
, _s = require('underscore.string');
var jsonpath;
try {
jsonpath = require('jsonpath-plus');
} catch (e) {
jsonpath = false;
console.warn('Could not require jsonpath-plus, JSONPath support is not ' +
'available.');
}
var errorModule = require('./errors')
, errors = errorModule.errors
, createError = errorModule.createError
, parseLinkHeaderValue = require('./parse_link_header_value');
function JsonAdapter(log) {
this.log = log;
}
JsonAdapter.mediaType = 'application/json';
JsonAdapter.prototype.findNextStep = function(t, link) {
validateLinkObject(link);
var doc = t.lastStep.doc;
this.log.debug('resolving link', link);
switch (link.type) {
case 'link-rel':
return this._handleLinkRel(doc, link);
case 'header':
return this._handleHeader(t.lastStep.response, link);
case 'link-header':
return this._handleLinkHeader(t.lastStep.response, link);
default:
throw createError('Link objects with type ' + link.type + ' are not ' +
'supported by this adapter.', errors.InvalidArgumentError, link);
}
};
JsonAdapter.prototype._handleLinkRel = function(doc, link) {
var linkRel = link.value;
this.log.debug('looking for link-rel', linkRel, 'in doc', doc);
var url;
if (this._testJSONPath(linkRel)) {
return { url: this._resolveJSONPath(doc, linkRel) };
} else if (doc[linkRel]) {
return { url : doc[linkRel] };
} else {
throw createError('Could not find property ' + linkRel +
' in document.', errors.LinkError, doc);
}
};
function validateLinkObject(link) {
if (typeof link === 'undefined' || link === null) {
throw createError('Link object is null or undefined.',
errors.InvalidArgumentError);
}
if (typeof link !== 'object') {
throw createError('Links must be objects, not ' + typeof link +
'.', errors.InvalidArgumentError, link);
}
if (!link.type) {
throw createError('Link objects has no type attribute.',
errors.InvalidArgumentError, link);
}
}
JsonAdapter.prototype._testJSONPath = function(link) {
return _s.startsWith(link, '$.') || _s.startsWith(link, '$[');
};
JsonAdapter.prototype._resolveJSONPath = function(doc, link) {
if (!jsonpath) {
throw createError('JSONPath support is not available.');
}
var matches = jsonpath({
json: doc,
path: link,
});
if (matches.length === 1) {
var url = matches[0];
if (!url) {
throw createError('JSONPath expression ' + link +
' was resolved but the result was null, undefined or an empty' +
' string in document:\n' + JSON.stringify(doc),
errors.JSONPathError, doc);
}
if (typeof url !== 'string') {
throw createError('JSONPath expression ' + link +
' was resolved but the result is not a property of type string. ' +
'Instead it has type "' + (typeof url) +
'" in document:\n' + JSON.stringify(doc), errors.JSONPathError,
doc);
}
return url;
} else if (matches.length > 1) {
// ambigious match
throw createError('JSONPath expression ' + link +
' returned more than one match in document:\n' +
JSON.stringify(doc), errors.JSONPathError, doc);
} else {
// no match at all
throw createError('JSONPath expression ' + link +
' returned no match in document:\n' + JSON.stringify(doc),
errors.JSONPathError, doc);
}
};
JsonAdapter.prototype._handleHeader = function(httpResponse, link) {
switch (link.value) {
case 'location':
var locationHeader = httpResponse.headers.location;
if (!locationHeader) {
throw createError('Following the location header but there was no ' +
'location header in the last response.', errors.LinkError,
httpResponse.headers);
}
return { url : locationHeader };
default:
throw createError('Link objects with type header and value ' +
link.value + ' are not supported by this adapter.',
errors.InvalidArgumentError, link);
}
};
JsonAdapter.prototype._handleLinkHeader = function(httpResponse, link) {
if (!httpResponse.headers.link)
throw createError('There was no link header in the last response.',
errors.InvalidArgumentError, link);
var links = parseLinkHeaderValue(httpResponse.headers.link);
if (links[link.value]) {
return { url : links[link.value].url};
} else {
throw createError('Link with relation ' + link.value +
' not found in link header.',
errors.InvalidArgumentError, link);
}
};
module.exports = JsonAdapter;
},{"./errors":5,"./parse_link_header_value":13,"jsonpath-plus":128,"minilog":1,"underscore.string":3}],9:[function(require,module,exports){
'use strict';
var mediaTypes = require('./media_types');
var registry = {};
exports.register = function register(contentType, constructor) {
registry[contentType] = constructor;
};
exports.get = function get(contentType) {
return registry[contentType];
};
exports.register(mediaTypes.CONTENT_NEGOTIATION,
require('./negotiation_adapter'));
exports.register(mediaTypes.JSON, require('./json_adapter'));
},{"./json_adapter":8,"./media_types":10,"./negotiation_adapter":12}],10:[function(require,module,exports){
'use strict';
var JsonAdapter = require('./json_adapter');
module.exports = {
CONTENT_NEGOTIATION: 'content-negotiation',
JSON: JsonAdapter.mediaType,
JSON_HAL: 'application/hal+json',
};
},{"./json_adapter":8}],11:[function(require,module,exports){
'use strict';
// TODO Maybe replace with https://github.com/Raynos/xtend
// check browser build size, though.
function mergeRecursive(obj1, obj2) {
if (!obj1 && obj2) {
obj1 = {};
}
for (var key in obj2) {
if (!obj2.hasOwnProperty(key)) {
continue;
}
merge(obj1, obj2, key);
}
return obj1;
}
function merge(obj1, obj2, key) {
if (typeof obj2[key] === 'object') {
// if it is an object (that is, a non-leave in the tree),
// and it is not present in obj1
if (!obj1[key] || typeof obj1[key] !== 'object') {
// ... we create an empty object in obj1
obj1[key] = {};
}
// and we recurse deeper into the structure
mergeRecursive(obj1[key], obj2[key]);
} else {
// if it is primitive (string, number, boolean) or a function, we overwrite/
// add it to obj1
obj1[key] = obj2[key];
}
}
module.exports = mergeRecursive;
},{}],12:[function(require,module,exports){
'use strict';
var errorModule = require('./errors')
, errors = errorModule.errors
, createError = errorModule.createError;
function NegotiationAdapter(log) {}
NegotiationAdapter.prototype.findNextStep = function(doc, link) {
throw createError('Content negotiation did not happen',
errors.InvalidStateError);
};
module.exports = NegotiationAdapter;
},{"./errors":5}],13:[function(require,module,exports){
var mergeRecursive = require('./merge_recursive');
module.exports = function parseLinkHeaderValue(linkHeader) {
if (!linkHeader) {
return null;
}
return linkHeader
.split(/,\s*</)
.map(parseLink)
.filter(hasRel)
.reduce(intoRels, {});
};
function parseLink(link) {
try {
var parts = link.split(';');
var linkUrl = parts.shift().replace(/[<>]/g, '');
var info = parts.reduce(createObjects, {});
info.url = linkUrl;
return info;
} catch (e) {
return null;
}
}
function createObjects(acc, p) {
// rel="next" => 1: rel 2: next
var m = p.match(/\s*(.+)\s*=\s*"?([^"]+)"?/);
if (m) acc[m[1]] = m[2];
return acc;
}
function hasRel(linkHeaderValuePart) {
return linkHeaderValuePart && linkHeaderValuePart.rel;
}
function intoRels(acc, linkHeaderValuePart) {
function splitRel (rel) {
acc[rel] = mergeRecursive({ rel: rel }, linkHeaderValuePart);
}
linkHeaderValuePart.rel.split(/\s+/).forEach(splitRel);
return acc;
}
},{"./merge_recursive":11}],14:[function(require,module,exports){
(function (process){(function (){
/* jshint loopfunc: true */
'use strict';
var minilog = require('minilog')
, log = minilog('traverson');
var nextTickAvailable = process &&
Object.hasOwnProperty.call(process, 'nextTick');
/*
* Applies async and sync transforms, one after another.
*/
function applyTransforms(transforms, t, callback) {
log.debug('applying', transforms.length, 'transforms');
for (var i = 0; i < transforms.length; i++) {
var transform = transforms[i];
log.debug('next transform', transform);
if (transform.isAsync) {
// asynchronous case
return transform(t, function(t) {
// this is only called when the async transform was successful,
// otherwise t.callback has already been called with an error.
applyTransforms(transforms.slice(i + 1), t, callback);
});
} else {
// synchronous case
var result = transform(t);
if (!result) {
log.debug('transform has failed or was a final transform');
// stop processing t.callback has already been called
return;
}
}
}
log.debug('all transformations done, starting next step');
if (nextTickAvailable) {
return process.nextTick(function() {
callback(t);
});
}
return callback(t);
}
module.exports = applyTransforms;
}).call(this)}).call(this,require('_process'))
},{"_process":150,"minilog":1}],15:[function(require,module,exports){
'use strict';
var minilog = require('minilog')
, log = minilog('traverson');
var mediaTypeRegistry = require('../media_type_registry')
, errorModule = require('../errors')
, errors = errorModule.errors
, createError = errorModule.createError;
module.exports = function detectContentType(t, callback) {
if (t.contentNegotiation &&
t.step.response &&
t.step.response.headers &&
t.step.response.headers['content-type']) {
var contentType = t.step.response.headers['content-type'].split(/[; ]/)[0];
log.debug('found content type string', contentType);
var AdapterType = mediaTypeRegistry.get(contentType);
if (!AdapterType) {
log.error('no adapter for content type', contentType);
callback(createError('Unknown content type for content ' +
'type detection: ' + contentType, errors.UnsupportedMediaType), t);
return false;
}
// switch to new Adapter depending on Content-Type header of server
t.adapter = new AdapterType(log);
log.debug('switched to media type adapter',
t.adapter.name || t.adapter.constructor.name);
}
return true;
};
},{"../errors":5,"../media_type_registry":9,"minilog":1}],16:[function(require,module,exports){
(function (process){(function (){
'use strict';
var minilog = require('minilog')
, log = minilog('traverson')
, abortTraversal = require('../abort_traversal')
, isContinuation = require('../is_continuation')
, httpRequests = require('../http_requests');
var nextTickAvailable = process &&
Object.hasOwnProperty.call(process, 'nextTick');
/*
* Execute the next step in the traversal. In most cases that is an HTTP get to
*the next URL.
*/
function fetchResource(t, callback) {
if (isContinuation(t)) {
convertContinuation(t, callback);
} else {
fetchViaHttp(t, callback);
}
}
fetchResource.isAsync = true;
/*
* This is a continuation of an earlier traversal process.
* We need to shortcut to the next step (without executing the final HTTP
* request of the last traversal again.
*/
function convertContinuation(t, callback) {
log.debug('continuing from last traversal process (walker)');
if (nextTickAvailable) {
return process.nextTick(function() { // de-zalgo continuations
callback(t);
});
}
return callback(t);
}
function fetchViaHttp(t, callback) {
// always check for aborted before doing an HTTP request
if (t.aborted) {
return abortTraversal.callCallbackOnAbort(t);
}
httpRequests.fetchResource(t, function(err, t) {
log.debug('fetchResource returned');
if (err) {
if (!err.aborted) {
log.debug('error while executing http request');
log.error(err);
}
return t.callback(err);
}
callback(t);
});
}
module.exports = fetchResource;
}).call(this)}).call(this,require('_process'))
},{"../abort_traversal":4,"../http_requests":6,"../is_continuation":7,"_process":150,"minilog":1}],17:[function(require,module,exports){
'use strict';
var minilog = require('minilog')
, log = minilog('traverson')
, util = require('util')
, mergeRecursive = require('../merge_recursive');
module.exports = function getOptionsForStep(t) {
var options = t.requestOptions;
if (util.isArray(t.requestOptions)) {
options = t.requestOptions[t.step.index] || {};
}
if (t.autoHeaders) {
addAutoHeaders(t, options);
}
log.debug('options', options);
return options;
};
function addAutoHeaders(t, options) {
var autoHeaderValue =
// we accept a static mediaType property as well as an instance property
t.adapter.constructor.mediaType ||
t.adapter.mediaType;
// The content negotiation adapter does not (and can not) provide a value
// for automatical Accept and Content-Type headers, in this case auto header
// value is undefined and we skip setting auto headers. This also happens
// arbitrary media type plug-ins that do not behave well and have no
// mediaType property.
if (autoHeaderValue) {
if (!options.headers) {
options.headers = createAutoHeaders(options, autoHeaderValue);
} else {
options.headers =
mergeRecursive(
createAutoHeaders(options, autoHeaderValue),
options.headers
);
}
}
}
function createAutoHeaders(options, autoHeaderValue) {
if (!options.form) {
// default: set Accept and Content-Type header
return {
'Accept': autoHeaderValue,
'Content-Type': autoHeaderValue,
};
} else {
// if options.form is set, we only set the Accept header, but not not the
// Content-Type header. The Content-Type header is set automatically by
// request/request or by browser/lib/shim/request.js.
return {
'Accept': autoHeaderValue
};
}
}
},{"../merge_recursive":11,"minilog":1,"util":2}],18:[function(require,module,exports){
"use strict";
var every = require("./prototypes/array").every;
/**
* @private
*/
function hasCallsLeft(callMap, spy) {
if (callMap[spy.id] === undefined) {
callMap[spy.id] = 0;
}
return callMap[spy.id] < spy.callCount;
}
/**
* @private
*/
function checkAdjacentCalls(callMap, spy, index, spies) {
var calledBeforeNext = true;
if (index !== spies.length - 1) {
calledBeforeNext = spy.calledBefore(spies[index + 1]);
}
if (hasCallsLeft(callMap, spy) && calledBeforeNext) {
callMap[spy.id] += 1;
return true;
}
return false;
}
/**
* A Sinon proxy object (fake, spy, stub)
*
* @typedef {object} SinonProxy
* @property {Function} calledBefore - A method that determines if this proxy was called before another one
* @property {string} id - Some id
* @property {number} callCount - Number of times this proxy has been called
*/
/**
* Returns true when the spies have been called in the order they were supplied in
*
* @param {SinonProxy[] | SinonProxy} spies An array of proxies, or several proxies as arguments
* @returns {boolean} true when spies are called in order, false otherwise
*/
function calledInOrder(spies) {
var callMap = {};
// eslint-disable-next-line no-underscore-dangle
var _spies = arguments.length > 1 ? arguments : spies;
return every(_spies, checkAdjacentCalls.bind(null, callMap));
}
module.exports = calledInOrder;
},{"./prototypes/array":26}],19:[function(require,module,exports){
"use strict";
var functionName = require("./function-name");
/**
* Returns a display name for a value from a constructor
*
* @param {object} value A value to examine
* @returns {(string|null)} A string or null
*/
function className(value) {
return (
(value.constructor && value.constructor.name) ||
// The next branch is for IE11 support only:
// Because the name property is not set on the prototype
// of the Function object, we finally try to grab the
// name from its definition. This will never be reached
// in node, so we are not able to test this properly.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
(typeof value.constructor === "function" &&
/* istanbul ignore next */
functionName(value.constructor)) ||
null
);
}
module.exports = className;
},{"./function-name":22}],20:[function(require,module,exports){
(function (process){(function (){
/* eslint-disable no-console */
"use strict";
/**
* Returns a function that will invoke the supplied function and print a
* deprecation warning to the console each time it is called.
*
* @param {Function} func
* @param {string} msg
* @returns {Function}
*/
exports.wrap = function(func, msg) {
var wrapped = function() {
exports.printWarning(msg);
return func.apply(this, arguments);
};
if (func.prototype) {
wrapped.prototype = func.prototype;
}
return wrapped;
};
/**
* Returns a string which can be supplied to `wrap()` to notify the user that a
* particular part of the sinon API has been deprecated.
*
* @param {string} packageName
* @param {string} funcName
* @returns {string}
*/
exports.defaultMsg = function(packageName, funcName) {
return (
packageName +
"." +
funcName +
" is deprecated and will be removed from the public API in a future version of " +
packageName +
"."
);
};
/**
* Prints a warning on the console, when it exists
*
* @param {string} msg
* @returns {undefined}
*/
exports.printWarning = function(msg) {
/* istanbul ignore next */
if (typeof process === "object" && process.emitWarning) {
// Emit Warnings in Node
process.emitWarning(msg);
} else if (console.info) {
console.info(msg);
} else {
console.log(msg);
}
};
}).call(this)}).call(this,require('_process'))
},{"_process":150}],21:[function(require,module,exports){
"use strict";
/**
* Returns true when fn returns true for all members of obj.
* This is an every implementation that works for all iterables
*
* @param {object} obj
* @param {Function} fn
* @returns {boolean}
*/
module.exports = function every(obj, fn) {
var pass = true;
try {
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
obj.forEach(function() {
if (!fn.apply(this, arguments)) {
// Throwing an error is the only way to break `forEach`
throw new Error();
}
});
} catch (e) {
pass = false;
}
return pass;
};
},{}],22:[function(require,module,exports){
"use strict";
/**
* Returns a display name for a function
*
* @param {Function} func
* @returns {string}
*/
module.exports = function functionName(func) {
if (!func) {
return "";
}
try {
return (
func.displayName ||
func.name ||
// Use function decomposition as a last resort to get function
// name. Does not rely on function decomposition to work - if it
// doesn't debugging will be slightly less informative
// (i.e. toString will say 'spy' rather than 'myFunc').
(String(func).match(/function ([^\s(]+)/) || [])[1]
);
} catch (e) {
// Stringify may fail and we might get an exception, as a last-last
// resort fall back to empty string.
return "";
}
};
},{}],23:[function(require,module,exports){
(function (global){(function (){
"use strict";
/**
* A reference to the global object
*
* @type {object} globalObject
*/
var globalObject;
/* istanbul ignore else */
if (typeof global !== "undefined") {
// Node
globalObject = global;
} else if (typeof window !== "undefined") {
// Browser
globalObject = window;
} else {
// WebWorker
globalObject = self;
}
module.exports = globalObject;
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],24:[function(require,module,exports){
"use strict";
module.exports = {
global: require("./global"),
calledInOrder: require("./called-in-order"),
className: require("./class-name"),
deprecated: require("./deprecated"),
every: require("./every"),
functionName: require("./function-name"),
orderByFirstCall: require("./order-by-first-call"),
prototypes: require("./prototypes"),
typeOf: require("./type-of"),
valueToString: require("./value-to-string")
};
},{"./called-in-order":18,"./class-name":19,"./deprecated":20,"./every":21,"./function-name":22,"./global":23,"./order-by-first-call":25,"./prototypes":29,"./type-of":34,"./value-to-string":35}],25:[function(require,module,exports){
"use strict";
var sort = require("./prototypes/array").sort;
var slice = require("./prototypes/array").slice;
/**
* @private
*/
function comparator(a, b) {
// uuid, won't ever be equal
var aCall = a.getCall(0);
var bCall = b.getCall(0);
var aId = (aCall && aCall.callId) || -1;
var bId = (bCall && bCall.callId) || -1;
return aId < bId ? -1 : 1;
}
/**
* A Sinon proxy object (fake, spy, stub)
*
* @typedef {object} SinonProxy
* @property {Function} getCall - A method that can return the first call
*/
/**
* Sorts an array of SinonProxy instances (fake, spy, stub) by their first call
*
* @param {SinonProxy[] | SinonProxy} spies
* @returns {SinonProxy[]}
*/
function orderByFirstCall(spies) {
return sort(slice(spies), comparator);
}
module.exports = orderByFirstCall;
},{"./prototypes/array":26}],26:[function(require,module,exports){
"use strict";
var copyPrototype = require("./copy-prototype");
module.exports = copyPrototype(Array.prototype);
},{"./copy-prototype":27}],27:[function(require,module,exports){
"use strict";
var call = Function.call;
module.exports = function copyPrototypeMethods(prototype) {
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
return Object.getOwnPropertyNames(prototype).reduce(function(result, name) {
// ignore size because it throws from Map
if (
name !== "size" &&
name !== "caller" &&
name !== "callee" &&
name !== "arguments" &&
typeof prototype[name] === "function"
) {
result[name] = call.bind(prototype[name]);
}
return result;
}, Object.create(null));
};
},{}],28:[function(require,module,exports){
"use strict";
var copyPrototype = require("./copy-prototype");
module.exports = copyPrototype(Function.prototype);
},{"./copy-prototype":27}],29:[function(require,module,exports){
"use strict";
module.exports = {
array: require("./array"),
function: require("./function"),
map: require("./map"),
object: require("./object"),
set: require("./set"),
string: require("./string")
};
},{"./array":26,"./function":28,"./map":30,"./object":31,"./set":32,"./string":33}],30:[function(require,module,exports){
"use strict";
var copyPrototype = require("./copy-prototype");
module.exports = copyPrototype(Map.prototype);
},{"./copy-prototype":27}],31:[function(require,module,exports){
"use strict";
var copyPrototype = require("./copy-prototype");
module.exports = copyPrototype(Object.prototype);
},{"./copy-prototype":27}],32:[function(require,module,exports){
"use strict";
var copyPrototype = require("./copy-prototype");
module.exports = copyPrototype(Set.prototype);
},{"./copy-prototype":27}],33:[function(require,module,exports){
"use strict";
var copyPrototype = require("./copy-prototype");
module.exports = copyPrototype(String.prototype);
},{"./copy-prototype":27}],34:[function(require,module,exports){
"use strict";
var type = require("type-detect");
/**
* Returns the lower-case result of running type from type-detect on the value
*
* @param {*} value
* @returns {string}
*/
module.exports = function typeOf(value) {
return type(value).toLowerCase();
};
},{"type-detect":195}],35:[function(require,module,exports){
"use strict";
/**
* Returns a string representation of the value
*
* @param {*} value
* @returns {string}
*/
function valueToString(value) {
if (value && value.toString) {
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
return value.toString();
}
return String(value);
}
module.exports = valueToString;
},{}],36:[function(require,module,exports){
"use strict";
const globalObject = require("@sinonjs/commons").global;
/**
* @typedef {object} IdleDeadline
* @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout
* @property {function():number} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period
*/
/**
* Queues a function to be called during a browser's idle periods
*
* @callback RequestIdleCallback
* @param {function(IdleDeadline)} callback
* @param {{timeout: number}} options - an options object
* @returns {number} the id
*/
/**
* @callback NextTick
* @param {VoidVarArgsFunc} callback - the callback to run
* @param {...*} arguments - optional arguments to call the callback with
* @returns {void}
*/
/**
* @callback SetImmediate
* @param {VoidVarArgsFunc} callback - the callback to run
* @param {...*} arguments - optional arguments to call the callback with
* @returns {NodeImmediate}
*/
/**
* @callback VoidVarArgsFunc
* @param {...*} callback - the callback to run
* @returns {void}
*/
/**
* @typedef RequestAnimationFrame
* @property {function(number):void} requestAnimationFrame
* @returns {number} - the id
*/
/**
* @typedef Performance
* @property {function(): number} now
*/
/* eslint-disable jsdoc/require-property-description */
/**
* @typedef {object} Clock
* @property {number} now - the current time
* @property {Date} Date - the Date constructor
* @property {number} loopLimit - the maximum number of timers before assuming an infinite loop
* @property {RequestIdleCallback} requestIdleCallback
* @property {function(number):void} cancelIdleCallback
* @property {setTimeout} setTimeout
* @property {clearTimeout} clearTimeout
* @property {NextTick} nextTick
* @property {queueMicrotask} queueMicrotask
* @property {setInterval} setInterval
* @property {clearInterval} clearInterval
* @property {SetImmediate} setImmediate
* @property {function(NodeImmediate):void} clearImmediate
* @property {function():number} countTimers
* @property {RequestAnimationFrame} requestAnimationFrame
* @property {function(number):void} cancelAnimationFrame
* @property {function():void} runMicrotasks
* @property {function(string | number): number} tick
* @property {function(string | number): Promise<number>} tickAsync
* @property {function(): number} next
* @property {function(): Promise<number>} nextAsync
* @property {function(): number} runAll
* @property {function(): number} runToFrame
* @property {function(): Promise<number>} runAllAsync
* @property {function(): number} runToLast
* @property {function(): Promise<number>} runToLastAsync
* @property {function(): void} reset
* @property {function(number | Date): void} setSystemTime
* @property {Performance} performance
* @property {function(number[]): number[]} hrtime - process.hrtime (legacy)
* @property {function(): void} uninstall Uninstall the clock.
* @property {Function[]} methods - the methods that are faked
* @property {boolean} [shouldClearNativeTimers] inherited from config
*/
/* eslint-enable jsdoc/require-property-description */
/**
* Configuration object for the `install` method.
*
* @typedef {object} Config
* @property {number|Date} [now] a number (in milliseconds) or a Date object (default epoch)
* @property {string[]} [toFake] names of the methods that should be faked.
* @property {number} [loopLimit] the maximum number of timers that will be run when calling runAll()
* @property {boolean} [shouldAdvanceTime] tells FakeTimers to increment mocked time automatically (default false)
* @property {number} [advanceTimeDelta] increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
* @property {boolean} [shouldClearNativeTimers] forwards clear timer calls to native functions if they are not fakes (default: false)
*/
/* eslint-disable jsdoc/require-property-description */
/**
* The internal structure to describe a scheduled fake timer
*
* @typedef {object} Timer
* @property {Function} func
* @property {*[]} args
* @property {number} delay
* @property {number} callAt
* @property {number} createdAt
* @property {boolean} immediate
* @property {number} id
* @property {Error} [error]
*/
/**
* A Node timer
*
* @typedef {object} NodeImmediate
* @property {function(): boolean} hasRef
* @property {function(): NodeImmediate} ref
* @property {function(): NodeImmediate} unref
*/
/* eslint-enable jsdoc/require-property-description */
/* eslint-disable complexity */
/**
* Mocks available features in the specified global namespace.
*
* @param {*} _global Namespace to mock (e.g. `window`)
* @returns {FakeTimers}
*/
function withGlobal(_global) {
const userAgent = _global.navigator && _global.navigator.userAgent;
const isRunningInIE = userAgent && userAgent.indexOf("MSIE ") > -1;
const maxTimeout = Math.pow(2, 31) - 1; //see https://heycam.github.io/webidl/#abstract-opdef-converttoint
const idCounterStart = 1e12; // arbitrarily large number to avoid collisions with native timer IDs
const NOOP = function () {
return undefined;
};
const NOOP_ARRAY = function () {
return [];
};
const timeoutResult = _global.setTimeout(NOOP, 0);
const addTimerReturnsObject = typeof timeoutResult === "object";
const hrtimePresent =
_global.process && typeof _global.process.hrtime === "function";
const hrtimeBigintPresent =
hrtimePresent && typeof _global.process.hrtime.bigint === "function";
const nextTickPresent =
_global.process && typeof _global.process.nextTick === "function";
const utilPromisify = _global.process && require("util").promisify;
const performancePresent =
_global.performance && typeof _global.performance.now === "function";
const hasPerformancePrototype =
_global.Performance &&
(typeof _global.Performance).match(/^(function|object)$/);
const hasPerformanceConstructorPrototype =
_global.performance &&
_global.performance.constructor &&
_global.performance.constructor.prototype;
const queueMicrotaskPresent = _global.hasOwnProperty("queueMicrotask");
const requestAnimationFramePresent =
_global.requestAnimationFrame &&
typeof _global.requestAnimationFrame === "function";
const cancelAnimationFramePresent =
_global.cancelAnimationFrame &&
typeof _global.cancelAnimationFrame === "function";
const requestIdleCallbackPresent =
_global.requestIdleCallback &&
typeof _global.requestIdleCallback === "function";
const cancelIdleCallbackPresent =
_global.cancelIdleCallback &&
typeof _global.cancelIdleCallback === "function";
const setImmediatePresent =
_global.setImmediate && typeof _global.setImmediate === "function";
// Make properties writable in IE, as per
// https://www.adequatelygood.com/Replacing-setTimeout-Globally.html
/* eslint-disable no-self-assign */
if (isRunningInIE) {
_global.setTimeout = _global.setTimeout;
_global.clearTimeout = _global.clearTimeout;
_global.setInterval = _global.setInterval;
_global.clearInterval = _global.clearInterval;
_global.Date = _global.Date;
}
// setImmediate is not a standard function
// avoid adding the prop to the window object if not present
if (setImmediatePresent) {
_global.setImmediate = _global.setImmediate;
_global.clearImmediate = _global.clearImmediate;
}
/* eslint-enable no-self-assign */
_global.clearTimeout(timeoutResult);
const NativeDate = _global.Date;
let uniqueTimerId = idCounterStart;
/**
* @param {number} num
* @returns {boolean}
*/
function isNumberFinite(num) {
if (Number.isFinite) {
return Number.isFinite(num);
}
return isFinite(num);
}
let isNearInfiniteLimit = false;
/**
* @param {Clock} clock
* @param {number} i
*/
function checkIsNearInfiniteLimit(clock, i) {
if (clock.loopLimit && i === clock.loopLimit - 1) {
isNearInfiniteLimit = true;
}
}
/**
*
*/
function resetIsNearInfiniteLimit() {
isNearInfiniteLimit = false;
}
/**
* Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
* number of milliseconds. This is used to support human-readable strings passed
* to clock.tick()
*
* @param {string} str
* @returns {number}
*/
function parseTime(str) {
if (!str) {
return 0;
}
const strings = str.split(":");
const l = strings.length;
let i = l;
let ms = 0;
let parsed;
if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
throw new Error(
"tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits"
);
}
while (i--) {
parsed = parseInt(strings[i], 10);
if (parsed >= 60) {
throw new Error(`Invalid time ${str}`);
}
ms += parsed * Math.pow(60, l - i - 1);
}
return ms * 1000;
}
/**
* Get the decimal part of the millisecond value as nanoseconds
*
* @param {number} msFloat the number of milliseconds
* @returns {number} an integer number of nanoseconds in the range [0,1e6)
*
* Example: nanoRemainer(123.456789) -> 456789
*/
function nanoRemainder(msFloat) {
const modulo = 1e6;
const remainder = (msFloat * 1e6) % modulo;
const positiveRemainder =
remainder < 0 ? remainder + modulo : remainder;
return Math.floor(positiveRemainder);
}
/**
* Used to grok the `now` parameter to createClock.
*
* @param {Date|number} epoch the system time
* @returns {number}
*/
function getEpoch(epoch) {
if (!epoch) {
return 0;
}
if (typeof epoch.getTime === "function") {
return epoch.getTime();
}
if (typeof epoch === "number") {
return epoch;
}
throw new TypeError("now should be milliseconds since UNIX epoch");
}
/**
* @param {number} from
* @param {number} to
* @param {Timer} timer
* @returns {boolean}
*/
function inRange(from, to, timer) {
return timer && timer.callAt >= from && timer.callAt <= to;
}
/**
* @param {Clock} clock
* @param {Timer} job
*/
function getInfiniteLoopError(clock, job) {
const infiniteLoopError = new Error(
`Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!`
);
if (!job.error) {
return infiniteLoopError;
}
// pattern never matched in Node
const computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/;
let clockMethodPattern = new RegExp(
String(Object.keys(clock).join("|"))
);
if (addTimerReturnsObject) {
// node.js environment
clockMethodPattern = new RegExp(
`\\s+at (Object\\.)?(?:${Object.keys(clock).join("|")})\\s+`
);
}
let matchedLineIndex = -1;
job.error.stack.split("\n").some(function (line, i) {
// If we've matched a computed target line (e.g. setTimeout) then we
// don't need to look any further. Return true to stop iterating.
const matchedComputedTarget = line.match(computedTargetPattern);
/* istanbul ignore if */
if (matchedComputedTarget) {
matchedLineIndex = i;
return true;
}
// If we've matched a clock method line, then there may still be
// others further down the trace. Return false to keep iterating.
const matchedClockMethod = line.match(clockMethodPattern);
if (matchedClockMethod) {
matchedLineIndex = i;
return false;
}
// If we haven't matched anything on this line, but we matched
// previously and set the matched line index, then we can stop.
// If we haven't matched previously, then we should keep iterating.
return matchedLineIndex >= 0;
});
const stack = `${infiniteLoopError}\n${job.type || "Microtask"} - ${
job.func.name || "anonymous"
}\n${job.error.stack
.split("\n")
.slice(matchedLineIndex + 1)
.join("\n")}`;
try {
Object.defineProperty(infiniteLoopError, "stack", {
value: stack,
});
} catch (e) {
// noop
}
return infiniteLoopError;
}
/**
* @param {Date} target
* @param {Date} source
* @returns {Date} the target after modifications
*/
function mirrorDateProperties(target, source) {
let prop;
for (prop in source) {