index.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. 'use strict';
  2. /*
  3. * cls-bluebird
  4. * Module entry point
  5. */
  6. // Modules
  7. var isBluebird = require('is-bluebird');
  8. // Require Bluebird library
  9. // Ignore errors if cannot be required
  10. var Bluebird;
  11. try {
  12. Bluebird = require('bluebird');
  13. } catch (err) {}
  14. // Imports
  15. var shimMethod = require('./shimMethod'),
  16. shimOnCancel = require('./shimOnCancel'),
  17. shimCall = require('./shimCall'),
  18. shimUsing = require('./shimUsing'),
  19. shimCoroutine = require('./shimCoroutine');
  20. // Exports
  21. /**
  22. * Patch bluebird to run maintain CLS context for a specific namespace.
  23. * If a Bluebird Promise constructor is provided, it is patched.
  24. * If not provided, the version returned by `require('bluebird')` is used.
  25. *
  26. * @param {Object} ns - CLS namespace object
  27. * @param {Function} [Promise] - Bluebird Promise constructor to patch (optional)
  28. * @returns {Function} - Bluebird Promise constructor
  29. * @throws {TypeError} - If `ns` or `Promise` are not of correct type
  30. * @throws {Error} - If `Promise` not provided and cannot require `bluebird` module
  31. */
  32. module.exports = function patchBluebird(ns, Promise) {
  33. // Check namespace is valid
  34. if (!ns || typeof ns !== 'object' || typeof ns.bind !== 'function' || typeof ns.run !== 'function') throw new TypeError('Must provide CLS namespace to patch Bluebird against');
  35. // Check Promise implementation is some variation of Bluebird
  36. // If none provided, use default Bluebird
  37. if (!Promise) {
  38. Promise = Bluebird;
  39. if (!Promise) throw new Error('Could not require Bluebird');
  40. } else if (!isBluebird.ctor(Promise)) {
  41. throw new TypeError('Promise implementation provided must be Bluebird');
  42. }
  43. // Patch all methods to carry CLS context
  44. var v3 = isBluebird.ctor.v3(Promise);
  45. /*
  46. * Core
  47. *
  48. * Not patched as always run callback synchronously:
  49. * new Promise()
  50. * Promise.try() / Promise.attempt()
  51. *
  52. * Not patched as do not take a callback:
  53. * Promise.bind() / .bind()
  54. * Promise.resolve() / Promise.fulfilled() / Promise.cast()
  55. * Promise.reject() / Promise.rejected()
  56. *
  57. * Not patched as call another patched method synchronously
  58. * .error() - calls .catch()
  59. *
  60. * Not patched as are wrappers:
  61. * Promise.method()
  62. *
  63. * NB Due to bug in bluebird v2 https://github.com/petkaantonov/bluebird/issues/1153
  64. * `Promise.join()` calls the callback synchronously if input is only values or
  65. * resolved promises, but async if any promises are pending.
  66. * So handler is sometimes bound to CLS context unnecessarily, but this does no harm
  67. * beyond the very slight performance overhead of an extra `ns.bind()` call.
  68. */
  69. shimProto('then', v3 ? [0, 1] : [0, 1, 2]);
  70. shimProto('spread', v3 ? [0] : [0, 1]);
  71. shimProto('finally', [0]);
  72. Promise.prototype.lastly = Promise.prototype.finally;
  73. shimStatic('join', [-1]);
  74. if (!v3) {
  75. // Only patched in bluebird v2.
  76. // In bluebird v3 `.catch()` calls `.then()` immediately which binds callback.
  77. shimProto('catch', [-1]);
  78. Promise.prototype.caught = Promise.prototype.catch;
  79. }
  80. /*
  81. * Synchronous inspection
  82. *
  83. * Not patched as do not take a callback:
  84. * .isFulfilled()
  85. * .isRejected()
  86. * .isPending()
  87. * .isCancelled()
  88. * .isResolved()
  89. * .value()
  90. * .reason()
  91. * .reflect()
  92. */
  93. /*
  94. * Collections
  95. *
  96. * Not patched as do not take a callback:
  97. * Promise.all() / .all()
  98. * Promise.props() / .props()
  99. * Promise.any() / .any()
  100. * Promise.some() / .some()
  101. * Promise.race() / .race()
  102. */
  103. shimBoth('map', [0]);
  104. shimBoth('filter', [0]);
  105. shimBoth('reduce', [0]);
  106. shimBoth('each', [0]);
  107. // In bluebird v2, there is no `Promise.mapSeries()`/`.mapSeries()` method
  108. if (v3) shimBoth('mapSeries', [0]);
  109. /*
  110. * Resource management
  111. *
  112. * NB disposer callbacks are bound to context at time disposer created, not when utilized in `using()`
  113. */
  114. shimUsing(Promise, ns, v3); // shims `Promise.using()`
  115. shimProto('disposer', [0]);
  116. /*
  117. * Promisification
  118. *
  119. * Not patched as always run callback synchronously:
  120. * Promise.fromCallback()
  121. * Promise.fromNode()
  122. *
  123. * Not patched as they are wrappers:
  124. * Promise.promisify()
  125. * Promise.promisifyAll()
  126. */
  127. shimProto('asCallback', [0]);
  128. Promise.prototype.nodeify = Promise.prototype.asCallback;
  129. /*
  130. * Timers
  131. *
  132. * Not patched as do not take a callback:
  133. * Promise.delay() / .delay()
  134. * .timeout()
  135. */
  136. /*
  137. * Cancellation
  138. *
  139. * Not patched as does not take a callback:
  140. * .cancel() / .break()
  141. * .isCancellable()
  142. * .cancellable() (bluebird v2 only)
  143. * .uncancellable() (bluebird v2 only)
  144. *
  145. * NB In bluebird v3 `onCancel` handler will be called
  146. * in CLS context of call to `onCancel()`.
  147. */
  148. // Patch `Promise.prototype._resolveFromExecutor`
  149. // in order to patch `onCancel` handler in `new Promise()`.
  150. if (v3) shimOnCancel(Promise, ns);
  151. /*
  152. * Generators
  153. *
  154. * Not patched as does not take a callback:
  155. * Promise.coroutine.addYieldHandler()
  156. *
  157. * NB `options.yieldHandler` will run in whatever CLS context is active at time of `yield`
  158. */
  159. var addYieldHandler = Promise.coroutine.addYieldHandler;
  160. shimCoroutine('coroutine', Promise, ns, v3); // shims `Promise.coroutine()`
  161. Promise.coroutine.addYieldHandler = addYieldHandler;
  162. /*
  163. * Utility
  164. *
  165. * Not patched as do not take a callback:
  166. * .get()
  167. * .return() / .thenReturn()
  168. * .throw() / .thenThrow()
  169. * .catchReturn()
  170. * .catchThrow()
  171. * Promise.getNewLibraryCopy()
  172. * Promise.noConflict()
  173. * Promise.setScheduler()
  174. */
  175. shimProto('tap', [0]);
  176. if (v3) shimProto('tapCatch', [-1]);
  177. shimCall(Promise, ns); // shims `.call()`
  178. /*
  179. * Configuration
  180. *
  181. * Not patched as do not take a callback:
  182. * Promise.config()
  183. * .suppressUnhandledRejections()
  184. * Promise.longStackTraces()
  185. * Promise.hasLongStackTraces()
  186. *
  187. * Not patched as meaningless to do so:
  188. * Promise.onPossiblyUnhandledRejection()
  189. * Promise.onUnhandledRejectionHandled()
  190. *
  191. * NB Error handlers will run with unknown CLS context.
  192. * CLS context should not be relied upon to be the context at the time error was thrown.
  193. * Catch errors with `.catch()` instead!
  194. */
  195. shimProto('done', v3 ? [0, 1] : [0, 1, 2]);
  196. /*
  197. * Progression (bluebird v2 only)
  198. */
  199. if (!v3) shimProto('progressed', [0]);
  200. /*
  201. * Undocumented
  202. *
  203. * Not patched as do not take a callback:
  204. * Promise.is()
  205. * Promise.settle() / .settle()
  206. * Promise.defer() / Promise.pending()
  207. * .toString()
  208. * .toJSON()
  209. */
  210. // `.fork()` does not exist in bluebird v3
  211. if (!v3) shimProto('fork', [0, 1, 2]);
  212. shimCoroutine('spawn', Promise, ns, v3); // shims `Promise.spawn()`
  213. // Return patched Bluebird constructor
  214. return Promise;
  215. /*
  216. * Patching functions
  217. */
  218. function shimStatic(methodName, args) {
  219. shimMethod(Promise, methodName, args, ns);
  220. }
  221. function shimProto(methodName, args) {
  222. shimMethod(Promise.prototype, methodName, args, ns);
  223. }
  224. function shimBoth(methodName, args) {
  225. shimProto(methodName, args);
  226. shimStatic(methodName, args.map(function(arg) { return arg < 0 ? arg : arg + 1; }));
  227. }
  228. };