init source
This commit is contained in:
+142
@@ -0,0 +1,142 @@
|
||||
# continuation-local-storage support for bluebird promises
|
||||
|
||||
[](https://www.npmjs.com/package/cls-bluebird)
|
||||
[](http://travis-ci.org/TimBeyer/cls-bluebird)
|
||||
[](https://david-dm.org/TimBeyer/cls-bluebird)
|
||||
[](https://david-dm.org/TimBeyer/cls-bluebird)
|
||||
[](https://coveralls.io/r/TimBeyer/cls-bluebird)
|
||||
|
||||
Patch [bluebird](https://www.npmjs.com/package/bluebird) for [continuation-local-storage](https://www.npmjs.com/package/continuation-local-storage) support.
|
||||
|
||||
## Current Status
|
||||
|
||||
Version 2.x of cls-bluebird is a complete re-write aiming to make it 100% reliable and robust. Features comprehensive test coverage (over 100,000 tests) which cover pretty much all conceivable cases.
|
||||
|
||||
Compatible with [bluebird](https://www.npmjs.com/package/bluebird) v2.x and v3.x. Tests cover both versions.
|
||||
|
||||
Please use with latest version of [bluebird](https://www.npmjs.com/package/bluebird) in either v2.x or v3.x branches. Older versions are not guaranteed to work.
|
||||
|
||||
## Usage
|
||||
|
||||
### `clsBluebird( ns [, Promise] )`
|
||||
|
||||
```js
|
||||
var cls = require('continuation-local-storage');
|
||||
var ns = cls.createNamespace('myNamespace');
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var clsBluebird = require('cls-bluebird');
|
||||
|
||||
clsBluebird( ns );
|
||||
// Promise is now patched to maintain CLS context
|
||||
```
|
||||
|
||||
The above patches the "global" instance of bluebird. So anywhere else in the same app that calls `require('bluebird')` will get the patched version (assuming npm resolves to the same file).
|
||||
|
||||
### Patching a particular instance of Bluebird
|
||||
|
||||
So as not to alter the "global" instance of bluebird, it's recommended to first create a independent instance of the Bluebird constructor before patching, and pass it to cls-bluebird.
|
||||
|
||||
This is a more robust approach.
|
||||
|
||||
```js
|
||||
var Promise = require('bluebird').getNewLibraryCopy();
|
||||
var clsBluebird = require('cls-bluebird');
|
||||
|
||||
clsBluebird( ns, Promise );
|
||||
```
|
||||
|
||||
(see [Promise.getNewLibraryCopy()](http://bluebirdjs.com/docs/api/promise.getnewlibrarycopy.html) docs on Bluebird website)
|
||||
|
||||
### Nature of patching
|
||||
|
||||
Combining CLS and promises is a slightly tricky business. There are 3 different conventions one could use (see [this issue](https://github.com/othiym23/node-continuation-local-storage/issues/64) for more detail).
|
||||
|
||||
`cls-bluebird` follows the convention of binding `.then()` callbacks **to the context in which `.then()` is called**.
|
||||
|
||||
```js
|
||||
var promise;
|
||||
ns.run(function() {
|
||||
ns.set('foo', 123);
|
||||
promise = Promise.resolve();
|
||||
});
|
||||
|
||||
ns.run(function() {
|
||||
ns.set('foo', 456);
|
||||
promise.then(print);
|
||||
});
|
||||
|
||||
function print() {
|
||||
console.log(ns.get('foo'));
|
||||
}
|
||||
|
||||
// this outputs '456' (the value of `foo` at the time `.then()` was called)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
#### Coroutines
|
||||
|
||||
The patch ensures that when execution in a coroutine continues after a `yield` statement, it always does so in the CLS context *in which the coroutine started running*.
|
||||
|
||||
```js
|
||||
var fn = Promise.coroutine(function* () {
|
||||
console.log('Context 1:', ns.get('foo'));
|
||||
yield Promise.resolve();
|
||||
console.log('Context 2:', ns.get('foo'));
|
||||
});
|
||||
|
||||
ns.run(function(ctx) {
|
||||
ns.set('foo', 123);
|
||||
fn();
|
||||
});
|
||||
```
|
||||
|
||||
outputs:
|
||||
|
||||
```
|
||||
Context 1: 123
|
||||
Context 2: 123
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
1. If the `yield`-ed expression loses CLS context, the original CLS context will be restored after the `yield`.
|
||||
2. Any code before the `yield` which changes CLS context will only be effective until the next `yield`.
|
||||
|
||||
#### Global error handlers
|
||||
|
||||
`Promise.onPossiblyUnhandledRejection()` and `Promise.onUnhandledRejectionHandled()` allow you to attach global handlers to intercept unhandled rejections.
|
||||
|
||||
The CLS context in which callbacks are called is unknown. It's probably unwise to rely on the CLS context in the callback being that when the rejection occurred - use `.catch()` on the end of the promise chain that's created within `namespace.run()` instead.
|
||||
|
||||
#### Progression
|
||||
|
||||
Bluebird v2.x contains a deprecated API for handling progression (`.progressed()`) etc. These methods are patched and *should* work fine but they're not covered by the tests.
|
||||
|
||||
## Tests
|
||||
|
||||
The tests cover every possible combination of input promises and callbacks that the Bluebird API allows. There's around 100,000 tests in total and the aim is to ensure cls-bluebird is as robust and reliable as possible.
|
||||
|
||||
Use `npm test` to run the tests. Use `npm run cover` to check coverage.
|
||||
|
||||
For more info on test tests, see [tests/README.md](https://github.com/TimBeyer/cls-bluebird/blob/master/test/README.md)
|
||||
|
||||
## Changelog
|
||||
|
||||
See [changelog.md](https://github.com/TimBeyer/cls-bluebird/blob/master/changelog.md)
|
||||
|
||||
## Issues/bugs
|
||||
|
||||
If you discover a bug, please raise an issue on Github. https://github.com/TimBeyer/cls-bluebird/issues
|
||||
|
||||
We are very keen to ensure cls-bluebird is completely bug-free and any bugs discovered will be fixed as soon as possible.
|
||||
|
||||
## Contribution
|
||||
|
||||
Pull requests are very welcome. Please:
|
||||
|
||||
* ensure all tests pass before submitting PR
|
||||
* add an entry to changelog
|
||||
* add tests for new features
|
||||
* document new functionality/API additions in README
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Changelog
|
||||
|
||||
## 2.1.0
|
||||
|
||||
* Fix: `Promise.coroutine.addYieldHandler()` is maintained
|
||||
* Patch `.tapCatch()` prototype method
|
||||
* Tests: Workaround for uncaught rejections bug in Bluebird v3.5.1
|
||||
* Travis CI run tests on Node versions 8 + 9
|
||||
* Travis CI does not run tests on Node versions before v4
|
||||
* Skip Travis CI runs on release tags
|
||||
* Test against latest Bluebird v3
|
||||
* Update dev dependencies
|
||||
* Code style: Fix spacing in tests
|
||||
|
||||
## 2.0.1
|
||||
|
||||
* Correct typos in README
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Complete re-write
|
||||
* New test suite
|
||||
|
||||
## 1.1.3
|
||||
|
||||
* Improved validation that `ns` argument is a namespace
|
||||
* Add npm keywords
|
||||
|
||||
## 1.1.2
|
||||
|
||||
* README
|
||||
|
||||
## 1.1.1
|
||||
|
||||
* Use `is-bluebird` module for checking `Promise` argument
|
||||
|
||||
## 1.1.0
|
||||
|
||||
* Remove peer dependencies
|
||||
* Update `shimmer` dependency
|
||||
|
||||
## 1.0.1
|
||||
|
||||
* Fix: `domain` argument for `_addCallbacks`
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* Initial release
|
||||
+265
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
};
|
||||
});
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"cls-bluebird@2.1.0",
|
||||
"/home/app"
|
||||
]
|
||||
],
|
||||
"_from": "cls-bluebird@2.1.0",
|
||||
"_id": "cls-bluebird@2.1.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=",
|
||||
"_location": "/cls-bluebird",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "cls-bluebird@2.1.0",
|
||||
"name": "cls-bluebird",
|
||||
"escapedName": "cls-bluebird",
|
||||
"rawSpec": "2.1.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "2.1.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/sequelize"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz",
|
||||
"_spec": "2.1.0",
|
||||
"_where": "/home/app",
|
||||
"author": {
|
||||
"name": "Tim Beyer",
|
||||
"email": "tim.beyer@gmail.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/TimBeyer/cls-bluebird/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-bluebird": "^1.0.2",
|
||||
"shimmer": "^1.1.0"
|
||||
},
|
||||
"description": "Make bluebird work with the continuation-local-storage module.",
|
||||
"devDependencies": {
|
||||
"bluebird": "^2.10.2",
|
||||
"bluebird2": "^3.0.0",
|
||||
"bluebird3": "^3.1.1",
|
||||
"chai": "^4.1.2",
|
||||
"continuation-local-storage": "^3.2.1",
|
||||
"coveralls": "^3.0.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"jshint": "^2.9.5",
|
||||
"lodash": "^4.17.4",
|
||||
"mocha": "^4.0.1"
|
||||
},
|
||||
"homepage": "https://github.com/TimBeyer/cls-bluebird#readme",
|
||||
"keywords": [
|
||||
"continuation-local-storage",
|
||||
"cls",
|
||||
"bluebird",
|
||||
"continuation",
|
||||
"local",
|
||||
"storage",
|
||||
"promise",
|
||||
"promises",
|
||||
"async",
|
||||
"thread",
|
||||
"glue",
|
||||
"baling-wire",
|
||||
"patch"
|
||||
],
|
||||
"license": "BSD-2-Clause",
|
||||
"main": "lib/index.js",
|
||||
"name": "cls-bluebird",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/TimBeyer/cls-bluebird.git"
|
||||
},
|
||||
"scripts": {
|
||||
"cover": "npm run cover-main && rm -rf coverage",
|
||||
"cover-main": "COVERAGE=true BLUEBIRD_VERSION=3 istanbul cover _mocha --report lcovonly -- -R spec 'test/**/*.test.js'",
|
||||
"coveralls": "npm run cover-main && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage",
|
||||
"jshint": "jshint lib test",
|
||||
"test": "npm run jshint && npm run test-all",
|
||||
"test-all": "npm run test-bluebird2 && npm run test-bluebird3",
|
||||
"test-bluebird2": "BLUEBIRD_VERSION=2 npm run test-main",
|
||||
"test-bluebird3": "BLUEBIRD_VERSION=3 npm run test-main",
|
||||
"test-main": "mocha 'test/**/*.test.js'",
|
||||
"travis": "bin/travis.sh"
|
||||
},
|
||||
"version": "2.1.0"
|
||||
}
|
||||
Reference in New Issue
Block a user