init source

This commit is contained in:
Le Viet
2022-03-07 22:07:57 +07:00
parent e4376f3777
commit 8aba590a8d
11240 changed files with 1012977 additions and 0 deletions
+265
View File
@@ -0,0 +1,265 @@
'use strict';
/*
* cls-bluebird
* Module entry point
*/
// Modules
var isBluebird = require('is-bluebird');
// Require Bluebird library
// Ignore errors if cannot be required
var Bluebird;
try {
Bluebird = require('bluebird');
} catch (err) {}
// Imports
var shimMethod = require('./shimMethod'),
shimOnCancel = require('./shimOnCancel'),
shimCall = require('./shimCall'),
shimUsing = require('./shimUsing'),
shimCoroutine = require('./shimCoroutine');
// Exports
/**
* Patch bluebird to run maintain CLS context for a specific namespace.
* If a Bluebird Promise constructor is provided, it is patched.
* If not provided, the version returned by `require('bluebird')` is used.
*
* @param {Object} ns - CLS namespace object
* @param {Function} [Promise] - Bluebird Promise constructor to patch (optional)
* @returns {Function} - Bluebird Promise constructor
* @throws {TypeError} - If `ns` or `Promise` are not of correct type
* @throws {Error} - If `Promise` not provided and cannot require `bluebird` module
*/
module.exports = function patchBluebird(ns, Promise) {
// Check namespace is valid
if (!ns || typeof ns !== 'object' || typeof ns.bind !== 'function' || typeof ns.run !== 'function') throw new TypeError('Must provide CLS namespace to patch Bluebird against');
// Check Promise implementation is some variation of Bluebird
// If none provided, use default Bluebird
if (!Promise) {
Promise = Bluebird;
if (!Promise) throw new Error('Could not require Bluebird');
} else if (!isBluebird.ctor(Promise)) {
throw new TypeError('Promise implementation provided must be Bluebird');
}
// Patch all methods to carry CLS context
var v3 = isBluebird.ctor.v3(Promise);
/*
* Core
*
* Not patched as always run callback synchronously:
* new Promise()
* Promise.try() / Promise.attempt()
*
* Not patched as do not take a callback:
* Promise.bind() / .bind()
* Promise.resolve() / Promise.fulfilled() / Promise.cast()
* Promise.reject() / Promise.rejected()
*
* Not patched as call another patched method synchronously
* .error() - calls .catch()
*
* Not patched as are wrappers:
* Promise.method()
*
* NB Due to bug in bluebird v2 https://github.com/petkaantonov/bluebird/issues/1153
* `Promise.join()` calls the callback synchronously if input is only values or
* resolved promises, but async if any promises are pending.
* So handler is sometimes bound to CLS context unnecessarily, but this does no harm
* beyond the very slight performance overhead of an extra `ns.bind()` call.
*/
shimProto('then', v3 ? [0, 1] : [0, 1, 2]);
shimProto('spread', v3 ? [0] : [0, 1]);
shimProto('finally', [0]);
Promise.prototype.lastly = Promise.prototype.finally;
shimStatic('join', [-1]);
if (!v3) {
// Only patched in bluebird v2.
// In bluebird v3 `.catch()` calls `.then()` immediately which binds callback.
shimProto('catch', [-1]);
Promise.prototype.caught = Promise.prototype.catch;
}
/*
* Synchronous inspection
*
* Not patched as do not take a callback:
* .isFulfilled()
* .isRejected()
* .isPending()
* .isCancelled()
* .isResolved()
* .value()
* .reason()
* .reflect()
*/
/*
* Collections
*
* Not patched as do not take a callback:
* Promise.all() / .all()
* Promise.props() / .props()
* Promise.any() / .any()
* Promise.some() / .some()
* Promise.race() / .race()
*/
shimBoth('map', [0]);
shimBoth('filter', [0]);
shimBoth('reduce', [0]);
shimBoth('each', [0]);
// In bluebird v2, there is no `Promise.mapSeries()`/`.mapSeries()` method
if (v3) shimBoth('mapSeries', [0]);
/*
* Resource management
*
* NB disposer callbacks are bound to context at time disposer created, not when utilized in `using()`
*/
shimUsing(Promise, ns, v3); // shims `Promise.using()`
shimProto('disposer', [0]);
/*
* Promisification
*
* Not patched as always run callback synchronously:
* Promise.fromCallback()
* Promise.fromNode()
*
* Not patched as they are wrappers:
* Promise.promisify()
* Promise.promisifyAll()
*/
shimProto('asCallback', [0]);
Promise.prototype.nodeify = Promise.prototype.asCallback;
/*
* Timers
*
* Not patched as do not take a callback:
* Promise.delay() / .delay()
* .timeout()
*/
/*
* Cancellation
*
* Not patched as does not take a callback:
* .cancel() / .break()
* .isCancellable()
* .cancellable() (bluebird v2 only)
* .uncancellable() (bluebird v2 only)
*
* NB In bluebird v3 `onCancel` handler will be called
* in CLS context of call to `onCancel()`.
*/
// Patch `Promise.prototype._resolveFromExecutor`
// in order to patch `onCancel` handler in `new Promise()`.
if (v3) shimOnCancel(Promise, ns);
/*
* Generators
*
* Not patched as does not take a callback:
* Promise.coroutine.addYieldHandler()
*
* NB `options.yieldHandler` will run in whatever CLS context is active at time of `yield`
*/
var addYieldHandler = Promise.coroutine.addYieldHandler;
shimCoroutine('coroutine', Promise, ns, v3); // shims `Promise.coroutine()`
Promise.coroutine.addYieldHandler = addYieldHandler;
/*
* Utility
*
* Not patched as do not take a callback:
* .get()
* .return() / .thenReturn()
* .throw() / .thenThrow()
* .catchReturn()
* .catchThrow()
* Promise.getNewLibraryCopy()
* Promise.noConflict()
* Promise.setScheduler()
*/
shimProto('tap', [0]);
if (v3) shimProto('tapCatch', [-1]);
shimCall(Promise, ns); // shims `.call()`
/*
* Configuration
*
* Not patched as do not take a callback:
* Promise.config()
* .suppressUnhandledRejections()
* Promise.longStackTraces()
* Promise.hasLongStackTraces()
*
* Not patched as meaningless to do so:
* Promise.onPossiblyUnhandledRejection()
* Promise.onUnhandledRejectionHandled()
*
* NB Error handlers will run with unknown CLS context.
* CLS context should not be relied upon to be the context at the time error was thrown.
* Catch errors with `.catch()` instead!
*/
shimProto('done', v3 ? [0, 1] : [0, 1, 2]);
/*
* Progression (bluebird v2 only)
*/
if (!v3) shimProto('progressed', [0]);
/*
* Undocumented
*
* Not patched as do not take a callback:
* Promise.is()
* Promise.settle() / .settle()
* Promise.defer() / Promise.pending()
* .toString()
* .toJSON()
*/
// `.fork()` does not exist in bluebird v3
if (!v3) shimProto('fork', [0, 1, 2]);
shimCoroutine('spawn', Promise, ns, v3); // shims `Promise.spawn()`
// Return patched Bluebird constructor
return Promise;
/*
* Patching functions
*/
function shimStatic(methodName, args) {
shimMethod(Promise, methodName, args, ns);
}
function shimProto(methodName, args) {
shimMethod(Promise.prototype, methodName, args, ns);
}
function shimBoth(methodName, args) {
shimProto(methodName, args);
shimStatic(methodName, args.map(function(arg) { return arg < 0 ? arg : arg + 1; }));
}
};
+50
View File
@@ -0,0 +1,50 @@
'use strict';
/*
* cls-bluebird
* Function to shim `Promise.prototype.call`
*/
// Modules
var shimmer = require('shimmer');
// Exports
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* Patch `call` method to run callbacks in current CLS context.
*
* @param {Function} Promise - Bluebird Promise constructor to patch
* @param {Object} ns - CLS namespace to bind callbacks to
* @returns {undefined}
*/
module.exports = function(Promise, ns) {
// Patch method
shimmer.wrap(Promise.prototype, 'call', function(callOriginal) {
return function() {
// Temporarily wrap `this._then` to bind the object method to current CLS context
// (`this.call()` will call `this._then()` synchronously)
var _thenOriginal = this._then,
ownProperty = hasOwnProperty.call(this, '_then');
this._then = function() {
// Unwrap `this._then`
if (ownProperty) {
this._then = _thenOriginal;
} else {
delete this._then;
}
// Bind function that will be called to call object method to CLS context
arguments[0] = ns.bind(arguments[0]);
// Run original `this._then` method
return _thenOriginal.apply(this, arguments);
};
// Call original `call` method
return callOriginal.apply(this, arguments);
};
});
};
+85
View File
@@ -0,0 +1,85 @@
'use strict';
/*
* cls-bluebird
* Function to shim `Promise.coroutine`
*
* Works by binding the `.next()` and `.throw()` methods of generator to CLS context
* at time when coroutine is executed.
*
* In bluebird v3.x, running the coroutine internally calls `.lastly()` if cancellation is enabled.
* To prevent unnecessary binding of the `.lastly()` callback to CLS context, this patch
* temporarily disables the patch on `Promise.prototype.lastly`.
* NB This patch could break if bluebird internals change, but this is covered by the tests.
*/
// Modules
var shimmer = require('shimmer');
// Exports
/**
* Patch `Promise.coroutine` or `Promise.spawn` to maintain current CLS context after all `yield` statements.
*
* @param {string} methodName - method name (either 'coroutine' or 'spawn')
* @param {Function} Promise - Bluebird Promise constructor to patch
* @param {Object} ns - CLS namespace to bind callbacks to
* @returns {undefined}
*/
module.exports = function(methodName, Promise, ns, v3) {
var lastlyPatched = Promise.prototype.lastly,
lastlyOriginal = Promise.prototype.lastly.__original;
// Patch method
shimmer.wrap(Promise, methodName, function(original) {
return function(generatorFunction, options) {
// NB If `generatorFunction` is not a function, do not alter it.
// Pass value directly to bluebird which will throw an error.
if (typeof generatorFunction === 'function') {
// Create proxy generator function
var generatorFunctionOriginal = generatorFunction;
generatorFunction = function() {
// Create generator from generator function
var generator = generatorFunctionOriginal.apply(this, arguments);
// Bind `.next()`, '.throw()' and `.return()` to current CLS context.
// NB CLS context is from when coroutine is called, not when created.
['next', 'throw', 'return'].forEach(function(name) {
if (typeof generator[name] === 'function') generator[name] = ns.bind(generator[name]);
});
return generator;
};
}
// Temporarily remove patch from `Promise.prototype.lastly` in bluebird v3
// to avoid unnecessary binding to CLS context.
var self = this;
if (methodName === 'spawn' && v3) {
return tempPatchLastly(function() {
return original.call(self, generatorFunction, options);
});
}
var fn = original.call(this, generatorFunction, options);
if (methodName === 'coroutine' && v3) {
return function() {
var self = this, args = arguments;
return tempPatchLastly(function() {
return fn.apply(self, args);
});
};
}
return fn;
};
});
function tempPatchLastly(fn) {
Promise.prototype.lastly = lastlyOriginal;
var res = fn();
Promise.prototype.lastly = lastlyPatched;
return res;
}
};
+41
View File
@@ -0,0 +1,41 @@
'use strict';
/*
* cls-bluebird
* Function to shim an object method to retain CLS context
*/
// Modules
var shimmer = require('shimmer');
// Exports
/**
* Patch method to run callbacks in current CLS context.
*
* @param {Object} obj - Object on which to find method
* @param {string} methodName - method name
* @param {Array} args - Array of indexes of arguments which are callbacks
* (negative numbers count from end e.g. -1 is last argument, -2 is penultimate)
* @param {Object} ns - CLS namespace to bind callbacks to
* @returns {undefined}
*/
module.exports = function(obj, methodName, args, ns) {
// Skip non-existent methods
if (!obj[methodName]) return;
// Patch method
shimmer.wrap(obj, methodName, function(original) {
return function() {
for (var i = 0; i < args.length; i++) {
var argIndex = args[i];
if (argIndex < 0) argIndex += arguments.length;
var callback = arguments[argIndex];
if (typeof callback === 'function') arguments[argIndex] = ns.bind(callback);
}
return original.apply(this, arguments);
};
});
};
+44
View File
@@ -0,0 +1,44 @@
'use strict';
/*
* cls-bluebird
* Function to shim `Promise.prototype._resolveFromExecutor`
* in order to patch `onCancel` handler in `new Promise()`.
*/
// Modules
var shimmer = require('shimmer');
// Exports
/**
* Patch `_resolveFromExecutor` proto method to run `onCancel` callbacks in current CLS context.
*
* @param {Function} Promise - Bluebird Promise constructor to patch
* @param {Object} ns - CLS namespace to bind callbacks to
* @returns {undefined}
*/
module.exports = function(Promise, ns) {
// Patch method
shimmer.wrap(Promise.prototype, '_resolveFromExecutor', function(_resolveFromExecutorOriginal) {
return function(executor) {
// Patch executor
var executorPatched = function() {
var onCancel = arguments[2];
if (onCancel) {
// Patch onCancel function
arguments[2] = function(fn) {
// Bind onCancel handler to current CLS context
if (typeof fn === 'function') fn = ns.bind(fn);
return onCancel.call(this, fn);
};
}
return executor.apply(this, arguments);
};
// Call original method
return _resolveFromExecutorOriginal.call(this, executorPatched);
};
});
};
+114
View File
@@ -0,0 +1,114 @@
'use strict';
/*
* cls-bluebird
* Function to shim `Promise.using`
*
* `Promise.using()` calls `.then()` and `.lastly` internally which leads to
* unnecessary CLS context binding with a naive patch.
*
* This custom patch intercepts calls to `Promise.all()` (v3) or `Promise.settle()` (v2)
* within `Promise.using()` and patches the resulting promise's `.then`/`.lastly` methods
* so they do not bind callbacks to CLS context.
*
* NB This patch could break if bluebird internals change, but this is covered by the tests.
*/
// Modules
var shimmer = require('shimmer');
// Exports
/**
* Patch `Promise.using` method to run callbacks in current CLS context.
*
* @param {Function} Promise - Bluebird Promise constructor to patch
* @param {Object} ns - CLS namespace to bind callbacks to
* @param {boolean} v3 - `true` if bluebird being patched is v3.x
* @returns {undefined}
*/
module.exports = function(Promise, ns, v3) {
(v3 ? patchV3 : patchV2)(Promise, ns);
};
// Patch for `Promise.using()` in bluebird v3
function patchV3(Promise, ns) {
var thenOriginal = Promise.prototype.then.__original,
lastlyOriginal = Promise.prototype.lastly.__original;
// Patch method
shimmer.wrap(Promise, 'using', function(usingOriginal) {
return function() {
// Bind `using` callback (last arg)
var argIndex = arguments.length - 1,
callback = arguments[argIndex];
if (typeof callback === 'function') arguments[argIndex] = ns.bind(callback);
// Temporarily patch `Promise.all()`
shimmer.wrap(Promise, 'all', function(allOriginal) {
return function(promises) {
// Remove temporary patch on `Promise.all()`
Promise.all = allOriginal;
// Call original `Promise.all()`
var p = allOriginal.call(this, promises);
// Patch `.then()` method on this promise to not bind callbacks
p.then = function() {
var p = thenOriginal.apply(this, arguments);
// Patch `.lastly()` method on this promise to not bind callbacks
p.lastly = lastlyOriginal;
return p;
};
return p;
};
});
// Call original `Promise.using()` method
return usingOriginal.apply(this, arguments);
};
});
}
// Patch for `Promise.using()` in bluebird v2
function patchV2(Promise, ns) {
var thenOriginal = Promise.prototype.then.__original;
// Patch method
shimmer.wrap(Promise, 'using', function(usingOriginal) {
return function() {
// Bind `using` callback (last arg)
var argIndex = arguments.length - 1,
callback = arguments[argIndex];
if (typeof callback === 'function') arguments[argIndex] = ns.bind(callback);
// Temporarily patch `Promise.settle()`
shimmer.wrap(Promise, 'settle', function(settleOriginal) {
return function(resources) {
// Remove temporary patch on `Promise.settle()`
Promise.settle = settleOriginal;
// Call original `Promise.settle()`
var p = settleOriginal.call(this, resources);
// Patch `.then()` method on this promise to not bind callbacks
p.then = function() {
var p = thenOriginal.apply(this, arguments);
// Patch `.then()` method on this promise to not bind callbacks
p.then = thenOriginal;
return p;
};
return p;
};
});
// Call original `Promise.using()` method
return usingOriginal.apply(this, arguments);
};
});
}