Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

parley

Package Overview
Dependencies
Maintainers
2
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

parley - npm Package Compare versions

Comparing version 3.4.3 to 3.4.4

1156

lib/private/Deferred.js

@@ -9,3 +9,2 @@ /**

var flaverr = require('flaverr');
var bindUserlandAfterExecLC = require('./bind-userland-after-exec-lc');

@@ -193,416 +192,2 @@ // Optimization: Pull process env check up here.

// This variable (`cb`) is used as our callback. In the next few lines, we determine
// what it will be. This is just a potentially-more-efficient alternative to a series
// of self-calling functions, We only do this to afford better performance in the general case.
//
// > (Normally, this sort of micro-optimization wouldn't matter, but this is an extradordinarily
// > hot code path. Note that if we can prove self-calling functions are just as good, or even
// > good enough, it would be preferable to use them instead (not only for consistency, but
// > certainly for clarity as well).)
var cb;
// To begin with, no matter what, intercept `_cb` by wrapping it in another function
// (a new one that we'll call `cb`) which adds some additional checks.
cb = function _tryToRunCb(/*…*/) {
// > Note that we don't use .slice() on the `arguments` keyword -- this is for perf.
// > (see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#what-is-safe-arguments-usage)
var errCbArg;
var resultCbArg;
var extraCbArgs;
if (arguments.length > 2) {
errCbArg = arguments[0];
resultCbArg = arguments[1];
extraCbArgs = Array.prototype.slice.call(arguments, 2);
} else if (arguments.length > 1) {
errCbArg = arguments[0];
resultCbArg = arguments[1];
} else if (arguments.length > 0) {
errCbArg = arguments[0];
}
// ┬ ┬┬─┐┌─┐┌─┐ ┌─┐┌─┐┌┬┐┌─┐┌┐┌┌┬┐┬┌─┐┬ ┬ ┬ ┬ ┌─┐┌─┐┌┐┌┌─┐┬ ┬┌─┐┬┌┐┌┌─┐ ┌─┐┬─┐┬─┐┌─┐┬─┐┌─┐
// │││├┬┘├─┤├─┘ ├─┘│ │ │ ├┤ │││ │ │├─┤│ │ └┬┘───│ │ ││││├┤ │ │└─┐│││││ ┬ ├┤ ├┬┘├┬┘│ │├┬┘└─┐
// └┴┘┴└─┴ ┴┴ ┴ └─┘ ┴ └─┘┘└┘ ┴ ┴┴ ┴┴─┘┴─┘┴ └─┘└─┘┘└┘└ └─┘└─┘┴┘└┘└─┘ └─┘┴└─┴└─└─┘┴└─└─┘
// ┌─┐┬─┐┌─┐┌┬┐ ┌─┐┌┬┐┬ ┬┌─┐┬─┐ ┌┬┐┌─┐┌─┐┌─┐┬─┐┬─┐┌─┐┌┬┐ ┬┌┐┌┬ ┬┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐
// ├┤ ├┬┘│ ││││ │ │ │ ├─┤├┤ ├┬┘ ││├┤ ├┤ ├┤ ├┬┘├┬┘├┤ ││ ││││└┐┌┘│ ││ ├─┤ │ ││ ││││└─┐
// └ ┴└─└─┘┴ ┴ └─┘ ┴ ┴ ┴└─┘┴└─ ─┴┘└─┘└ └─┘┴└─┴└─└─┘─┴┘ ┴┘└┘ └┘ └─┘└─┘┴ ┴ ┴ ┴└─┘┘└┘└─┘
// ┬ ┬┬┌┬┐┬ ┬┬┌┐┌ ┌┬┐┬ ┬┌─┐ ┬ ┬┌─┐┌┐┌┌┬┐┬ ┌─┐┌─┐─┐ ┬┌─┐┌─┐ ┌─┐┬ ┬┌┐┌┌─┐┌┬┐┬┌─┐┌┐┌
// ││││ │ ├─┤││││ │ ├─┤├┤ ├─┤├─┤│││ │││ ├┤ ├┤ ┌┴┬┘├┤ │ ├┤ │ │││││ │ ││ ││││
// └┴┘┴ ┴ ┴ ┴┴┘└┘ ┴ ┴ ┴└─┘ ┴ ┴┴ ┴┘└┘─┴┘┴─┘└─┘└─┘┴ └─└─┘└─┘ └ └─┘┘└┘└─┘ ┴ ┴└─┘┘└┘
if (errCbArg) {
var doWrap;
// If we see E_NOT_SYNCHRONOUS, it should ALWAYS be wrapped.
// (The only time it would ever come from THIS Deferred is if we called .now() --
// and the code that checks that is not even part of .exec())
if (_.isObject(errCbArg) && errCbArg.code === 'E_NOT_SYNCHRONOUS') {
doWrap = true;
}
// If we see a TimeoutError from a Deferred **OTHER** than this one,
// then wrap it.
else if (_.isObject(errCbArg) && errCbArg.name === 'TimeoutError' && errCbArg.traceRef !== self) {
doWrap = true;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Note: An easy way to test this is to run something like the following in the Node REPL:
// ````
// require('machine')({identity: 'outside', fn: (inputs, exits)=>{ require('machine')({identity: 'inside',timeout: 2000, exits: {notFound:{}}, fn: (inputs, exits)=>{ /*deliberately never exits...*/ }})().exec((err)=>{ if (err){return exits.error(err);} return exits.success(); }); }})()
// ````
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
// If instructed to do so, perform the wrapping.
if (doWrap) {
errCbArg = flaverr.wrap({
code:
'E_FROM_WITHIN',
message:
'Some logic inside this function\'s implementation encountered an error.\n'+
' [?] See `.raw` for more details, or visit https://sailsjs.com/support for help.',
}, errCbArg, self._omen);
}//fi
}//fi
// ┬ ┬┌─┐┌─┐┬─┐┬ ┌─┐┌┐┌┌┬┐ ┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐─┐ ┬┌─┐┌─┐ ┬ ┌─┐┌─┐
// │ │└─┐├┤ ├┬┘│ ├─┤│││ ││ ├─┤├┤ │ ├┤ ├┬┘├┤ ┌┴┬┘├┤ │ │ │ └─┐
// └─┘└─┘└─┘┴└─┴─┘┴ ┴┘└┘─┴┘ ┴ ┴└ ┴ └─┘┴└─└─┘┴ └─└─┘└─┘ ┴─┘└─┘└─┘
// TODO: finish .retry() support, which probably means making .intercept() and .tolerate() support AsyncFunctions
try {
// If this Deferred was configured with after-exec lifecycle callbacks from
// userland via .intercept(), .tolerate(), or .retry(), then call those
// lifecycle callbacks now, if appropriate, picking up the potentially-changed
// (even potentially-reconstructed!) error or result.
//
// > Note that this is only relevant if there was an error of some kind.
if (self._userlandAfterExecLCs && errCbArg) {
// Now before proceeding further, check for a match (if there are any configured).
// > NOTE: We only ever run one of these handlers for any given response!
var matchingUserlandLC;
for (var i = 0; i < self._userlandAfterExecLCs.length; i++) {
var lcDef = self._userlandAfterExecLCs[i];
if (lcDef.rule === undefined) {
matchingUserlandLC = lcDef;
break;
} else if (flaverr.taste(lcDef.rule, errCbArg)) {
matchingUserlandLC = lcDef;
break;
}
}//∞
// Now, if we have a match...
if (matchingUserlandLC) {
// Get reasonable default for handler, if no explicit handler function was configured.
if (matchingUserlandLC.handler === undefined && matchingUserlandLC.type === 'tolerate') {
matchingUserlandLC.handler = function(){ return; };
}
else if (matchingUserlandLC.handler === undefined && matchingUserlandLC.type === 'intercept') {
matchingUserlandLC.handler = function(err){ return err; };
}
// Run userland LC.
var resultFromHandler;
try {
resultFromHandler = matchingUserlandLC.handler(errCbArg);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: add support for this, beginning with something like the
// following incomplete implementation:
//
// ```
// if (matchingUserlandLC.handler.constructor.name === 'AsyncFunction') {
// var interceptPromise;
// try {
// interceptPromise = matchingLifecycleInstruction.handler();
// } catch (err) {
// if (err === false) { return proceed(undefined, true); }//« special case (`throw false`)
// else { return proceed(err); }
// }
//
// interceptPromise.then(function(_resultFromHandler){
// resultFromHandler = _resultFromHandler;
// proceed(undefined, resultFromHandler);
// });
// interceptPromise.catch(function(err) {
// /* eslint-disable callback-return */
// if (err === false) { proceed(undefined, true); }//« special case (`throw false`)
// else { proceed(err); }
// /* eslint-enable callback-return */
// });
// }
// else {
// resultFromHandler = matchingUserlandLC.handler(errCbArg);
// }
// ```
//
// *** Don't forget to update bind-userland-after-exec-lc to remove the
// check that displays an error message instead of allowing this usage! ***
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} catch (err) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: (Maybe) Specifically for `.tolerate()`, allow throwing special exit signals
// from within the handler.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// If this is an .intercept() handler, then it's possible the handler threw on purpose,
// perhaps because it was attempting to send a special signal to its caller (e.g. the
// implementation of an action/helper/etc) where it presumably has a special meaning.
// So in this case, we customize the error message to reflect that possibility and to
// suggest an appropriate resolution.
if (matchingUserlandLC.type === 'intercept') {
throw flaverr({
name:
'UsageError',
message:
'Caught unexpected error in `.intercept()` handler, which should not throw:\n'+
flaverr.parseOrBuildError(err).message+'\n'+
'If this was intentional, i.e. to communicate a signal to the caller, then\n'+
'please just return the new or modified error you would like to use instead.\n'+
'The value returned to `.intercept()` will be used as the new Error.\n'+
' [?] See https://sailsjs.com/support for help.',
raw:
err
}, self._omen);
}
else {
// Otherwise, we'll just consider this a standard unexpected error:
throw flaverr({
name:
'UsageError',
message:
'Encountered unexpected error in `.'+matchingUserlandLC.type+'()` handler. '+
flaverr.parseOrBuildError(err).message+'\n'+
' [?] See https://sailsjs.com/support for help.',
raw:
err
}, self._omen);
}
}
// Now swallow or swap out the error, if instructed to do so.
// Swallow:
// > i.e. if a matching `.tolerate()` was encountered, then consider
// > this successful no matter what, and use the value returned by the
// > LC as the new result.
if (matchingUserlandLC.type === 'tolerate') {
errCbArg = undefined;
resultCbArg = resultFromHandler;
}
// Swap:
//
// > i.e. if a matching `.intercept()` was encountered, then consider
// > whatever the intercept handler returned to be our new Error.
else if (matchingUserlandLC.type === 'intercept') {
// If the handler returned `undefined`, then fail with an error.
// (this shouldn't happen, an indicates invalid usage)
if (resultFromHandler === undefined) {
throw flaverr({
name:
'UsageError',
message:
'`.intercept()` handler returned `undefined`, but this should never happen.\n'+
'Regardless, here is a summary of the original underlying error:\n'+
flaverr.parseOrBuildError(errCbArg).message+'\n'+
' [?] See https://sailsjs.com/support for help.',
raw:
errCbArg
}, self._omen);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Normally, these errors must ALWAYS be Error instances already.
// But for this special case, where the original Error value
// is being overridden through the use of `.intercept()`, we'd
// LIKE to make a special exception to the rule (no pun intended.)
//
// There's only one problem: Because of bluebird's "maybeWrapAsError"
// implementation, we can't send certain non-Errors through to it
// (specifically primitives) because they get autowrapped.
//
// > Here's the relevant bit of code:
// > https://github.com/petkaantonov/bluebird/blob/e8d8525a0517280d11d6c77ae6b61df86419232b/src/promisify.js#L182-L184
//
// Again, most of the time, this would be fine. But while bluebird's
// looking out for us here is admirable, there are some situations.
// where this is not welcome -- such as when trying to throw a string.
//
// > Why throw a string?
// > This is useful for throwing special signals-- e.g. from the inside
// > of an actions2 action or a helper in Sails, a machine's fn in a
// > machinepack, or from a commandline script.
//
// So anyway, to work around this, we have to come up with a consistent
// way of wrapping up non-Errors to look like Errors. That's what we
// do next.
//
// ** Note that we also do this in a couple of other places in parley. **
// ** (look for `flaverr.parseOrBuildError()` calls) **
//
// > (If ever we find ourselves wanting to revert this approach, the old
// > code that used to check for non-Errors was removed in parley@376208fd1c0ab70e7a6b9c4ecfa563ec0d77a3a8.
// > But... as mentioned above-- there are some good reasons to keep things
// > the new way that they are now.)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var interceptError = flaverr.parseOrBuildError(resultFromHandler, self._omen);
// Stuff the Error in our arguments.
errCbArg = interceptError;
}//fi </ if this is an .intercept() >
}//fi </ if there is a matching userland LC >
}//fi </ if we got an error AND any userland LCs were provided >
} catch (err) {
// If any error was encountered above, then stuff it in `errCbArg` so
// that it can still be handled gracefully-- including getting any final
// treatment from implementorland's `finalAfterExecLC`.
errCbArg = err;
}
// (function(){
// See TODO above
// })(function(err){
//
// });//_∏_ (†)
// ╔═╗╦╔╗╔╔═╗╦ ┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐─┐ ┬┌─┐┌─┐ ┬ ┌─┐
// ╠╣ ║║║║╠═╣║ ├─┤├┤ │ ├┤ ├┬┘├┤ ┌┴┬┘├┤ │ │ │
// ╚ ╩╝╚╝╩ ╩╩═╝ ┴ ┴└ ┴ └─┘┴└─└─┘┴ └─└─┘└─┘ ┴─┘└─┘
// ┌─┐┬─┐┌─┐┌┬┐ ┬┌┬┐┌─┐┬ ┌─┐┌┬┐┌─┐┌┐┌┌┬┐┌─┐┬─┐┬ ┌─┐┌┐┌┌┬┐
// ├┤ ├┬┘│ ││││ ││││├─┘│ ├┤ │││├┤ │││ │ │ │├┬┘│ ├─┤│││ ││
// └ ┴└─└─┘┴ ┴ ┴┴ ┴┴ ┴─┘└─┘┴ ┴└─┘┘└┘ ┴ └─┘┴└─┴─┘┴ ┴┘└┘─┴┘
// If this Deferred was built with an `finalAfterExecLC` lifecycle callback,
// then intercept our normal flow to call that lifecycle callback, picking up
// the potentially-changed (even potentially-reconstructed!) error or result.
if (self._finalAfterExecLC) {
if (errCbArg) {
errCbArg = self._finalAfterExecLC(errCbArg);
}
else {
resultCbArg = self._finalAfterExecLC(undefined, resultCbArg);
}
}//fi
// ┌┐┌┌─┐┬ ┬ ┌─┐┌─┐┌┬┐┬ ┬┌─┐┬ ┬ ┬ ┬ ┌┬┐┬─┐┬┌─┐┌─┐┌─┐┬─┐ ┌─┐┌┐ ┌─┐┌┐┌┌─┐┬ ┬┬─┐┬┌┐┌┌─┐
// ││││ ││││ ├─┤│ │ │ │├─┤│ │ └┬┘ │ ├┬┘││ ┬│ ┬├┤ ├┬┘ │ ├┴┐ ├┤ │││└─┐│ │├┬┘│││││ ┬
// ┘└┘└─┘└┴┘ ┴ ┴└─┘ ┴ └─┘┴ ┴┴─┘┴─┘┴ ┴ ┴└─┴└─┘└─┘└─┘┴└─ └─┘└─┘┘ └─┘┘└┘└─┘└─┘┴└─┴┘└┘└─┘
// ┌┐┌┌─┐ ┌─┐┌─┐┌─┐┬┌┬┐┌─┐┌┐┌┌┬┐┌─┐┬ ┌─┐┬ ┬┌─┐┬ ┬ ┌─┐┬ ┬┬┌┐┌┌─┐ ┌─┐┌─┐ ┌─┐┬─┐┬─┐┌─┐┬─┐┌─┐
// ││││ │ ├─┤│ │ │ ││├┤ │││ │ ├─┤│ └─┐│││├─┤│ │ │ │││││││││ ┬ │ │├┤ ├┤ ├┬┘├┬┘│ │├┬┘└─┐
// ┘└┘└─┘ ┴ ┴└─┘└─┘┴─┴┘└─┘┘└┘ ┴ ┴ ┴┴─┘ └─┘└┴┘┴ ┴┴─┘┴─┘└─┘└┴┘┴┘└┘└─┘ └─┘└ └─┘┴└─┴└─└─┘┴└─└─┘
// ┌─┐┬─┐┌─┐┌┬┐ ┌─┐┌─┐┬ ┬ ┌┐ ┌─┐┌─┐┬┌─┌─┐
// ├┤ ├┬┘│ ││││ │ ├─┤│ │ ├┴┐├─┤│ ├┴┐└─┐
// └ ┴└─└─┘┴ ┴ └─┘┴ ┴┴─┘┴─┘└─┘┴ ┴└─┘┴ ┴└─┘
// If this callback is being called after at least one tick has elapsed...
if (self._hasAlreadyWaitedAtLeastOneTick) {
// If 2nd argument (handleUncaughtException) was provided to .exec(), then run that
// instead of throwing. This protects against unexpected, uncaught exceptions in
// asynchronous callbacks.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: Probably deprecate this, then remove support (see above).
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if (handleUncaughtException) {
try {
if (extraCbArgs) {
return _cb.apply(undefined, [errCbArg, resultCbArg].concat(extraCbArgs));
} else if (resultCbArg !== undefined) {
return _cb(errCbArg, resultCbArg);
} else {
return _cb(errCbArg);
}
} catch (unexpectedErrorFromCallback) {
return handleUncaughtException(unexpectedErrorFromCallback);
}
}//•
// Otherwise, just trigger the callback as-is.
// (If it throws, it will crash the process!)
if (extraCbArgs) {
return _cb.apply(undefined, [errCbArg, resultCbArg].concat(extraCbArgs));
} else if (resultCbArg !== undefined) {
return _cb(errCbArg, resultCbArg);
} else {
return _cb(errCbArg);
}
}//•
//‡
// Otherwise, our logic is synchronous (i.e. <1 async tick has elapsed at the time it's being
// called). So wrap the `_cb` from userland in a try/catch. If an unhandled error of any kind
// is thrown from the userland cb, our wrapper uses a special Envelope to bust out of the `try`
// block, ensuring that the unhandled exception is thrown up to userland.
//
// > NOTE:
// > Without this extra code here, we'd end up with the old behavior: outputting a puzzling error
// > message -- e.g. about something unexpected things happening in the Deferred, or a warning
// > about triggering the callback twice (when actually, the issue is that something went wrong
// > in the callback-- and that the Deferred logic happened to be synchronous, so it wasn't able
// > to escape parley's internal `try` block.)
// >
// > Some relevant links for reference:
// > • https://github.com/node-machine/machine/blob/7fdcf8a869605d0951909725061379cd27bd7f0d/lib/private/intercept-exit-callbacks.js#L186-L238
// > • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_hasFnYieldedYet&type=
// > • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_runningSynchronously&type=
else {
try {
if (extraCbArgs) {
return _cb.apply(undefined, [errCbArg, resultCbArg].concat(extraCbArgs));
} else if (resultCbArg !== undefined) {
return _cb(errCbArg, resultCbArg);
} else {
return _cb(errCbArg);
}
} catch (unexpectedErrorFromCallback) {
throw flaverr.wrap({
code: 'E_ESCAPE_HATCH',
traceRef: self
}, unexpectedErrorFromCallback, self._omen);
}
}//•
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: Additionally, this additional layer of wrapping could take care of improving
// stack traces, even in the case where an Error comes up from inside the implementation.
// If done carefully, this can be done in a way that protects characteristics of the
// internal Error (e.g. its "code", etc.), while also providing a better stack trace.
//
// For example, something like this:
// ```
// var relevantPropNames = _.difference(
// _.union(
// ['name', 'message'],
// Object.getOwnPropertyNames(underlyingError)
// ),
// ['stack']
// );
// var errTemplate = _.pick(underlyingError, relevantPropNames);
// errTemplate.raw = underlyingError;//<< could override stuff-- that's ok (see below).
// var newError = flaverr(errTemplate, omen);
// ```
// > Note that, above, we also kept the original error (and thus _its_ trace) and
// > attached that as a separate property. If the original error already has "raw",
// > that's ok. This is one thing that it makes sense for us to mutate-- and any
// > attempt to do otherwise would probably be more confusing (you can imagine a while
// > loop where we add underscores in front of the string "raw", and use that as a keyname.
// > But again, that ends up being more confusing from a userland perspective.)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
};//ƒ </ definition of `cb` >
// Userland spinlock

@@ -692,3 +277,3 @@ if (self._hasBegunExecuting) {

}, self._omen);
return cb(err);
return proceedToInterceptsAndChecks(err, undefined, undefined, self, _cb, handleUncaughtException);

