jsforce
Version:
Salesforce API Library for JavaScript
1,740 lines (1,536 loc) • 73.2 kB
JavaScript
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self);var f=o;f=f.jsforce||(f.jsforce={}),f=f.modules||(f.modules={}),f=f.api||(f.api={}),f.Chatter=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
* @file Manages Salesforce Chatter REST API calls
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
*/
var util = jsforce.require('util'),
_ = jsforce.require('underscore'),
Promise = require('../promise');
/**
* API class for Chatter REST API call
*
* @class
* @param {Connection} conn Connection
*/
var Chatter = module.exports = function(conn) {
this._conn = conn;
};
/**
* Sending request to API endpoint
* @private
*/
Chatter.prototype._request = function(params, callback) {
if (/^(put|post|patch)$/i.test(params.method)) {
if (_.isObject(params.body)) {
params.headers = {
"Content-Type": "application/json"
};
params.body = JSON.stringify(params.body);
}
}
params.url = this._normalizeUrl(params.url);
return this._conn._request(params, callback);
};
/**
* Convert path to site root relative url
* @private
*/
Chatter.prototype._normalizeUrl = function(url) {
if (url.indexOf('/chatter/') === 0 || url.indexOf('/connect/') === 0) {
return '/services/data/v' + this._conn.version + url;
} else if (/^\/v[\d]+\.[\d]+\//.test(url)) {
return '/services/data' + url;
} else if (url.indexOf('/services/') !== 0 && url[0] === '/') {
return '/services/data/v' + this._conn.version + '/chatter' + url;
} else {
return url;
}
};
/**
* @typedef {Object} Chatter~RequestParams
* @prop {String} method - HTTP method
* @prop {String} url - Resource URL
* @prop {String} [body] - HTTP body (in POST/PUT/PATCH methods)
*/
/**
* @typedef {Object} Chatter~RequestResult
*/
/**
* Make a request for chatter API resource
*
* @param {Chatter~RequestParams} params - Paramters representing HTTP request
* @param {Callback.<Chatter~RequestResult>} [callback] - Callback func
* @returns {Chatter~Request}
*/
Chatter.prototype.request = function(params, callback) {
return new Request(this, params).thenCall(callback);
};
/**
* Make a resource request to chatter API
*
* @param {String} url - Resource URL
* @param {Object} [queryParams] - Query parameters (in hash object)
* @returns {Chatter~Resource}
*/
Chatter.prototype.resource = function(url, queryParams) {
return new Resource(this, url, queryParams);
};
/**
* @typedef {Object} Chatter~BatchRequestResult
* @prop {Boolean} hasError - Flag if the batch has one or more errors
* @prop {Array.<Object>} results - Batch request results in array
* @prop {Number} results.statusCode - HTTP response status code
* @prop {Chatter~RequestResult} results.result - Parsed HTTP response body
*/
/**
* Make a batch request to chatter API
*
* @params {Array.<Chatter~Request>} requests - Chatter API requests
* @param {Callback.<Chatter~BatchRequestResult>} [callback] - Callback func
* @returns {Promise.<Chatter~BatchRequestResult>}
*/
Chatter.prototype.batch = function(requests, callback) {
var self = this;
var batchRequests = [], batchDeferreds = [];
_.forEach(requests, function(request) {
var deferred = Promise.defer();
request._promise = deferred.promise;
batchRequests.push(request.batchParams());
batchDeferreds.push(deferred);
});
var params = {
method: 'POST',
url: this._normalizeUrl('/connect/batch'),
body: {
batchRequests: batchRequests
}
};
return this._request(params).then(function(res) {
_.forEach(res.results, function(result, i) {
var deferred = batchDeferreds[i];
if (result.statusCode >= 400) {
deferred.reject(result.result);
} else {
deferred.resolve(result.result);
}
});
return res;
}).thenCall(callback);
};
/*--------------------------------------------*/
/**
* A class representing chatter API request
*
* @protected
* @class Chatter~Request
* @implements {Promise.<Chatter~RequestResult>}
* @param {Chatter} chatter - Chatter API object
* @param {Chatter~RequestParams} params - Paramters representing HTTP request
*/
var Request = function(chatter, params) {
this._chatter = chatter;
this._params = params;
this._promise = null;
};
/**
* @typedef {Object} Chatter~BatchRequestParams
* @prop {String} method - HTTP method
* @prop {String} url - Resource URL
* @prop {String} [richInput] - HTTP body (in POST/PUT/PATCH methods)
*/
/**
* Retrieve parameters in batch request form
*
* @method Chatter~Request#batchParams
* @returns {Chatter~BatchRequestParams}
*/
Request.prototype.batchParams = function() {
var params = this._params;
var batchParams = {
method: params.method,
url: this._chatter._normalizeUrl(params.url)
};
if (this._params.body) {
batchParams.richInput = this._params.body;
}
return batchParams;
};
/**
* Retrieve parameters in batch request form
*
* @method Chatter~Request#promise
* @returns {Promise.<Chatter~RequestResult>}
*/
Request.prototype.promise = function() {
return this._promise || this._chatter._request(this._params);
};
/**
* Returns Node.js Stream object for request
*
* @method Chatter~Request#stream
* @returns {stream.Stream}
*/
Request.prototype.stream = function() {
return this._chatter._request(this._params).stream();
};
/**
* Promise/A+ interface
* http://promises-aplus.github.io/promises-spec/
*
* Delegate to deferred promise, return promise instance for batch result
*
* @method Chatter~Request#then
*/
Request.prototype.then = function(onResolve, onReject) {
return this.promise().then(onResolve, onReject);
};
/**
* Promise/A+ extension
* Call "then" using given node-style callback function
*
* @method Chatter~Request#thenCall
*/
Request.prototype.thenCall = function(callback) {
return _.isFunction(callback) ? this.promise().thenCall(callback) : this;
};
/*--------------------------------------------*/
/**
* A class representing chatter API resource
*
* @protected
* @class Chatter~Resource
* @extends Chatter~Request
* @param {Chatter} chatter - Chatter API object
* @param {String} url - Resource URL
* @param {Object} [queryParams] - Query parameters (in hash object)
*/
var Resource = function(chatter, url, queryParams) {
if (queryParams) {
var qstring = _.map(_.keys(queryParams), function(name) {
return name + "=" + encodeURIComponent(queryParams[name]);
}).join('&');
url += (url.indexOf('?') > 0 ? '&' : '?') + qstring;
}
Resource.super_.call(this, chatter, { method: 'GET', url: url });
this._url = url;
};
util.inherits(Resource, Request);
/**
* Create a new resource
*
* @method Chatter~Resource#create
* @param {Object} data - Data to newly post
* @param {Callback.<Chatter~RequestResult>} [callback] - Callback function
* @returns {Chatter~Request}
*/
Resource.prototype.create = function(data, callback) {
return this._chatter.request({
method: 'POST',
url: this._url,
body: data
}).thenCall(callback);
};
/**
* Retrieve resource content
*
* @method Chatter~Resource#retrieve
* @param {Callback.<Chatter~RequestResult>} [callback] - Callback function
* @returns {Chatter~Request}
*/
Resource.prototype.retrieve = function(callback) {
return this.thenCall(callback);
};
/**
* Update specified resource
*
* @method Chatter~Resource#update
* @param {Obejct} data - Data to update
* @param {Callback.<Chatter~RequestResult>} [callback] - Callback function
* @returns {Chatter~Request}
*/
Resource.prototype.update = function(data, callback) {
return this._chatter.request({
method: 'POST',
url: this._url,
body: data
}).thenCall(callback);
};
/**
* Synonym of Resource#delete()
*
* @method Chatter~Resource#del
* @param {Callback.<Chatter~RequestResult>} [callback] - Callback function
* @returns {Chatter~Request}
*/
/**
* Delete specified resource
*
* @method Chatter~Resource#delete
* @param {Callback.<Chatter~RequestResult>} [callback] - Callback function
* @returns {Chatter~Request}
*/
Resource.prototype.del =
Resource.prototype["delete"] = function(callback) {
return this._chatter.request({
method: 'DELETE',
url: this._url
}).thenCall(callback);
};
},{"../promise":2}],2:[function(require,module,exports){
(function (process){
/*global process*/
var Q = require('q'),
_ = jsforce.require('underscore')._;
/**
* Promises/A+ spec compliant class, with a little extension
* http://promises-aplus.github.io/promises-spec/
*
* @class Promise
* @constructor
* @param {Promise.<T>|T} o - Object to wrap with promise
* @template T
*/
var Promise = function(o) {
this._promise = Q(o);
};
/**
* @callback FulfilledCallback
* @param {T} result - Fulfilled value
* @returns {S}
* @template T,S
*/
/**
* @callback RejectedCallback
* @param {Error} reason - Rejected reason
* @returns {S}
* @template S
*/
/**
* The "then" method from the Promises/A+ specification
*
* @param {FulfilledCallback.<T, S1>} [onFulfilled]
* @param {RejectedCallback.<S2>} [onRejected]
* @returns {Promise.<S1|S2>}
*/
Promise.prototype.then = function() {
// Delegate Q promise implementation and wrap by our Promise instance
return new Promise(this._promise.then.apply(this._promise, arguments));
};
/**
* Call "then" using given node-style callback function
*
* @param {Callback.<T>} [callback] - Callback function
* @returns {Promise}
*/
Promise.prototype.thenCall = function(callback) {
if (_.isFunction(callback)) {
this.then(function(res) {
process.nextTick(function() {
callback(null, res);
});
}, function(err) {
process.nextTick(function() {
callback(err);
});
});
}
return this;
};
/**
* A sugar method, equivalent to promise.then(undefined, onRejected).
*
* @param {RejectedCallback.<S>} onRejected
* @returns {Promise.<S>}
*/
Promise.prototype.fail = function() {
return new Promise(this._promise.fail.apply(this._promise, arguments));
};
/**
* Alias for completion
*
* @param {FulfilledCallback.<T, S>} [onFulfilled]
* @returns {Promise.<S>}
*/
Promise.prototype.done = function() {
return new Promise(this._promise.done.apply(this._promise, arguments));
};
/**
* @param {...Promise.<*>} p
*/
Promise.when = function() {
return new Promise(Q.when.apply(Q, arguments));
};
/**
* Returns rejecting promise with given reason
*
* @param {Error} reason - Rejecting reason
* @returns {Promise}
*/
Promise.reject = function(reason) {
return new Promise(Q.reject(reason));
};
/**
* Returns a promise that is fulfilled with an array containing the fulfillment value of each promise,
* or is rejected with the same rejection reason as the first promise to be rejected.
*
* @param {Array.<Promise.<*>|*>} promises
* @returns {Promise.<Array.<*>>}
*/
Promise.all = function() {
return new Promise(Q.all.apply(Q, arguments));
};
/**
* Returns a deferred object
*
* @returns {Deferred}
*/
Promise.defer = function() {
return new Deferred();
};
/**
* Deferred object
*
* @protected
* @constructor
*/
var Deferred = function() {
this._deferred = Q.defer();
this.promise = new Promise(this._deferred.promise);
};
/**
* Resolve promise
* @param {*} result - Resolving result
*/
Deferred.prototype.resolve = function() {
return this._deferred.resolve.apply(this._promise, arguments);
};
/**
* Reject promise
* @param {Error} error - Rejecting reason
*/
Deferred.prototype.reject = function() {
return this._deferred.reject.apply(this._promise, arguments);
};
/**
*
*/
module.exports = Promise;
}).call(this,require('_process'))
},{"_process":3,"q":4}],3:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener
;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('process-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.binding = function (name) {
throw new Error('process.binding is not supported');
}
// TODO(shtylman)
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
},{}],4:[function(require,module,exports){
(function (process){
// 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) {
// Turn off strict mode for this function so we can assign to global.Q
/* jshint strict: false */
// 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 (typeof exports === "object") {
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 {
Q = definition();
}
})(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 does have the nice side-effect of reducing the size
// of the code by reducing x.call() to merely x(), eliminating many
// hard-to-minify characters.
// 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;
};
}
// Until V8 3.19 / Chromium 29 is released, SpiderMonkey is the only
// engine that has a deployed base of browsers that support generators.
// However, SM's generators use the Python-inspired semantics of
// outdated ES6 drafts. We would like to support ES6, but we'd also
// like to make it possible to use generators in deployed browsers, so
// we also support Python-style generators. At some point we can remove
// this block.
var hasES6Generators;
try {
/* jshint evil: true, nonew: false */
new Function("(function* (){ yield 1; })");
hasES6Generators = true;
} catch (e) {
hasES6Generators = false;
}
// 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 (isPromise(value)) {
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;
/**
* 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 {
nextTick(function () {
resolvedPromise.promiseDispatch.apply(resolvedPromise, args);
});
}
};
// XXX deprecated
promise.valueOf = deprecate(function () {
if (messages) {
return promise;
}
var nearerValue = nearer(resolvedPromise);
if (isPromise(nearerValue)) {
resolvedPromise = nearerValue; // shorten chain
}
return nearerValue;
}, "valueOf", "inspect");
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) {
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) {
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;
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;
}
// 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 fulfilled.
* @param answers {Array[Any*]} promises to race
* @returns {Any*} the first promise to be fulfilled
*/
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 = deprecate(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;
}
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;
};
/**
* 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 isObject(object) &&
typeof object.promiseDispatch === "function" &&
typeof object.inspect === "function";
}
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 unhandledReasonsDisplayed = false;
var trackUnhandledRejections = true;
function displayUnhandledReasons() {
if (
!unhandledReasonsDisplayed &&
typeof window !== "undefined" &&
!window.Touch &&
window.console
) {
console.warn("[Q] Unhandled rejection reasons (should be empty):",
unhandledReasons);
}
unhandledReasonsDisplayed = true;
}
function logUnhandledReasons() {
for (var i = 0; i < unhandledReasons.length; i++) {
var reason = unhandledReasons[i];
console.warn("Unhandled rejection reason:", reason);
}
}
function resetUnhandledRejections() {
unhandledReasons.length = 0;
unhandledRejections.length = 0;
unhandledReasonsDisplayed = false;
if (!trackUnhandledRejections) {
trackUnhandledRejections = true;
// Show unhandled rejection reasons if Node exits without handling an
// outstanding rejection. (Note that Browserify presently produces a
// `process` global without the `EventEmitter` `on` method.)
if (typeof process !== "undefined" && process.on) {
process.on("exit", logUnhandledReasons);
}
}
}
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);
}
displayUnhandledReasons();
}
function untrackRejection(promise) {
if (!trackUnhandledRejections) {
return;
}
var at = array_indexOf(unhandledRejections, promise);
if (at !== -1) {
unhandledRejections.splice(at, 1);
unhandledReasons.splice(at, 1);
}
}
Q.resetUnhandledRejections = resetUnhandledRejections;
Q.getUnhandledReasons = function () {
// Make a copy so that consumers can't interfere with our internal state.
return unhandledReasons.slice();
};
Q.stopUnhandledRejectionTracking = function () {
resetUnhandledRejections();
if (typeof process !== "undefined" && process.on) {
process.removeListener("exit", logUnhandledReasons);
}
trackUnhandledRejections = false;
};
resetUnhandledRejections();
//// END UNHANDLED REJECTION TRACKING
/**
* Constructs a rejected promise.
* @param reason value describing the failure
*/
Q.reject = reject;
function reject(reason) {
var rejection = Promise({
"when": function (rejected) {
// note that the error has been handled
if (rejected) {
untrackRejection(this);
}
return rejected ? rejected(reason) : this;
}
}, function fallback() {
return this;
}, function inspect() {
return { state: "rejected", reason: reason };
});
// Note that the reason has not been handled.
trackRejection(rejection, reason);
return rejection;
}
/**
* Constructs a fulfilled promise for an immediate reference.
* @param value immediate reference
*/
Q.fulfill = fulfill;
function fulfill(value) {
return Promise({
"when": function () {
return value;
},
"get": function (name) {
return value[name];
},
"set": function (name, rhs) {
value[name] = rhs;
},
"delete": function (name) {
delete value[name];
},
"post": function (name, args) {
// Mark Miller proposes that post with no name should apply a
// promised function.
if (name === null || name === void 0) {
return value.apply(void 0, args);
} else {
return value[name].apply(value, args);
}
},
"apply": function (thisp, args) {
return value.apply(thisp, args);
},
"keys": function () {
return object_keys(value);
}
}, void 0, function inspect() {
return { state: "fulfilled", value: value };
});
}
/**
* Converts thenables to Q promises.
* @param promise thenable promise
* @returns a Q promise
*/
function coerce(promise) {
var deferred = defer();
nextTick(function () {
try {
promise.then(deferred.resolve, deferred.reject, deferred.notify);
} catch (exception) {
deferred.reject(exception);
}
});
return deferred.promise;
}
/**
* Annotates an object such that it will never be
* transferred away from this process over any promise
* communication channel.
* @param object
* @returns promise a wrapping of that object that
* additionally responds to the "isDef" message
* without a rejection.
*/
Q.master = master;
function master(object) {
return Promise({
"isDef": function () {}
}, function fallback(op, args) {
return dispatch(object, op, args);
}, function () {
return Q(object).inspect();
});
}
/**
* Spreads the values of a promised array of arguments into the
* fulfillment callback.
* @param fulfilled callback that receives variadic arguments from the
* promised array
* @param rejected callback that receives the exception if the promise
* is rejected.
* @returns a promise for the return value or thrown exception of
* either callback.
*/
Q.spread = spread;
function spread(value, fulfilled, rejected) {
return Q(value).spread(fulfilled, rejected);
}
Promise.prototype.spread = function (fulfilled, rejected) {
return this.all().then(function (array) {
return fulfilled.apply(void 0, array);
}, rejected);
};
/**
* The async function is a decorator for generator functions, turning
* them into asynchronous generators. Although generators are only part
* of the newest ECMAScript 6 drafts, this code does not cause syntax
* errors in older engines. This code should continue to work and will
* in fact improve over time as the language improves.
*
* ES6 generators are currently part of V8 version 3.19 with the
* --harmony-generators runtime flag enabled. SpiderMonkey has had them
* for longer, but under an older Python-inspired form. This function
* works on both kinds of generators.
*
* Decorates a generator function such that:
* - it may yield promises
* - execution will continue when that promise is fulfilled
* - the value of the yield expression will be the fulfilled value
* - it returns a promise for the return value (when the generator
* stops iterating)
* - the decorated function returns a promise for the return value
* of the generator or the first rejected promise among those
* yielded.
* - if an error is thrown in the generator, it propagates through
* every following yield until it is caught, or until it escapes
* the generator function altogether, and is translated into a
* rejection for the pro