jolokia
Version:
Implementation of jolokia JMX client
1,674 lines (1,440 loc) • 79.9 kB
JavaScript
/**
* Jolokia JavaScript client library
*
* Version 1.0.6
*
* GitHub repository can be found at https://github.com/janjakubnanista/jolokia-js-client
*
* Released under MIT license
*
* Created by Ján Jakub Naništa <jan.jakub.nanista@gmail.com>
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("$"));
else if(typeof define === 'function' && define.amd)
define(["$"], factory);
else if(typeof exports === 'object')
exports["Jolokia"] = factory(require("$"));
else
root["Jolokia"] = factory(root["$"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var BaseJolokia = __webpack_require__(2);
var $ = __webpack_require__(1);
var q = __webpack_require__(4);
var utils = __webpack_require__(3);
var Jolokia = module.exports = function() {
BaseJolokia.apply(this, arguments);
};
utils.inherit(Jolokia, BaseJolokia);
Jolokia.prototype.httpRequest = function(options) {
options.processData = false;
options.dataType = options.dataType || 'json';
if (options.auth) {
var username = options.auth.username,
password = options.auth.password;
// Create base64 encoded authorization token
options.headers = {
Authorization: 'Basic ' + utils.base64encode(username + ':' + password)
};
// Add appropriate field for CORS access
options.xhrFields = {
// Please note that for CORS access with credentials, the request
// must be asynchronous (see https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#the-withcredentials-attribute)
// It works synchronously in Chrome nevertheless, but fails in Firefox.
withCredentials: true
};
delete options.auth;
}
return q($.ajax(options));
};
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
module.exports = $;
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var utils = __webpack_require__(3);
var q = __webpack_require__(4);
/**
* Constructor method.
*
* Accepts one argument, either an URL of backend
* to use with requests or hash of options (refer to documentation of Jolokia.request
* for available options).
*
* @param {String|Object} options Hash of options or String URL to use with requests
*/
var Jolokia = module.exports = function(options) {
if (typeof(options) === 'string') {
options = { url: options };
}
options = utils.extend({}, options);
this.options = function() {
return utils.extend({}, options);
};
};
/**
* Escapes !, " and /, then URI encodes string
*
* @param {String} string String to be escaped
* @return {String} Escaped string
*/
Jolokia.escape = function(string) {
return encodeURIComponent(string.replace(/(["!\/])/g, '!$1'));
};
/**
* Returns true if passed response object represents an error state
*
* @param {Object} response Jolokia server response
* @return {Boolean} True if response object represents an error
*/
Jolokia.isError = function(response) {
return !response.status || response.status !== 200;
};
/**
* Converts a value to string to be used to construct URL for GET requests.
* Value is not escaped though, it must be done separately via Jolokia.escape method
*
* @param {Any} value Value to be converted
* @return {String} String representation of the value
*/
Jolokia.toString = function(value) {
if (utils.isArray(value)) {
return value.map(valueToString).join(',');
} else {
return valueToString(value);
}
};
/**
* Automatically determine HTTP request method to be used for particular request.
* This follows the specification for jolokia requests that can be found at
* http://www.jolokia.org/reference/html/clients.html#js-request-sync-async
*
* @param {Object|Array} request Jolokia request
* @return {String} Lowercase HTTP method, either post or get
*/
Jolokia.prototype.determineRequestMethod = function(request) {
var method =
utils.isArray(request) ||
request.config ||
(request.type.match(/read/i) && utils.isArray(request.attribute)) ||
request.target ?
'post' : 'get';
return method;
};
/**
* Create an url to be used with GET requests
*
* @param {Object} request Jolokia request
* @return {String} URL to be used with HTTP GET request
*/
Jolokia.prototype.constructUrl = function(request) {
var type = request.type.toLowerCase();
var urlParts = [];
var urlSuffix = '';
if (type === 'read') {
if (request.attribute) {
urlParts = [request.mbean, request.attribute];
urlSuffix = request.path;
} else {
urlParts = [request.mbean];
}
} else if (type === 'write') {
urlParts = [request.mbean, request.attribute, Jolokia.toString(request.value)];
urlSuffix = request.path;
} else if (type === 'exec') {
urlParts = [request.mbean, request.operation];
if (request.arguments) {
urlParts = urlParts.concat(request.arguments.map(Jolokia.toString));
}
} else if (type === 'search') {
urlParts = [request.mbean];
} else if (type === 'list') {
urlSuffix = request.path;
}
if (utils.isArray(urlSuffix)) {
urlSuffix = urlSuffix.map(Jolokia.escape).join('/');
}
// Add request operation at the beginning of the URL
urlParts.unshift(type);
urlParts = urlParts.map(Jolokia.escape);
urlSuffix = urlSuffix ? urlSuffix.replace(/^\//, '') : '';
return urlParts.join('/') + '/' + urlSuffix;
};
/**
* Central method used to create HTTP requests
*
* @param {Object|Array} request Jolokia request object or an array of objects
* @param {Object} options [optional] Additional options for request
* @return {jQuery.Promise} jQuery jXHR promise resolved with an array of jolokia server response objects
*/
Jolokia.prototype.request = function (request, options) {
options = utils.extend({}, this.options(), options);
if (!options.url || typeof(options.url) !== 'string') {
throw 'Invalid URL : ' + options.url;
}
var method = (options.method || this.determineRequestMethod(request)).toLowerCase();
if (method !== 'get' && method !== 'post') {
throw new Error('Unsupported request method: ' + method);
}
if (method === 'get') {
if (utils.isArray(request)) {
throw new Error('Cannot use GET with bulk requests');
}
if (request.type.match(/read/i) && utils.isArray(request.attribute)) {
throw new Error('Cannot use GET for read with multiple attributes');
}
if (request.target) {
throw new Error('Cannot use GET request with proxy mode');
}
if (request.config) {
throw new Error('Cannot use GET with request specific config');
}
}
// Add trailing slash to URL
options.url = options.url.replace(/\/*$/, '/');
if (method === 'get') {
options.url += this.constructUrl(request, options);
} else {
options.data = JSON.stringify(request);
}
// Add request method and URL
options.method = method;
options.url = addQueryParamsToUrl(options.url, options.query || {});
return this.httpRequest(options).then(function(response) {
var responses = utils.isArray(response) ? response: [response];
if (typeof(options.success) === 'function') {
options.success.call(this, responses);
}
return responses;
}.bind(this), options.error);
};
/**
* Get one or more attributes
*
* @param {String} mbean name of MBean to query. Can be a pattern.
* @param {String} attribute attribute name. If an array, multiple attributes are fetched.
* If <code>null</code>, all attributes are fetched.
* @param {String} path optional path within the return value.
* For multi-attribute fetch, the path is ignored.
* @param {Object} options options passed to Jolokia.request()
*
* @return {jQuery.Promise}
*/
Jolokia.prototype.get = function(mbean, attribute, path, options) {
if (arguments.length === 3 && utils.isObject(path)) {
options = path;
path = null;
} else if (arguments.length === 2 && utils.isObject(attribute)) {
options = attribute;
attribute = null;
path = null;
}
var request = { type: 'read', mbean: mbean, attribute: attribute };
if (path) {
request.path = path;
}
return this.request(request, options).then(returnValueOrThrow);
};
/**
* Set an attribute on a MBean.
*
* @param mbean objectname of MBean to set
* @param attribute the attribute to set
* @param value the value to set
* @param path an optional <em>inner path</em> which, when given, is used to determine
* an inner object to set the value on
* @param opts additional options passed to Jolokia.request()
* @return the previous value
*/
Jolokia.prototype.set = function(mbean, attribute, value, path, options) {
if (arguments.length === 4 && utils.isObject(path)) {
options = path;
path = null;
}
var request = { type: 'write', mbean: mbean, attribute: attribute, value: value };
if (path) {
request.path = path;
}
return this.request(request, options).then(returnValueOrThrow);
};
/**
* Execute a JMX operation and return the result value
*
* @param mbean objectname of the MBean to operate on
* @param operation name of operation to execute. Can contain a signature in case overloaded
* operations are to be called (comma separated fully qualified argument types
* append to the operation name within parentheses)
* @param arg1, arg2, ..... one or more argument required for executing the operation.
* @param opts optional options for Jolokia.request() (must be an object)
* @return the return value of the JMX operation.
*/
Jolokia.prototype.execute = function(mbean, operation) {
var request = { type: 'exec', mbean: mbean, operation: operation };
var options,
args = Array.prototype.slice.call(arguments, 0);
if (args.length > 2 && utils.isObject(args[args.length - 1])) {
options = args.pop();
}
if (args.length > 2) {
request.arguments = args.slice(2);
}
return this.request(request, options).then(returnValueOrThrow);
};
/**
* Search for MBean based on a pattern and return a reference to the list of found
* MBeans names (as string). If no MBean can be found, <code>null</code> is returned. For
* example,
*
* jolokia.search("*:j2eeType=J2EEServer,*")
*
* searches all MBeans whose name are matching this pattern, which are according
* to JSR77 all application servers in all available domains.
*
* @param mbeanPattern pattern to search for
* @param opts optional options for Jolokia.request()
* @return an array with ObjectNames as string
*/
Jolokia.prototype.search = function(mbeanPattern, options) {
var request = { type: 'search', mbean: mbeanPattern };
return this.request(request, options).then(returnValueOrThrow);
};
/**
* This method return the version of the agent and the Jolokia protocol
* version as part of an object. If available, server specific information
* like the application server's name are returned as wel.
* A typical response looks like
*
* <pre>
* {
* protocol: "4.0",
* agent: "0.82",
* info: {
* product: "glassfish",
* vendor": "Sun",
* extraInfo: {
* amxBooted: false
* }
* }
* </pre>
*
* @param opts optional options for Jolokia.request()
* @param version and other meta information as object
*/
Jolokia.prototype.version = function(options) {
return this.request({ type: 'version' }, options).then(returnValueOrThrow);
};
/**
* Get all MBeans as registered at the specified server. A C<$path> can be
* specified in order to fetchy only a subset of the information. When no path is
* given, the returned value has the following format
*
* <pre>
* {
* <domain> :
* {
* <canonical property list> :
* {
* "attr" :
* {
* <atrribute name> :
* {
* desc : <description of attribute>
* type : <java type>,
* rw : true/false
* },
* ....
* },
* "op" :
* {
* <operation name> :
* {
* "desc" : <description of operation>
* "ret" : <return java type>
* "args" :
* [
* {
* "desc" : <description>,
* "name" : <name>,
* "type" : <java type>
* },
* ....
* ]
* },
* ....
* },
* ....
* }
* ....
* }
* </pre>
*
* A complete path has the format <domain>/property
* list>/("attribute"|"operation")/<index>">
* (e.g. <code>java.lang/name=Code Cache,type=MemoryPool/attribute/0</code>). A path can be
* provided partially, in which case the remaining map/array is returned. The path given must
* be already properly escaped (i.e. slashes must be escaped like <code>!/</code> and exlamation
* marks like <code>!!</code>.
* See also the Jolokia Reference Manual for a more detailed discussion of inner paths and escaping.
*
*
* @param path optional path for diving into the list
* @param opts optional opts passed to Jolokia.request()
*/
Jolokia.prototype.list = function(path, options) {
if (arguments.length === 1 && !utils.isArray(path) && utils.isObject(path)) {
options = path;
path = null;
}
var request = { type: 'list' };
if (path) {
request.path = path;
}
return this.request(request, options).then(returnValueOrThrow);
};
/**
* Private helper function.
* Returns the value attribute from server response or throws an exception
* in case of error.
*
* @param {Array} responses Jolokia server responses
* @return {Mixed} Response value
*/
function returnValueOrThrow(responses) {
if (responses[0].status !== 200) {
return q.reject(responses[0]);
}
return responses[0].value;
}
/**
* Private helper function.
* Serializes value to be passed over to Jolokia backend
* following Jolokia protocol.
*
* @param {Object} value Value to serialize
* @return {String} Serialized value
*/
function valueToString(value) {
if (value === null || value === undefined) return '[null]';
if (value === '') return '""';
return value.toString();
}
/**
* Private helper function.
* Adds URL query parameters to URL.
*
* @param {String} url URL
* @param {Object} params Hash containing query parameters
*/
function addQueryParamsToUrl(url, params) {
var query = [];
for (var key in params) {
query.push(key + '=' + params[key]);
}
query = query.join('&');
if (query) {
if (url.indexOf('?') === -1) {
url = url + '?' + query;
} else {
url = url.replace('?', '?' + query + '&');
}
}
return url;
}
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
/**
* Mix multiple objects into target object.
*
* @param {Object} target Target object
* @param {...Object} mixins Mixins to enhance target object with
* @return {Object} Target object
*/
exports.extend = function(target) {
var mixins = Array.prototype.slice.call(arguments, 1);
var getter, setter;
return mixins.reduce(function(target, mixin) {
if (mixin === null || typeof(mixin) !== 'object') return target;
return Object.keys(mixin).reduce(function(target, key) {
getter = mixin.__lookupGetter__(key);
setter = mixin.__lookupSetter__(key);
if (getter || setter) {
if (getter) target.__defineGetter__(key, getter);
if (setter) target.__defineSetter__(key, setter);
} else {
target[key] = mixin[key];
}
return target;
}, target);
}, target);
};
exports.isArray = function(object) {
return Object.prototype.toString.call(object) === '[object Array]';
};
exports.isObject = function(object) {
return object !== null && typeof(object) === 'object';
};
exports.base64encode = function(string) {
if (typeof(window) !== 'undefined') {
return window.btoa(string);
}
if (typeof('Buffer') !== undefined) {
return new Buffer(string, 'utf8').toString('base64');
}
throw new Error('Unable to find window.btoa() or Buffer to convert to Base64');
};
exports.inherit = function(subClass, superClass) {
// Inherit from BaseJolokia
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
for (var i in superClass) {
if (typeof(superClass[i]) === 'function') {
subClass[i] = superClass[i];
}
}
return subClass;
};
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
// vim:ts=4:sts=4:sw=4:
/*!
*
* Copyright 2009-2012 Kris Kowal under the terms of the MIT
* license found at http://github.com/kriskowal/q/raw/master/LICENSE
*
* With parts by Tyler Close
* Copyright 2007-2009 Tyler Close under the terms of the MIT X license found
* at http://www.opensource.org/licenses/mit-license.html
* Forked at ref_send.js version: 2009-05-11
*
* With parts by Mark Miller
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
(function (definition) {
"use strict";
// This file will function properly as a <script> tag, or a module
// using CommonJS and NodeJS or RequireJS module formats. In
// Common/Node/RequireJS, the module exports the Q API and when
// executed as a simple <script>, it creates a Q global instead.
// Montage Require
if (typeof bootstrap === "function") {
bootstrap("promise", definition);
// CommonJS
} else if (true) {
module.exports = definition();
// RequireJS
} else if (typeof define === "function" && define.amd) {
define(definition);
// SES (Secure EcmaScript)
} else if (typeof ses !== "undefined") {
if (!ses.ok()) {
return;
} else {
ses.makeQ = definition;
}
// <script>
} else if (typeof self !== "undefined") {
self.Q = definition();
} else {
throw new Error("This environment was not anticiapted by Q. Please file a bug.");
}
})(function () {
"use strict";
var hasStacks = false;
try {
throw new Error();
} catch (e) {
hasStacks = !!e.stack;
}
// All code after this point will be filtered from stack traces reported
// by Q.
var qStartingLine = captureLine();
var qFileName;
// shims
// used for fallback in "allResolved"
var noop = function () {};
// Use the fastest possible means to execute a task in a future turn
// of the event loop.
var nextTick =(function () {
// linked list of tasks (single, with head node)
var head = {task: void 0, next: null};
var tail = head;
var flushing = false;
var requestTick = void 0;
var isNodeJS = false;
function flush() {
/* jshint loopfunc: true */
while (head.next) {
head = head.next;
var task = head.task;
head.task = void 0;
var domain = head.domain;
if (domain) {
head.domain = void 0;
domain.enter();
}
try {
task();
} catch (e) {
if (isNodeJS) {
// In node, uncaught exceptions are considered fatal errors.
// Re-throw them synchronously to interrupt flushing!
// Ensure continuation if the uncaught exception is suppressed
// listening "uncaughtException" events (as domains does).
// Continue in next event to avoid tick recursion.
if (domain) {
domain.exit();
}
setTimeout(flush, 0);
if (domain) {
domain.enter();
}
throw e;
} else {
// In browsers, uncaught exceptions are not fatal.
// Re-throw them asynchronously to avoid slow-downs.
setTimeout(function() {
throw e;
}, 0);
}
}
if (domain) {
domain.exit();
}
}
flushing = false;
}
nextTick = function (task) {
tail = tail.next = {
task: task,
domain: isNodeJS && process.domain,
next: null
};
if (!flushing) {
flushing = true;
requestTick();
}
};
if (typeof process !== "undefined" && process.nextTick) {
// Node.js before 0.9. Note that some fake-Node environments, like the
// Mocha test runner, introduce a `process` global without a `nextTick`.
isNodeJS = true;
requestTick = function () {
process.nextTick(flush);
};
} else if (typeof setImmediate === "function") {
// In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
if (typeof window !== "undefined") {
requestTick = setImmediate.bind(window, flush);
} else {
requestTick = function () {
setImmediate(flush);
};
}
} else if (typeof MessageChannel !== "undefined") {
// modern browsers
// http://www.nonblocking.io/2011/06/windownexttick.html
var channel = new MessageChannel();
// At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create
// working message ports the first time a page loads.
channel.port1.onmessage = function () {
requestTick = requestPortTick;
channel.port1.onmessage = flush;
flush();
};
var requestPortTick = function () {
// Opera requires us to provide a message payload, regardless of
// whether we use it.
channel.port2.postMessage(0);
};
requestTick = function () {
setTimeout(flush, 0);
requestPortTick();
};
} else {
// old browsers
requestTick = function () {
setTimeout(flush, 0);
};
}
return nextTick;
})();
// Attempt to make generics safe in the face of downstream
// modifications.
// There is no situation where this is necessary.
// If you need a security guarantee, these primordials need to be
// deeply frozen anyway, and if you don’t need a security guarantee,
// this is just plain paranoid.
// However, this **might** have the nice side-effect of reducing the size of
// the minified code by reducing x.call() to merely x()
// See Mark Miller’s explanation of what this does.
// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
var call = Function.call;
function uncurryThis(f) {
return function () {
return call.apply(f, arguments);
};
}
// This is equivalent, but slower:
// uncurryThis = Function_bind.bind(Function_bind.call);
// http://jsperf.com/uncurrythis
var array_slice = uncurryThis(Array.prototype.slice);
var array_reduce = uncurryThis(
Array.prototype.reduce || function (callback, basis) {
var index = 0,
length = this.length;
// concerning the initial value, if one is not provided
if (arguments.length === 1) {
// seek to the first value in the array, accounting
// for the possibility that is is a sparse array
do {
if (index in this) {
basis = this[index++];
break;
}
if (++index >= length) {
throw new TypeError();
}
} while (1);
}
// reduce
for (; index < length; index++) {
// account for the possibility that the array is sparse
if (index in this) {
basis = callback(basis, this[index], index);
}
}
return basis;
}
);
var array_indexOf = uncurryThis(
Array.prototype.indexOf || function (value) {
// not a very good shim, but good enough for our one use of it
for (var i = 0; i < this.length; i++) {
if (this[i] === value) {
return i;
}
}
return -1;
}
);
var array_map = uncurryThis(
Array.prototype.map || function (callback, thisp) {
var self = this;
var collect = [];
array_reduce(self, function (undefined, value, index) {
collect.push(callback.call(thisp, value, index, self));
}, void 0);
return collect;
}
);
var object_create = Object.create || function (prototype) {
function Type() { }
Type.prototype = prototype;
return new Type();
};
var object_hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty);
var object_keys = Object.keys || function (object) {
var keys = [];
for (var key in object) {
if (object_hasOwnProperty(object, key)) {
keys.push(key);
}
}
return keys;
};
var object_toString = uncurryThis(Object.prototype.toString);
function isObject(value) {
return value === Object(value);
}
// generator related shims
// FIXME: Remove this function once ES6 generators are in SpiderMonkey.
function isStopIteration(exception) {
return (
object_toString(exception) === "[object StopIteration]" ||
exception instanceof QReturnValue
);
}
// FIXME: Remove this helper and Q.return once ES6 generators are in
// SpiderMonkey.
var QReturnValue;
if (typeof ReturnValue !== "undefined") {
QReturnValue = ReturnValue;
} else {
QReturnValue = function (value) {
this.value = value;
};
}
// long stack traces
var STACK_JUMP_SEPARATOR = "From previous event:";
function makeStackTraceLong(error, promise) {
// If possible, transform the error stack trace by removing Node and Q
// cruft, then concatenating with the stack trace of `promise`. See #57.
if (hasStacks &&
promise.stack &&
typeof error === "object" &&
error !== null &&
error.stack &&
error.stack.indexOf(STACK_JUMP_SEPARATOR) === -1
) {
var stacks = [];
for (var p = promise; !!p; p = p.source) {
if (p.stack) {
stacks.unshift(p.stack);
}
}
stacks.unshift(error.stack);
var concatedStacks = stacks.join("\n" + STACK_JUMP_SEPARATOR + "\n");
error.stack = filterStackString(concatedStacks);
}
}
function filterStackString(stackString) {
var lines = stackString.split("\n");
var desiredLines = [];
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
if (!isInternalFrame(line) && !isNodeFrame(line) && line) {
desiredLines.push(line);
}
}
return desiredLines.join("\n");
}
function isNodeFrame(stackLine) {
return stackLine.indexOf("(module.js:") !== -1 ||
stackLine.indexOf("(node.js:") !== -1;
}
function getFileNameAndLineNumber(stackLine) {
// Named functions: "at functionName (filename:lineNumber:columnNumber)"
// In IE10 function name can have spaces ("Anonymous function") O_o
var attempt1 = /at .+ \((.+):(\d+):(?:\d+)\)$/.exec(stackLine);
if (attempt1) {
return [attempt1[1], Number(attempt1[2])];
}
// Anonymous functions: "at filename:lineNumber:columnNumber"
var attempt2 = /at ([^ ]+):(\d+):(?:\d+)$/.exec(stackLine);
if (attempt2) {
return [attempt2[1], Number(attempt2[2])];
}
// Firefox style: "function@filename:lineNumber or @filename:lineNumber"
var attempt3 = /.*@(.+):(\d+)$/.exec(stackLine);
if (attempt3) {
return [attempt3[1], Number(attempt3[2])];
}
}
function isInternalFrame(stackLine) {
var fileNameAndLineNumber = getFileNameAndLineNumber(stackLine);
if (!fileNameAndLineNumber) {
return false;
}
var fileName = fileNameAndLineNumber[0];
var lineNumber = fileNameAndLineNumber[1];
return fileName === qFileName &&
lineNumber >= qStartingLine &&
lineNumber <= qEndingLine;
}
// discover own file name and line number range for filtering stack
// traces
function captureLine() {
if (!hasStacks) {
return;
}
try {
throw new Error();
} catch (e) {
var lines = e.stack.split("\n");
var firstLine = lines[0].indexOf("@") > 0 ? lines[1] : lines[2];
var fileNameAndLineNumber = getFileNameAndLineNumber(firstLine);
if (!fileNameAndLineNumber) {
return;
}
qFileName = fileNameAndLineNumber[0];
return fileNameAndLineNumber[1];
}
}
function deprecate(callback, name, alternative) {
return function () {
if (typeof console !== "undefined" &&
typeof console.warn === "function") {
console.warn(name + " is deprecated, use " + alternative +
" instead.", new Error("").stack);
}
return callback.apply(callback, arguments);
};
}
// end of shims
// beginning of real work
/**
* Constructs a promise for an immediate reference, passes promises through, or
* coerces promises from different systems.
* @param value immediate reference or promise
*/
function Q(value) {
// If the object is already a Promise, return it directly. This enables
// the resolve function to both be used to created references from objects,
// but to tolerably coerce non-promises to promises.
if (value instanceof Promise) {
return value;
}
// assimilate thenables
if (isPromiseAlike(value)) {
return coerce(value);
} else {
return fulfill(value);
}
}
Q.resolve = Q;
/**
* Performs a task in a future turn of the event loop.
* @param {Function} task
*/
Q.nextTick = nextTick;
/**
* Controls whether or not long stack traces will be on
*/
Q.longStackSupport = false;
// enable long stacks if Q_DEBUG is set
if (typeof process === "object" && process && process.env && process.env.Q_DEBUG) {
Q.longStackSupport = true;
}
/**
* Constructs a {promise, resolve, reject} object.
*
* `resolve` is a callback to invoke with a more resolved value for the
* promise. To fulfill the promise, invoke `resolve` with any value that is
* not a thenable. To reject the promise, invoke `resolve` with a rejected
* thenable, or invoke `reject` with the reason directly. To resolve the
* promise to another thenable, thus putting it in the same state, invoke
* `resolve` with that other thenable.
*/
Q.defer = defer;
function defer() {
// if "messages" is an "Array", that indicates that the promise has not yet
// been resolved. If it is "undefined", it has been resolved. Each
// element of the messages array is itself an array of complete arguments to
// forward to the resolved promise. We coerce the resolution value to a
// promise using the `resolve` function because it handles both fully
// non-thenable values and other thenables gracefully.
var messages = [], progressListeners = [], resolvedPromise;
var deferred = object_create(defer.prototype);
var promise = object_create(Promise.prototype);
promise.promiseDispatch = function (resolve, op, operands) {
var args = array_slice(arguments);
if (messages) {
messages.push(args);
if (op === "when" && operands[1]) { // progress operand
progressListeners.push(operands[1]);
}
} else {
Q.nextTick(function () {
resolvedPromise.promiseDispatch.apply(resolvedPromise, args);
});
}
};
// XXX deprecated
promise.valueOf = function () {
if (messages) {
return promise;
}
var nearerValue = nearer(resolvedPromise);
if (isPromise(nearerValue)) {
resolvedPromise = nearerValue; // shorten chain
}
return nearerValue;
};
promise.inspect = function () {
if (!resolvedPromise) {
return { state: "pending" };
}
return resolvedPromise.inspect();
};
if (Q.longStackSupport && hasStacks) {
try {
throw new Error();
} catch (e) {
// NOTE: don't try to use `Error.captureStackTrace` or transfer the
// accessor around; that causes memory leaks as per GH-111. Just
// reify the stack trace as a string ASAP.
//
// At the same time, cut off the first line; it's always just
// "[object Promise]\n", as per the `toString`.
promise.stack = e.stack.substring(e.stack.indexOf("\n") + 1);
}
}
// NOTE: we do the checks for `resolvedPromise` in each method, instead of
// consolidating them into `become`, since otherwise we'd create new
// promises with the lines `become(whatever(value))`. See e.g. GH-252.
function become(newPromise) {
resolvedPromise = newPromise;
promise.source = newPromise;
array_reduce(messages, function (undefined, message) {
Q.nextTick(function () {
newPromise.promiseDispatch.apply(newPromise, message);
});
}, void 0);
messages = void 0;
progressListeners = void 0;
}
deferred.promise = promise;
deferred.resolve = function (value) {
if (resolvedPromise) {
return;
}
become(Q(value));
};
deferred.fulfill = function (value) {
if (resolvedPromise) {
return;
}
become(fulfill(value));
};
deferred.reject = function (reason) {
if (resolvedPromise) {
return;
}
become(reject(reason));
};
deferred.notify = function (progress) {
if (resolvedPromise) {
return;
}
array_reduce(progressListeners, function (undefined, progressListener) {
Q.nextTick(function () {
progressListener(progress);
});
}, void 0);
};
return deferred;
}
/**
* Creates a Node-style callback that will resolve or reject the deferred
* promise.
* @returns a nodeback
*/
defer.prototype.makeNodeResolver = function () {
var self = this;
return function (error, value) {
if (error) {
self.reject(error);
} else if (arguments.length > 2) {
self.resolve(array_slice(arguments, 1));
} else {
self.resolve(value);
}
};
};
/**
* @param resolver {Function} a function that returns nothing and accepts
* the resolve, reject, and notify functions for a deferred.
* @returns a promise that may be resolved with the given resolve and reject
* functions, or rejected by a thrown exception in resolver
*/
Q.Promise = promise; // ES6
Q.promise = promise;
function promise(resolver) {
if (typeof resolver !== "function") {
throw new TypeError("resolver must be a function.");
}
var deferred = defer();
try {
resolver(deferred.resolve, deferred.reject, deferred.notify);
} catch (reason) {
deferred.reject(reason);
}
return deferred.promise;
}
promise.race = race; // ES6
promise.all = all; // ES6
promise.reject = reject; // ES6
promise.resolve = Q; // ES6
// XXX experimental. This method is a way to denote that a local value is
// serializable and should be immediately dispatched to a remote upon request,
// instead of passing a reference.
Q.passByCopy = function (object) {
//freeze(object);
//passByCopies.set(object, true);
return object;
};
Promise.prototype.passByCopy = function () {
//freeze(object);
//passByCopies.set(object, true);
return this;
};
/**
* If two promises eventually fulfill to the same value, promises that value,
* but otherwise rejects.
* @param x {Any*}
* @param y {Any*}
* @returns {Any*} a promise for x and y if they are the same, but a rejection
* otherwise.
*
*/
Q.join = function (x, y) {
return Q(x).join(y);
};
Promise.prototype.join = function (that) {
return Q([this, that]).spread(function (x, y) {
if (x === y) {
// TODO: "===" should be Object.is or equiv
return x;
} else {
throw new Error("Can't join: not the same: " + x + " " + y);
}
});
};
/**
* Returns a promise for the first of an array of promises to become settled.
* @param answers {Array[Any*]} promises to race
* @returns {Any*} the first promise to be settled
*/
Q.race = race;
function race(answerPs) {
return promise(function(resolve, reject) {
// Switch to this once we can assume at least ES5
// answerPs.forEach(function(answerP) {
// Q(answerP).then(resolve, reject);
// });
// Use this in the meantime
for (var i = 0, len = answerPs.length; i < len; i++) {
Q(answerPs[i]).then(resolve, reject);
}
});
}
Promise.prototype.race = function () {
return this.then(Q.race);
};
/**
* Constructs a Promise with a promise descriptor object and optional fallback
* function. The descriptor contains methods like when(rejected), get(name),
* set(name, value), post(name, args), and delete(name), which all
* return either a value, a promise for a value, or a rejection. The fallback
* accepts the operation name, a resolver, and any further arguments that would
* have been forwarded to the appropriate method above had a method been
* provided with the proper name. The API makes no guarantees about the nature
* of the returned object, apart from that it is usable whereever promises are
* bought and sold.
*/
Q.makePromise = Promise;
function Promise(descriptor, fallback, inspect) {
if (fallback === void 0) {
fallback = function (op) {
return reject(new Error(
"Promise does not support operation: " + op
));
};
}
if (inspect === void 0) {
inspect = function () {
return {state: "unknown"};
};
}
var promise = object_create(Promise.prototype);
promise.promiseDispatch = function (resolve, op, args) {
var result;
try {
if (descriptor[op]) {
result = descriptor[op].apply(promise, args);
} else {
result = fallback.call(promise, op, args);
}
} catch (exception) {
result = reject(exception);
}
if (resolve) {
resolve(result);
}
};
promise.inspect = inspect;
// XXX deprecated `valueOf` and `exception` support
if (inspect) {
var inspected = inspect();
if (inspected.state === "rejected") {
promise.exception = inspected.reason;
}
promise.valueOf = function () {
var inspected = inspect();
if (inspected.state === "pending" ||
inspected.state === "rejected") {
return promise;
}
return inspected.value;
};
}
return promise;
}
Promise.prototype.toString = function () {
return "[object Promise]";
};
Promise.prototype.then = function (fulfilled, rejected, progressed) {
var self = this;
var deferred = defer();
var done = false; // ensure the untrusted promise makes at most a
// single call to one of the callbacks
function _fulfilled(value) {
try {
return typeof fulfilled === "function" ? fulfilled(value) : value;
} catch (exception) {
return reject(exception);
}
}
function _rejected(exception) {
if (typeof rejected === "function") {
makeStackTraceLong(exception, self);
try {
return rejected(exception);
} catch (newException) {
return reject(newException);
}
}
return reject(exception);
}
function _progressed(value) {
return typeof progressed === "function" ? progressed(value) : value;
}
Q.nextTick(function () {
self.promiseDispatch(function (value) {
if (done) {
return;
}
done = true;
deferred.resolve(_fulfilled(value));
}, "when", [function (exception) {
if (done) {
return;
}
done = true;
deferred.resolve(_rejected(exception));
}]);
});
// Progress propagator need to be attached in the current tick.
self.promiseDispatch(void 0, "when", [void 0, function (value) {
var newValue;
var threw = false;
try {
newValue = _progressed(value);
} catch (e) {
threw = true;
if (Q.onerror) {
Q.onerror(e);
} else {
throw e;
}
}
if (!threw) {
deferred.notify(newValue);
}
}]);
return deferred.promise;
};
Q.tap = function (promise, callback) {
return Q(promise).tap(callback);
};
/**
* Works almost like "finally", but not called for rejections.
* Original resolution value is passed through callback unaffected.
* Callback may return a promise that will be awaited for.
* @param {Function} callback
* @returns {Q.Promise}
* @example
* doSomething()
* .then(...)
* .tap(console.log)
* .then(...);
*/
Promise.prototype.tap = function (callback) {
callback = Q(callback);
return this.then(function (value) {
return callback.fcall(value).thenResolve(value);
});
};
/**
* Registers an observer on a promise.
*
* Guarantees:
*
* 1. that fulfilled and rejected will be called only once.
* 2. that either the fulfilled callback or the rejected callback will be
* called, but not both.
* 3. that fulfilled and rejected will not be called in this turn.
*
* @param value promise or immediate reference to observe
* @param fulfilled function to be called with the fulfilled value
* @param rejected function to be called with the rejection exception
* @param progressed function to be called on any progress notifications
* @return promise for the return value from the invoked callback
*/
Q.when = when;
function when(value, fulfilled, rejected, progressed) {
return Q(value).then(fulfilled, rejected, progressed);
}
Promise.prototype.thenResolve = function (value) {
return this.then(function () { return value; });
};
Q.thenResolve = function (promise, value) {
return Q(promise).thenResolve(value);
};
Promise.prototype.thenReject = function (reason) {
return this.then(function () { throw reason; });
};
Q.thenReject = function (promise, reason) {
return Q(promise).thenReject(reason);
};
/**
* If an object is not a promise, it is as "near" as possible.
* If a promise is rejected, it is as "near" as possible too.
* If it’s a fulfilled promise, the fulfillment value is nearer.
* If it’s a deferred promise and the deferred has been resolved, the
* resolution is "nearer".
* @param object
* @returns most resolved (nearest) form of the object
*/
// XXX should we re-do this?
Q.nearer = nearer;
function nearer(value) {
if (isPromise(value)) {
var inspected = value.inspect();
if (inspected.state === "fulfilled") {
return inspected.value;
}
}
return value;
}
/**
* @returns whether the given object is a promise.
* Otherwise it is a fulfilled value.
*/
Q.isPromise = isPromise;
function isPromise(object) {
return object instanceof Promise;
}
Q.isPromiseAlike = isPromiseAlike;
function isPromiseAlike(object) {
return isObject(object) && typeof object.then === "function";
}
/**
* @returns whether the given object is a pending promise, meaning not
* fulfilled or rejected.
*/
Q.isPending = isPending;
function isPending(object) {
return isPromise(object) && object.inspect().state === "pending";
}
Promise.prototype.isPending = function () {
return this.inspect().state === "pending";
};
/**
* @returns whether the given object is a value or fulfilled
* promise.
*/
Q.isFulfilled = isFulfilled;
function isFulfilled(object) {
return !isPromise(object) || object.inspect().state === "fulfilled";
}
Promise.prototype.isFulfilled = function () {
return this.inspect().state === "fulfilled";
};
/**
* @returns whether the given object is a rejected promise.
*/
Q.isRejected = isRejected;
function isRejected(object) {
return isPromise(object) && object.inspect().state === "rejected";
}
Promise.prototype.isRejected = function () {
return this.inspect().state === "rejected";
};
//// BEGIN UNHANDLED REJECTION TRACKING
// This promise library consumes exceptions thrown in handlers so they can be
// handled by a subsequent promise. The exceptions get added to this array when
// they are created, and removed when they are handled. Note that in ES6 or
// shimmed environments, this would naturally be a `Set`.
var unhandledReasons = [];
var unhandledRejections = [];
var trackUnhandledRejections = true;
function resetUnhandledRejections() {
unhandledReasons.length = 0;
unhandledRejections.length = 0;
if (!trackUnhandledRejections) {
trackUnhandledRejections = true;
}
}
function trackRejection(promise, reason) {
if (!trackUnhandledRejections) {
return;
}
unhandledRejections.push(promise);
if (reason && typeof reason.stack !== "undefined") {
unhandledReasons.push(reason.stack);
} else {
unhandledReasons.push("(no stack) " + reason);
}
}
function untrackRejection(promise) {
if (!trackUnhandledRejections) {
return;
}
var at = array_indexOf(unh