@@ -704,85 +289,23 @@ }, self._timeout);// _∏_ (invoking `setTimeout()`)

self._handleExec(function (err, result) {
// Implementorland spinlock
if (self._hasFinishedExecuting && !self._skipImplSpinlockWarning) {
console.warn(
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n'+
'WARNING: Something seems to be wrong with this function.\n'+
'It is trying to signal that it has finished AGAIN, after\n'+
'already resolving/rejecting once.\n'+
'(silently ignoring this...)\n'+
(self._omen?(
'\n'+
'To assist you in hunting this down, here is a stack trace:\n'+
'```\n'+
flaverr.getBareTrace(self._omen)+'\n'+
'```\n'+
'\n'
):'')+
' [?] For more help, visit https://sailsjs.com/support\n'+
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
);
return;
}
// If the deferred has already timed out, then there's no need to warn
// (This was _bound_ to happen beings as how we timed out.)
//
// > Note that we still set a flag to track that this happened. This is to make sure
// > this if/then statement can't possibly be true more than once (because that would
// > definitely still be unexpected-- and really hard to anticipate / debug if it were
// > to happen to you)
if (self._hasTimedOut) {
self._hasFinishedExecuting = true;
return;
}
// Clear timeout, if relevant.
if (timeoutAlarm) {
clearTimeout(timeoutAlarm);
}
if (err) {
// Ensure we're dealing w/ an Error instance.
err = flaverr.parseOrBuildError(err, self._omen);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ^ FUTURE: Better error message for non-Errors?
// (See impl of parseOrBuildError() in flaverr for more context.)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
self._hasFinishedExecuting = true;
return cb(err);
}//-•
// IWMIH, there was no error.
self._hasFinishedExecuting = true;
// If there are any extra arguments, send them back too.
// (This is unconventional, but permitted to allow for extra metadata,
// which is sometimes handy when you want to expose advanced usage.)
self._handleExec(function( /*…*/ ){
// > Note that we don't use .slice() on the `arguments` keyword -- this is for perf.
// > (see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#what-is-safe-arguments-usage)
var errCbArg;
var resultCbArg;
var extraCbArgs;
if (arguments.length > 2) {
// > Note that we don't use .slice() -- this is for perf.
// > (see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#what-is-safe-arguments-usage)
return cb.apply(undefined, arguments);
errCbArg = arguments[0];
resultCbArg = arguments[1];
extraCbArgs = Array.prototype.slice.call(arguments, 2);
} else if (arguments.length > 1) {
errCbArg = arguments[0];
resultCbArg = arguments[1];
} else if (arguments.length > 0) {
errCbArg = arguments[0];
}
// Otherwise, this is the normal case.
// If there's no result, just call the callback w/ no args.
// (This just makes for better log output, etc.)
else if (result === undefined) {
return cb();
}
// Otherwise, there's a result, so send it back.
else {
return cb(undefined, result);
}
});//</self._handleExec>
proceedToAfterExecSpinlocks(errCbArg, resultCbArg, extraCbArgs, self, _cb, handleUncaughtException, timeoutAlarm);
});//_∏_ </self._handleExec>
// Handle errors thrown synchronously by the `_handleExec` implementation:
} catch (e) {
} catch (e) {// Handle errors thrown synchronously by the `_handleExec` implementation:

@@ -859,3 +382,3 @@ // Check to make sure this error isn't a special "escape hatch" from

return cb(err);
return proceedToInterceptsAndChecks(err, undefined, undefined, self, _cb, handleUncaughtException);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

@@ -868,3 +391,3 @@ // FUTURE: Consider using the more detailed explanation for ALL 4 (!!) of the cases

// ```
// return cb(flaverr({
// return proceedToInterceptsAndChecks(flaverr({
// message:

@@ -880,3 +403,3 @@ // 'Unexpected error was thrown while executing '+

// '```'
// }, self._omen));
// }, self._omen), undefined, undefined, self, _cb, handleUncaughtException);
// ```

@@ -1247,1 +770,638 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

module.exports = Deferred;
//////////////////////////////////////////////////////////////////////////////////////////
// ██████╗ ██████╗ ████████╗██╗███╗ ███╗██╗███████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗███████╗
// ██╔═══██╗██╔══██╗╚══██╔══╝██║████╗ ████║██║╚══███╔╝██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝██╗
// ██║ ██║██████╔╝ ██║ ██║██╔████╔██║██║ ███╔╝ ███████║ ██║ ██║██║ ██║██╔██╗ ██║███████╗╚═╝
// ██║ ██║██╔═══╝ ██║ ██║██║╚██╔╝██║██║ ███╔╝ ██╔══██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║██╗
// ╚██████╔╝██║ ██║ ██║██║ ╚═╝ ██║██║███████╗██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║███████║╚═╝
// ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝
//
// Our callback (`_cb`) is intercepted by a couple of other functions. This is
// just a slightly-more-efficient alternative to a series of self-calling functions.
// We only do this to afford better performance in the general case.
//
// > (Normally, this sort of micro-optimization wouldn't matter, but this is an extradordinarily
// > hot code path. Note that if we can prove self-calling functions are just as good, or even
// > good enough, it would be preferable to use them instead (not only for consistency, but
// > certainly for clarity as well).)
//
// To begin with, no matter what, intercept `_cb` by wrapping it in another function
// (a new one that we'll call `cb`) which adds some additional checks.
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Used exclusively by `Deferred.prototype.exec()`, this function is an optimization.
* It would be much better to use an IIFE instead of defining this function, but we're
* dealing with a very hot code path, so the performance gain is worth it.
* That said, this optimization should never be applied in normal userland code!
*/
function proceedToAfterExecSpinlocks (errCbArg, resultCbArg, extraCbArgs, self, _cb, handleUncaughtException, timeoutAlarm) {
// Implementorland spinlock
if (self._hasFinishedExecuting && !self._skipImplSpinlockWarning) {
console.warn(
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n'+
'WARNING: Something seems to be wrong with this function.\n'+
'It is trying to signal that it has finished AGAIN, after\n'+
'already resolving/rejecting once.\n'+
'(silently ignoring this...)\n'+
(self._omen?(
'\n'+
'To assist you in hunting this down, here is a stack trace:\n'+
'```\n'+
flaverr.getBareTrace(self._omen)+'\n'+
'```\n'+
'\n'
):'')+
' [?] For more help, visit https://sailsjs.com/support\n'+
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
);
return;
}//•
// If the deferred has already timed out, then there's no need to warn
// (This was _bound_ to happen beings as how we timed out.)
//
// > Note that we still set a flag to track that this happened. This is to make sure
// > this if/then statement can't possibly be true more than once (because that would
// > definitely still be unexpected-- and really hard to anticipate / debug if it were
// > to happen to you)
if (self._hasTimedOut) {
self._hasFinishedExecuting = true;
return;
}
// Clear timeout, if relevant.
if (timeoutAlarm) {
clearTimeout(timeoutAlarm);
}
if (errCbArg) {
// Ensure we're dealing w/ an Error instance.
errCbArg = flaverr.parseOrBuildError(errCbArg, self._omen);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ^ FUTURE: Better error message for non-Errors?
// (See impl of parseOrBuildError() in flaverr for more context.)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
self._hasFinishedExecuting = true;
} else {
// IWMIH, there was no error.
self._hasFinishedExecuting = true;
}//fi
return proceedToInterceptsAndChecks(errCbArg, resultCbArg, extraCbArgs, self, _cb, handleUncaughtException);
}//ƒ </proceedToAfterExecSpinlocks>
/**
* Used exclusively by `Deferred.prototype.exec()` and `proceedToAfterExecSpinlocks`, this function is an optimization.
* It would be much better to use an IIFE instead of defining this function, but we're
* dealing with a very hot code path, so the performance gain is worth it.
* That said, this optimization should never be applied in normal userland code!
*/
function proceedToInterceptsAndChecks (errCbArg, resultCbArg, extraCbArgs, self, _cb, handleUncaughtException) {
// ┬ ┬┬─┐┌─┐┌─┐ ┌─┐┌─┐┌┬┐┌─┐┌┐┌┌┬┐┬┌─┐┬ ┬ ┬ ┬ ┌─┐┌─┐┌┐┌┌─┐┬ ┬┌─┐┬┌┐┌┌─┐ ┌─┐┬─┐┬─┐┌─┐┬─┐┌─┐
// │││├┬┘├─┤├─┘ ├─┘│ │ │ ├┤ │││ │ │├─┤│ │ └┬┘───│ │ ││││├┤ │ │└─┐│││││ ┬ ├┤ ├┬┘├┬┘│ │├┬┘└─┐
// └┴┘┴└─┴ ┴┴ ┴ └─┘ ┴ └─┘┘└┘ ┴ ┴┴ ┴┴─┘┴─┘┴ └─┘└─┘┘└┘└ └─┘└─┘┴┘└┘└─┘ └─┘┴└─┴└─└─┘┴└─└─┘
// ┌─┐┬─┐┌─┐┌┬┐ ┌─┐┌┬┐┬ ┬┌─┐┬─┐ ┌┬┐┌─┐┌─┐┌─┐┬─┐┬─┐┌─┐┌┬┐ ┬┌┐┌┬ ┬┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐
// ├┤ ├┬┘│ ││││ │ │ │ ├─┤├┤ ├┬┘ ││├┤ ├┤ ├┤ ├┬┘├┬┘├┤ ││ ││││└┐┌┘│ ││ ├─┤ │ ││ ││││└─┐
// └ ┴└─└─┘┴ ┴ └─┘ ┴ ┴ ┴└─┘┴└─ ─┴┘└─┘└ └─┘┴└─┴└─└─┘─┴┘ ┴┘└┘ └┘ └─┘└─┘┴ ┴ ┴ ┴└─┘┘└┘└─┘
// ┬ ┬┬┌┬┐┬ ┬┬┌┐┌ ┌┬┐┬ ┬┌─┐ ┬ ┬┌─┐┌┐┌┌┬┐┬ ┌─┐┌─┐─┐ ┬┌─┐┌─┐ ┌─┐┬ ┬┌┐┌┌─┐┌┬┐┬┌─┐┌┐┌
// ││││ │ ├─┤││││ │ ├─┤├┤ ├─┤├─┤│││ │││ ├┤ ├┤ ┌┴┬┘├┤ │ ├┤ │ │││││ │ ││ ││││
// └┴┘┴ ┴ ┴ ┴┴┘└┘ ┴ ┴ ┴└─┘ ┴ ┴┴ ┴┘└┘─┴┘┴─┘└─┘└─┘┴ └─└─┘└─┘ └ └─┘┘└┘└─┘ ┴ ┴└─┘┘└┘
if (errCbArg) {
var doWrap;
// If we see E_NOT_SYNCHRONOUS, it should ALWAYS be wrapped.
// (The only time it would ever come from THIS Deferred is if we called .now() --
// and the code that checks that is not even part of .exec())
if (_.isObject(errCbArg) && errCbArg.code === 'E_NOT_SYNCHRONOUS') {
doWrap = true;
}
// If we see a TimeoutError from a Deferred **OTHER** than this one,
// then wrap it.
else if (_.isObject(errCbArg) && errCbArg.name === 'TimeoutError' && errCbArg.traceRef !== self) {
doWrap = true;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Note: An easy way to test this is to run something like the following in the Node REPL:
// ````
// require('machine')({identity: 'outside', fn: (inputs, exits)=>{ require('machine')({identity: 'inside',timeout: 2000, exits: {notFound:{}}, fn: (inputs, exits)=>{ /*deliberately never exits...*/ }})().exec((err)=>{ if (err){return exits.error(err);} return exits.success(); }); }})()
// ````
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
// If instructed to do so, perform the wrapping.
if (doWrap) {
errCbArg = flaverr.wrap({
code:
'E_FROM_WITHIN',
message:
'Some logic inside this function\'s implementation encountered an error.\n'+
' [?] See `.raw` for more details, or visit https://sailsjs.com/support for help.',
}, errCbArg, self._omen);
}//fi
}//fi
// ┬ ┬┌─┐┌─┐┬─┐┬ ┌─┐┌┐┌┌┬┐ ┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐─┐ ┬┌─┐┌─┐ ┬ ┌─┐┌─┐
// │ │└─┐├┤ ├┬┘│ ├─┤│││ ││ ├─┤├┤ │ ├┤ ├┬┘├┤ ┌┴┬┘├┤ │ │ │ └─┐
// └─┘└─┘└─┘┴└─┴─┘┴ ┴┘└┘─┴┘ ┴ ┴└ ┴ └─┘┴└─└─┘┴ └─└─┘└─┘ ┴─┘└─┘└─┘
// If this Deferred was configured with after-exec lifecycle callbacks from
// userland via .intercept(), .tolerate(), or .retry(), then call those
// lifecycle callbacks now, if appropriate, picking up the potentially-changed
// (even potentially-reconstructed!) error or result.
//
// > Note that this is only relevant if there was an error of some kind.
try {
if (self._userlandAfterExecLCs && errCbArg) {
// Now before proceeding further, check for a match (if there are any configured).
// > NOTE: We only ever run one of these handlers for any given response!
var matchingUserlandLC;
for (var i = 0; i < self._userlandAfterExecLCs.length; i++) {
var lcDef = self._userlandAfterExecLCs[i];
if (lcDef.rule === undefined) {
matchingUserlandLC = lcDef;
break;
} else if (flaverr.taste(lcDef.rule, errCbArg)) {
matchingUserlandLC = lcDef;
break;
}
}//∞
// Now, if we have a match...
if (matchingUserlandLC) {
// Get reasonable default for handler, if no explicit handler function was configured.
if (matchingUserlandLC.handler === undefined && matchingUserlandLC.type === 'tolerate') {
matchingUserlandLC.handler = function(){ return; };
}
else if (matchingUserlandLC.handler === undefined && matchingUserlandLC.type === 'intercept') {
matchingUserlandLC.handler = function(err){ return err; };
}
// Run userland LC.
var resultFromHandler;
try {
resultFromHandler = matchingUserlandLC.handler(errCbArg);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: add support for this, beginning with something like the
// following incomplete implementation:
//
// ```
// if (matchingUserlandLC.handler.constructor.name === 'AsyncFunction') {
// var interceptPromise;
// try {
// interceptPromise = matchingLifecycleInstruction.handler();
// } catch (err) {
// if (err === false) { return proceed(undefined, true); }//« special case (`throw false`)
// else { return proceed(err); }
// }
//
// interceptPromise.then(function(_resultFromHandler){
// resultFromHandler = _resultFromHandler;
// proceed(undefined, resultFromHandler);
// });
// interceptPromise.catch(function(err) {
// /* eslint-disable callback-return */
// if (err === false) { proceed(undefined, true); }//« special case (`throw false`)
// else { proceed(err); }
// /* eslint-enable callback-return */
// });
// }
// else {
// resultFromHandler = matchingUserlandLC.handler(errCbArg);
// }
// ```
//
// *** Don't forget to update bindUserlandAfterExecLC to remove the
// check that displays an error message instead of allowing this usage! ***
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} catch (err) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: (Maybe) Specifically for `.tolerate()`, allow throwing special exit signals
// from within the handler.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// If this is an .intercept() handler, then it's possible the handler threw on purpose,
// perhaps because it was attempting to send a special signal to its caller (e.g. the
// implementation of an action/helper/etc) where it presumably has a special meaning.
// So in this case, we customize the error message to reflect that possibility and to
// suggest an appropriate resolution.
if (matchingUserlandLC.type === 'intercept') {
throw flaverr({
name:
'UsageError',
message:
'Caught unexpected error in `.intercept()` handler, which should not throw:\n'+
flaverr.parseOrBuildError(err).message+'\n'+
'If this was intentional, i.e. to communicate a signal to the caller, then\n'+
'please just return the new or modified error you would like to use instead.\n'+
'The value returned to `.intercept()` will be used as the new Error.\n'+
' [?] See https://sailsjs.com/support for help.',
raw:
err
}, self._omen);
}
else {
// Otherwise, we'll just consider this a standard unexpected error:
throw flaverr({
name:
'UsageError',
message:
'Encountered unexpected error in `.'+matchingUserlandLC.type+'()` handler. '+
flaverr.parseOrBuildError(err).message+'\n'+
' [?] See https://sailsjs.com/support for help.',
raw:
err
}, self._omen);
}
}//ç
// Now swallow or swap out the error, if instructed to do so.
// Swallow:
// > i.e. if a matching `.tolerate()` was encountered, then consider
// > this successful no matter what, and use the value returned by the
// > LC as the new result.
if (matchingUserlandLC.type === 'tolerate') {
errCbArg = undefined;
resultCbArg = resultFromHandler;
}
// Swap:
//
// > i.e. if a matching `.intercept()` was encountered, then consider
// > whatever the intercept handler returned to be our new Error.
else if (matchingUserlandLC.type === 'intercept') {
// If the handler returned `undefined`, then fail with an error.
// (this shouldn't happen, an indicates invalid usage)
if (resultFromHandler === undefined) {
throw flaverr({
name:
'UsageError',
message:
'`.intercept()` handler returned `undefined`, but this should never happen.\n'+
'Regardless, here is a summary of the original underlying error:\n'+
flaverr.parseOrBuildError(errCbArg).message+'\n'+
' [?] See https://sailsjs.com/support for help.',
raw:
errCbArg
}, self._omen);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Normally, these errors must ALWAYS be Error instances already.
// But for this special case, where the original Error value
// is being overridden through the use of `.intercept()`, we'd
// LIKE to make a special exception to the rule (no pun intended.)
//
// There's only one problem: Because of bluebird's "maybeWrapAsError"
// implementation, we can't send certain non-Errors through to it
// (specifically primitives) because they get autowrapped.
//
// > Here's the relevant bit of code:
// > https://github.com/petkaantonov/bluebird/blob/e8d8525a0517280d11d6c77ae6b61df86419232b/src/promisify.js#L182-L184
//
// Again, most of the time, this would be fine. But while bluebird's
// looking out for us here is admirable, there are some situations.
// where this is not welcome -- such as when trying to throw a string.
//
// > Why throw a string?
// > This is useful for throwing special signals-- e.g. from the inside
// > of an actions2 action or a helper in Sails, a machine's fn in a
// > machinepack, or from a commandline script.
//
// So anyway, to work around this, we have to come up with a consistent
// way of wrapping up non-Errors to look like Errors. That's what we
// do next.
//
// ** Note that we also do this in a couple of other places in parley. **
// ** (look for `flaverr.parseOrBuildError()` calls) **
//
// > (If ever we find ourselves wanting to revert this approach, the old
// > code that used to check for non-Errors was removed in parley@376208fd1c0ab70e7a6b9c4ecfa563ec0d77a3a8.
// > But... as mentioned above-- there are some good reasons to keep things
// > the new way that they are now.)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var interceptError = flaverr.parseOrBuildError(resultFromHandler, self._omen);
errCbArg = interceptError;
}//fi </ if this is an .intercept() >
}//fi </ if there is a matching userland LC >
}//fi </ if we got an error AND any userland LCs were provided >
} catch (err) {
// If any error was encountered above, then stuff it in `errCbArg` so
// that it can still be handled gracefully-- including getting any final
// treatment from implementorland's `finalAfterExecLC`.
errCbArg = err;
}
// Continue to the final bits (note: only reason we didn't use an IIFE here is for performance)
return proceedToFinalAfterExecLC(errCbArg, resultCbArg, extraCbArgs, self, _cb, handleUncaughtException);
}//ƒ </ definition of `proceedToInterceptsAndChecks` >
/**
* Used exclusively by `proceedToInterceptsAndChecks()`, this function is an optimization.
* It would be much better to use an IIFE instead of defining this function, but we're
* dealing with a very hot code path, so the performance gain is worth it.
* That said, this optimization should never be applied in normal userland code!
*/
function proceedToFinalAfterExecLC(errCbArg, resultCbArg, extraCbArgs, self, _cb, handleUncaughtException) {
// ╔═╗╦╔╗╔╔═╗╦ ┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐─┐ ┬┌─┐┌─┐ ┬ ┌─┐
// ╠╣ ║║║║╠═╣║ ├─┤├┤ │ ├┤ ├┬┘├┤ ┌┴┬┘├┤ │ │ │
// ╚ ╩╝╚╝╩ ╩╩═╝ ┴ ┴└ ┴ └─┘┴└─└─┘┴ └─└─┘└─┘ ┴─┘└─┘
// ┌─┐┬─┐┌─┐┌┬┐ ┬┌┬┐┌─┐┬ ┌─┐┌┬┐┌─┐┌┐┌┌┬┐┌─┐┬─┐┬ ┌─┐┌┐┌┌┬┐
// ├┤ ├┬┘│ ││││ ││││├─┘│ ├┤ │││├┤ │││ │ │ │├┬┘│ ├─┤│││ ││
// └ ┴└─└─┘┴ ┴ ┴┴ ┴┴ ┴─┘└─┘┴ ┴└─┘┘└┘ ┴ └─┘┴└─┴─┘┴ ┴┘└┘─┴┘
// If this Deferred was built with an `finalAfterExecLC` lifecycle callback,
// then intercept our normal flow to call that lifecycle callback, picking up
// the potentially-changed (even potentially-reconstructed!) error or result.
if (self._finalAfterExecLC) {
if (errCbArg) {
errCbArg = self._finalAfterExecLC(errCbArg);
}
else {
resultCbArg = self._finalAfterExecLC(undefined, resultCbArg);
}
}//fi
// ┌┐┌┌─┐┬ ┬ ┌─┐┌─┐┌┬┐┬ ┬┌─┐┬ ┬ ┬ ┬ ┌┬┐┬─┐┬┌─┐┌─┐┌─┐┬─┐ ┌─┐┌┐ ┌─┐┌┐┌┌─┐┬ ┬┬─┐┬┌┐┌┌─┐
// ││││ ││││ ├─┤│ │ │ │├─┤│ │ └┬┘ │ ├┬┘││ ┬│ ┬├┤ ├┬┘ │ ├┴┐ ├┤ │││└─┐│ │├┬┘│││││ ┬
// ┘└┘└─┘└┴┘ ┴ ┴└─┘ ┴ └─┘┴ ┴┴─┘┴─┘┴ ┴ ┴└─┴└─┘└─┘└─┘┴└─ └─┘└─┘┘ └─┘┘└┘└─┘└─┘┴└─┴┘└┘└─┘
// ┌┐┌┌─┐ ┌─┐┌─┐┌─┐┬┌┬┐┌─┐┌┐┌┌┬┐┌─┐┬ ┌─┐┬ ┬┌─┐┬ ┬ ┌─┐┬ ┬┬┌┐┌┌─┐ ┌─┐┌─┐ ┌─┐┬─┐┬─┐┌─┐┬─┐┌─┐
// ││││ │ ├─┤│ │ │ ││├┤ │││ │ ├─┤│ └─┐│││├─┤│ │ │ │││││││││ ┬ │ │├┤ ├┤ ├┬┘├┬┘│ │├┬┘└─┐
// ┘└┘└─┘ ┴ ┴└─┘└─┘┴─┴┘└─┘┘└┘ ┴ ┴ ┴┴─┘ └─┘└┴┘┴ ┴┴─┘┴─┘└─┘└┴┘┴┘└┘└─┘ └─┘└ └─┘┴└─┴└─└─┘┴└─└─┘
// ┌─┐┬─┐┌─┐┌┬┐ ┌─┐┌─┐┬ ┬ ┌┐ ┌─┐┌─┐┬┌─┌─┐
// ├┤ ├┬┘│ ││││ │ ├─┤│ │ ├┴┐├─┤│ ├┴┐└─┐
// └ ┴└─└─┘┴ ┴ └─┘┴ ┴┴─┘┴─┘└─┘┴ ┴└─┘┴ ┴└─┘
// If there are any extra arguments, send them back too.
// (This is unconventional, but permitted to allow for extra metadata,
// which is sometimes handy when you want to expose advanced usage.)
// Otherwise, if there's no result, just call the callback w/ no args.
// (This just makes for better log output, etc.)
// More on that below!
// If this callback is being called after at least one tick has elapsed...
if (self._hasAlreadyWaitedAtLeastOneTick) {
// If 2nd argument (handleUncaughtException) was provided to .exec(), then run that
// instead of throwing. This protects against unexpected, uncaught exceptions in
// asynchronous callbacks.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: Probably deprecate this, then remove support (see above).
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if (handleUncaughtException) {
try {
if (extraCbArgs) {
return _cb.apply(undefined, [errCbArg, resultCbArg].concat(extraCbArgs));
} else if (errCbArg !== undefined) {
return _cb(errCbArg);
} else if (resultCbArg !== undefined) {
return _cb(undefined, resultCbArg);
} else {
return _cb();
}
} catch (unexpectedErrorFromCallback) {
return handleUncaughtException(unexpectedErrorFromCallback);
}
}//•
// Otherwise, just trigger the callback as-is.
// (If it throws, it will crash the process!)
if (extraCbArgs) {
return _cb.apply(undefined, [errCbArg, resultCbArg].concat(extraCbArgs));
} else if (errCbArg !== undefined) {
return _cb(errCbArg);
} else if (resultCbArg !== undefined) {
return _cb(undefined, resultCbArg);
} else {
return _cb();
}
}//•
//‡
// Otherwise, our logic is synchronous (i.e. <1 async tick has elapsed at the time it's being
// called). So wrap the `_cb` from userland in a try/catch. If an unhandled error of any kind
// is thrown from the userland cb, our wrapper uses a special Envelope to bust out of the `try`
// block, ensuring that the unhandled exception is thrown up to userland.
//
// > NOTE:
// > Without this extra code here, we'd end up with the old behavior: outputting a puzzling error
// > message -- e.g. about something unexpected things happening in the Deferred, or a warning
// > about triggering the callback twice (when actually, the issue is that something went wrong
// > in the callback-- and that the Deferred logic happened to be synchronous, so it wasn't able
// > to escape parley's internal `try` block.)
// >
// > Some relevant links for reference:
// > • https://github.com/node-machine/machine/blob/7fdcf8a869605d0951909725061379cd27bd7f0d/lib/private/intercept-exit-callbacks.js#L186-L238
// > • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_hasFnYieldedYet&type=
// > • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_runningSynchronously&type=
else {
try {
if (extraCbArgs) {
return _cb.apply(undefined, [errCbArg, resultCbArg].concat(extraCbArgs));
} else if (errCbArg !== undefined) {
return _cb(errCbArg);
} else if (resultCbArg !== undefined) {
return _cb(undefined, resultCbArg);
} else {
return _cb();
}
} catch (unexpectedErrorFromCallback) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: expand the "if" conditional that determines synchronousness to
// support the possibility of asynchronous userland after-exec LCs
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
throw flaverr.wrap({
code: 'E_ESCAPE_HATCH',
traceRef: self
}, unexpectedErrorFromCallback, self._omen);
}
}//•
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: Additionally, this additional layer of wrapping could take care of improving
// stack traces, even in the case where an Error comes up from inside the implementation.
// If done carefully, this can be done in a way that protects characteristics of the
// internal Error (e.g. its "code", etc.), while also providing a better stack trace.
//
// For example, something like this:
// ```
// var relevantPropNames = _.difference(
// _.union(
// ['name', 'message'],
// Object.getOwnPropertyNames(underlyingError)
// ),
// ['stack']
// );
// var errTemplate = _.pick(underlyingError, relevantPropNames);
// errTemplate.raw = underlyingError;//<< could override stuff-- that's ok (see below).
// var newError = flaverr(errTemplate, omen);
// ```
// > Note that, above, we also kept the original error (and thus _its_ trace) and
// > attached that as a separate property. If the original error already has "raw",
// > that's ok. This is one thing that it makes sense for us to mutate-- and any
// > attempt to do otherwise would probably be more confusing (you can imagine a while
// > loop where we add underscores in front of the string "raw", and use that as a keyname.
// > But again, that ends up being more confusing from a userland perspective.)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}//ƒ
/**
* bindUserlandAfterExecLC()
*
* Used exclusively by `Deferred.prototype.intercept()` & `.tolerate()`, this function is an optimization.
* It would be much better to use an IIFE instead of defining this function, but we're
* dealing with a very hot code path, so the performance gain is worth it.
* That said, this optimization should never be applied in normal userland code!
*
* @param {String} lcType
* @param {String|Dictionary|Function} negotiationRuleOrWildcardHandler
* @param {Function?} specificHandler
* @param {Deferred} deferred
*
*
* > The lifecycle callback attached here will run *before* this Deferred's
* > `_finalAfterExecLC` function (if it has one configured from implementorland.)
* >
* > Historical notes:
* > https://gist.github.com/mikermcneil/c1bc2d57f5bedae810295e5ed8c5f935
*/
function bindUserlandAfterExecLC(lcType, negotiationRuleOrWildcardHandler, specificHandler, deferred){
// Handle variadic usage.
var handler;
var negotiationRule;
if (_.isFunction(negotiationRuleOrWildcardHandler) && specificHandler === undefined) {
handler = negotiationRuleOrWildcardHandler;
}
else {
negotiationRule = negotiationRuleOrWildcardHandler;
handler = specificHandler;
}
// Validate arguments.
if (handler !== undefined && !_.isFunction(handler)) {
throw flaverr({
name:
'UsageError',
message:
'Invalid usage of `.'+lcType+'()`. Provided handler function is invalid.\n'+
' [?] See https://sailsjs.com/support for help.'
}, deferred._omen);
}//•
if (handler !== undefined && handler.constructor.name === 'AsyncFunction') {
throw flaverr({
name:
'UsageError',
message:
'`async` functions are not currently supported for `.'+lcType+'()` '+
'handlers, so please stick to synchronous logic for now. In the mean time, if you '+
'need to use asynchronous logic while intercepting or tolerating an error (such as '+
'additional database queries or HTTP requests) you can still accomplish this '+
'by refactoring your code.\n'+
' [?] For advice or assistance, come visit https://sailsjs.com/support'
});
}//•
if (handler === undefined && lcType === 'intercept') {
throw flaverr({
name:
'UsageError',
message:
'Invalid usage of `.intercept()`. No handler function provided.\n'+
' [?] See https://sailsjs.com/support for help.'
}, deferred._omen);
}//•
if (handler === undefined && negotiationRule === undefined && lcType === 'tolerate') {
throw flaverr({
name:
'UsageError',
message:
'Invalid usage of `.tolerate()`. No handler function was provided, and no\n'+
'negotiation rule was provided either. It would be unsafe to continue.\n'+
'It is never a good idea to tolerate *ALL* errors a function might\n'+
'encounter, because doing so would make it easy to accidentally swallow\n'+
'real problems or bugs. So instead, please provide some way of narrowing\n'+
'down the errors which you\'d like to tolerate, like `.tolerate(\'E_FOOBAR\')`.\n'+
' [?] See https://sailsjs.com/support for help.'
}, deferred._omen);
}//•
if (negotiationRule !== undefined) {
if (_.isString(negotiationRule) && negotiationRule) {
// Ok, we'll assume it's fine.
}
else if (_.isArray(negotiationRule)) {
// you can bind multiple LCs at the same time
// (array rules are automatically split into sub-rules)
}
else if (_.isObject(negotiationRule) && !_.isArray(negotiationRule) && !_.isFunction(negotiationRule)) {
// flaverr/bluebird/lodash-style dictionary negotiation rules are now supported.
}
else {
throw flaverr({
name:
'UsageError',
message:
'Invalid usage of `.'+lcType+'()`. Invalid error negotiation rule: `'+util.inspect(negotiationRule,{depth:null})+'`.\n'+
' [?] For advice or assistance, come visit https://sailsjs.com/support'
}, deferred._omen);
}
}//fi
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: MAYBE add a best-effort check to make sure there is no pre-existing
// after exec LC rule that matches this one (i.e. already previously registered
// using .tolerate() or .intercept())
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if (!deferred._userlandAfterExecLCs) {
deferred._userlandAfterExecLCs = [];
}//fi
if (_.isArray(negotiationRule)) {
for (var i=0; i<negotiationRule.length; i++) {
deferred._userlandAfterExecLCs.push({
type: lcType,
rule: negotiationRule[i],
handler: handler
});
}//∞
}
else {
deferred._userlandAfterExecLCs.push({
type: lcType,
rule: negotiationRule,
handler: handler
});
}
return deferred;
}//ƒ
{
"name": "parley",
"version": "3.4.3",
"version": "3.4.4",
"description": "Practical, lightweight flow control for Node.js. Supports `await`, callbacks and promises.",

@@ -5,0 +5,0 @@ "main": "lib/parley.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc