@sentry/node 9.7.0 → 9.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/build/cjs/integrations/anr/index.js +1 -1
  2. package/build/cjs/integrations/http/SentryHttpInstrumentationBeforeOtel.js +56 -41
  3. package/build/cjs/integrations/http/SentryHttpInstrumentationBeforeOtel.js.map +1 -1
  4. package/build/cjs/integrations/http/index.js +12 -4
  5. package/build/cjs/integrations/http/index.js.map +1 -1
  6. package/build/cjs/integrations/local-variables/local-variables-async.js +1 -1
  7. package/build/cjs/integrations/tracing/fastify.js +28 -8
  8. package/build/cjs/integrations/tracing/fastify.js.map +1 -1
  9. package/build/esm/integrations/anr/index.js +1 -1
  10. package/build/esm/integrations/anr/worker-script.js +2 -2
  11. package/build/esm/integrations/http/SentryHttpInstrumentationBeforeOtel.js +56 -41
  12. package/build/esm/integrations/http/SentryHttpInstrumentationBeforeOtel.js.map +1 -1
  13. package/build/esm/integrations/http/index.js +12 -4
  14. package/build/esm/integrations/http/index.js.map +1 -1
  15. package/build/esm/integrations/local-variables/local-variables-async.js +1 -1
  16. package/build/esm/integrations/local-variables/worker-script.js +2 -2
  17. package/build/esm/integrations/tracing/fastify.js +28 -8
  18. package/build/esm/integrations/tracing/fastify.js.map +1 -1
  19. package/build/esm/package.json +1 -1
  20. package/build/types/integrations/http/SentryHttpInstrumentationBeforeOtel.d.ts.map +1 -1
  21. package/build/types/integrations/http/index.d.ts.map +1 -1
  22. package/build/types/integrations/tracing/fastify.d.ts +58 -2
  23. package/build/types/integrations/tracing/fastify.d.ts.map +1 -1
  24. package/build/types/types.d.ts +18 -0
  25. package/build/types/types.d.ts.map +1 -1
  26. package/build/types-ts3.8/integrations/tracing/fastify.d.ts +58 -2
  27. package/build/types-ts3.8/types.d.ts +18 -0
  28. package/package.json +3 -3
@@ -9,7 +9,7 @@ const debug = require('../../utils/debug.js');
9
9
  const { isPromise } = util.types;
10
10
 
11
11
  // This string is a placeholder that gets overwritten with the worker code.
12
- const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZSA5LjcuMCAoNTI2ZWQ4ZSkgfCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0ICovCmltcG9ydHtTZXNzaW9uIGFzIHR9ZnJvbSJub2RlOmluc3BlY3RvciI7aW1wb3J0e3dvcmtlckRhdGEgYXMgbixwYXJlbnRQb3J0IGFzIGV9ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtpbXBvcnR7cG9zaXggYXMgcixzZXAgYXMgb31mcm9tIm5vZGU6cGF0aCI7aW1wb3J0KmFzIHMgZnJvbSJub2RlOmh0dHAiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwcyI7aW1wb3J0e1JlYWRhYmxlIGFzIGN9ZnJvbSJub2RlOnN0cmVhbSI7aW1wb3J0e2NyZWF0ZUd6aXAgYXMgdX1mcm9tIm5vZGU6emxpYiI7aW1wb3J0KmFzIGEgZnJvbSJub2RlOm5ldCI7aW1wb3J0KmFzIGYgZnJvbSJub2RlOnRscyI7Y29uc3QgaD0idW5kZWZpbmVkIj09dHlwZW9mIF9fU0VOVFJZX0RFQlVHX198fF9fU0VOVFJZX0RFQlVHX18scD1nbG9iYWxUaGlzLGw9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLGQ9IjkuNy4wIjtmdW5jdGlvbiBtKCl7cmV0dXJuIGcocCkscH1mdW5jdGlvbiBnKHQpe2NvbnN0IG49dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIG4udmVyc2lvbj1uLnZlcnNpb258fGQsbltkXT1uW2RdfHx7fX1mdW5jdGlvbiB5KHQsbixlPXApe2NvbnN0IHI9ZS5fX1NFTlRSWV9fPWUuX19TRU5UUllfX3x8e30sbz1yW2RdPXJbZF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1uKCkpfWNvbnN0IGI9WyJkZWJ1ZyIsImluZm8iLCJ3YXJuIiwiZXJyb3IiLCJsb2ciLCJhc3NlcnQiLCJ0cmFjZSJdLF89e307ZnVuY3Rpb24gdih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3Qgbj1wLmNvbnNvbGUsZT17fSxyPU9iamVjdC5rZXlzKF8pO3IuZm9yRWFjaCgodD0+e2NvbnN0IHI9X1t0XTtlW3RdPW5bdF0sblt0XT1yfSkpO3RyeXtyZXR1cm4gdCgpfWZpbmFsbHl7ci5mb3JFYWNoKCh0PT57blt0XT1lW3RdfSkpfX1jb25zdCB3PXkoImxvZ2dlciIsKGZ1bmN0aW9uKCl7bGV0IHQ9ITE7Y29uc3Qgbj17ZW5hYmxlOigpPT57dD0hMH0sZGlzYWJsZTooKT0+e3Q9ITF9LGlzRW5hYmxlZDooKT0+dH07cmV0dXJuIGw/Yi5mb3JFYWNoKChlPT57bltlXT0oLi4ubik9Pnt0JiZ2KCgoKT0+e3AuY29uc29sZVtlXShgU2VudHJ5IExvZ2dlciBbJHtlfV06YCwuLi5uKX0pKX19KSk6Yi5mb3JFYWNoKCh0PT57blt0XT0oKT0+e319KSksbn0pKSxTPTUwLCQ9Ij8iLEU9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIHgodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IE49Ijxhbm9ueW1vdXM+Ijtjb25zdCBUPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7ZnVuY3Rpb24gQyh0LG4pe3JldHVybiBULmNhbGwodCk9PT1gW29iamVjdCAke259XWB9ZnVuY3Rpb24gaih0KXtyZXR1cm4gQyh0LCJTdHJpbmciKX1mdW5jdGlvbiBSKHQpe3JldHVybiBDKHQsIk9iamVjdCIpfWZ1bmN0aW9uIGsodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gTyh0LG4pe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIG59Y2F0Y2godCl7cmV0dXJuITF9fWNvbnN0IEQ9cCxJPTgwO2Z1bmN0aW9uIEEodCxuKXtjb25zdCBlPXQscj1bXTtpZighZT8udGFnTmFtZSlyZXR1cm4iIjtpZihELkhUTUxFbGVtZW50JiZlIGluc3RhbmNlb2YgSFRNTEVsZW1lbnQmJmUuZGF0YXNldCl7aWYoZS5kYXRhc2V0LnNlbnRyeUNvbXBvbmVudClyZXR1cm4gZS5kYXRhc2V0LnNlbnRyeUNvbXBvbmVudDtpZihlLmRhdGFzZXQuc2VudHJ5RWxlbWVudClyZXR1cm4gZS5kYXRhc2V0LnNlbnRyeUVsZW1lbnR9ci5wdXNoKGUudGFnTmFtZS50b0xvd2VyQ2FzZSgpKTtjb25zdCBvPW4/Lmxlbmd0aD9uLmZpbHRlcigodD0+ZS5nZXRBdHRyaWJ1dGUodCkpKS5tYXAoKHQ9Plt0LGUuZ2V0QXR0cmlidXRlKHQpXSkpOm51bGw7aWYobz8ubGVuZ3RoKW8uZm9yRWFjaCgodD0+e3IucHVzaChgWyR7dFswXX09IiR7dFsxXX0iXWApfSkpO2Vsc2V7ZS5pZCYmci5wdXNoKGAjJHtlLmlkfWApO2NvbnN0IHQ9ZS5jbGFzc05hbWU7aWYodCYmaih0KSl7Y29uc3Qgbj10LnNwbGl0KC9ccysvKTtmb3IoY29uc3QgdCBvZiBuKXIucHVzaChgLiR7dH1gKX19Y29uc3Qgcz1bImFyaWEtbGFiZWwiLCJ0eXBlIiwibmFtZSIsInRpdGxlIiwiYWx0Il07Zm9yKGNvbnN0IHQgb2Ygcyl7Y29uc3Qgbj1lLmdldEF0dHJpYnV0ZSh0KTtuJiZyLnB1c2goYFske3R9PSIke259Il1gKX1yZXR1cm4gci5qb2luKCIiKX1mdW5jdGlvbiBQKHQpe2lmKGZ1bmN0aW9uKHQpe3N3aXRjaChULmNhbGwodCkpe2Nhc2UiW29iamVjdCBFcnJvcl0iOmNhc2UiW29iamVjdCBFeGNlcHRpb25dIjpjYXNlIltvYmplY3QgRE9NRXhjZXB0aW9uXSI6Y2FzZSJbb2JqZWN0IFdlYkFzc2VtYmx5LkV4Y2VwdGlvbl0iOnJldHVybiEwO2RlZmF1bHQ6cmV0dXJuIE8odCxFcnJvcil9fSh0KSlyZXR1cm57bWVzc2FnZTp0Lm1lc3NhZ2UsbmFtZTp0Lm5hbWUsc3RhY2s6dC5zdGFjaywuLi5NKHQpfTtpZihuPXQsInVuZGVmaW5lZCIhPXR5cGVvZiBFdmVudCYmTyhuLEV2ZW50KSl7Y29uc3Qgbj17dHlwZTp0LnR5cGUsdGFyZ2V0OlUodC50YXJnZXQpLGN1cnJlbnRUYXJnZXQ6VSh0LmN1cnJlbnRUYXJnZXQpLC4uLk0odCl9O3JldHVybiJ1bmRlZmluZWQiIT10eXBlb2YgQ3VzdG9tRXZlbnQmJk8odCxDdXN0b21FdmVudCkmJihuLmRldGFpbD10LmRldGFpbCksbn1yZXR1cm4gdDt2YXIgbn1mdW5jdGlvbiBVKHQpe3RyeXtyZXR1cm4gbj10LCJ1bmRlZmluZWQiIT10eXBlb2YgRWxlbWVudCYmTyhuLEVsZW1lbnQpP2Z1bmN0aW9uKHQsbj17fSl7aWYoIXQpcmV0dXJuIjx1bmtub3duPiI7dHJ5e2xldCBlPXQ7Y29uc3Qgcj01LG89W107bGV0IHM9MCxpPTA7Y29uc3QgYz0iID4gIix1PWMubGVuZ3RoO2xldCBhO2NvbnN0IGY9QXJyYXkuaXNBcnJheShuKT9uOm4ua2V5QXR0cnMsaD0hQXJyYXkuaXNBcnJheShuKSYmbi5tYXhTdHJpbmdMZW5ndGh8fEk7Zm9yKDtlJiZzKys8ciYmKGE9QShlLGYpLCEoImh0bWwiPT09YXx8cz4xJiZpK28ubGVuZ3RoKnUrYS5sZW5ndGg+PWgpKTspby5wdXNoKGEpLGkrPWEubGVuZ3RoLGU9ZS5wYXJlbnROb2RlO3JldHVybiBvLnJldmVyc2UoKS5qb2luKGMpfWNhdGNoKHQpe3JldHVybiI8dW5rbm93bj4ifX0odCk6T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHQpfWNhdGNoKHQpe3JldHVybiI8dW5rbm93bj4ifXZhciBufWZ1bmN0aW9uIE0odCl7aWYoIm9iamVjdCI9PXR5cGVvZiB0JiZudWxsIT09dCl7Y29uc3Qgbj17fTtmb3IoY29uc3QgZSBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LGUpJiYobltlXT10W2VdKTtyZXR1cm4gbn1yZXR1cm57fX1mdW5jdGlvbiBMKHQpe3JldHVybiBCKHQsbmV3IE1hcCl9ZnVuY3Rpb24gQih0LG4pe2lmKGZ1bmN0aW9uKHQpe2lmKCFSKHQpKXJldHVybiExO3RyeXtjb25zdCBuPU9iamVjdC5nZXRQcm90b3R5cGVPZih0KS5jb25zdHJ1Y3Rvci5uYW1lO3JldHVybiFufHwiT2JqZWN0Ij09PW59Y2F0Y2h7cmV0dXJuITB9fSh0KSl7Y29uc3QgZT1uLmdldCh0KTtpZih2b2lkIDAhPT1lKXJldHVybiBlO2NvbnN0IHI9e307bi5zZXQodCxyKTtmb3IoY29uc3QgZSBvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyh0KSl2b2lkIDAhPT10W2VdJiYocltlXT1CKHRbZV0sbikpO3JldHVybiByfWlmKEFycmF5LmlzQXJyYXkodCkpe2NvbnN0IGU9bi5nZXQodCk7aWYodm9pZCAwIT09ZSlyZXR1cm4gZTtjb25zdCByPVtdO3JldHVybiBuLnNldCh0LHIpLHQuZm9yRWFjaCgodD0+e3IucHVzaChCKHQsbikpfSkpLHJ9cmV0dXJuIHR9Y29uc3QgRz0xZTM7ZnVuY3Rpb24gSigpe3JldHVybiBEYXRlLm5vdygpL0d9Y29uc3QgWT1mdW5jdGlvbigpe2NvbnN0e3BlcmZvcm1hbmNlOnR9PXA7aWYoIXQ/Lm5vdylyZXR1cm4gSjtjb25zdCBuPURhdGUubm93KCktdC5ub3coKSxlPW51bGw9PXQudGltZU9yaWdpbj9uOnQudGltZU9yaWdpbjtyZXR1cm4oKT0+KGUrdC5ub3coKSkvR30oKTtmdW5jdGlvbiB6KHQ9ZnVuY3Rpb24oKXtjb25zdCB0PXA7cmV0dXJuIHQuY3J5cHRvfHx0Lm1zQ3J5cHRvfSgpKXtsZXQgbj0oKT0+MTYqTWF0aC5yYW5kb20oKTt0cnl7aWYodD8ucmFuZG9tVVVJRClyZXR1cm4gdC5yYW5kb21VVUlEKCkucmVwbGFjZSgvLS9nLCIiKTt0Py5nZXRSYW5kb21WYWx1ZXMmJihuPSgpPT57Y29uc3Qgbj1uZXcgVWludDhBcnJheSgxKTtyZXR1cm4gdC5nZXRSYW5kb21WYWx1ZXMobiksblswXX0pfWNhdGNoKHQpe31yZXR1cm4oWzFlN10rMWUzKzRlMys4ZTMrMWUxMSkucmVwbGFjZSgvWzAxOF0vZywodD0+KHReKDE1Jm4oKSk+PnQvNCkudG9TdHJpbmcoMTYpKSl9dmFyIEg7ZnVuY3Rpb24gVyh0KXtyZXR1cm4gbmV3IEYoKG49PntuKHQpfSkpfSFmdW5jdGlvbih0KXt0W3QuUEVORElORz0wXT0iUEVORElORyI7dFt0LlJFU09MVkVEPTFdPSJSRVNPTFZFRCI7dFt0LlJFSkVDVEVEPTJdPSJSRUpFQ1RFRCJ9KEh8fChIPXt9KSk7Y2xhc3MgRntjb25zdHJ1Y3Rvcih0KXt0aGlzLnQ9SC5QRU5ESU5HLHRoaXMubz1bXSx0aGlzLmkodCl9dGhlbih0LG4pe3JldHVybiBuZXcgRigoKGUscik9Pnt0aGlzLm8ucHVzaChbITEsbj0+e2lmKHQpdHJ5e2UodChuKSl9Y2F0Y2godCl7cih0KX1lbHNlIGUobil9LHQ9PntpZihuKXRyeXtlKG4odCkpfWNhdGNoKHQpe3IodCl9ZWxzZSByKHQpfV0pLHRoaXMudSgpfSkpfWNhdGNoKHQpe3JldHVybiB0aGlzLnRoZW4oKHQ9PnQpLHQpfWZpbmFsbHkodCl7cmV0dXJuIG5ldyBGKCgobixlKT0+e2xldCByLG87cmV0dXJuIHRoaXMudGhlbigobj0+e289ITEscj1uLHQmJnQoKX0pLChuPT57bz0hMCxyPW4sdCYmdCgpfSkpLnRoZW4oKCgpPT57bz9lKHIpOm4ocil9KSl9KSl9dSgpe2lmKHRoaXMudD09PUguUEVORElORylyZXR1cm47Y29uc3QgdD10aGlzLm8uc2xpY2UoKTt0aGlzLm89W10sdC5mb3JFYWNoKCh0PT57dFswXXx8KHRoaXMudD09PUguUkVTT0xWRUQmJnRbMV0odGhpcy5oKSx0aGlzLnQ9PT1ILlJFSkVDVEVEJiZ0WzJdKHRoaXMuaCksdFswXT0hMCl9KSl9aSh0KXtjb25zdCBuPSh0LG4pPT57dGhpcy50PT09SC5QRU5ESU5HJiYoayhuKT9uLnRoZW4oZSxyKToodGhpcy50PXQsdGhpcy5oPW4sdGhpcy51KCkpKX0sZT10PT57bihILlJFU09MVkVELHQpfSxyPXQ9PntuKEguUkVKRUNURUQsdCl9O3RyeXt0KGUscil9Y2F0Y2godCl7cih0KX19fWZ1bmN0aW9uIEsodCl7Y29uc3Qgbj1ZKCksZT17c2lkOnooKSxpbml0OiEwLHRpbWVzdGFtcDpuLHN0YXJ0ZWQ6bixkdXJhdGlvbjowLHN0YXR1czoib2siLGVycm9yczowLGlnbm9yZUR1cmF0aW9uOiExLHRvSlNPTjooKT0+ZnVuY3Rpb24odCl7cmV0dXJuIEwoe3NpZDpgJHt0LnNpZH1gLGluaXQ6dC5pbml0LHN0YXJ0ZWQ6bmV3IERhdGUoMWUzKnQuc3RhcnRlZCkudG9JU09TdHJpbmcoKSx0aW1lc3RhbXA6bmV3IERhdGUoMWUzKnQudGltZXN0YW1wKS50b0lTT1N0cmluZygpLHN0YXR1czp0LnN0YXR1cyxlcnJvcnM6dC5lcnJvcnMsZGlkOiJudW1iZXIiPT10eXBlb2YgdC5kaWR8fCJzdHJpbmciPT10eXBlb2YgdC5kaWQ/YCR7dC5kaWR9YDp2b2lkIDAsZHVyYXRpb246dC5kdXJhdGlvbixhYm5vcm1hbF9tZWNoYW5pc206dC5hYm5vcm1hbF9tZWNoYW5pc20sYXR0cnM6e3JlbGVhc2U6dC5yZWxlYXNlLGVudmlyb25tZW50OnQuZW52aXJvbm1lbnQsaXBfYWRkcmVzczp0LmlwQWRkcmVzcyx1c2VyX2FnZW50OnQudXNlckFnZW50fX0pfShlKX07cmV0dXJuIHQmJlYoZSx0KSxlfWZ1bmN0aW9uIFYodCxuPXt9KXtpZihuLnVzZXImJighdC5pcEFkZHJlc3MmJm4udXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9bi51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxuLmRpZHx8KHQuZGlkPW4udXNlci5pZHx8bi51c2VyLmVtYWlsfHxuLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1uLnRpbWVzdGFtcHx8WSgpLG4uYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209bi5hYm5vcm1hbF9tZWNoYW5pc20pLG4uaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPW4uaWdub3JlRHVyYXRpb24pLG4uc2lkJiYodC5zaWQ9MzI9PT1uLnNpZC5sZW5ndGg/bi5zaWQ6eigpKSx2b2lkIDAhPT1uLmluaXQmJih0LmluaXQ9bi5pbml0KSwhdC5kaWQmJm4uZGlkJiYodC5kaWQ9YCR7bi5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBuLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9bi5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBuLmR1cmF0aW9uKXQuZHVyYXRpb249bi5kdXJhdGlvbjtlbHNle2NvbnN0IG49dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249bj49MD9uOjB9bi5yZWxlYXNlJiYodC5yZWxlYXNlPW4ucmVsZWFzZSksbi5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9bi5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZuLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPW4uaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJm4udXNlckFnZW50JiYodC51c2VyQWdlbnQ9bi51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2Ygbi5lcnJvcnMmJih0LmVycm9ycz1uLmVycm9ycyksbi5zdGF0dXMmJih0LnN0YXR1cz1uLnN0YXR1cyl9ZnVuY3Rpb24gWigpe3JldHVybiB6KCl9ZnVuY3Rpb24gcSgpe3JldHVybiB6KCkuc3Vic3RyaW5nKDE2KX1mdW5jdGlvbiBRKHQsbixlPTIpe2lmKCFufHwib2JqZWN0IiE9dHlwZW9mIG58fGU8PTApcmV0dXJuIG47aWYodCYmMD09PU9iamVjdC5rZXlzKG4pLmxlbmd0aClyZXR1cm4gdDtjb25zdCByPXsuLi50fTtmb3IoY29uc3QgdCBpbiBuKU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChuLHQpJiYoclt0XT1RKHJbdF0sblt0XSxlLTEpKTtyZXR1cm4gcn1jb25zdCBYPSJfc2VudHJ5U3BhbiI7ZnVuY3Rpb24gdHQodCxuKXtuP2Z1bmN0aW9uKHQsbixlKXt0cnl7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsbix7dmFsdWU6ZSx3cml0YWJsZTohMCxjb25maWd1cmFibGU6ITB9KX1jYXRjaChlKXtsJiZ3LmxvZyhgRmFpbGVkIHRvIGFkZCBub24tZW51bWVyYWJsZSBwcm9wZXJ0eSAiJHtufSIgdG8gb2JqZWN0YCx0KX19KHQsWCxuKTpkZWxldGUgdFtYXX1mdW5jdGlvbiBudCh0KXtyZXR1cm4gdFtYXX1jbGFzcyBldHtjb25zdHJ1Y3Rvcigpe3RoaXMucD0hMSx0aGlzLmw9W10sdGhpcy5tPVtdLHRoaXMuXz1bXSx0aGlzLnY9W10sdGhpcy5TPXt9LHRoaXMuTj17fSx0aGlzLlQ9e30sdGhpcy5DPXt9LHRoaXMuaj17fSx0aGlzLlI9e3RyYWNlSWQ6WigpLHNhbXBsZVJhbmQ6TWF0aC5yYW5kb20oKX19Y2xvbmUoKXtjb25zdCB0PW5ldyBldDtyZXR1cm4gdC5fPVsuLi50aGlzLl9dLHQuTj17Li4udGhpcy5OfSx0LlQ9ey4uLnRoaXMuVH0sdC5DPXsuLi50aGlzLkN9LHRoaXMuQy5mbGFncyYmKHQuQy5mbGFncz17dmFsdWVzOlsuLi50aGlzLkMuZmxhZ3MudmFsdWVzXX0pLHQuUz10aGlzLlMsdC5rPXRoaXMuayx0Lk89dGhpcy5PLHQuRD10aGlzLkQsdC5JPXRoaXMuSSx0Lm09Wy4uLnRoaXMubV0sdC52PVsuLi50aGlzLnZdLHQuaj17Li4udGhpcy5qfSx0LlI9ey4uLnRoaXMuUn0sdC5BPXRoaXMuQSx0LlA9dGhpcy5QLHR0KHQsbnQodGhpcykpLHR9c2V0Q2xpZW50KHQpe3RoaXMuQT10fXNldExhc3RFdmVudElkKHQpe3RoaXMuUD10fWdldENsaWVudCgpe3JldHVybiB0aGlzLkF9bGFzdEV2ZW50SWQoKXtyZXR1cm4gdGhpcy5QfWFkZFNjb3BlTGlzdGVuZXIodCl7dGhpcy5sLnB1c2godCl9YWRkRXZlbnRQcm9jZXNzb3IodCl7cmV0dXJuIHRoaXMubS5wdXNoKHQpLHRoaXN9c2V0VXNlcih0KXtyZXR1cm4gdGhpcy5TPXR8fHtlbWFpbDp2b2lkIDAsaWQ6dm9pZCAwLGlwX2FkZHJlc3M6dm9pZCAwLHVzZXJuYW1lOnZvaWQgMH0sdGhpcy5PJiZWKHRoaXMuTyx7dXNlcjp0fSksdGhpcy5VKCksdGhpc31nZXRVc2VyKCl7cmV0dXJuIHRoaXMuU31zZXRUYWdzKHQpe3JldHVybiB0aGlzLk49ey4uLnRoaXMuTiwuLi50fSx0aGlzLlUoKSx0aGlzfXNldFRhZyh0LG4pe3JldHVybiB0aGlzLk49ey4uLnRoaXMuTixbdF06bn0sdGhpcy5VKCksdGhpc31zZXRFeHRyYXModCl7cmV0dXJuIHRoaXMuVD17Li4udGhpcy5ULC4uLnR9LHRoaXMuVSgpLHRoaXN9c2V0RXh0cmEodCxuKXtyZXR1cm4gdGhpcy5UPXsuLi50aGlzLlQsW3RdOm59LHRoaXMuVSgpLHRoaXN9c2V0RmluZ2VycHJpbnQodCl7cmV0dXJuIHRoaXMuST10LHRoaXMuVSgpLHRoaXN9c2V0TGV2ZWwodCl7cmV0dXJuIHRoaXMuaz10LHRoaXMuVSgpLHRoaXN9c2V0VHJhbnNhY3Rpb25OYW1lKHQpe3JldHVybiB0aGlzLkQ9dCx0aGlzLlUoKSx0aGlzfXNldENvbnRleHQodCxuKXtyZXR1cm4gbnVsbD09PW4/ZGVsZXRlIHRoaXMuQ1t0XTp0aGlzLkNbdF09bix0aGlzLlUoKSx0aGlzfXNldFNlc3Npb24odCl7cmV0dXJuIHQ/dGhpcy5PPXQ6ZGVsZXRlIHRoaXMuTyx0aGlzLlUoKSx0aGlzfWdldFNlc3Npb24oKXtyZXR1cm4gdGhpcy5PfXVwZGF0ZSh0KXtpZighdClyZXR1cm4gdGhpcztjb25zdCBuPSJmdW5jdGlvbiI9PXR5cGVvZiB0P3QodGhpcyk6dCxlPW4gaW5zdGFuY2VvZiBldD9uLmdldFNjb3BlRGF0YSgpOlIobik/dDp2b2lkIDAse3RhZ3M6cixleHRyYTpvLHVzZXI6cyxjb250ZXh0czppLGxldmVsOmMsZmluZ2VycHJpbnQ6dT1bXSxwcm9wYWdhdGlvbkNvbnRleHQ6YX09ZXx8e307cmV0dXJuIHRoaXMuTj17Li4udGhpcy5OLC4uLnJ9LHRoaXMuVD17Li4udGhpcy5ULC4uLm99LHRoaXMuQz17Li4udGhpcy5DLC4uLml9LHMmJk9iamVjdC5rZXlzKHMpLmxlbmd0aCYmKHRoaXMuUz1zKSxjJiYodGhpcy5rPWMpLHUubGVuZ3RoJiYodGhpcy5JPXUpLGEmJih0aGlzLlI9YSksdGhpc31jbGVhcigpe3JldHVybiB0aGlzLl89W10sdGhpcy5OPXt9LHRoaXMuVD17fSx0aGlzLlM9e30sdGhpcy5DPXt9LHRoaXMuaz12b2lkIDAsdGhpcy5EPXZvaWQgMCx0aGlzLkk9dm9pZCAwLHRoaXMuTz12b2lkIDAsdHQodGhpcyx2b2lkIDApLHRoaXMudj1bXSx0aGlzLnNldFByb3BhZ2F0aW9uQ29udGV4dCh7dHJhY2VJZDpaKCksc2FtcGxlUmFuZDpNYXRoLnJhbmRvbSgpfSksdGhpcy5VKCksdGhpc31hZGRCcmVhZGNydW1iKHQsbil7Y29uc3QgZT0ibnVtYmVyIj09dHlwZW9mIG4/bjoxMDA7aWYoZTw9MClyZXR1cm4gdGhpcztjb25zdCByPXt0aW1lc3RhbXA6SigpLC4uLnR9O3JldHVybiB0aGlzLl8ucHVzaChyKSx0aGlzLl8ubGVuZ3RoPmUmJih0aGlzLl89dGhpcy5fLnNsaWNlKC1lKSx0aGlzLkE/LnJlY29yZERyb3BwZWRFdmVudCgiYnVmZmVyX292ZXJmbG93IiwibG9nX2l0ZW0iKSksdGhpcy5VKCksdGhpc31nZXRMYXN0QnJlYWRjcnVtYigpe3JldHVybiB0aGlzLl9bdGhpcy5fLmxlbmd0aC0xXX1jbGVhckJyZWFkY3J1bWJzKCl7cmV0dXJuIHRoaXMuXz1bXSx0aGlzLlUoKSx0aGlzfWFkZEF0dGFjaG1lbnQodCl7cmV0dXJuIHRoaXMudi5wdXNoKHQpLHRoaXN9Y2xlYXJBdHRhY2htZW50cygpe3JldHVybiB0aGlzLnY9W10sdGhpc31nZXRTY29wZURhdGEoKXtyZXR1cm57YnJlYWRjcnVtYnM6dGhpcy5fLGF0dGFjaG1lbnRzOnRoaXMudixjb250ZXh0czp0aGlzLkMsdGFnczp0aGlzLk4sZXh0cmE6dGhpcy5ULHVzZXI6dGhpcy5TLGxldmVsOnRoaXMuayxmaW5nZXJwcmludDp0aGlzLkl8fFtdLGV2ZW50UHJvY2Vzc29yczp0aGlzLm0scHJvcGFnYXRpb25Db250ZXh0OnRoaXMuUixzZGtQcm9jZXNzaW5nTWV0YWRhdGE6dGhpcy5qLHRyYW5zYWN0aW9uTmFtZTp0aGlzLkQsc3BhbjpudCh0aGlzKX19c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLmo9USh0aGlzLmosdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5SPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5SfWNhcHR1cmVFeGNlcHRpb24odCxuKXtjb25zdCBlPW4/LmV2ZW50X2lkfHx6KCk7aWYoIXRoaXMuQSlyZXR1cm4gdy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLGU7Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5BLmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5uLGV2ZW50X2lkOmV9LHRoaXMpLGV9Y2FwdHVyZU1lc3NhZ2UodCxuLGUpe2NvbnN0IHI9ZT8uZXZlbnRfaWR8fHooKTtpZighdGhpcy5BKXJldHVybiB3Lndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1uZXcgRXJyb3IodCk7cmV0dXJuIHRoaXMuQS5jYXB0dXJlTWVzc2FnZSh0LG4se29yaWdpbmFsRXhjZXB0aW9uOnQsc3ludGhldGljRXhjZXB0aW9uOm8sLi4uZSxldmVudF9pZDpyfSx0aGlzKSxyfWNhcHR1cmVFdmVudCh0LG4pe2NvbnN0IGU9bj8uZXZlbnRfaWR8fHooKTtyZXR1cm4gdGhpcy5BPyh0aGlzLkEuY2FwdHVyZUV2ZW50KHQsey4uLm4sZXZlbnRfaWQ6ZX0sdGhpcyksZSk6KHcud2FybigiTm8gY2xpZW50IGNvbmZpZ3VyZWQgb24gc2NvcGUgLSB3aWxsIG5vdCBjYXB0dXJlIGV2ZW50ISIpLGUpfVUoKXt0aGlzLnB8fCh0aGlzLnA9ITAsdGhpcy5sLmZvckVhY2goKHQ9Pnt0KHRoaXMpfSkpLHRoaXMucD0hMSl9fWNsYXNzIHJ0e2NvbnN0cnVjdG9yKHQsbil7bGV0IGUscjtlPXR8fG5ldyBldCxyPW58fG5ldyBldCx0aGlzLk09W3tzY29wZTplfV0sdGhpcy5MPXJ9d2l0aFNjb3BlKHQpe2NvbnN0IG49dGhpcy5CKCk7bGV0IGU7dHJ5e2U9dChuKX1jYXRjaCh0KXt0aHJvdyB0aGlzLkcoKSx0fXJldHVybiBrKGUpP2UudGhlbigodD0+KHRoaXMuRygpLHQpKSwodD0+e3Rocm93IHRoaXMuRygpLHR9KSk6KHRoaXMuRygpLGUpfWdldENsaWVudCgpe3JldHVybiB0aGlzLmdldFN0YWNrVG9wKCkuY2xpZW50fWdldFNjb3BlKCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5zY29wZX1nZXRJc29sYXRpb25TY29wZSgpe3JldHVybiB0aGlzLkx9Z2V0U3RhY2tUb3AoKXtyZXR1cm4gdGhpcy5NW3RoaXMuTS5sZW5ndGgtMV19Qigpe2NvbnN0IHQ9dGhpcy5nZXRTY29wZSgpLmNsb25lKCk7cmV0dXJuIHRoaXMuTS5wdXNoKHtjbGllbnQ6dGhpcy5nZXRDbGllbnQoKSxzY29wZTp0fSksdH1HKCl7cmV0dXJuISh0aGlzLk0ubGVuZ3RoPD0xKSYmISF0aGlzLk0ucG9wKCl9fWZ1bmN0aW9uIG90KCl7Y29uc3QgdD1nKG0oKSk7cmV0dXJuIHQuc3RhY2s9dC5zdGFja3x8bmV3IHJ0KHkoImRlZmF1bHRDdXJyZW50U2NvcGUiLCgoKT0+bmV3IGV0KSkseSgiZGVmYXVsdElzb2xhdGlvblNjb3BlIiwoKCk9Pm5ldyBldCkpKX1mdW5jdGlvbiBzdCh0KXtyZXR1cm4gb3QoKS53aXRoU2NvcGUodCl9ZnVuY3Rpb24gaXQodCxuKXtjb25zdCBlPW90KCk7cmV0dXJuIGUud2l0aFNjb3BlKCgoKT0+KGUuZ2V0U3RhY2tUb3AoKS5zY29wZT10LG4odCkpKSl9ZnVuY3Rpb24gY3QodCl7cmV0dXJuIG90KCkud2l0aFNjb3BlKCgoKT0+dChvdCgpLmdldElzb2xhdGlvblNjb3BlKCkpKSl9ZnVuY3Rpb24gdXQodCl7Y29uc3Qgbj1nKHQpO3JldHVybiBuLmFjcz9uLmFjczp7d2l0aElzb2xhdGlvblNjb3BlOmN0LHdpdGhTY29wZTpzdCx3aXRoU2V0U2NvcGU6aXQsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LG4pPT5jdChuKSxnZXRDdXJyZW50U2NvcGU6KCk9Pm90KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+b3QoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiBhdCgpe3JldHVybiB1dChtKCkpLmdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpfWNvbnN0IGZ0PSJzZW50cnkuc291cmNlIixodD0ic2VudHJ5LnNhbXBsZV9yYXRlIixwdD0ic2VudHJ5Lm9wIixsdD0ic2VudHJ5Lm9yaWdpbiIsZHQ9MCxtdD0xLGd0PSJfc2VudHJ5U2NvcGUiLHl0PSJfc2VudHJ5SXNvbGF0aW9uU2NvcGUiO2Z1bmN0aW9uIGJ0KHQpe3JldHVybntzY29wZTp0W2d0XSxpc29sYXRpb25TY29wZTp0W3l0XX19Y29uc3QgX3Q9InNlbnRyeS0iLHZ0PS9ec2VudHJ5LS87ZnVuY3Rpb24gd3QodCl7Y29uc3Qgbj1mdW5jdGlvbih0KXtpZighdHx8IWoodCkmJiFBcnJheS5pc0FycmF5KHQpKXJldHVybjtpZihBcnJheS5pc0FycmF5KHQpKXJldHVybiB0LnJlZHVjZSgoKHQsbik9Pntjb25zdCBlPVN0KG4pO3JldHVybiBPYmplY3QuZW50cmllcyhlKS5mb3JFYWNoKCgoW24sZV0pPT57dFtuXT1lfSkpLHR9KSx7fSk7cmV0dXJuIFN0KHQpfSh0KTtpZighbilyZXR1cm47Y29uc3QgZT1PYmplY3QuZW50cmllcyhuKS5yZWR1Y2UoKCh0LFtuLGVdKT0+e2lmKG4ubWF0Y2godnQpKXt0W24uc2xpY2UoX3QubGVuZ3RoKV09ZX1yZXR1cm4gdH0pLHt9KTtyZXR1cm4gT2JqZWN0LmtleXMoZSkubGVuZ3RoPjA/ZTp2b2lkIDB9ZnVuY3Rpb24gU3QodCl7cmV0dXJuIHQuc3BsaXQoIiwiKS5tYXAoKHQ9PnQuc3BsaXQoIj0iKS5tYXAoKHQ9PmRlY29kZVVSSUNvbXBvbmVudCh0LnRyaW0oKSkpKSkpLnJlZHVjZSgoKHQsW24sZV0pPT4obiYmZSYmKHRbbl09ZSksdCkpLHt9KX1jb25zdCAkdD0xO2Z1bmN0aW9uIEV0KHQpe2NvbnN0e3NwYW5JZDpuLHRyYWNlSWQ6ZSxpc1JlbW90ZTpyfT10LnNwYW5Db250ZXh0KCksbz1yP246Q3QodCkucGFyZW50X3NwYW5faWQscz1idCh0KS5zY29wZTtyZXR1cm4gTCh7cGFyZW50X3NwYW5faWQ6byxzcGFuX2lkOnI/cz8uZ2V0UHJvcGFnYXRpb25Db250ZXh0KCkucHJvcGFnYXRpb25TcGFuSWR8fHEoKTpuLHRyYWNlX2lkOmV9KX1mdW5jdGlvbiB4dCh0KXtyZXR1cm4gdCYmdC5sZW5ndGg+MD90Lm1hcCgoKHtjb250ZXh0OntzcGFuSWQ6dCx0cmFjZUlkOm4sdHJhY2VGbGFnczplLC4uLnJ9LGF0dHJpYnV0ZXM6b30pPT4oe3NwYW5faWQ6dCx0cmFjZV9pZDpuLHNhbXBsZWQ6ZT09PSR0LGF0dHJpYnV0ZXM6bywuLi5yfSkpKTp2b2lkIDB9ZnVuY3Rpb24gTnQodCl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiB0P1R0KHQpOkFycmF5LmlzQXJyYXkodCk/dFswXSt0WzFdLzFlOTp0IGluc3RhbmNlb2YgRGF0ZT9UdCh0LmdldFRpbWUoKSk6WSgpfWZ1bmN0aW9uIFR0KHQpe3JldHVybiB0Pjk5OTk5OTk5OTk/dC8xZTM6dH1mdW5jdGlvbiBDdCh0KXtpZihmdW5jdGlvbih0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgdC5nZXRTcGFuSlNPTn0odCkpcmV0dXJuIHQuZ2V0U3BhbkpTT04oKTtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmV9PXQuc3BhbkNvbnRleHQoKTtpZihmdW5jdGlvbih0KXtjb25zdCBuPXQ7cmV0dXJuISEobi5hdHRyaWJ1dGVzJiZuLnN0YXJ0VGltZSYmbi5uYW1lJiZuLmVuZFRpbWUmJm4uc3RhdHVzKX0odCkpe2NvbnN0e2F0dHJpYnV0ZXM6cixzdGFydFRpbWU6byxuYW1lOnMsZW5kVGltZTppLHBhcmVudFNwYW5JZDpjLHN0YXR1czp1LGxpbmtzOmF9PXQ7cmV0dXJuIEwoe3NwYW5faWQ6bix0cmFjZV9pZDplLGRhdGE6cixkZXNjcmlwdGlvbjpzLHBhcmVudF9zcGFuX2lkOmMsc3RhcnRfdGltZXN0YW1wOk50KG8pLHRpbWVzdGFtcDpOdChpKXx8dm9pZCAwLHN0YXR1czpqdCh1KSxvcDpyW3B0XSxvcmlnaW46cltsdF0sbGlua3M6eHQoYSl9KX1yZXR1cm57c3Bhbl9pZDpuLHRyYWNlX2lkOmUsc3RhcnRfdGltZXN0YW1wOjAsZGF0YTp7fX19ZnVuY3Rpb24ganQodCl7aWYodCYmdC5jb2RlIT09ZHQpcmV0dXJuIHQuY29kZT09PW10PyJvayI6dC5tZXNzYWdlfHwidW5rbm93bl9lcnJvciJ9Y29uc3QgUnQ9Il9zZW50cnlSb290U3BhbiI7ZnVuY3Rpb24ga3QodCl7cmV0dXJuIHRbUnRdfHx0fWNvbnN0IE90PSJwcm9kdWN0aW9uIixEdD0iX2Zyb3plbkRzYyI7ZnVuY3Rpb24gSXQodCl7Y29uc3Qgbj1hdCgpO2lmKCFuKXJldHVybnt9O2NvbnN0IGU9a3QodCkscj1DdChlKSxvPXIuZGF0YSxzPWUuc3BhbkNvbnRleHQoKS50cmFjZVN0YXRlLGk9cz8uZ2V0KCJzZW50cnkuc2FtcGxlX3JhdGUiKT8/b1todF07ZnVuY3Rpb24gYyh0KXtyZXR1cm4ibnVtYmVyIiE9dHlwZW9mIGkmJiJzdHJpbmciIT10eXBlb2YgaXx8KHQuc2FtcGxlX3JhdGU9YCR7aX1gKSx0fWNvbnN0IHU9ZVtEdF07aWYodSlyZXR1cm4gYyh1KTtjb25zdCBhPXM/LmdldCgic2VudHJ5LmRzYyIpLGY9YSYmd3QoYSk7aWYoZilyZXR1cm4gYyhmKTtjb25zdCBoPWZ1bmN0aW9uKHQsbil7Y29uc3QgZT1uLmdldE9wdGlvbnMoKSx7cHVibGljS2V5OnJ9PW4uZ2V0RHNuKCl8fHt9LG89TCh7ZW52aXJvbm1lbnQ6ZS5lbnZpcm9ubWVudHx8T3QscmVsZWFzZTplLnJlbGVhc2UscHVibGljX2tleTpyLHRyYWNlX2lkOnR9KTtyZXR1cm4gbi5lbWl0KCJjcmVhdGVEc2MiLG8pLG99KHQuc3BhbkNvbnRleHQoKS50cmFjZUlkLG4pLHA9b1tmdF0sbD1yLmRlc2NyaXB0aW9uO3JldHVybiJ1cmwiIT09cCYmbCYmKGgudHJhbnNhY3Rpb249bCksZnVuY3Rpb24odCl7aWYoImJvb2xlYW4iPT10eXBlb2YgX19TRU5UUllfVFJBQ0lOR19fJiYhX19TRU5UUllfVFJBQ0lOR19fKXJldHVybiExO2NvbnN0IG49dHx8YXQoKT8uZ2V0T3B0aW9ucygpO3JldHVybiEoIW58fG51bGw9PW4udHJhY2VzU2FtcGxlUmF0ZSYmIW4udHJhY2VzU2FtcGxlcil9KCkmJihoLnNhbXBsZWQ9U3RyaW5nKGZ1bmN0aW9uKHQpe2NvbnN0e3RyYWNlRmxhZ3M6bn09dC5zcGFuQ29udGV4dCgpO3JldHVybiBuPT09JHR9KGUpKSxoLnNhbXBsZV9yYW5kPXM/LmdldCgic2VudHJ5LnNhbXBsZV9yYW5kIik/P2J0KGUpLnNjb3BlPy5nZXRQcm9wYWdhdGlvbkNvbnRleHQoKS5zYW1wbGVSYW5kLnRvU3RyaW5nKCkpLGMoaCksbi5lbWl0KCJjcmVhdGVEc2MiLGgsZSksaH1mdW5jdGlvbiBBdCh0LG49ITEpe2NvbnN0e2hvc3Q6ZSxwYXRoOnIscGFzczpvLHBvcnQ6cyxwcm9qZWN0SWQ6aSxwcm90b2NvbDpjLHB1YmxpY0tleTp1fT10O3JldHVybmAke2N9Oi8vJHt1fSR7biYmbz9gOiR7b31gOiIifUAke2V9JHtzP2A6JHtzfWA6IiJ9LyR7cj9gJHtyfS9gOnJ9JHtpfWB9ZnVuY3Rpb24gUHQodCxuPTEwMCxlPTEvMCl7dHJ5e3JldHVybiBVdCgiIix0LG4sZSl9Y2F0Y2godCl7cmV0dXJue0VSUk9SOmAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHt0fSlgfX19ZnVuY3Rpb24gVXQodCxuLGU9MS8wLHI9MS8wLG89ZnVuY3Rpb24oKXtjb25zdCB0PW5ldyBXZWFrU2V0O2Z1bmN0aW9uIG4obil7cmV0dXJuISF0LmhhcyhuKXx8KHQuYWRkKG4pLCExKX1mdW5jdGlvbiBlKG4pe3QuZGVsZXRlKG4pfXJldHVybltuLGVdfSgpKXtjb25zdFtzLGldPW87aWYobnVsbD09bnx8WyJib29sZWFuIiwic3RyaW5nIl0uaW5jbHVkZXModHlwZW9mIG4pfHwibnVtYmVyIj09dHlwZW9mIG4mJk51bWJlci5pc0Zpbml0ZShuKSlyZXR1cm4gbjtjb25zdCBjPWZ1bmN0aW9uKHQsbil7dHJ5e2lmKCJkb21haW4iPT09dCYmbiYmIm9iamVjdCI9PXR5cGVvZiBuJiZuLkopcmV0dXJuIltEb21haW5dIjtpZigiZG9tYWluRW1pdHRlciI9PT10KXJldHVybiJbRG9tYWluRW1pdHRlcl0iO2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZ2xvYmFsJiZuPT09Z2xvYmFsKXJldHVybiJbR2xvYmFsXSI7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiB3aW5kb3cmJm49PT13aW5kb3cpcmV0dXJuIltXaW5kb3ddIjtpZigidW5kZWZpbmVkIiE9dHlwZW9mIGRvY3VtZW50JiZuPT09ZG9jdW1lbnQpcmV0dXJuIltEb2N1bWVudF0iO2lmKCJvYmplY3QiPT10eXBlb2YoZT1uKSYmbnVsbCE9PWUmJihlLl9faXNWdWV8fGUuWSkpcmV0dXJuIltWdWVWaWV3TW9kZWxdIjtpZihmdW5jdGlvbih0KXtyZXR1cm4gUih0KSYmIm5hdGl2ZUV2ZW50ImluIHQmJiJwcmV2ZW50RGVmYXVsdCJpbiB0JiYic3RvcFByb3BhZ2F0aW9uImluIHR9KG4pKXJldHVybiJbU3ludGhldGljRXZlbnRdIjtpZigibnVtYmVyIj09dHlwZW9mIG4mJiFOdW1iZXIuaXNGaW5pdGUobikpcmV0dXJuYFske259XWA7aWYoImZ1bmN0aW9uIj09dHlwZW9mIG4pcmV0dXJuYFtGdW5jdGlvbjogJHtmdW5jdGlvbih0KXt0cnl7cmV0dXJuIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0JiZ0Lm5hbWV8fE59Y2F0Y2godCl7cmV0dXJuIE59fShuKX1dYDtpZigic3ltYm9sIj09dHlwZW9mIG4pcmV0dXJuYFske1N0cmluZyhuKX1dYDtpZigiYmlnaW50Ij09dHlwZW9mIG4pcmV0dXJuYFtCaWdJbnQ6ICR7U3RyaW5nKG4pfV1gO2NvbnN0IHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj1PYmplY3QuZ2V0UHJvdG90eXBlT2YodCk7cmV0dXJuIG4/LmNvbnN0cnVjdG9yP24uY29uc3RydWN0b3IubmFtZToibnVsbCBwcm90b3R5cGUifShuKTtyZXR1cm4vXkhUTUwoXHcqKUVsZW1lbnQkLy50ZXN0KHIpP2BbSFRNTEVsZW1lbnQ6ICR7cn1dYDpgW29iamVjdCAke3J9XWB9Y2F0Y2godCl7cmV0dXJuYCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9dmFyIGV9KHQsbik7aWYoIWMuc3RhcnRzV2l0aCgiW29iamVjdCAiKSlyZXR1cm4gYztpZihuLl9fc2VudHJ5X3NraXBfbm9ybWFsaXphdGlvbl9fKXJldHVybiBuO2NvbnN0IHU9Im51bWJlciI9PXR5cGVvZiBuLl9fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXz9uLl9fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXzplO2lmKDA9PT11KXJldHVybiBjLnJlcGxhY2UoIm9iamVjdCAiLCIiKTtpZihzKG4pKXJldHVybiJbQ2lyY3VsYXIgfl0iO2NvbnN0IGE9bjtpZihhJiYiZnVuY3Rpb24iPT10eXBlb2YgYS50b0pTT04pdHJ5e3JldHVybiBVdCgiIixhLnRvSlNPTigpLHUtMSxyLG8pfWNhdGNoKHQpe31jb25zdCBmPUFycmF5LmlzQXJyYXkobik/W106e307bGV0IGg9MDtjb25zdCBwPVAobik7Zm9yKGNvbnN0IHQgaW4gcCl7aWYoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChwLHQpKWNvbnRpbnVlO2lmKGg+PXIpe2ZbdF09IltNYXhQcm9wZXJ0aWVzIH5dIjticmVha31jb25zdCBuPXBbdF07Zlt0XT1VdCh0LG4sdS0xLHIsbyksaCsrfXJldHVybiBpKG4pLGZ9ZnVuY3Rpb24gTXQodCxuKXtjb25zdCBlPW4ucmVwbGFjZSgvXFwvZywiLyIpLnJlcGxhY2UoL1t8XFx7fSgpW1xdXiQrKj8uXS9nLCJcXCQmIik7bGV0IHI9dDt0cnl7cj1kZWNvZGVVUkkodCl9Y2F0Y2godCl7fXJldHVybiByLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC93ZWJwYWNrOlwvPy9nLCIiKS5yZXBsYWNlKG5ldyBSZWdFeHAoYChmaWxlOi8vKT8vKiR7ZX0vKmAsImlnIiksImFwcDovLy8iKX1mdW5jdGlvbiBMdCh0LG49W10pe3JldHVyblt0LG5dfWZ1bmN0aW9uIEJ0KHQsbil7Y29uc3QgZT10WzFdO2Zvcihjb25zdCB0IG9mIGUpe2lmKG4odCx0WzBdLnR5cGUpKXJldHVybiEwfXJldHVybiExfWZ1bmN0aW9uIEd0KHQpe2NvbnN0IG49ZyhwKTtyZXR1cm4gbi5lbmNvZGVQb2x5ZmlsbD9uLmVuY29kZVBvbHlmaWxsKHQpOihuZXcgVGV4dEVuY29kZXIpLmVuY29kZSh0KX1mdW5jdGlvbiBKdCh0KXtjb25zdFtuLGVdPXQ7bGV0IHI9SlNPTi5zdHJpbmdpZnkobik7ZnVuY3Rpb24gbyh0KXsic3RyaW5nIj09dHlwZW9mIHI/cj0ic3RyaW5nIj09dHlwZW9mIHQ/cit0OltHdChyKSx0XTpyLnB1c2goInN0cmluZyI9PXR5cGVvZiB0P0d0KHQpOnQpfWZvcihjb25zdCB0IG9mIGUpe2NvbnN0W24sZV09dDtpZihvKGBcbiR7SlNPTi5zdHJpbmdpZnkobil9XG5gKSwic3RyaW5nIj09dHlwZW9mIGV8fGUgaW5zdGFuY2VvZiBVaW50OEFycmF5KW8oZSk7ZWxzZXtsZXQgdDt0cnl7dD1KU09OLnN0cmluZ2lmeShlKX1jYXRjaChuKXt0PUpTT04uc3RyaW5naWZ5KFB0KGUpKX1vKHQpfX1yZXR1cm4ic3RyaW5nIj09dHlwZW9mIHI/cjpmdW5jdGlvbih0KXtjb25zdCBuPXQucmVkdWNlKCgodCxuKT0+dCtuLmxlbmd0aCksMCksZT1uZXcgVWludDhBcnJheShuKTtsZXQgcj0wO2Zvcihjb25zdCBuIG9mIHQpZS5zZXQobixyKSxyKz1uLmxlbmd0aDtyZXR1cm4gZX0ocil9Y29uc3QgWXQ9e3Nlc3Npb246InNlc3Npb24iLHNlc3Npb25zOiJzZXNzaW9uIixhdHRhY2htZW50OiJhdHRhY2htZW50Iix0cmFuc2FjdGlvbjoidHJhbnNhY3Rpb24iLGV2ZW50OiJlcnJvciIsY2xpZW50X3JlcG9ydDoiaW50ZXJuYWwiLHVzZXJfcmVwb3J0OiJkZWZhdWx0Iixwcm9maWxlOiJwcm9maWxlIixwcm9maWxlX2NodW5rOiJwcm9maWxlIixyZXBsYXlfZXZlbnQ6InJlcGxheSIscmVwbGF5X3JlY29yZGluZzoicmVwbGF5IixjaGVja19pbjoibW9uaXRvciIsZmVlZGJhY2s6ImZlZWRiYWNrIixzcGFuOiJzcGFuIixyYXdfc2VjdXJpdHk6InNlY3VyaXR5IixvdGVsX2xvZzoibG9nX2l0ZW0ifTtmdW5jdGlvbiB6dCh0KXtpZighdD8uc2RrKXJldHVybjtjb25zdHtuYW1lOm4sdmVyc2lvbjplfT10LnNkaztyZXR1cm57bmFtZTpuLHZlcnNpb246ZX19ZnVuY3Rpb24gSHQodCxuLGUscil7Y29uc3Qgbz16dChlKSxzPXQudHlwZSYmInJlcGxheV9ldmVudCIhPT10LnR5cGU/dC50eXBlOiJldmVudCI7IWZ1bmN0aW9uKHQsbil7biYmKHQuc2RrPXQuc2RrfHx7fSx0LnNkay5uYW1lPXQuc2RrLm5hbWV8fG4ubmFtZSx0LnNkay52ZXJzaW9uPXQuc2RrLnZlcnNpb258fG4udmVyc2lvbix0LnNkay5pbnRlZ3JhdGlvbnM9Wy4uLnQuc2RrLmludGVncmF0aW9uc3x8W10sLi4ubi5pbnRlZ3JhdGlvbnN8fFtdXSx0LnNkay5wYWNrYWdlcz1bLi4udC5zZGsucGFja2FnZXN8fFtdLC4uLm4ucGFja2FnZXN8fFtdXSl9KHQsZT8uc2RrKTtjb25zdCBpPWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDoobmV3IERhdGUpLnRvSVNPU3RyaW5nKCksLi4ubiYme3NkazpufSwuLi4hIWUmJnImJntkc246QXQocil9LC4uLm8mJnt0cmFjZTpMKHsuLi5vfSl9fX0odCxvLHIsbik7ZGVsZXRlIHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhO3JldHVybiBMdChpLFtbe3R5cGU6c30sdF1dKX1jb25zdCBXdD0iX19TRU5UUllfU1VQUFJFU1NfVFJBQ0lOR19fIjtmdW5jdGlvbiBGdCh0KXtjb25zdCBuPXV0KG0oKSk7cmV0dXJuIG4uc3VwcHJlc3NUcmFjaW5nP24uc3VwcHJlc3NUcmFjaW5nKHQpOmZ1bmN0aW9uKC4uLnQpe2NvbnN0IG49dXQobSgpKTtpZigyPT09dC5sZW5ndGgpe2NvbnN0W2Uscl09dDtyZXR1cm4gZT9uLndpdGhTZXRTY29wZShlLHIpOm4ud2l0aFNjb3BlKHIpfXJldHVybiBuLndpdGhTY29wZSh0WzBdKX0oKG49PihuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1d0XTohMH0pLHQoKSkpKX1mdW5jdGlvbiBLdCh0LG4pe2NvbnN0e2ZpbmdlcnByaW50OmUsc3BhbjpyLGJyZWFkY3J1bWJzOm8sc2RrUHJvY2Vzc2luZ01ldGFkYXRhOnN9PW47IWZ1bmN0aW9uKHQsbil7Y29uc3R7ZXh0cmE6ZSx0YWdzOnIsdXNlcjpvLGNvbnRleHRzOnMsbGV2ZWw6aSx0cmFuc2FjdGlvbk5hbWU6Y309bix1PUwoZSk7T2JqZWN0LmtleXModSkubGVuZ3RoJiYodC5leHRyYT17Li4udSwuLi50LmV4dHJhfSk7Y29uc3QgYT1MKHIpO09iamVjdC5rZXlzKGEpLmxlbmd0aCYmKHQudGFncz17Li4uYSwuLi50LnRhZ3N9KTtjb25zdCBmPUwobyk7T2JqZWN0LmtleXMoZikubGVuZ3RoJiYodC51c2VyPXsuLi5mLC4uLnQudXNlcn0pO2NvbnN0IGg9TChzKTtPYmplY3Qua2V5cyhoKS5sZW5ndGgmJih0LmNvbnRleHRzPXsuLi5oLC4uLnQuY29udGV4dHN9KTtpJiYodC5sZXZlbD1pKTtjJiYidHJhbnNhY3Rpb24iIT09dC50eXBlJiYodC50cmFuc2FjdGlvbj1jKX0odCxuKSxyJiZmdW5jdGlvbih0LG4pe3QuY29udGV4dHM9e3RyYWNlOkV0KG4pLC4uLnQuY29udGV4dHN9LHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXtkeW5hbWljU2FtcGxpbmdDb250ZXh0Okl0KG4pLC4uLnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhfTtjb25zdCBlPWt0KG4pLHI9Q3QoZSkuZGVzY3JpcHRpb247ciYmIXQudHJhbnNhY3Rpb24mJiJ0cmFuc2FjdGlvbiI9PT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPXIpfSh0LHIpLGZ1bmN0aW9uKHQsbil7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sbiYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQobikpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxlKSxmdW5jdGlvbih0LG4pe2NvbnN0IGU9Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLm5dO3QuYnJlYWRjcnVtYnM9ZS5sZW5ndGg/ZTp2b2lkIDB9KHQsbyksZnVuY3Rpb24odCxuKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4ubn19KHQscyl9Y29uc3QgVnQ9IjciO2NsYXNzIFp0IGV4dGVuZHMgRXJyb3J7Y29uc3RydWN0b3IodCxuPSJ3YXJuIil7c3VwZXIodCksdGhpcy5tZXNzYWdlPXQsdGhpcy5sb2dMZXZlbD1ufX1mdW5jdGlvbiBxdCh0KXtjb25zdCBuPVtdO2Z1bmN0aW9uIGUodCl7cmV0dXJuIG4uc3BsaWNlKG4uaW5kZXhPZih0KSwxKVswXXx8UHJvbWlzZS5yZXNvbHZlKHZvaWQgMCl9cmV0dXJueyQ6bixhZGQ6ZnVuY3Rpb24ocil7aWYoISh2b2lkIDA9PT10fHxuLmxlbmd0aDx0KSlyZXR1cm4gbz1uZXcgWnQoIk5vdCBhZGRpbmcgUHJvbWlzZSBiZWNhdXNlIGJ1ZmZlciBsaW1pdCB3YXMgcmVhY2hlZC4iKSxuZXcgRigoKHQsbik9PntuKG8pfSkpO3ZhciBvO2NvbnN0IHM9cigpO3JldHVybi0xPT09bi5pbmRleE9mKHMpJiZuLnB1c2gocykscy50aGVuKCgoKT0+ZShzKSkpLnRoZW4obnVsbCwoKCk9PmUocykudGhlbihudWxsLCgoKT0+e30pKSkpLHN9LGRyYWluOmZ1bmN0aW9uKHQpe3JldHVybiBuZXcgRigoKGUscik9PntsZXQgbz1uLmxlbmd0aDtpZighbylyZXR1cm4gZSghMCk7Y29uc3Qgcz1zZXRUaW1lb3V0KCgoKT0+e3QmJnQ+MCYmZSghMSl9KSx0KTtuLmZvckVhY2goKHQ9PntXKHQpLnRoZW4oKCgpPT57LS1vfHwoY2xlYXJUaW1lb3V0KHMpLGUoITApKX0pLHIpfSkpfSkpfX19Y29uc3QgUXQ9NmU0O2Z1bmN0aW9uIFh0KHQse3N0YXR1c0NvZGU6bixoZWFkZXJzOmV9LHI9RGF0ZS5ub3coKSl7Y29uc3Qgbz17Li4udH0scz1lPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0saT1lPy5bInJldHJ5LWFmdGVyIl07aWYocylmb3IoY29uc3QgdCBvZiBzLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtuLGUsLCxzXT10LnNwbGl0KCI6Iiw1KSxpPXBhcnNlSW50KG4sMTApLGM9MWUzKihpc05hTihpKT82MDppKTtpZihlKWZvcihjb25zdCB0IG9mIGUuc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZzJiYhcy5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIGk/by5hbGw9citmdW5jdGlvbih0LG49RGF0ZS5ub3coKSl7Y29uc3QgZT1wYXJzZUludChgJHt0fWAsMTApO2lmKCFpc05hTihlKSlyZXR1cm4gMWUzKmU7Y29uc3Qgcj1EYXRlLnBhcnNlKGAke3R9YCk7cmV0dXJuIGlzTmFOKHIpP1F0OnItbn0oaSxyKTo0Mjk9PT1uJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWNvbnN0IHRuPTY0O2Z1bmN0aW9uIG5uKHQsbixlPXF0KHQuYnVmZmVyU2l6ZXx8dG4pKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKEJ0KHQsKCh0LG4pPT57Y29uc3QgZT1mdW5jdGlvbih0KXtyZXR1cm4gWXRbdF19KG4pOyhmdW5jdGlvbih0LG4sZT1EYXRlLm5vdygpKXtyZXR1cm4gZnVuY3Rpb24odCxuKXtyZXR1cm4gdFtuXXx8dC5hbGx8fDB9KHQsbik+ZX0pKHIsZSl8fG8ucHVzaCh0KX0pKSwwPT09by5sZW5ndGgpcmV0dXJuIFcoe30pO2NvbnN0IHM9THQodFswXSxvKSxpPXQ9PntCdChzLCgodCxuKT0+e30pKX07cmV0dXJuIGUuYWRkKCgoKT0+bih7Ym9keTpKdChzKX0pLnRoZW4oKHQ9Pih2b2lkIDAhPT10LnN0YXR1c0NvZGUmJih0LnN0YXR1c0NvZGU8MjAwfHx0LnN0YXR1c0NvZGU+PTMwMCkmJmgmJncud2FybihgU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlICR7dC5zdGF0dXNDb2RlfSB0byBzZW50IGV2ZW50LmApLHI9WHQocix0KSx0KSksKHQ9Pnt0aHJvdyBpKCksdH0pKSkpLnRoZW4oKHQ9PnQpLCh0PT57aWYodCBpbnN0YW5jZW9mIFp0KXJldHVybiBoJiZ3LmVycm9yKCJTa2lwcGVkIHNlbmRpbmcgZXZlbnQgYmVjYXVzZSBidWZmZXIgaXMgZnVsbC4iKSxpKCksVyh7fSk7dGhyb3cgdH0pKX0sZmx1c2g6dD0+ZS5kcmFpbih0KX19Y29uc3QgZW49L14oXFMrOlxcfFwvPykoW1xzXFNdKj8pKCg/OlwuezEsMn18W14vXFxdKz98KShcLlteLi9cXF0qfCkpKD86Wy9cXF0qKSQvO2Z1bmN0aW9uIHJuKHQpe2NvbnN0IG49ZnVuY3Rpb24odCl7Y29uc3Qgbj10Lmxlbmd0aD4xMDI0P2A8dHJ1bmNhdGVkPiR7dC5zbGljZSgtMTAyNCl9YDp0LGU9ZW4uZXhlYyhuKTtyZXR1cm4gZT9lLnNsaWNlKDEpOltdfSh0KSxlPW5bMF18fCIiO2xldCByPW5bMV07cmV0dXJuIGV8fHI/KHImJihyPXIuc2xpY2UoMCxyLmxlbmd0aC0xKSksZStyKToiLiJ9ZnVuY3Rpb24gb24odCxuPSExKXtyZXR1cm4hKG58fHQmJiF0LnN0YXJ0c1dpdGgoIi8iKSYmIXQubWF0Y2goL15bQS1aXTovKSYmIXQuc3RhcnRzV2l0aCgiLiIpJiYhdC5tYXRjaCgvXlthLXpBLVpdKFthLXpBLVowLTkuXC0rXSkqOlwvXC8vKSkmJnZvaWQgMCE9PXQmJiF0LmluY2x1ZGVzKCJub2RlX21vZHVsZXMvIil9Y29uc3Qgc249U3ltYm9sKCJBZ2VudEJhc2VJbnRlcm5hbFN0YXRlIik7Y2xhc3MgY24gZXh0ZW5kcyBzLkFnZW50e2NvbnN0cnVjdG9yKHQpe3N1cGVyKHQpLHRoaXNbc25dPXt9fWlzU2VjdXJlRW5kcG9pbnQodCl7aWYodCl7aWYoImJvb2xlYW4iPT10eXBlb2YgdC5zZWN1cmVFbmRwb2ludClyZXR1cm4gdC5zZWN1cmVFbmRwb2ludDtpZigic3RyaW5nIj09dHlwZW9mIHQucHJvdG9jb2wpcmV0dXJuImh0dHBzOiI9PT10LnByb3RvY29sfWNvbnN0e3N0YWNrOm59PW5ldyBFcnJvcjtyZXR1cm4ic3RyaW5nIj09dHlwZW9mIG4mJm4uc3BsaXQoIlxuIikuc29tZSgodD0+LTEhPT10LmluZGV4T2YoIihodHRwcy5qczoiKXx8LTEhPT10LmluZGV4T2YoIm5vZGU6aHR0cHM6IikpKX1jcmVhdGVTb2NrZXQodCxuLGUpe2NvbnN0IHI9ey4uLm4sc2VjdXJlRW5kcG9pbnQ6dGhpcy5pc1NlY3VyZUVuZHBvaW50KG4pfTtQcm9taXNlLnJlc29sdmUoKS50aGVuKCgoKT0+dGhpcy5jb25uZWN0KHQscikpKS50aGVuKChvPT57aWYobyBpbnN0YW5jZW9mIHMuQWdlbnQpcmV0dXJuIG8uYWRkUmVxdWVzdCh0LHIpO3RoaXNbc25dLmN1cnJlbnRTb2NrZXQ9byxzdXBlci5jcmVhdGVTb2NrZXQodCxuLGUpfSksZSl9Y3JlYXRlQ29ubmVjdGlvbigpe2NvbnN0IHQ9dGhpc1tzbl0uY3VycmVudFNvY2tldDtpZih0aGlzW3NuXS5jdXJyZW50U29ja2V0PXZvaWQgMCwhdCl0aHJvdyBuZXcgRXJyb3IoIk5vIHNvY2tldCB3YXMgcmV0dXJuZWQgaW4gdGhlIGBjb25uZWN0KClgIGZ1bmN0aW9uIik7cmV0dXJuIHR9Z2V0IGRlZmF1bHRQb3J0KCl7cmV0dXJuIHRoaXNbc25dLmRlZmF1bHRQb3J0Pz8oImh0dHBzOiI9PT10aGlzLnByb3RvY29sPzQ0Mzo4MCl9c2V0IGRlZmF1bHRQb3J0KHQpe3RoaXNbc25dJiYodGhpc1tzbl0uZGVmYXVsdFBvcnQ9dCl9Z2V0IHByb3RvY29sKCl7cmV0dXJuIHRoaXNbc25dLnByb3RvY29sPz8odGhpcy5pc1NlY3VyZUVuZHBvaW50KCk/Imh0dHBzOiI6Imh0dHA6Iil9c2V0IHByb3RvY29sKHQpe3RoaXNbc25dJiYodGhpc1tzbl0ucHJvdG9jb2w9dCl9fWZ1bmN0aW9uIHVuKC4uLnQpe3cubG9nKCJbaHR0cHMtcHJveHktYWdlbnQ6cGFyc2UtcHJveHktcmVzcG9uc2VdIiwuLi50KX1mdW5jdGlvbiBhbih0KXtyZXR1cm4gbmV3IFByb21pc2UoKChuLGUpPT57bGV0IHI9MDtjb25zdCBvPVtdO2Z1bmN0aW9uIHMoKXtjb25zdCBjPXQucmVhZCgpO2M/ZnVuY3Rpb24oYyl7by5wdXNoKGMpLHIrPWMubGVuZ3RoO2NvbnN0IHU9QnVmZmVyLmNvbmNhdChvLHIpLGE9dS5pbmRleE9mKCJcclxuXHJcbiIpO2lmKC0xPT09YSlyZXR1cm4gdW4oImhhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uIiksdm9pZCBzKCk7Y29uc3QgZj11LnN1YmFycmF5KDAsYSkudG9TdHJpbmcoImFzY2lpIikuc3BsaXQoIlxyXG4iKSxoPWYuc2hpZnQoKTtpZighaClyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoIk5vIGhlYWRlciByZWNlaXZlZCBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2UiKSk7Y29uc3QgcD1oLnNwbGl0KCIgIiksbD0rKHBbMV18fDApLGQ9cC5zbGljZSgyKS5qb2luKCIgIiksbT17fTtmb3IoY29uc3QgbiBvZiBmKXtpZighbiljb250aW51ZTtjb25zdCByPW4uaW5kZXhPZigiOiIpO2lmKC0xPT09cilyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoYEludmFsaWQgaGVhZGVyIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZTogIiR7bn0iYCkpO2NvbnN0IG89bi5zbGljZSgwLHIpLnRvTG93ZXJDYXNlKCkscz1uLnNsaWNlKHIrMSkudHJpbVN0YXJ0KCksaT1tW29dOyJzdHJpbmciPT10eXBlb2YgaT9tW29dPVtpLHNdOkFycmF5LmlzQXJyYXkoaSk/aS5wdXNoKHMpOm1bb109c311bigiZ290IHByb3h5IHNlcnZlciByZXNwb25zZTogJW8gJW8iLGgsbSksaSgpLG4oe2Nvbm5lY3Q6e3N0YXR1c0NvZGU6bCxzdGF0dXNUZXh0OmQsaGVhZGVyczptfSxidWZmZXJlZDp1fSl9KGMpOnQub25jZSgicmVhZGFibGUiLHMpfWZ1bmN0aW9uIGkoKXt0LnJlbW92ZUxpc3RlbmVyKCJlbmQiLGMpLHQucmVtb3ZlTGlzdGVuZXIoImVycm9yIix1KSx0LnJlbW92ZUxpc3RlbmVyKCJyZWFkYWJsZSIscyl9ZnVuY3Rpb24gYygpe2koKSx1bigib25lbmQiKSxlKG5ldyBFcnJvcigiUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UiKSl9ZnVuY3Rpb24gdSh0KXtpKCksdW4oIm9uZXJyb3IgJW8iLHQpLGUodCl9dC5vbigiZXJyb3IiLHUpLHQub24oImVuZCIsYykscygpfSkpfWZ1bmN0aW9uIGZuKC4uLnQpe3cubG9nKCJbaHR0cHMtcHJveHktYWdlbnRdIiwuLi50KX1jbGFzcyBobiBleHRlbmRzIGNue3N0YXRpYyBfX2luaXRTdGF0aWMoKXt0aGlzLnByb3RvY29scz1bImh0dHAiLCJodHRwcyJdfWNvbnN0cnVjdG9yKHQsbil7c3VwZXIobiksdGhpcy5vcHRpb25zPXt9LHRoaXMucHJveHk9InN0cmluZyI9PXR5cGVvZiB0P25ldyBVUkwodCk6dCx0aGlzLnByb3h5SGVhZGVycz1uPy5oZWFkZXJzPz97fSxmbigiQ3JlYXRpbmcgbmV3IEh0dHBzUHJveHlBZ2VudCBpbnN0YW5jZTogJW8iLHRoaXMucHJveHkuaHJlZik7Y29uc3QgZT0odGhpcy5wcm94eS5ob3N0bmFtZXx8dGhpcy5wcm94eS5ob3N0KS5yZXBsYWNlKC9eXFt8XF0kL2csIiIpLHI9dGhpcy5wcm94eS5wb3J0P3BhcnNlSW50KHRoaXMucHJveHkucG9ydCwxMCk6Imh0dHBzOiI9PT10aGlzLnByb3h5LnByb3RvY29sPzQ0Mzo4MDt0aGlzLmNvbm5lY3RPcHRzPXtBTFBOUHJvdG9jb2xzOlsiaHR0cC8xLjEiXSwuLi5uP2xuKG4sImhlYWRlcnMiKTpudWxsLGhvc3Q6ZSxwb3J0OnJ9fWFzeW5jIGNvbm5lY3QodCxuKXtjb25zdHtwcm94eTplfT10aGlzO2lmKCFuLmhvc3QpdGhyb3cgbmV3IFR5cGVFcnJvcignTm8gImhvc3QiIHByb3ZpZGVkJyk7bGV0IHI7aWYoImh0dHBzOiI9PT1lLnByb3RvY29sKXtmbigiQ3JlYXRpbmcgYHRscy5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cyk7Y29uc3QgdD10aGlzLmNvbm5lY3RPcHRzLnNlcnZlcm5hbWV8fHRoaXMuY29ubmVjdE9wdHMuaG9zdDtyPWYuY29ubmVjdCh7Li4udGhpcy5jb25uZWN0T3B0cyxzZXJ2ZXJuYW1lOnQmJmEuaXNJUCh0KT92b2lkIDA6dH0pfWVsc2UgZm4oIkNyZWF0aW5nIGBuZXQuU29ja2V0YDogJW8iLHRoaXMuY29ubmVjdE9wdHMpLHI9YS5jb25uZWN0KHRoaXMuY29ubmVjdE9wdHMpO2NvbnN0IG89ImZ1bmN0aW9uIj09dHlwZW9mIHRoaXMucHJveHlIZWFkZXJzP3RoaXMucHJveHlIZWFkZXJzKCk6ey4uLnRoaXMucHJveHlIZWFkZXJzfSxzPWEuaXNJUHY2KG4uaG9zdCk/YFske24uaG9zdH1dYDpuLmhvc3Q7bGV0IGk9YENPTk5FQ1QgJHtzfToke24ucG9ydH0gSFRUUC8xLjFcclxuYDtpZihlLnVzZXJuYW1lfHxlLnBhc3N3b3JkKXtjb25zdCB0PWAke2RlY29kZVVSSUNvbXBvbmVudChlLnVzZXJuYW1lKX06JHtkZWNvZGVVUklDb21wb25lbnQoZS5wYXNzd29yZCl9YDtvWyJQcm94eS1BdXRob3JpemF0aW9uIl09YEJhc2ljICR7QnVmZmVyLmZyb20odCkudG9TdHJpbmcoImJhc2U2NCIpfWB9by5Ib3N0PWAke3N9OiR7bi5wb3J0fWAsb1siUHJveHktQ29ubmVjdGlvbiJdfHwob1siUHJveHktQ29ubmVjdGlvbiJdPXRoaXMua2VlcEFsaXZlPyJLZWVwLUFsaXZlIjoiY2xvc2UiKTtmb3IoY29uc3QgdCBvZiBPYmplY3Qua2V5cyhvKSlpKz1gJHt0fTogJHtvW3RdfVxyXG5gO2NvbnN0IGM9YW4ocik7ci53cml0ZShgJHtpfVxyXG5gKTtjb25zdHtjb25uZWN0OnUsYnVmZmVyZWQ6aH09YXdhaXQgYztpZih0LmVtaXQoInByb3h5Q29ubmVjdCIsdSksdGhpcy5lbWl0KCJwcm94eUNvbm5lY3QiLHUsdCksMjAwPT09dS5zdGF0dXNDb2RlKXtpZih0Lm9uY2UoInNvY2tldCIscG4pLG4uc2VjdXJlRW5kcG9pbnQpe2ZuKCJVcGdyYWRpbmcgc29ja2V0IGNvbm5lY3Rpb24gdG8gVExTIik7Y29uc3QgdD1uLnNlcnZlcm5hbWV8fG4uaG9zdDtyZXR1cm4gZi5jb25uZWN0KHsuLi5sbihuLCJob3N0IiwicGF0aCIsInBvcnQiKSxzb2NrZXQ6cixzZXJ2ZXJuYW1lOmEuaXNJUCh0KT92b2lkIDA6dH0pfXJldHVybiByfXIuZGVzdHJveSgpO2NvbnN0IHA9bmV3IGEuU29ja2V0KHt3cml0YWJsZTohMX0pO3JldHVybiBwLnJlYWRhYmxlPSEwLHQub25jZSgic29ja2V0IiwodD0+e2ZuKCJSZXBsYXlpbmcgcHJveHkgYnVmZmVyIGZvciBmYWlsZWQgcmVxdWVzdCIpLHQucHVzaChoKSx0LnB1c2gobnVsbCl9KSkscH19ZnVuY3Rpb24gcG4odCl7dC5yZXN1bWUoKX1mdW5jdGlvbiBsbih0LC4uLm4pe2NvbnN0IGU9e307bGV0IHI7Zm9yKHIgaW4gdCluLmluY2x1ZGVzKHIpfHwoZVtyXT10W3JdKTtyZXR1cm4gZX1obi5fX2luaXRTdGF0aWMoKTtjb25zdCBkbj0zMjc2ODtmdW5jdGlvbiBtbih0KXtyZXR1cm4gdC5yZXBsYWNlKC9eW0EtWl06LywiIikucmVwbGFjZSgvXFwvZywiLyIpfWNvbnN0IGduPW47bGV0IHluLGJuPTAsX249e307ZnVuY3Rpb24gdm4odCl7Z24uZGVidWcmJmNvbnNvbGUubG9nKGBbQU5SIFdvcmtlcl0gJHt0fWApfXZhciB3bixTbiwkbjtjb25zdCBFbj1mdW5jdGlvbih0KXtsZXQgbjt0cnl7bj1uZXcgVVJMKHQudXJsKX1jYXRjaChuKXtyZXR1cm4gdigoKCk9Pntjb25zb2xlLndhcm4oIltAc2VudHJ5L25vZGVdOiBJbnZhbGlkIGRzbiBvciB0dW5uZWwgb3B0aW9uLCB3aWxsIG5vdCBzZW5kIGFueSBldmVudHMuIFRoZSB0dW5uZWwgb3B0aW9uIG11c3QgYmUgYSBmdWxsIFVSTCB3aGVuIHVzZWQuIil9KSksbm4odCwoKCk9PlByb21pc2UucmVzb2x2ZSh7fSkpKX1jb25zdCBlPSJodHRwczoiPT09bi5wcm90b2NvbCxyPWZ1bmN0aW9uKHQsbil7Y29uc3R7bm9fcHJveHk6ZX09cHJvY2Vzcy5lbnYscj1lPy5zcGxpdCgiLCIpLnNvbWUoKG49PnQuaG9zdC5lbmRzV2l0aChuKXx8dC5ob3N0bmFtZS5lbmRzV2l0aChuKSkpO3JldHVybiByP3ZvaWQgMDpufShuLHQucHJveHl8fChlP3Byb2Nlc3MuZW52Lmh0dHBzX3Byb3h5OnZvaWQgMCl8fHByb2Nlc3MuZW52Lmh0dHBfcHJveHkpLG89ZT9pOnMsYT12b2lkIDAhPT10LmtlZXBBbGl2ZSYmdC5rZWVwQWxpdmUsZj1yP25ldyBobihyKTpuZXcgby5BZ2VudCh7a2VlcEFsaXZlOmEsbWF4U29ja2V0czozMCx0aW1lb3V0OjJlM30pLGg9ZnVuY3Rpb24odCxuLGUpe2NvbnN0e2hvc3RuYW1lOnIscGF0aG5hbWU6byxwb3J0OnMscHJvdG9jb2w6aSxzZWFyY2g6YX09bmV3IFVSTCh0LnVybCk7cmV0dXJuIGZ1bmN0aW9uKGYpe3JldHVybiBuZXcgUHJvbWlzZSgoKGgscCk9PntGdCgoKCk9PntsZXQgbD1mdW5jdGlvbih0KXtyZXR1cm4gbmV3IGMoe3JlYWQoKXt0aGlzLnB1c2godCksdGhpcy5wdXNoKG51bGwpfX0pfShmLmJvZHkpO2NvbnN0IGQ9ey4uLnQuaGVhZGVyc307Zi5ib2R5Lmxlbmd0aD5kbiYmKGRbImNvbnRlbnQtZW5jb2RpbmciXT0iZ3ppcCIsbD1sLnBpcGUodSgpKSk7Y29uc3QgbT1uLnJlcXVlc3Qoe21ldGhvZDoiUE9TVCIsYWdlbnQ6ZSxoZWFkZXJzOmQsaG9zdG5hbWU6cixwYXRoOmAke299JHthfWAscG9ydDpzLHByb3RvY29sOmksY2E6dC5jYUNlcnRzfSwodD0+e3Qub24oImRhdGEiLCgoKT0+e30pKSx0Lm9uKCJlbmQiLCgoKT0+e30pKSx0LnNldEVuY29kaW5nKCJ1dGY4Iik7Y29uc3Qgbj10LmhlYWRlcnNbInJldHJ5LWFmdGVyIl0/P251bGwsZT10LmhlYWRlcnNbIngtc2VudHJ5LXJhdGUtbGltaXRzIl0/P251bGw7aCh7c3RhdHVzQ29kZTp0LnN0YXR1c0NvZGUsaGVhZGVyczp7InJldHJ5LWFmdGVyIjpuLCJ4LXNlbnRyeS1yYXRlLWxpbWl0cyI6QXJyYXkuaXNBcnJheShlKT9lWzBdfHxudWxsOmV9fSl9KSk7bS5vbigiZXJyb3IiLHApLGwucGlwZShtKX0pKX0pKX19KHQsdC5odHRwTW9kdWxlPz9vLGYpO3JldHVybiBubih0LGgpfSh7dXJsOih3bj1nbi5kc24sU249Z24udHVubmVsLCRuPWduLnNka01ldGFkYXRhLnNkayxTbnx8YCR7ZnVuY3Rpb24odCl7cmV0dXJuYCR7ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnByb3RvY29sP2Ake3QucHJvdG9jb2x9OmA6IiIsZT10LnBvcnQ/YDoke3QucG9ydH1gOiIiO3JldHVybmAke259Ly8ke3QuaG9zdH0ke2V9JHt0LnBhdGg/YC8ke3QucGF0aH1gOiIifS9hcGkvYH0odCl9JHt0LnByb2plY3RJZH0vZW52ZWxvcGUvYH0od24pfT8ke2Z1bmN0aW9uKHQsbil7Y29uc3QgZT17c2VudHJ5X3ZlcnNpb246VnR9O3JldHVybiB0LnB1YmxpY0tleSYmKGUuc2VudHJ5X2tleT10LnB1YmxpY0tleSksbiYmKGUuc2VudHJ5X2NsaWVudD1gJHtuLm5hbWV9LyR7bi52ZXJzaW9ufWApLG5ldyBVUkxTZWFyY2hQYXJhbXMoZSkudG9TdHJpbmcoKX0od24sJG4pfWApfSk7YXN5bmMgZnVuY3Rpb24geG4oKXtpZih5bil7dm4oIlNlbmRpbmcgYWJub3JtYWwgc2Vzc2lvbiIpLFYoeW4se3N0YXR1czoiYWJub3JtYWwiLGFibm9ybWFsX21lY2hhbmlzbToiYW5yX2ZvcmVncm91bmQiLHJlbGVhc2U6Z24ucmVsZWFzZSxlbnZpcm9ubWVudDpnbi5lbnZpcm9ubWVudH0pO2NvbnN0IHQ9ZnVuY3Rpb24odCxuLGUscil7Y29uc3Qgbz16dChlKTtyZXR1cm4gTHQoe3NlbnRfYXQ6KG5ldyBEYXRlKS50b0lTT1N0cmluZygpLC4uLm8mJntzZGs6b30sLi4uISFyJiZuJiZ7ZHNuOkF0KG4pfX0sWyJhZ2dyZWdhdGVzImluIHQ/W3t0eXBlOiJzZXNzaW9ucyJ9LHRdOlt7dHlwZToic2Vzc2lvbiJ9LHQudG9KU09OKCldXSl9KHluLGduLmRzbixnbi5zZGtNZXRhZGF0YSxnbi50dW5uZWwpO3ZuKEpTT04uc3RyaW5naWZ5KHQpKSxhd2FpdCBFbi5zZW5kKHQpO3RyeXtlPy5wb3N0TWVzc2FnZSgic2Vzc2lvbi1lbmRlZCIpfWNhdGNoKHQpe319fWZ1bmN0aW9uIE5uKHQpe2lmKCF0KXJldHVybjtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0Lmxlbmd0aClyZXR1cm5bXTtjb25zdCBuPUFycmF5LmZyb20odCk7cmV0dXJuL3NlbnRyeVdyYXBwZWQvLnRlc3QoeChuKS5mdW5jdGlvbnx8IiIpJiZuLnBvcCgpLG4ucmV2ZXJzZSgpLEUudGVzdCh4KG4pLmZ1bmN0aW9ufHwiIikmJihuLnBvcCgpLEUudGVzdCh4KG4pLmZ1bmN0aW9ufHwiIikmJm4ucG9wKCkpLG4uc2xpY2UoMCxTKS5tYXAoKHQ9Pih7Li4udCxmaWxlbmFtZTp0LmZpbGVuYW1lfHx4KG4pLmZpbGVuYW1lLGZ1bmN0aW9uOnQuZnVuY3Rpb258fCR9KSkpfSh0KTtpZihnbi5hcHBSb290UGF0aClmb3IoY29uc3QgdCBvZiBuKXQuZmlsZW5hbWUmJih0LmZpbGVuYW1lPU10KHQuZmlsZW5hbWUsZ24uYXBwUm9vdFBhdGgpKTtyZXR1cm4gbn1hc3luYyBmdW5jdGlvbiBUbih0LG4pe2lmKGJuPj1nbi5tYXhBbnJFdmVudHMpcmV0dXJuO2JuKz0xLGF3YWl0IHhuKCksdm4oIlNlbmRpbmcgZXZlbnQiKTtjb25zdCBlPXtldmVudF9pZDp6KCksY29udGV4dHM6Z24uY29udGV4dHMscmVsZWFzZTpnbi5yZWxlYXNlLGVudmlyb25tZW50OmduLmVudmlyb25tZW50LGRpc3Q6Z24uZGlzdCxwbGF0Zm9ybToibm9kZSIsbGV2ZWw6ImVycm9yIixleGNlcHRpb246e3ZhbHVlczpbe3R5cGU6IkFwcGxpY2F0aW9uTm90UmVzcG9uZGluZyIsdmFsdWU6YEFwcGxpY2F0aW9uIE5vdCBSZXNwb25kaW5nIGZvciBhdCBsZWFzdCAke2duLmFuclRocmVzaG9sZH0gbXNgLHN0YWNrdHJhY2U6e2ZyYW1lczpObih0KX0sbWVjaGFuaXNtOnt0eXBlOiJBTlIifX1dfSx0YWdzOmduLnN0YXRpY1RhZ3N9O24mJmZ1bmN0aW9uKHQsbil7aWYoS3QodCxuKSwhdC5jb250ZXh0cz8udHJhY2Upe2NvbnN0e3RyYWNlSWQ6ZSxwYXJlbnRTcGFuSWQ6cixwcm9wYWdhdGlvblNwYW5JZDpvfT1uLnByb3BhZ2F0aW9uQ29udGV4dDt0LmNvbnRleHRzPXt0cmFjZTp7dHJhY2VfaWQ6ZSxzcGFuX2lkOm98fHEoKSxwYXJlbnRfc3Bhbl9pZDpyfSwuLi50LmNvbnRleHRzfX19KGUsbiksZnVuY3Rpb24odCl7aWYoMD09PU9iamVjdC5rZXlzKF9uKS5sZW5ndGgpcmV0dXJuO2NvbnN0IG49Z24uYXBwUm9vdFBhdGg/e306X247aWYoZ24uYXBwUm9vdFBhdGgpZm9yKGNvbnN0W3QsZV1vZiBPYmplY3QuZW50cmllcyhfbikpbltNdCh0LGduLmFwcFJvb3RQYXRoKV09ZTtjb25zdCBlPW5ldyBNYXA7Zm9yKGNvbnN0IHIgb2YgdC5leGNlcHRpb24/LnZhbHVlc3x8W10pZm9yKGNvbnN0IHQgb2Ygci5zdGFja3RyYWNlPy5mcmFtZXN8fFtdKXtjb25zdCByPXQuYWJzX3BhdGh8fHQuZmlsZW5hbWU7ciYmbltyXSYmZS5zZXQocixuW3JdKX1pZihlLnNpemU+MCl7Y29uc3Qgbj1bXTtmb3IoY29uc3RbdCxyXW9mIGUuZW50cmllcygpKW4ucHVzaCh7dHlwZToic291cmNlbWFwIixjb2RlX2ZpbGU6dCxkZWJ1Z19pZDpyfSk7dC5kZWJ1Z19tZXRhPXtpbWFnZXM6bn19fShlKTtjb25zdCByPUh0KGUsZ24uZHNuLGduLnNka01ldGFkYXRhLGduLnR1bm5lbCk7dm4oSlNPTi5zdHJpbmdpZnkocikpLGF3YWl0IEVuLnNlbmQociksYXdhaXQgRW4uZmx1c2goMmUzKSxibj49Z24ubWF4QW5yRXZlbnRzJiZzZXRUaW1lb3V0KCgoKT0+e3Byb2Nlc3MuZXhpdCgwKX0pLDVlMyl9bGV0IENuO2lmKHZuKCJTdGFydGVkIiksZ24uY2FwdHVyZVN0YWNrVHJhY2Upe3ZuKCJDb25uZWN0aW5nIHRvIGRlYnVnZ2VyIik7Y29uc3Qgbj1uZXcgdDtuLmNvbm5lY3RUb01haW5UaHJlYWQoKSx2bigiQ29ubmVjdGVkIHRvIGRlYnVnZ2VyIik7Y29uc3QgZT1uZXcgTWFwO24ub24oIkRlYnVnZ2VyLnNjcmlwdFBhcnNlZCIsKHQ9PntlLnNldCh0LnBhcmFtcy5zY3JpcHRJZCx0LnBhcmFtcy51cmwpfSkpLG4ub24oIkRlYnVnZ2VyLnBhdXNlZCIsKHQ9PntpZigib3RoZXIiPT09dC5wYXJhbXMucmVhc29uKXRyeXt2bigiRGVidWdnZXIgcGF1c2VkIik7Y29uc3Qgcz1bLi4udC5wYXJhbXMuY2FsbEZyYW1lc10saT1nbi5hcHBSb290UGF0aD9mdW5jdGlvbih0PShwcm9jZXNzLmFyZ3ZbMV0/cm4ocHJvY2Vzcy5hcmd2WzFdKTpwcm9jZXNzLmN3ZCgpKSxuPSJcXCI9PT1vKXtjb25zdCBlPW4/bW4odCk6dDtyZXR1cm4gdD0+e2lmKCF0KXJldHVybjtjb25zdCBvPW4/bW4odCk6dDtsZXR7ZGlyOnMsYmFzZTppLGV4dDpjfT1yLnBhcnNlKG8pOyIuanMiIT09YyYmIi5tanMiIT09YyYmIi5janMiIT09Y3x8KGk9aS5zbGljZSgwLC0xKmMubGVuZ3RoKSk7Y29uc3QgdT1kZWNvZGVVUklDb21wb25lbnQoaSk7c3x8KHM9Ii4iKTtjb25zdCBhPXMubGFzdEluZGV4T2YoIi9ub2RlX21vZHVsZXMiKTtpZihhPi0xKXJldHVybmAke3Muc2xpY2UoYSsxNCkucmVwbGFjZSgvXC8vZywiLiIpfToke3V9YDtpZihzLnN0YXJ0c1dpdGgoZSkpe2NvbnN0IHQ9cy5zbGljZShlLmxlbmd0aCsxKS5yZXBsYWNlKC9cLy9nLCIuIik7cmV0dXJuIHQ/YCR7dH06JHt1fWA6dX1yZXR1cm4gdX19KGduLmFwcFJvb3RQYXRoKTooKT0+e30sYz1zLm1hcCgodD0+ZnVuY3Rpb24odCxuLGUpe2NvbnN0IHI9bj9uLnJlcGxhY2UoL15maWxlOlwvXC8vLCIiKTp2b2lkIDAsbz10LmxvY2F0aW9uLmNvbHVtbk51bWJlcj90LmxvY2F0aW9uLmNvbHVtbk51bWJlcisxOnZvaWQgMCxzPXQubG9jYXRpb24ubGluZU51bWJlcj90LmxvY2F0aW9uLmxpbmVOdW1iZXIrMTp2b2lkIDA7cmV0dXJuIEwoe2ZpbGVuYW1lOnIsbW9kdWxlOmUociksZnVuY3Rpb246dC5mdW5jdGlvbk5hbWV8fCQsY29sbm86byxsaW5lbm86cyxpbl9hcHA6cj9vbihyKTp2b2lkIDB9KX0odCxlLmdldCh0LmxvY2F0aW9uLnNjcmlwdElkKSxpKSkpLHU9c2V0VGltZW91dCgoKCk9PntUbihjKS50aGVuKG51bGwsKCgpPT57dm4oIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pKX0pLDVlMyk7bi5wb3N0KCJSdW50aW1lLmV2YWx1YXRlIix7ZXhwcmVzc2lvbjoiZ2xvYmFsLl9fU0VOVFJZX0dFVF9TQ09QRVNfXygpOyIsc2lsZW50OiEwLHJldHVybkJ5VmFsdWU6ITB9LCgodCxlKT0+e3QmJnZuKGBFcnJvciBleGVjdXRpbmcgc2NyaXB0OiAnJHt0Lm1lc3NhZ2V9J2ApLGNsZWFyVGltZW91dCh1KTtjb25zdCByPWU/LnJlc3VsdD9lLnJlc3VsdC52YWx1ZTp2b2lkIDA7bi5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxuLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSxUbihjLHIpLnRoZW4obnVsbCwoKCk9Pnt2bigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSkpfSkpfWNhdGNoKHQpe3Rocm93IG4ucG9zdCgiRGVidWdnZXIucmVzdW1lIiksbi5wb3N0KCJEZWJ1Z2dlci5kaXNhYmxlIiksdH19KSksQ249KCk9Pnt0cnl7bi5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiLCgoKT0+e24ucG9zdCgiRGVidWdnZXIucGF1c2UiKX0pKX1jYXRjaCh0KXt9fX1jb25zdHtwb2xsOmpufT1mdW5jdGlvbih0LG4sZSxyKXtjb25zdCBvPXQoKTtsZXQgcz0hMSxpPSEwO3JldHVybiBzZXRJbnRlcnZhbCgoKCk9Pntjb25zdCB0PW8uZ2V0VGltZU1zKCk7ITE9PT1zJiZ0Pm4rZSYmKHM9ITAsaSYmcigpKSx0PG4rZSYmKHM9ITEpfSksMjApLHtwb2xsOigpPT57by5yZXNldCgpfSxlbmFibGVkOnQ9PntpPXR9fX0oKGZ1bmN0aW9uKCl7bGV0IHQ9cHJvY2Vzcy5ocnRpbWUoKTtyZXR1cm57Z2V0VGltZU1zOigpPT57Y29uc3RbbixlXT1wcm9jZXNzLmhydGltZSh0KTtyZXR1cm4gTWF0aC5mbG9vcigxZTMqbitlLzFlNil9LHJlc2V0OigpPT57dD1wcm9jZXNzLmhydGltZSgpfX19KSxnbi5wb2xsSW50ZXJ2YWwsZ24uYW5yVGhyZXNob2xkLChmdW5jdGlvbigpe3ZuKCJXYXRjaGRvZyB0aW1lb3V0IiksQ24/KHZuKCJQYXVzaW5nIGRlYnVnZ2VyIHRvIGNhcHR1cmUgc3RhY2sgdHJhY2UiKSxDbigpKToodm4oIkNhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UiKSxUbigpLnRoZW4obnVsbCwoKCk9Pnt2bigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkIG9uIHdhdGNoZG9nIHRpbWVvdXQuIil9KSkpfSkpO2U/Lm9uKCJtZXNzYWdlIiwodD0+e3Quc2Vzc2lvbiYmKHluPUsodC5zZXNzaW9uKSksdC5kZWJ1Z0ltYWdlcyYmKF9uPXQuZGVidWdJbWFnZXMpLGpuKCl9KSk7';
12
+ const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZSA5LjkuMCAoNDNjNGM4MykgfCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0ICovCmltcG9ydHtTZXNzaW9uIGFzIHR9ZnJvbSJub2RlOmluc3BlY3RvciI7aW1wb3J0e3dvcmtlckRhdGEgYXMgbixwYXJlbnRQb3J0IGFzIGV9ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtpbXBvcnR7cG9zaXggYXMgcixzZXAgYXMgb31mcm9tIm5vZGU6cGF0aCI7aW1wb3J0KmFzIHMgZnJvbSJub2RlOmh0dHAiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwcyI7aW1wb3J0e1JlYWRhYmxlIGFzIGN9ZnJvbSJub2RlOnN0cmVhbSI7aW1wb3J0e2NyZWF0ZUd6aXAgYXMgdX1mcm9tIm5vZGU6emxpYiI7aW1wb3J0KmFzIGEgZnJvbSJub2RlOm5ldCI7aW1wb3J0KmFzIGYgZnJvbSJub2RlOnRscyI7Y29uc3QgaD0idW5kZWZpbmVkIj09dHlwZW9mIF9fU0VOVFJZX0RFQlVHX198fF9fU0VOVFJZX0RFQlVHX18scD1nbG9iYWxUaGlzLGw9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLGQ9IjkuOS4wIjtmdW5jdGlvbiBtKCl7cmV0dXJuIGcocCkscH1mdW5jdGlvbiBnKHQpe2NvbnN0IG49dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIG4udmVyc2lvbj1uLnZlcnNpb258fGQsbltkXT1uW2RdfHx7fX1mdW5jdGlvbiB5KHQsbixlPXApe2NvbnN0IHI9ZS5fX1NFTlRSWV9fPWUuX19TRU5UUllfX3x8e30sbz1yW2RdPXJbZF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1uKCkpfWNvbnN0IGI9WyJkZWJ1ZyIsImluZm8iLCJ3YXJuIiwiZXJyb3IiLCJsb2ciLCJhc3NlcnQiLCJ0cmFjZSJdLF89e307ZnVuY3Rpb24gdih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3Qgbj1wLmNvbnNvbGUsZT17fSxyPU9iamVjdC5rZXlzKF8pO3IuZm9yRWFjaCgodD0+e2NvbnN0IHI9X1t0XTtlW3RdPW5bdF0sblt0XT1yfSkpO3RyeXtyZXR1cm4gdCgpfWZpbmFsbHl7ci5mb3JFYWNoKCh0PT57blt0XT1lW3RdfSkpfX1jb25zdCB3PXkoImxvZ2dlciIsKGZ1bmN0aW9uKCl7bGV0IHQ9ITE7Y29uc3Qgbj17ZW5hYmxlOigpPT57dD0hMH0sZGlzYWJsZTooKT0+e3Q9ITF9LGlzRW5hYmxlZDooKT0+dH07cmV0dXJuIGw/Yi5mb3JFYWNoKChlPT57bltlXT0oLi4ubik9Pnt0JiZ2KCgoKT0+e3AuY29uc29sZVtlXShgU2VudHJ5IExvZ2dlciBbJHtlfV06YCwuLi5uKX0pKX19KSk6Yi5mb3JFYWNoKCh0PT57blt0XT0oKT0+e319KSksbn0pKSxTPTUwLCQ9Ij8iLEU9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIHgodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IE49Ijxhbm9ueW1vdXM+Ijtjb25zdCBUPTFlMztmdW5jdGlvbiBDKCl7cmV0dXJuIERhdGUubm93KCkvVH1jb25zdCBqPWZ1bmN0aW9uKCl7Y29uc3R7cGVyZm9ybWFuY2U6dH09cDtpZighdD8ubm93KXJldHVybiBDO2NvbnN0IG49RGF0ZS5ub3coKS10Lm5vdygpLGU9bnVsbD09dC50aW1lT3JpZ2luP246dC50aW1lT3JpZ2luO3JldHVybigpPT4oZSt0Lm5vdygpKS9UfSgpLFI9T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZztmdW5jdGlvbiBrKHQsbil7cmV0dXJuIFIuY2FsbCh0KT09PWBbb2JqZWN0ICR7bn1dYH1mdW5jdGlvbiBPKHQpe3JldHVybiBrKHQsIlN0cmluZyIpfWZ1bmN0aW9uIEQodCl7cmV0dXJuIGsodCwiT2JqZWN0Iil9ZnVuY3Rpb24gSSh0KXtyZXR1cm4gQm9vbGVhbih0Py50aGVuJiYiZnVuY3Rpb24iPT10eXBlb2YgdC50aGVuKX1mdW5jdGlvbiBBKHQsbil7dHJ5e3JldHVybiB0IGluc3RhbmNlb2Ygbn1jYXRjaCh0KXtyZXR1cm4hMX19Y29uc3QgUD1wLFU9ODA7ZnVuY3Rpb24gTSh0LG4pe2NvbnN0IGU9dCxyPVtdO2lmKCFlPy50YWdOYW1lKXJldHVybiIiO2lmKFAuSFRNTEVsZW1lbnQmJmUgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCYmZS5kYXRhc2V0KXtpZihlLmRhdGFzZXQuc2VudHJ5Q29tcG9uZW50KXJldHVybiBlLmRhdGFzZXQuc2VudHJ5Q29tcG9uZW50O2lmKGUuZGF0YXNldC5zZW50cnlFbGVtZW50KXJldHVybiBlLmRhdGFzZXQuc2VudHJ5RWxlbWVudH1yLnB1c2goZS50YWdOYW1lLnRvTG93ZXJDYXNlKCkpO2NvbnN0IG89bj8ubGVuZ3RoP24uZmlsdGVyKCh0PT5lLmdldEF0dHJpYnV0ZSh0KSkpLm1hcCgodD0+W3QsZS5nZXRBdHRyaWJ1dGUodCldKSk6bnVsbDtpZihvPy5sZW5ndGgpby5mb3JFYWNoKCh0PT57ci5wdXNoKGBbJHt0WzBdfT0iJHt0WzFdfSJdYCl9KSk7ZWxzZXtlLmlkJiZyLnB1c2goYCMke2UuaWR9YCk7Y29uc3QgdD1lLmNsYXNzTmFtZTtpZih0JiZPKHQpKXtjb25zdCBuPXQuc3BsaXQoL1xzKy8pO2Zvcihjb25zdCB0IG9mIG4pci5wdXNoKGAuJHt0fWApfX1jb25zdCBzPVsiYXJpYS1sYWJlbCIsInR5cGUiLCJuYW1lIiwidGl0bGUiLCJhbHQiXTtmb3IoY29uc3QgdCBvZiBzKXtjb25zdCBuPWUuZ2V0QXR0cmlidXRlKHQpO24mJnIucHVzaChgWyR7dH09IiR7bn0iXWApfXJldHVybiByLmpvaW4oIiIpfWZ1bmN0aW9uIEwodCl7aWYoZnVuY3Rpb24odCl7c3dpdGNoKFIuY2FsbCh0KSl7Y2FzZSJbb2JqZWN0IEVycm9yXSI6Y2FzZSJbb2JqZWN0IEV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBET01FeGNlcHRpb25dIjpjYXNlIltvYmplY3QgV2ViQXNzZW1ibHkuRXhjZXB0aW9uXSI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4gQSh0LEVycm9yKX19KHQpKXJldHVybnttZXNzYWdlOnQubWVzc2FnZSxuYW1lOnQubmFtZSxzdGFjazp0LnN0YWNrLC4uLkcodCl9O2lmKG49dCwidW5kZWZpbmVkIiE9dHlwZW9mIEV2ZW50JiZBKG4sRXZlbnQpKXtjb25zdCBuPXt0eXBlOnQudHlwZSx0YXJnZXQ6Qih0LnRhcmdldCksY3VycmVudFRhcmdldDpCKHQuY3VycmVudFRhcmdldCksLi4uRyh0KX07cmV0dXJuInVuZGVmaW5lZCIhPXR5cGVvZiBDdXN0b21FdmVudCYmQSh0LEN1c3RvbUV2ZW50KSYmKG4uZGV0YWlsPXQuZGV0YWlsKSxufXJldHVybiB0O3ZhciBufWZ1bmN0aW9uIEIodCl7dHJ5e3JldHVybiBuPXQsInVuZGVmaW5lZCIhPXR5cGVvZiBFbGVtZW50JiZBKG4sRWxlbWVudCk/ZnVuY3Rpb24odCxuPXt9KXtpZighdClyZXR1cm4iPHVua25vd24+Ijt0cnl7bGV0IGU9dDtjb25zdCByPTUsbz1bXTtsZXQgcz0wLGk9MDtjb25zdCBjPSIgPiAiLHU9Yy5sZW5ndGg7bGV0IGE7Y29uc3QgZj1BcnJheS5pc0FycmF5KG4pP246bi5rZXlBdHRycyxoPSFBcnJheS5pc0FycmF5KG4pJiZuLm1heFN0cmluZ0xlbmd0aHx8VTtmb3IoO2UmJnMrKzxyJiYoYT1NKGUsZiksISgiaHRtbCI9PT1hfHxzPjEmJmkrby5sZW5ndGgqdSthLmxlbmd0aD49aCkpOylvLnB1c2goYSksaSs9YS5sZW5ndGgsZT1lLnBhcmVudE5vZGU7cmV0dXJuIG8ucmV2ZXJzZSgpLmpvaW4oYyl9Y2F0Y2godCl7cmV0dXJuIjx1bmtub3duPiJ9fSh0KTpPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodCl9Y2F0Y2godCl7cmV0dXJuIjx1bmtub3duPiJ9dmFyIG59ZnVuY3Rpb24gRyh0KXtpZigib2JqZWN0Ij09dHlwZW9mIHQmJm51bGwhPT10KXtjb25zdCBuPXt9O2Zvcihjb25zdCBlIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQsZSkmJihuW2VdPXRbZV0pO3JldHVybiBufXJldHVybnt9fWZ1bmN0aW9uIEoodCl7cmV0dXJuIFkodCxuZXcgTWFwKX1mdW5jdGlvbiBZKHQsbil7aWYobnVsbD09PXR8fCJvYmplY3QiIT10eXBlb2YgdClyZXR1cm4gdDtjb25zdCBlPW4uZ2V0KHQpO2lmKHZvaWQgMCE9PWUpcmV0dXJuIGU7aWYoQXJyYXkuaXNBcnJheSh0KSl7Y29uc3QgZT1bXTtyZXR1cm4gbi5zZXQodCxlKSx0LmZvckVhY2goKHQ9PntlLnB1c2goWSh0LG4pKX0pKSxlfWlmKGZ1bmN0aW9uKHQpe2NvbnN0IG49dC5jb25zdHJ1Y3RvcjtyZXR1cm4gbj09PU9iamVjdHx8dm9pZCAwPT09bn0odCkpe2NvbnN0IGU9e307bi5zZXQodCxlKTtyZXR1cm4gT2JqZWN0LmtleXModCkuZm9yRWFjaCgocj0+e2NvbnN0IG89dFtyXTt2b2lkIDAhPT1vJiYoZVtyXT1ZKG8sbikpfSkpLGV9cmV0dXJuIHR9ZnVuY3Rpb24geih0PWZ1bmN0aW9uKCl7Y29uc3QgdD1wO3JldHVybiB0LmNyeXB0b3x8dC5tc0NyeXB0b30oKSl7bGV0IG49KCk9PjE2Kk1hdGgucmFuZG9tKCk7dHJ5e2lmKHQ/LnJhbmRvbVVVSUQpcmV0dXJuIHQucmFuZG9tVVVJRCgpLnJlcGxhY2UoLy0vZywiIik7dD8uZ2V0UmFuZG9tVmFsdWVzJiYobj0oKT0+e2NvbnN0IG49bmV3IFVpbnQ4QXJyYXkoMSk7cmV0dXJuIHQuZ2V0UmFuZG9tVmFsdWVzKG4pLG5bMF19KX1jYXRjaCh0KXt9cmV0dXJuKFsxZTddKzFlMys0ZTMrOGUzKzFlMTEpLnJlcGxhY2UoL1swMThdL2csKHQ9Pih0XigxNSZuKCkpPj50LzQpLnRvU3RyaW5nKDE2KSkpfXZhciBIO2Z1bmN0aW9uIFcodCl7cmV0dXJuIG5ldyBGKChuPT57bih0KX0pKX0hZnVuY3Rpb24odCl7dFt0LlBFTkRJTkc9MF09IlBFTkRJTkciO3RbdC5SRVNPTFZFRD0xXT0iUkVTT0xWRUQiO3RbdC5SRUpFQ1RFRD0yXT0iUkVKRUNURUQifShIfHwoSD17fSkpO2NsYXNzIEZ7Y29uc3RydWN0b3IodCl7dGhpcy50PUguUEVORElORyx0aGlzLm89W10sdGhpcy5pKHQpfXRoZW4odCxuKXtyZXR1cm4gbmV3IEYoKChlLHIpPT57dGhpcy5vLnB1c2goWyExLG49PntpZih0KXRyeXtlKHQobikpfWNhdGNoKHQpe3IodCl9ZWxzZSBlKG4pfSx0PT57aWYobil0cnl7ZShuKHQpKX1jYXRjaCh0KXtyKHQpfWVsc2Ugcih0KX1dKSx0aGlzLnUoKX0pKX1jYXRjaCh0KXtyZXR1cm4gdGhpcy50aGVuKCh0PT50KSx0KX1maW5hbGx5KHQpe3JldHVybiBuZXcgRigoKG4sZSk9PntsZXQgcixvO3JldHVybiB0aGlzLnRoZW4oKG49PntvPSExLHI9bix0JiZ0KCl9KSwobj0+e289ITAscj1uLHQmJnQoKX0pKS50aGVuKCgoKT0+e28/ZShyKTpuKHIpfSkpfSkpfXUoKXtpZih0aGlzLnQ9PT1ILlBFTkRJTkcpcmV0dXJuO2NvbnN0IHQ9dGhpcy5vLnNsaWNlKCk7dGhpcy5vPVtdLHQuZm9yRWFjaCgodD0+e3RbMF18fCh0aGlzLnQ9PT1ILlJFU09MVkVEJiZ0WzFdKHRoaXMuaCksdGhpcy50PT09SC5SRUpFQ1RFRCYmdFsyXSh0aGlzLmgpLHRbMF09ITApfSkpfWkodCl7Y29uc3Qgbj0odCxuKT0+e3RoaXMudD09PUguUEVORElORyYmKEkobik/bi50aGVuKGUscik6KHRoaXMudD10LHRoaXMuaD1uLHRoaXMudSgpKSl9LGU9dD0+e24oSC5SRVNPTFZFRCx0KX0scj10PT57bihILlJFSkVDVEVELHQpfTt0cnl7dChlLHIpfWNhdGNoKHQpe3IodCl9fX1mdW5jdGlvbiBLKHQpe2NvbnN0IG49aigpLGU9e3NpZDp6KCksaW5pdDohMCx0aW1lc3RhbXA6bixzdGFydGVkOm4sZHVyYXRpb246MCxzdGF0dXM6Im9rIixlcnJvcnM6MCxpZ25vcmVEdXJhdGlvbjohMSx0b0pTT046KCk9PmZ1bmN0aW9uKHQpe3JldHVybntzaWQ6YCR7dC5zaWR9YCxpbml0OnQuaW5pdCxzdGFydGVkOm5ldyBEYXRlKDFlMyp0LnN0YXJ0ZWQpLnRvSVNPU3RyaW5nKCksdGltZXN0YW1wOm5ldyBEYXRlKDFlMyp0LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKSxzdGF0dXM6dC5zdGF0dXMsZXJyb3JzOnQuZXJyb3JzLGRpZDoibnVtYmVyIj09dHlwZW9mIHQuZGlkfHwic3RyaW5nIj09dHlwZW9mIHQuZGlkP2Ake3QuZGlkfWA6dm9pZCAwLGR1cmF0aW9uOnQuZHVyYXRpb24sYWJub3JtYWxfbWVjaGFuaXNtOnQuYWJub3JtYWxfbWVjaGFuaXNtLGF0dHJzOntyZWxlYXNlOnQucmVsZWFzZSxlbnZpcm9ubWVudDp0LmVudmlyb25tZW50LGlwX2FkZHJlc3M6dC5pcEFkZHJlc3MsdXNlcl9hZ2VudDp0LnVzZXJBZ2VudH19fShlKX07cmV0dXJuIHQmJlYoZSx0KSxlfWZ1bmN0aW9uIFYodCxuPXt9KXtpZihuLnVzZXImJighdC5pcEFkZHJlc3MmJm4udXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9bi51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxuLmRpZHx8KHQuZGlkPW4udXNlci5pZHx8bi51c2VyLmVtYWlsfHxuLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1uLnRpbWVzdGFtcHx8aigpLG4uYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209bi5hYm5vcm1hbF9tZWNoYW5pc20pLG4uaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPW4uaWdub3JlRHVyYXRpb24pLG4uc2lkJiYodC5zaWQ9MzI9PT1uLnNpZC5sZW5ndGg/bi5zaWQ6eigpKSx2b2lkIDAhPT1uLmluaXQmJih0LmluaXQ9bi5pbml0KSwhdC5kaWQmJm4uZGlkJiYodC5kaWQ9YCR7bi5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBuLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9bi5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBuLmR1cmF0aW9uKXQuZHVyYXRpb249bi5kdXJhdGlvbjtlbHNle2NvbnN0IG49dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249bj49MD9uOjB9bi5yZWxlYXNlJiYodC5yZWxlYXNlPW4ucmVsZWFzZSksbi5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9bi5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZuLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPW4uaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJm4udXNlckFnZW50JiYodC51c2VyQWdlbnQ9bi51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2Ygbi5lcnJvcnMmJih0LmVycm9ycz1uLmVycm9ycyksbi5zdGF0dXMmJih0LnN0YXR1cz1uLnN0YXR1cyl9ZnVuY3Rpb24gWigpe3JldHVybiB6KCl9ZnVuY3Rpb24gcSgpe3JldHVybiB6KCkuc3Vic3RyaW5nKDE2KX1mdW5jdGlvbiBRKHQsbixlPTIpe2lmKCFufHwib2JqZWN0IiE9dHlwZW9mIG58fGU8PTApcmV0dXJuIG47aWYodCYmMD09PU9iamVjdC5rZXlzKG4pLmxlbmd0aClyZXR1cm4gdDtjb25zdCByPXsuLi50fTtmb3IoY29uc3QgdCBpbiBuKU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChuLHQpJiYoclt0XT1RKHJbdF0sblt0XSxlLTEpKTtyZXR1cm4gcn1jb25zdCBYPSJfc2VudHJ5U3BhbiI7ZnVuY3Rpb24gdHQodCxuKXtuP2Z1bmN0aW9uKHQsbixlKXt0cnl7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsbix7dmFsdWU6ZSx3cml0YWJsZTohMCxjb25maWd1cmFibGU6ITB9KX1jYXRjaChlKXtsJiZ3LmxvZyhgRmFpbGVkIHRvIGFkZCBub24tZW51bWVyYWJsZSBwcm9wZXJ0eSAiJHtufSIgdG8gb2JqZWN0YCx0KX19KHQsWCxuKTpkZWxldGUgdFtYXX1mdW5jdGlvbiBudCh0KXtyZXR1cm4gdFtYXX1jbGFzcyBldHtjb25zdHJ1Y3Rvcigpe3RoaXMucD0hMSx0aGlzLmw9W10sdGhpcy5tPVtdLHRoaXMuXz1bXSx0aGlzLnY9W10sdGhpcy5TPXt9LHRoaXMuTj17fSx0aGlzLlQ9e30sdGhpcy5DPXt9LHRoaXMuaj17fSx0aGlzLlI9e3RyYWNlSWQ6WigpLHNhbXBsZVJhbmQ6TWF0aC5yYW5kb20oKX19Y2xvbmUoKXtjb25zdCB0PW5ldyBldDtyZXR1cm4gdC5fPVsuLi50aGlzLl9dLHQuTj17Li4udGhpcy5OfSx0LlQ9ey4uLnRoaXMuVH0sdC5DPXsuLi50aGlzLkN9LHRoaXMuQy5mbGFncyYmKHQuQy5mbGFncz17dmFsdWVzOlsuLi50aGlzLkMuZmxhZ3MudmFsdWVzXX0pLHQuUz10aGlzLlMsdC5rPXRoaXMuayx0Lk89dGhpcy5PLHQuRD10aGlzLkQsdC5JPXRoaXMuSSx0Lm09Wy4uLnRoaXMubV0sdC52PVsuLi50aGlzLnZdLHQuaj17Li4udGhpcy5qfSx0LlI9ey4uLnRoaXMuUn0sdC5BPXRoaXMuQSx0LlA9dGhpcy5QLHR0KHQsbnQodGhpcykpLHR9c2V0Q2xpZW50KHQpe3RoaXMuQT10fXNldExhc3RFdmVudElkKHQpe3RoaXMuUD10fWdldENsaWVudCgpe3JldHVybiB0aGlzLkF9bGFzdEV2ZW50SWQoKXtyZXR1cm4gdGhpcy5QfWFkZFNjb3BlTGlzdGVuZXIodCl7dGhpcy5sLnB1c2godCl9YWRkRXZlbnRQcm9jZXNzb3IodCl7cmV0dXJuIHRoaXMubS5wdXNoKHQpLHRoaXN9c2V0VXNlcih0KXtyZXR1cm4gdGhpcy5TPXR8fHtlbWFpbDp2b2lkIDAsaWQ6dm9pZCAwLGlwX2FkZHJlc3M6dm9pZCAwLHVzZXJuYW1lOnZvaWQgMH0sdGhpcy5PJiZWKHRoaXMuTyx7dXNlcjp0fSksdGhpcy5VKCksdGhpc31nZXRVc2VyKCl7cmV0dXJuIHRoaXMuU31zZXRUYWdzKHQpe3JldHVybiB0aGlzLk49ey4uLnRoaXMuTiwuLi50fSx0aGlzLlUoKSx0aGlzfXNldFRhZyh0LG4pe3JldHVybiB0aGlzLk49ey4uLnRoaXMuTixbdF06bn0sdGhpcy5VKCksdGhpc31zZXRFeHRyYXModCl7cmV0dXJuIHRoaXMuVD17Li4udGhpcy5ULC4uLnR9LHRoaXMuVSgpLHRoaXN9c2V0RXh0cmEodCxuKXtyZXR1cm4gdGhpcy5UPXsuLi50aGlzLlQsW3RdOm59LHRoaXMuVSgpLHRoaXN9c2V0RmluZ2VycHJpbnQodCl7cmV0dXJuIHRoaXMuST10LHRoaXMuVSgpLHRoaXN9c2V0TGV2ZWwodCl7cmV0dXJuIHRoaXMuaz10LHRoaXMuVSgpLHRoaXN9c2V0VHJhbnNhY3Rpb25OYW1lKHQpe3JldHVybiB0aGlzLkQ9dCx0aGlzLlUoKSx0aGlzfXNldENvbnRleHQodCxuKXtyZXR1cm4gbnVsbD09PW4/ZGVsZXRlIHRoaXMuQ1t0XTp0aGlzLkNbdF09bix0aGlzLlUoKSx0aGlzfXNldFNlc3Npb24odCl7cmV0dXJuIHQ/dGhpcy5PPXQ6ZGVsZXRlIHRoaXMuTyx0aGlzLlUoKSx0aGlzfWdldFNlc3Npb24oKXtyZXR1cm4gdGhpcy5PfXVwZGF0ZSh0KXtpZighdClyZXR1cm4gdGhpcztjb25zdCBuPSJmdW5jdGlvbiI9PXR5cGVvZiB0P3QodGhpcyk6dCxlPW4gaW5zdGFuY2VvZiBldD9uLmdldFNjb3BlRGF0YSgpOkQobik/dDp2b2lkIDAse3RhZ3M6cixleHRyYTpvLHVzZXI6cyxjb250ZXh0czppLGxldmVsOmMsZmluZ2VycHJpbnQ6dT1bXSxwcm9wYWdhdGlvbkNvbnRleHQ6YX09ZXx8e307cmV0dXJuIHRoaXMuTj17Li4udGhpcy5OLC4uLnJ9LHRoaXMuVD17Li4udGhpcy5ULC4uLm99LHRoaXMuQz17Li4udGhpcy5DLC4uLml9LHMmJk9iamVjdC5rZXlzKHMpLmxlbmd0aCYmKHRoaXMuUz1zKSxjJiYodGhpcy5rPWMpLHUubGVuZ3RoJiYodGhpcy5JPXUpLGEmJih0aGlzLlI9YSksdGhpc31jbGVhcigpe3JldHVybiB0aGlzLl89W10sdGhpcy5OPXt9LHRoaXMuVD17fSx0aGlzLlM9e30sdGhpcy5DPXt9LHRoaXMuaz12b2lkIDAsdGhpcy5EPXZvaWQgMCx0aGlzLkk9dm9pZCAwLHRoaXMuTz12b2lkIDAsdHQodGhpcyx2b2lkIDApLHRoaXMudj1bXSx0aGlzLnNldFByb3BhZ2F0aW9uQ29udGV4dCh7dHJhY2VJZDpaKCksc2FtcGxlUmFuZDpNYXRoLnJhbmRvbSgpfSksdGhpcy5VKCksdGhpc31hZGRCcmVhZGNydW1iKHQsbil7Y29uc3QgZT0ibnVtYmVyIj09dHlwZW9mIG4/bjoxMDA7aWYoZTw9MClyZXR1cm4gdGhpcztjb25zdCByPXt0aW1lc3RhbXA6QygpLC4uLnR9O3JldHVybiB0aGlzLl8ucHVzaChyKSx0aGlzLl8ubGVuZ3RoPmUmJih0aGlzLl89dGhpcy5fLnNsaWNlKC1lKSx0aGlzLkE/LnJlY29yZERyb3BwZWRFdmVudCgiYnVmZmVyX292ZXJmbG93IiwibG9nX2l0ZW0iKSksdGhpcy5VKCksdGhpc31nZXRMYXN0QnJlYWRjcnVtYigpe3JldHVybiB0aGlzLl9bdGhpcy5fLmxlbmd0aC0xXX1jbGVhckJyZWFkY3J1bWJzKCl7cmV0dXJuIHRoaXMuXz1bXSx0aGlzLlUoKSx0aGlzfWFkZEF0dGFjaG1lbnQodCl7cmV0dXJuIHRoaXMudi5wdXNoKHQpLHRoaXN9Y2xlYXJBdHRhY2htZW50cygpe3JldHVybiB0aGlzLnY9W10sdGhpc31nZXRTY29wZURhdGEoKXtyZXR1cm57YnJlYWRjcnVtYnM6dGhpcy5fLGF0dGFjaG1lbnRzOnRoaXMudixjb250ZXh0czp0aGlzLkMsdGFnczp0aGlzLk4sZXh0cmE6dGhpcy5ULHVzZXI6dGhpcy5TLGxldmVsOnRoaXMuayxmaW5nZXJwcmludDp0aGlzLkl8fFtdLGV2ZW50UHJvY2Vzc29yczp0aGlzLm0scHJvcGFnYXRpb25Db250ZXh0OnRoaXMuUixzZGtQcm9jZXNzaW5nTWV0YWRhdGE6dGhpcy5qLHRyYW5zYWN0aW9uTmFtZTp0aGlzLkQsc3BhbjpudCh0aGlzKX19c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLmo9USh0aGlzLmosdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5SPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5SfWNhcHR1cmVFeGNlcHRpb24odCxuKXtjb25zdCBlPW4/LmV2ZW50X2lkfHx6KCk7aWYoIXRoaXMuQSlyZXR1cm4gdy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLGU7Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5BLmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5uLGV2ZW50X2lkOmV9LHRoaXMpLGV9Y2FwdHVyZU1lc3NhZ2UodCxuLGUpe2NvbnN0IHI9ZT8uZXZlbnRfaWR8fHooKTtpZighdGhpcy5BKXJldHVybiB3Lndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1uZXcgRXJyb3IodCk7cmV0dXJuIHRoaXMuQS5jYXB0dXJlTWVzc2FnZSh0LG4se29yaWdpbmFsRXhjZXB0aW9uOnQsc3ludGhldGljRXhjZXB0aW9uOm8sLi4uZSxldmVudF9pZDpyfSx0aGlzKSxyfWNhcHR1cmVFdmVudCh0LG4pe2NvbnN0IGU9bj8uZXZlbnRfaWR8fHooKTtyZXR1cm4gdGhpcy5BPyh0aGlzLkEuY2FwdHVyZUV2ZW50KHQsey4uLm4sZXZlbnRfaWQ6ZX0sdGhpcyksZSk6KHcud2FybigiTm8gY2xpZW50IGNvbmZpZ3VyZWQgb24gc2NvcGUgLSB3aWxsIG5vdCBjYXB0dXJlIGV2ZW50ISIpLGUpfVUoKXt0aGlzLnB8fCh0aGlzLnA9ITAsdGhpcy5sLmZvckVhY2goKHQ9Pnt0KHRoaXMpfSkpLHRoaXMucD0hMSl9fWNsYXNzIHJ0e2NvbnN0cnVjdG9yKHQsbil7bGV0IGUscjtlPXR8fG5ldyBldCxyPW58fG5ldyBldCx0aGlzLk09W3tzY29wZTplfV0sdGhpcy5MPXJ9d2l0aFNjb3BlKHQpe2NvbnN0IG49dGhpcy5CKCk7bGV0IGU7dHJ5e2U9dChuKX1jYXRjaCh0KXt0aHJvdyB0aGlzLkcoKSx0fXJldHVybiBJKGUpP2UudGhlbigodD0+KHRoaXMuRygpLHQpKSwodD0+e3Rocm93IHRoaXMuRygpLHR9KSk6KHRoaXMuRygpLGUpfWdldENsaWVudCgpe3JldHVybiB0aGlzLmdldFN0YWNrVG9wKCkuY2xpZW50fWdldFNjb3BlKCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5zY29wZX1nZXRJc29sYXRpb25TY29wZSgpe3JldHVybiB0aGlzLkx9Z2V0U3RhY2tUb3AoKXtyZXR1cm4gdGhpcy5NW3RoaXMuTS5sZW5ndGgtMV19Qigpe2NvbnN0IHQ9dGhpcy5nZXRTY29wZSgpLmNsb25lKCk7cmV0dXJuIHRoaXMuTS5wdXNoKHtjbGllbnQ6dGhpcy5nZXRDbGllbnQoKSxzY29wZTp0fSksdH1HKCl7cmV0dXJuISh0aGlzLk0ubGVuZ3RoPD0xKSYmISF0aGlzLk0ucG9wKCl9fWZ1bmN0aW9uIG90KCl7Y29uc3QgdD1nKG0oKSk7cmV0dXJuIHQuc3RhY2s9dC5zdGFja3x8bmV3IHJ0KHkoImRlZmF1bHRDdXJyZW50U2NvcGUiLCgoKT0+bmV3IGV0KSkseSgiZGVmYXVsdElzb2xhdGlvblNjb3BlIiwoKCk9Pm5ldyBldCkpKX1mdW5jdGlvbiBzdCh0KXtyZXR1cm4gb3QoKS53aXRoU2NvcGUodCl9ZnVuY3Rpb24gaXQodCxuKXtjb25zdCBlPW90KCk7cmV0dXJuIGUud2l0aFNjb3BlKCgoKT0+KGUuZ2V0U3RhY2tUb3AoKS5zY29wZT10LG4odCkpKSl9ZnVuY3Rpb24gY3QodCl7cmV0dXJuIG90KCkud2l0aFNjb3BlKCgoKT0+dChvdCgpLmdldElzb2xhdGlvblNjb3BlKCkpKSl9ZnVuY3Rpb24gdXQodCl7Y29uc3Qgbj1nKHQpO3JldHVybiBuLmFjcz9uLmFjczp7d2l0aElzb2xhdGlvblNjb3BlOmN0LHdpdGhTY29wZTpzdCx3aXRoU2V0U2NvcGU6aXQsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LG4pPT5jdChuKSxnZXRDdXJyZW50U2NvcGU6KCk9Pm90KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+b3QoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiBhdCgpe3JldHVybiB1dChtKCkpLmdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpfWNvbnN0IGZ0PSJzZW50cnkuc291cmNlIixodD0ic2VudHJ5LnNhbXBsZV9yYXRlIixwdD0ic2VudHJ5Lm9wIixsdD0ic2VudHJ5Lm9yaWdpbiIsZHQ9MCxtdD0xLGd0PSJfc2VudHJ5U2NvcGUiLHl0PSJfc2VudHJ5SXNvbGF0aW9uU2NvcGUiO2Z1bmN0aW9uIGJ0KHQpe3JldHVybntzY29wZTp0W2d0XSxpc29sYXRpb25TY29wZTp0W3l0XX19Y29uc3QgX3Q9InNlbnRyeS0iLHZ0PS9ec2VudHJ5LS87ZnVuY3Rpb24gd3QodCl7Y29uc3Qgbj1mdW5jdGlvbih0KXtpZighdHx8IU8odCkmJiFBcnJheS5pc0FycmF5KHQpKXJldHVybjtpZihBcnJheS5pc0FycmF5KHQpKXJldHVybiB0LnJlZHVjZSgoKHQsbik9Pntjb25zdCBlPVN0KG4pO3JldHVybiBPYmplY3QuZW50cmllcyhlKS5mb3JFYWNoKCgoW24sZV0pPT57dFtuXT1lfSkpLHR9KSx7fSk7cmV0dXJuIFN0KHQpfSh0KTtpZighbilyZXR1cm47Y29uc3QgZT1PYmplY3QuZW50cmllcyhuKS5yZWR1Y2UoKCh0LFtuLGVdKT0+e2lmKG4ubWF0Y2godnQpKXt0W24uc2xpY2UoX3QubGVuZ3RoKV09ZX1yZXR1cm4gdH0pLHt9KTtyZXR1cm4gT2JqZWN0LmtleXMoZSkubGVuZ3RoPjA/ZTp2b2lkIDB9ZnVuY3Rpb24gU3QodCl7cmV0dXJuIHQuc3BsaXQoIiwiKS5tYXAoKHQ9PnQuc3BsaXQoIj0iKS5tYXAoKHQ9PmRlY29kZVVSSUNvbXBvbmVudCh0LnRyaW0oKSkpKSkpLnJlZHVjZSgoKHQsW24sZV0pPT4obiYmZSYmKHRbbl09ZSksdCkpLHt9KX1jb25zdCAkdD0xO2Z1bmN0aW9uIEV0KHQpe2NvbnN0e3NwYW5JZDpuLHRyYWNlSWQ6ZSxpc1JlbW90ZTpyfT10LnNwYW5Db250ZXh0KCksbz1yP246Q3QodCkucGFyZW50X3NwYW5faWQscz1idCh0KS5zY29wZTtyZXR1cm4gSih7cGFyZW50X3NwYW5faWQ6byxzcGFuX2lkOnI/cz8uZ2V0UHJvcGFnYXRpb25Db250ZXh0KCkucHJvcGFnYXRpb25TcGFuSWR8fHEoKTpuLHRyYWNlX2lkOmV9KX1mdW5jdGlvbiB4dCh0KXtyZXR1cm4gdCYmdC5sZW5ndGg+MD90Lm1hcCgoKHtjb250ZXh0OntzcGFuSWQ6dCx0cmFjZUlkOm4sdHJhY2VGbGFnczplLC4uLnJ9LGF0dHJpYnV0ZXM6b30pPT4oe3NwYW5faWQ6dCx0cmFjZV9pZDpuLHNhbXBsZWQ6ZT09PSR0LGF0dHJpYnV0ZXM6bywuLi5yfSkpKTp2b2lkIDB9ZnVuY3Rpb24gTnQodCl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiB0P1R0KHQpOkFycmF5LmlzQXJyYXkodCk/dFswXSt0WzFdLzFlOTp0IGluc3RhbmNlb2YgRGF0ZT9UdCh0LmdldFRpbWUoKSk6aigpfWZ1bmN0aW9uIFR0KHQpe3JldHVybiB0Pjk5OTk5OTk5OTk/dC8xZTM6dH1mdW5jdGlvbiBDdCh0KXtpZihmdW5jdGlvbih0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgdC5nZXRTcGFuSlNPTn0odCkpcmV0dXJuIHQuZ2V0U3BhbkpTT04oKTtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmV9PXQuc3BhbkNvbnRleHQoKTtpZihmdW5jdGlvbih0KXtjb25zdCBuPXQ7cmV0dXJuISEobi5hdHRyaWJ1dGVzJiZuLnN0YXJ0VGltZSYmbi5uYW1lJiZuLmVuZFRpbWUmJm4uc3RhdHVzKX0odCkpe2NvbnN0e2F0dHJpYnV0ZXM6cixzdGFydFRpbWU6byxuYW1lOnMsZW5kVGltZTppLHBhcmVudFNwYW5JZDpjLHN0YXR1czp1LGxpbmtzOmF9PXQ7cmV0dXJuIEooe3NwYW5faWQ6bix0cmFjZV9pZDplLGRhdGE6cixkZXNjcmlwdGlvbjpzLHBhcmVudF9zcGFuX2lkOmMsc3RhcnRfdGltZXN0YW1wOk50KG8pLHRpbWVzdGFtcDpOdChpKXx8dm9pZCAwLHN0YXR1czpqdCh1KSxvcDpyW3B0XSxvcmlnaW46cltsdF0sbGlua3M6eHQoYSl9KX1yZXR1cm57c3Bhbl9pZDpuLHRyYWNlX2lkOmUsc3RhcnRfdGltZXN0YW1wOjAsZGF0YTp7fX19ZnVuY3Rpb24ganQodCl7aWYodCYmdC5jb2RlIT09ZHQpcmV0dXJuIHQuY29kZT09PW10PyJvayI6dC5tZXNzYWdlfHwidW5rbm93bl9lcnJvciJ9Y29uc3QgUnQ9Il9zZW50cnlSb290U3BhbiI7ZnVuY3Rpb24ga3QodCl7cmV0dXJuIHRbUnRdfHx0fWNvbnN0IE90PSJwcm9kdWN0aW9uIixEdD0iX2Zyb3plbkRzYyI7ZnVuY3Rpb24gSXQodCl7Y29uc3Qgbj1hdCgpO2lmKCFuKXJldHVybnt9O2NvbnN0IGU9a3QodCkscj1DdChlKSxvPXIuZGF0YSxzPWUuc3BhbkNvbnRleHQoKS50cmFjZVN0YXRlLGk9cz8uZ2V0KCJzZW50cnkuc2FtcGxlX3JhdGUiKT8/b1todF07ZnVuY3Rpb24gYyh0KXtyZXR1cm4ibnVtYmVyIiE9dHlwZW9mIGkmJiJzdHJpbmciIT10eXBlb2YgaXx8KHQuc2FtcGxlX3JhdGU9YCR7aX1gKSx0fWNvbnN0IHU9ZVtEdF07aWYodSlyZXR1cm4gYyh1KTtjb25zdCBhPXM/LmdldCgic2VudHJ5LmRzYyIpLGY9YSYmd3QoYSk7aWYoZilyZXR1cm4gYyhmKTtjb25zdCBoPWZ1bmN0aW9uKHQsbil7Y29uc3QgZT1uLmdldE9wdGlvbnMoKSx7cHVibGljS2V5OnJ9PW4uZ2V0RHNuKCl8fHt9LG89Sih7ZW52aXJvbm1lbnQ6ZS5lbnZpcm9ubWVudHx8T3QscmVsZWFzZTplLnJlbGVhc2UscHVibGljX2tleTpyLHRyYWNlX2lkOnR9KTtyZXR1cm4gbi5lbWl0KCJjcmVhdGVEc2MiLG8pLG99KHQuc3BhbkNvbnRleHQoKS50cmFjZUlkLG4pLHA9b1tmdF0sbD1yLmRlc2NyaXB0aW9uO3JldHVybiJ1cmwiIT09cCYmbCYmKGgudHJhbnNhY3Rpb249bCksZnVuY3Rpb24odCl7aWYoImJvb2xlYW4iPT10eXBlb2YgX19TRU5UUllfVFJBQ0lOR19fJiYhX19TRU5UUllfVFJBQ0lOR19fKXJldHVybiExO2NvbnN0IG49dHx8YXQoKT8uZ2V0T3B0aW9ucygpO3JldHVybiEoIW58fG51bGw9PW4udHJhY2VzU2FtcGxlUmF0ZSYmIW4udHJhY2VzU2FtcGxlcil9KCkmJihoLnNhbXBsZWQ9U3RyaW5nKGZ1bmN0aW9uKHQpe2NvbnN0e3RyYWNlRmxhZ3M6bn09dC5zcGFuQ29udGV4dCgpO3JldHVybiBuPT09JHR9KGUpKSxoLnNhbXBsZV9yYW5kPXM/LmdldCgic2VudHJ5LnNhbXBsZV9yYW5kIik/P2J0KGUpLnNjb3BlPy5nZXRQcm9wYWdhdGlvbkNvbnRleHQoKS5zYW1wbGVSYW5kLnRvU3RyaW5nKCkpLGMoaCksbi5lbWl0KCJjcmVhdGVEc2MiLGgsZSksaH1mdW5jdGlvbiBBdCh0LG49ITEpe2NvbnN0e2hvc3Q6ZSxwYXRoOnIscGFzczpvLHBvcnQ6cyxwcm9qZWN0SWQ6aSxwcm90b2NvbDpjLHB1YmxpY0tleTp1fT10O3JldHVybmAke2N9Oi8vJHt1fSR7biYmbz9gOiR7b31gOiIifUAke2V9JHtzP2A6JHtzfWA6IiJ9LyR7cj9gJHtyfS9gOnJ9JHtpfWB9ZnVuY3Rpb24gUHQodCxuPTEwMCxlPTEvMCl7dHJ5e3JldHVybiBVdCgiIix0LG4sZSl9Y2F0Y2godCl7cmV0dXJue0VSUk9SOmAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHt0fSlgfX19ZnVuY3Rpb24gVXQodCxuLGU9MS8wLHI9MS8wLG89ZnVuY3Rpb24oKXtjb25zdCB0PW5ldyBXZWFrU2V0O2Z1bmN0aW9uIG4obil7cmV0dXJuISF0LmhhcyhuKXx8KHQuYWRkKG4pLCExKX1mdW5jdGlvbiBlKG4pe3QuZGVsZXRlKG4pfXJldHVybltuLGVdfSgpKXtjb25zdFtzLGldPW87aWYobnVsbD09bnx8WyJib29sZWFuIiwic3RyaW5nIl0uaW5jbHVkZXModHlwZW9mIG4pfHwibnVtYmVyIj09dHlwZW9mIG4mJk51bWJlci5pc0Zpbml0ZShuKSlyZXR1cm4gbjtjb25zdCBjPWZ1bmN0aW9uKHQsbil7dHJ5e2lmKCJkb21haW4iPT09dCYmbiYmIm9iamVjdCI9PXR5cGVvZiBuJiZuLkopcmV0dXJuIltEb21haW5dIjtpZigiZG9tYWluRW1pdHRlciI9PT10KXJldHVybiJbRG9tYWluRW1pdHRlcl0iO2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZ2xvYmFsJiZuPT09Z2xvYmFsKXJldHVybiJbR2xvYmFsXSI7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiB3aW5kb3cmJm49PT13aW5kb3cpcmV0dXJuIltXaW5kb3ddIjtpZigidW5kZWZpbmVkIiE9dHlwZW9mIGRvY3VtZW50JiZuPT09ZG9jdW1lbnQpcmV0dXJuIltEb2N1bWVudF0iO2lmKCJvYmplY3QiPT10eXBlb2YoZT1uKSYmbnVsbCE9PWUmJihlLl9faXNWdWV8fGUuWSkpcmV0dXJuIltWdWVWaWV3TW9kZWxdIjtpZihmdW5jdGlvbih0KXtyZXR1cm4gRCh0KSYmIm5hdGl2ZUV2ZW50ImluIHQmJiJwcmV2ZW50RGVmYXVsdCJpbiB0JiYic3RvcFByb3BhZ2F0aW9uImluIHR9KG4pKXJldHVybiJbU3ludGhldGljRXZlbnRdIjtpZigibnVtYmVyIj09dHlwZW9mIG4mJiFOdW1iZXIuaXNGaW5pdGUobikpcmV0dXJuYFske259XWA7aWYoImZ1bmN0aW9uIj09dHlwZW9mIG4pcmV0dXJuYFtGdW5jdGlvbjogJHtmdW5jdGlvbih0KXt0cnl7cmV0dXJuIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0JiZ0Lm5hbWV8fE59Y2F0Y2godCl7cmV0dXJuIE59fShuKX1dYDtpZigic3ltYm9sIj09dHlwZW9mIG4pcmV0dXJuYFske1N0cmluZyhuKX1dYDtpZigiYmlnaW50Ij09dHlwZW9mIG4pcmV0dXJuYFtCaWdJbnQ6ICR7U3RyaW5nKG4pfV1gO2NvbnN0IHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj1PYmplY3QuZ2V0UHJvdG90eXBlT2YodCk7cmV0dXJuIG4/LmNvbnN0cnVjdG9yP24uY29uc3RydWN0b3IubmFtZToibnVsbCBwcm90b3R5cGUifShuKTtyZXR1cm4vXkhUTUwoXHcqKUVsZW1lbnQkLy50ZXN0KHIpP2BbSFRNTEVsZW1lbnQ6ICR7cn1dYDpgW29iamVjdCAke3J9XWB9Y2F0Y2godCl7cmV0dXJuYCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9dmFyIGV9KHQsbik7aWYoIWMuc3RhcnRzV2l0aCgiW29iamVjdCAiKSlyZXR1cm4gYztpZihuLl9fc2VudHJ5X3NraXBfbm9ybWFsaXphdGlvbl9fKXJldHVybiBuO2NvbnN0IHU9Im51bWJlciI9PXR5cGVvZiBuLl9fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXz9uLl9fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXzplO2lmKDA9PT11KXJldHVybiBjLnJlcGxhY2UoIm9iamVjdCAiLCIiKTtpZihzKG4pKXJldHVybiJbQ2lyY3VsYXIgfl0iO2NvbnN0IGE9bjtpZihhJiYiZnVuY3Rpb24iPT10eXBlb2YgYS50b0pTT04pdHJ5e3JldHVybiBVdCgiIixhLnRvSlNPTigpLHUtMSxyLG8pfWNhdGNoKHQpe31jb25zdCBmPUFycmF5LmlzQXJyYXkobik/W106e307bGV0IGg9MDtjb25zdCBwPUwobik7Zm9yKGNvbnN0IHQgaW4gcCl7aWYoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChwLHQpKWNvbnRpbnVlO2lmKGg+PXIpe2ZbdF09IltNYXhQcm9wZXJ0aWVzIH5dIjticmVha31jb25zdCBuPXBbdF07Zlt0XT1VdCh0LG4sdS0xLHIsbyksaCsrfXJldHVybiBpKG4pLGZ9ZnVuY3Rpb24gTXQodCxuKXtjb25zdCBlPW4ucmVwbGFjZSgvXFwvZywiLyIpLnJlcGxhY2UoL1t8XFx7fSgpW1xdXiQrKj8uXS9nLCJcXCQmIik7bGV0IHI9dDt0cnl7cj1kZWNvZGVVUkkodCl9Y2F0Y2godCl7fXJldHVybiByLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC93ZWJwYWNrOlwvPy9nLCIiKS5yZXBsYWNlKG5ldyBSZWdFeHAoYChmaWxlOi8vKT8vKiR7ZX0vKmAsImlnIiksImFwcDovLy8iKX1mdW5jdGlvbiBMdCh0LG49W10pe3JldHVyblt0LG5dfWZ1bmN0aW9uIEJ0KHQsbil7Y29uc3QgZT10WzFdO2Zvcihjb25zdCB0IG9mIGUpe2lmKG4odCx0WzBdLnR5cGUpKXJldHVybiEwfXJldHVybiExfWZ1bmN0aW9uIEd0KHQpe2NvbnN0IG49ZyhwKTtyZXR1cm4gbi5lbmNvZGVQb2x5ZmlsbD9uLmVuY29kZVBvbHlmaWxsKHQpOihuZXcgVGV4dEVuY29kZXIpLmVuY29kZSh0KX1mdW5jdGlvbiBKdCh0KXtjb25zdFtuLGVdPXQ7bGV0IHI9SlNPTi5zdHJpbmdpZnkobik7ZnVuY3Rpb24gbyh0KXsic3RyaW5nIj09dHlwZW9mIHI/cj0ic3RyaW5nIj09dHlwZW9mIHQ/cit0OltHdChyKSx0XTpyLnB1c2goInN0cmluZyI9PXR5cGVvZiB0P0d0KHQpOnQpfWZvcihjb25zdCB0IG9mIGUpe2NvbnN0W24sZV09dDtpZihvKGBcbiR7SlNPTi5zdHJpbmdpZnkobil9XG5gKSwic3RyaW5nIj09dHlwZW9mIGV8fGUgaW5zdGFuY2VvZiBVaW50OEFycmF5KW8oZSk7ZWxzZXtsZXQgdDt0cnl7dD1KU09OLnN0cmluZ2lmeShlKX1jYXRjaChuKXt0PUpTT04uc3RyaW5naWZ5KFB0KGUpKX1vKHQpfX1yZXR1cm4ic3RyaW5nIj09dHlwZW9mIHI/cjpmdW5jdGlvbih0KXtjb25zdCBuPXQucmVkdWNlKCgodCxuKT0+dCtuLmxlbmd0aCksMCksZT1uZXcgVWludDhBcnJheShuKTtsZXQgcj0wO2Zvcihjb25zdCBuIG9mIHQpZS5zZXQobixyKSxyKz1uLmxlbmd0aDtyZXR1cm4gZX0ocil9Y29uc3QgWXQ9e3Nlc3Npb246InNlc3Npb24iLHNlc3Npb25zOiJzZXNzaW9uIixhdHRhY2htZW50OiJhdHRhY2htZW50Iix0cmFuc2FjdGlvbjoidHJhbnNhY3Rpb24iLGV2ZW50OiJlcnJvciIsY2xpZW50X3JlcG9ydDoiaW50ZXJuYWwiLHVzZXJfcmVwb3J0OiJkZWZhdWx0Iixwcm9maWxlOiJwcm9maWxlIixwcm9maWxlX2NodW5rOiJwcm9maWxlIixyZXBsYXlfZXZlbnQ6InJlcGxheSIscmVwbGF5X3JlY29yZGluZzoicmVwbGF5IixjaGVja19pbjoibW9uaXRvciIsZmVlZGJhY2s6ImZlZWRiYWNrIixzcGFuOiJzcGFuIixyYXdfc2VjdXJpdHk6InNlY3VyaXR5IixvdGVsX2xvZzoibG9nX2l0ZW0ifTtmdW5jdGlvbiB6dCh0KXtpZighdD8uc2RrKXJldHVybjtjb25zdHtuYW1lOm4sdmVyc2lvbjplfT10LnNkaztyZXR1cm57bmFtZTpuLHZlcnNpb246ZX19ZnVuY3Rpb24gSHQodCxuLGUscil7Y29uc3Qgbz16dChlKSxzPXQudHlwZSYmInJlcGxheV9ldmVudCIhPT10LnR5cGU/dC50eXBlOiJldmVudCI7IWZ1bmN0aW9uKHQsbil7biYmKHQuc2RrPXQuc2RrfHx7fSx0LnNkay5uYW1lPXQuc2RrLm5hbWV8fG4ubmFtZSx0LnNkay52ZXJzaW9uPXQuc2RrLnZlcnNpb258fG4udmVyc2lvbix0LnNkay5pbnRlZ3JhdGlvbnM9Wy4uLnQuc2RrLmludGVncmF0aW9uc3x8W10sLi4ubi5pbnRlZ3JhdGlvbnN8fFtdXSx0LnNkay5wYWNrYWdlcz1bLi4udC5zZGsucGFja2FnZXN8fFtdLC4uLm4ucGFja2FnZXN8fFtdXSl9KHQsZT8uc2RrKTtjb25zdCBpPWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDoobmV3IERhdGUpLnRvSVNPU3RyaW5nKCksLi4ubiYme3NkazpufSwuLi4hIWUmJnImJntkc246QXQocil9LC4uLm8mJnt0cmFjZTpvfX19KHQsbyxyLG4pO2RlbGV0ZSB0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YTtyZXR1cm4gTHQoaSxbW3t0eXBlOnN9LHRdXSl9Y29uc3QgV3Q9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyI7ZnVuY3Rpb24gRnQodCl7Y29uc3Qgbj11dChtKCkpO3JldHVybiBuLnN1cHByZXNzVHJhY2luZz9uLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBuPXV0KG0oKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtlLHJdPXQ7cmV0dXJuIGU/bi53aXRoU2V0U2NvcGUoZSxyKTpuLndpdGhTY29wZShyKX1yZXR1cm4gbi53aXRoU2NvcGUodFswXSl9KChuPT4obi5zZXRTREtQcm9jZXNzaW5nTWV0YWRhdGEoe1tXdF06ITB9KSx0KCkpKSl9ZnVuY3Rpb24gS3QodCxuKXtjb25zdHtmaW5nZXJwcmludDplLHNwYW46cixicmVhZGNydW1iczpvLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTpzfT1uOyFmdW5jdGlvbih0LG4pe2NvbnN0e2V4dHJhOmUsdGFnczpyLHVzZXI6byxjb250ZXh0czpzLGxldmVsOmksdHJhbnNhY3Rpb25OYW1lOmN9PW47T2JqZWN0LmtleXMoZSkubGVuZ3RoJiYodC5leHRyYT17Li4uZSwuLi50LmV4dHJhfSk7T2JqZWN0LmtleXMocikubGVuZ3RoJiYodC50YWdzPXsuLi5yLC4uLnQudGFnc30pO09iamVjdC5rZXlzKG8pLmxlbmd0aCYmKHQudXNlcj17Li4ubywuLi50LnVzZXJ9KTtPYmplY3Qua2V5cyhzKS5sZW5ndGgmJih0LmNvbnRleHRzPXsuLi5zLC4uLnQuY29udGV4dHN9KTtpJiYodC5sZXZlbD1pKTtjJiYidHJhbnNhY3Rpb24iIT09dC50eXBlJiYodC50cmFuc2FjdGlvbj1jKX0odCxuKSxyJiZmdW5jdGlvbih0LG4pe3QuY29udGV4dHM9e3RyYWNlOkV0KG4pLC4uLnQuY29udGV4dHN9LHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXtkeW5hbWljU2FtcGxpbmdDb250ZXh0Okl0KG4pLC4uLnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhfTtjb25zdCBlPWt0KG4pLHI9Q3QoZSkuZGVzY3JpcHRpb247ciYmIXQudHJhbnNhY3Rpb24mJiJ0cmFuc2FjdGlvbiI9PT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPXIpfSh0LHIpLGZ1bmN0aW9uKHQsbil7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sbiYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQobikpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxlKSxmdW5jdGlvbih0LG4pe2NvbnN0IGU9Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLm5dO3QuYnJlYWRjcnVtYnM9ZS5sZW5ndGg/ZTp2b2lkIDB9KHQsbyksZnVuY3Rpb24odCxuKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4ubn19KHQscyl9Y29uc3QgVnQ9IjciO2NsYXNzIFp0IGV4dGVuZHMgRXJyb3J7Y29uc3RydWN0b3IodCxuPSJ3YXJuIil7c3VwZXIodCksdGhpcy5tZXNzYWdlPXQsdGhpcy5sb2dMZXZlbD1ufX1mdW5jdGlvbiBxdCh0KXtjb25zdCBuPVtdO2Z1bmN0aW9uIGUodCl7cmV0dXJuIG4uc3BsaWNlKG4uaW5kZXhPZih0KSwxKVswXXx8UHJvbWlzZS5yZXNvbHZlKHZvaWQgMCl9cmV0dXJueyQ6bixhZGQ6ZnVuY3Rpb24ocil7aWYoISh2b2lkIDA9PT10fHxuLmxlbmd0aDx0KSlyZXR1cm4gbz1uZXcgWnQoIk5vdCBhZGRpbmcgUHJvbWlzZSBiZWNhdXNlIGJ1ZmZlciBsaW1pdCB3YXMgcmVhY2hlZC4iKSxuZXcgRigoKHQsbik9PntuKG8pfSkpO3ZhciBvO2NvbnN0IHM9cigpO3JldHVybi0xPT09bi5pbmRleE9mKHMpJiZuLnB1c2gocykscy50aGVuKCgoKT0+ZShzKSkpLnRoZW4obnVsbCwoKCk9PmUocykudGhlbihudWxsLCgoKT0+e30pKSkpLHN9LGRyYWluOmZ1bmN0aW9uKHQpe3JldHVybiBuZXcgRigoKGUscik9PntsZXQgbz1uLmxlbmd0aDtpZighbylyZXR1cm4gZSghMCk7Y29uc3Qgcz1zZXRUaW1lb3V0KCgoKT0+e3QmJnQ+MCYmZSghMSl9KSx0KTtuLmZvckVhY2goKHQ9PntXKHQpLnRoZW4oKCgpPT57LS1vfHwoY2xlYXJUaW1lb3V0KHMpLGUoITApKX0pLHIpfSkpfSkpfX19Y29uc3QgUXQ9NmU0O2Z1bmN0aW9uIFh0KHQse3N0YXR1c0NvZGU6bixoZWFkZXJzOmV9LHI9RGF0ZS5ub3coKSl7Y29uc3Qgbz17Li4udH0scz1lPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0saT1lPy5bInJldHJ5LWFmdGVyIl07aWYocylmb3IoY29uc3QgdCBvZiBzLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtuLGUsLCxzXT10LnNwbGl0KCI6Iiw1KSxpPXBhcnNlSW50KG4sMTApLGM9MWUzKihpc05hTihpKT82MDppKTtpZihlKWZvcihjb25zdCB0IG9mIGUuc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZzJiYhcy5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIGk/by5hbGw9citmdW5jdGlvbih0LG49RGF0ZS5ub3coKSl7Y29uc3QgZT1wYXJzZUludChgJHt0fWAsMTApO2lmKCFpc05hTihlKSlyZXR1cm4gMWUzKmU7Y29uc3Qgcj1EYXRlLnBhcnNlKGAke3R9YCk7cmV0dXJuIGlzTmFOKHIpP1F0OnItbn0oaSxyKTo0Mjk9PT1uJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWNvbnN0IHRuPTY0O2Z1bmN0aW9uIG5uKHQsbixlPXF0KHQuYnVmZmVyU2l6ZXx8dG4pKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKEJ0KHQsKCh0LG4pPT57Y29uc3QgZT1mdW5jdGlvbih0KXtyZXR1cm4gWXRbdF19KG4pOyhmdW5jdGlvbih0LG4sZT1EYXRlLm5vdygpKXtyZXR1cm4gZnVuY3Rpb24odCxuKXtyZXR1cm4gdFtuXXx8dC5hbGx8fDB9KHQsbik+ZX0pKHIsZSl8fG8ucHVzaCh0KX0pKSwwPT09by5sZW5ndGgpcmV0dXJuIFcoe30pO2NvbnN0IHM9THQodFswXSxvKSxpPXQ9PntCdChzLCgodCxuKT0+e30pKX07cmV0dXJuIGUuYWRkKCgoKT0+bih7Ym9keTpKdChzKX0pLnRoZW4oKHQ9Pih2b2lkIDAhPT10LnN0YXR1c0NvZGUmJih0LnN0YXR1c0NvZGU8MjAwfHx0LnN0YXR1c0NvZGU+PTMwMCkmJmgmJncud2FybihgU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlICR7dC5zdGF0dXNDb2RlfSB0byBzZW50IGV2ZW50LmApLHI9WHQocix0KSx0KSksKHQ9Pnt0aHJvdyBpKCksdH0pKSkpLnRoZW4oKHQ9PnQpLCh0PT57aWYodCBpbnN0YW5jZW9mIFp0KXJldHVybiBoJiZ3LmVycm9yKCJTa2lwcGVkIHNlbmRpbmcgZXZlbnQgYmVjYXVzZSBidWZmZXIgaXMgZnVsbC4iKSxpKCksVyh7fSk7dGhyb3cgdH0pKX0sZmx1c2g6dD0+ZS5kcmFpbih0KX19Y29uc3QgZW49L14oXFMrOlxcfFwvPykoW1xzXFNdKj8pKCg/OlwuezEsMn18W14vXFxdKz98KShcLlteLi9cXF0qfCkpKD86Wy9cXF0qKSQvO2Z1bmN0aW9uIHJuKHQpe2NvbnN0IG49ZnVuY3Rpb24odCl7Y29uc3Qgbj10Lmxlbmd0aD4xMDI0P2A8dHJ1bmNhdGVkPiR7dC5zbGljZSgtMTAyNCl9YDp0LGU9ZW4uZXhlYyhuKTtyZXR1cm4gZT9lLnNsaWNlKDEpOltdfSh0KSxlPW5bMF18fCIiO2xldCByPW5bMV07cmV0dXJuIGV8fHI/KHImJihyPXIuc2xpY2UoMCxyLmxlbmd0aC0xKSksZStyKToiLiJ9ZnVuY3Rpb24gb24odCxuPSExKXtyZXR1cm4hKG58fHQmJiF0LnN0YXJ0c1dpdGgoIi8iKSYmIXQubWF0Y2goL15bQS1aXTovKSYmIXQuc3RhcnRzV2l0aCgiLiIpJiYhdC5tYXRjaCgvXlthLXpBLVpdKFthLXpBLVowLTkuXC0rXSkqOlwvXC8vKSkmJnZvaWQgMCE9PXQmJiF0LmluY2x1ZGVzKCJub2RlX21vZHVsZXMvIil9Y29uc3Qgc249U3ltYm9sKCJBZ2VudEJhc2VJbnRlcm5hbFN0YXRlIik7Y2xhc3MgY24gZXh0ZW5kcyBzLkFnZW50e2NvbnN0cnVjdG9yKHQpe3N1cGVyKHQpLHRoaXNbc25dPXt9fWlzU2VjdXJlRW5kcG9pbnQodCl7aWYodCl7aWYoImJvb2xlYW4iPT10eXBlb2YgdC5zZWN1cmVFbmRwb2ludClyZXR1cm4gdC5zZWN1cmVFbmRwb2ludDtpZigic3RyaW5nIj09dHlwZW9mIHQucHJvdG9jb2wpcmV0dXJuImh0dHBzOiI9PT10LnByb3RvY29sfWNvbnN0e3N0YWNrOm59PW5ldyBFcnJvcjtyZXR1cm4ic3RyaW5nIj09dHlwZW9mIG4mJm4uc3BsaXQoIlxuIikuc29tZSgodD0+LTEhPT10LmluZGV4T2YoIihodHRwcy5qczoiKXx8LTEhPT10LmluZGV4T2YoIm5vZGU6aHR0cHM6IikpKX1jcmVhdGVTb2NrZXQodCxuLGUpe2NvbnN0IHI9ey4uLm4sc2VjdXJlRW5kcG9pbnQ6dGhpcy5pc1NlY3VyZUVuZHBvaW50KG4pfTtQcm9taXNlLnJlc29sdmUoKS50aGVuKCgoKT0+dGhpcy5jb25uZWN0KHQscikpKS50aGVuKChvPT57aWYobyBpbnN0YW5jZW9mIHMuQWdlbnQpcmV0dXJuIG8uYWRkUmVxdWVzdCh0LHIpO3RoaXNbc25dLmN1cnJlbnRTb2NrZXQ9byxzdXBlci5jcmVhdGVTb2NrZXQodCxuLGUpfSksZSl9Y3JlYXRlQ29ubmVjdGlvbigpe2NvbnN0IHQ9dGhpc1tzbl0uY3VycmVudFNvY2tldDtpZih0aGlzW3NuXS5jdXJyZW50U29ja2V0PXZvaWQgMCwhdCl0aHJvdyBuZXcgRXJyb3IoIk5vIHNvY2tldCB3YXMgcmV0dXJuZWQgaW4gdGhlIGBjb25uZWN0KClgIGZ1bmN0aW9uIik7cmV0dXJuIHR9Z2V0IGRlZmF1bHRQb3J0KCl7cmV0dXJuIHRoaXNbc25dLmRlZmF1bHRQb3J0Pz8oImh0dHBzOiI9PT10aGlzLnByb3RvY29sPzQ0Mzo4MCl9c2V0IGRlZmF1bHRQb3J0KHQpe3RoaXNbc25dJiYodGhpc1tzbl0uZGVmYXVsdFBvcnQ9dCl9Z2V0IHByb3RvY29sKCl7cmV0dXJuIHRoaXNbc25dLnByb3RvY29sPz8odGhpcy5pc1NlY3VyZUVuZHBvaW50KCk/Imh0dHBzOiI6Imh0dHA6Iil9c2V0IHByb3RvY29sKHQpe3RoaXNbc25dJiYodGhpc1tzbl0ucHJvdG9jb2w9dCl9fWZ1bmN0aW9uIHVuKC4uLnQpe3cubG9nKCJbaHR0cHMtcHJveHktYWdlbnQ6cGFyc2UtcHJveHktcmVzcG9uc2VdIiwuLi50KX1mdW5jdGlvbiBhbih0KXtyZXR1cm4gbmV3IFByb21pc2UoKChuLGUpPT57bGV0IHI9MDtjb25zdCBvPVtdO2Z1bmN0aW9uIHMoKXtjb25zdCBjPXQucmVhZCgpO2M/ZnVuY3Rpb24oYyl7by5wdXNoKGMpLHIrPWMubGVuZ3RoO2NvbnN0IHU9QnVmZmVyLmNvbmNhdChvLHIpLGE9dS5pbmRleE9mKCJcclxuXHJcbiIpO2lmKC0xPT09YSlyZXR1cm4gdW4oImhhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uIiksdm9pZCBzKCk7Y29uc3QgZj11LnN1YmFycmF5KDAsYSkudG9TdHJpbmcoImFzY2lpIikuc3BsaXQoIlxyXG4iKSxoPWYuc2hpZnQoKTtpZighaClyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoIk5vIGhlYWRlciByZWNlaXZlZCBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2UiKSk7Y29uc3QgcD1oLnNwbGl0KCIgIiksbD0rKHBbMV18fDApLGQ9cC5zbGljZSgyKS5qb2luKCIgIiksbT17fTtmb3IoY29uc3QgbiBvZiBmKXtpZighbiljb250aW51ZTtjb25zdCByPW4uaW5kZXhPZigiOiIpO2lmKC0xPT09cilyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoYEludmFsaWQgaGVhZGVyIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZTogIiR7bn0iYCkpO2NvbnN0IG89bi5zbGljZSgwLHIpLnRvTG93ZXJDYXNlKCkscz1uLnNsaWNlKHIrMSkudHJpbVN0YXJ0KCksaT1tW29dOyJzdHJpbmciPT10eXBlb2YgaT9tW29dPVtpLHNdOkFycmF5LmlzQXJyYXkoaSk/aS5wdXNoKHMpOm1bb109c311bigiZ290IHByb3h5IHNlcnZlciByZXNwb25zZTogJW8gJW8iLGgsbSksaSgpLG4oe2Nvbm5lY3Q6e3N0YXR1c0NvZGU6bCxzdGF0dXNUZXh0OmQsaGVhZGVyczptfSxidWZmZXJlZDp1fSl9KGMpOnQub25jZSgicmVhZGFibGUiLHMpfWZ1bmN0aW9uIGkoKXt0LnJlbW92ZUxpc3RlbmVyKCJlbmQiLGMpLHQucmVtb3ZlTGlzdGVuZXIoImVycm9yIix1KSx0LnJlbW92ZUxpc3RlbmVyKCJyZWFkYWJsZSIscyl9ZnVuY3Rpb24gYygpe2koKSx1bigib25lbmQiKSxlKG5ldyBFcnJvcigiUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UiKSl9ZnVuY3Rpb24gdSh0KXtpKCksdW4oIm9uZXJyb3IgJW8iLHQpLGUodCl9dC5vbigiZXJyb3IiLHUpLHQub24oImVuZCIsYykscygpfSkpfWZ1bmN0aW9uIGZuKC4uLnQpe3cubG9nKCJbaHR0cHMtcHJveHktYWdlbnRdIiwuLi50KX1jbGFzcyBobiBleHRlbmRzIGNue3N0YXRpYyBfX2luaXRTdGF0aWMoKXt0aGlzLnByb3RvY29scz1bImh0dHAiLCJodHRwcyJdfWNvbnN0cnVjdG9yKHQsbil7c3VwZXIobiksdGhpcy5vcHRpb25zPXt9LHRoaXMucHJveHk9InN0cmluZyI9PXR5cGVvZiB0P25ldyBVUkwodCk6dCx0aGlzLnByb3h5SGVhZGVycz1uPy5oZWFkZXJzPz97fSxmbigiQ3JlYXRpbmcgbmV3IEh0dHBzUHJveHlBZ2VudCBpbnN0YW5jZTogJW8iLHRoaXMucHJveHkuaHJlZik7Y29uc3QgZT0odGhpcy5wcm94eS5ob3N0bmFtZXx8dGhpcy5wcm94eS5ob3N0KS5yZXBsYWNlKC9eXFt8XF0kL2csIiIpLHI9dGhpcy5wcm94eS5wb3J0P3BhcnNlSW50KHRoaXMucHJveHkucG9ydCwxMCk6Imh0dHBzOiI9PT10aGlzLnByb3h5LnByb3RvY29sPzQ0Mzo4MDt0aGlzLmNvbm5lY3RPcHRzPXtBTFBOUHJvdG9jb2xzOlsiaHR0cC8xLjEiXSwuLi5uP2xuKG4sImhlYWRlcnMiKTpudWxsLGhvc3Q6ZSxwb3J0OnJ9fWFzeW5jIGNvbm5lY3QodCxuKXtjb25zdHtwcm94eTplfT10aGlzO2lmKCFuLmhvc3QpdGhyb3cgbmV3IFR5cGVFcnJvcignTm8gImhvc3QiIHByb3ZpZGVkJyk7bGV0IHI7aWYoImh0dHBzOiI9PT1lLnByb3RvY29sKXtmbigiQ3JlYXRpbmcgYHRscy5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cyk7Y29uc3QgdD10aGlzLmNvbm5lY3RPcHRzLnNlcnZlcm5hbWV8fHRoaXMuY29ubmVjdE9wdHMuaG9zdDtyPWYuY29ubmVjdCh7Li4udGhpcy5jb25uZWN0T3B0cyxzZXJ2ZXJuYW1lOnQmJmEuaXNJUCh0KT92b2lkIDA6dH0pfWVsc2UgZm4oIkNyZWF0aW5nIGBuZXQuU29ja2V0YDogJW8iLHRoaXMuY29ubmVjdE9wdHMpLHI9YS5jb25uZWN0KHRoaXMuY29ubmVjdE9wdHMpO2NvbnN0IG89ImZ1bmN0aW9uIj09dHlwZW9mIHRoaXMucHJveHlIZWFkZXJzP3RoaXMucHJveHlIZWFkZXJzKCk6ey4uLnRoaXMucHJveHlIZWFkZXJzfSxzPWEuaXNJUHY2KG4uaG9zdCk/YFske24uaG9zdH1dYDpuLmhvc3Q7bGV0IGk9YENPTk5FQ1QgJHtzfToke24ucG9ydH0gSFRUUC8xLjFcclxuYDtpZihlLnVzZXJuYW1lfHxlLnBhc3N3b3JkKXtjb25zdCB0PWAke2RlY29kZVVSSUNvbXBvbmVudChlLnVzZXJuYW1lKX06JHtkZWNvZGVVUklDb21wb25lbnQoZS5wYXNzd29yZCl9YDtvWyJQcm94eS1BdXRob3JpemF0aW9uIl09YEJhc2ljICR7QnVmZmVyLmZyb20odCkudG9TdHJpbmcoImJhc2U2NCIpfWB9by5Ib3N0PWAke3N9OiR7bi5wb3J0fWAsb1siUHJveHktQ29ubmVjdGlvbiJdfHwob1siUHJveHktQ29ubmVjdGlvbiJdPXRoaXMua2VlcEFsaXZlPyJLZWVwLUFsaXZlIjoiY2xvc2UiKTtmb3IoY29uc3QgdCBvZiBPYmplY3Qua2V5cyhvKSlpKz1gJHt0fTogJHtvW3RdfVxyXG5gO2NvbnN0IGM9YW4ocik7ci53cml0ZShgJHtpfVxyXG5gKTtjb25zdHtjb25uZWN0OnUsYnVmZmVyZWQ6aH09YXdhaXQgYztpZih0LmVtaXQoInByb3h5Q29ubmVjdCIsdSksdGhpcy5lbWl0KCJwcm94eUNvbm5lY3QiLHUsdCksMjAwPT09dS5zdGF0dXNDb2RlKXtpZih0Lm9uY2UoInNvY2tldCIscG4pLG4uc2VjdXJlRW5kcG9pbnQpe2ZuKCJVcGdyYWRpbmcgc29ja2V0IGNvbm5lY3Rpb24gdG8gVExTIik7Y29uc3QgdD1uLnNlcnZlcm5hbWV8fG4uaG9zdDtyZXR1cm4gZi5jb25uZWN0KHsuLi5sbihuLCJob3N0IiwicGF0aCIsInBvcnQiKSxzb2NrZXQ6cixzZXJ2ZXJuYW1lOmEuaXNJUCh0KT92b2lkIDA6dH0pfXJldHVybiByfXIuZGVzdHJveSgpO2NvbnN0IHA9bmV3IGEuU29ja2V0KHt3cml0YWJsZTohMX0pO3JldHVybiBwLnJlYWRhYmxlPSEwLHQub25jZSgic29ja2V0IiwodD0+e2ZuKCJSZXBsYXlpbmcgcHJveHkgYnVmZmVyIGZvciBmYWlsZWQgcmVxdWVzdCIpLHQucHVzaChoKSx0LnB1c2gobnVsbCl9KSkscH19ZnVuY3Rpb24gcG4odCl7dC5yZXN1bWUoKX1mdW5jdGlvbiBsbih0LC4uLm4pe2NvbnN0IGU9e307bGV0IHI7Zm9yKHIgaW4gdCluLmluY2x1ZGVzKHIpfHwoZVtyXT10W3JdKTtyZXR1cm4gZX1obi5fX2luaXRTdGF0aWMoKTtjb25zdCBkbj0zMjc2ODtmdW5jdGlvbiBtbih0KXtyZXR1cm4gdC5yZXBsYWNlKC9eW0EtWl06LywiIikucmVwbGFjZSgvXFwvZywiLyIpfWNvbnN0IGduPW47bGV0IHluLGJuPTAsX249e307ZnVuY3Rpb24gdm4odCl7Z24uZGVidWcmJmNvbnNvbGUubG9nKGBbQU5SIFdvcmtlcl0gJHt0fWApfXZhciB3bixTbiwkbjtjb25zdCBFbj1mdW5jdGlvbih0KXtsZXQgbjt0cnl7bj1uZXcgVVJMKHQudXJsKX1jYXRjaChuKXtyZXR1cm4gdigoKCk9Pntjb25zb2xlLndhcm4oIltAc2VudHJ5L25vZGVdOiBJbnZhbGlkIGRzbiBvciB0dW5uZWwgb3B0aW9uLCB3aWxsIG5vdCBzZW5kIGFueSBldmVudHMuIFRoZSB0dW5uZWwgb3B0aW9uIG11c3QgYmUgYSBmdWxsIFVSTCB3aGVuIHVzZWQuIil9KSksbm4odCwoKCk9PlByb21pc2UucmVzb2x2ZSh7fSkpKX1jb25zdCBlPSJodHRwczoiPT09bi5wcm90b2NvbCxyPWZ1bmN0aW9uKHQsbil7Y29uc3R7bm9fcHJveHk6ZX09cHJvY2Vzcy5lbnYscj1lPy5zcGxpdCgiLCIpLnNvbWUoKG49PnQuaG9zdC5lbmRzV2l0aChuKXx8dC5ob3N0bmFtZS5lbmRzV2l0aChuKSkpO3JldHVybiByP3ZvaWQgMDpufShuLHQucHJveHl8fChlP3Byb2Nlc3MuZW52Lmh0dHBzX3Byb3h5OnZvaWQgMCl8fHByb2Nlc3MuZW52Lmh0dHBfcHJveHkpLG89ZT9pOnMsYT12b2lkIDAhPT10LmtlZXBBbGl2ZSYmdC5rZWVwQWxpdmUsZj1yP25ldyBobihyKTpuZXcgby5BZ2VudCh7a2VlcEFsaXZlOmEsbWF4U29ja2V0czozMCx0aW1lb3V0OjJlM30pLGg9ZnVuY3Rpb24odCxuLGUpe2NvbnN0e2hvc3RuYW1lOnIscGF0aG5hbWU6byxwb3J0OnMscHJvdG9jb2w6aSxzZWFyY2g6YX09bmV3IFVSTCh0LnVybCk7cmV0dXJuIGZ1bmN0aW9uKGYpe3JldHVybiBuZXcgUHJvbWlzZSgoKGgscCk9PntGdCgoKCk9PntsZXQgbD1mdW5jdGlvbih0KXtyZXR1cm4gbmV3IGMoe3JlYWQoKXt0aGlzLnB1c2godCksdGhpcy5wdXNoKG51bGwpfX0pfShmLmJvZHkpO2NvbnN0IGQ9ey4uLnQuaGVhZGVyc307Zi5ib2R5Lmxlbmd0aD5kbiYmKGRbImNvbnRlbnQtZW5jb2RpbmciXT0iZ3ppcCIsbD1sLnBpcGUodSgpKSk7Y29uc3QgbT1uLnJlcXVlc3Qoe21ldGhvZDoiUE9TVCIsYWdlbnQ6ZSxoZWFkZXJzOmQsaG9zdG5hbWU6cixwYXRoOmAke299JHthfWAscG9ydDpzLHByb3RvY29sOmksY2E6dC5jYUNlcnRzfSwodD0+e3Qub24oImRhdGEiLCgoKT0+e30pKSx0Lm9uKCJlbmQiLCgoKT0+e30pKSx0LnNldEVuY29kaW5nKCJ1dGY4Iik7Y29uc3Qgbj10LmhlYWRlcnNbInJldHJ5LWFmdGVyIl0/P251bGwsZT10LmhlYWRlcnNbIngtc2VudHJ5LXJhdGUtbGltaXRzIl0/P251bGw7aCh7c3RhdHVzQ29kZTp0LnN0YXR1c0NvZGUsaGVhZGVyczp7InJldHJ5LWFmdGVyIjpuLCJ4LXNlbnRyeS1yYXRlLWxpbWl0cyI6QXJyYXkuaXNBcnJheShlKT9lWzBdfHxudWxsOmV9fSl9KSk7bS5vbigiZXJyb3IiLHApLGwucGlwZShtKX0pKX0pKX19KHQsdC5odHRwTW9kdWxlPz9vLGYpO3JldHVybiBubih0LGgpfSh7dXJsOih3bj1nbi5kc24sU249Z24udHVubmVsLCRuPWduLnNka01ldGFkYXRhLnNkayxTbnx8YCR7ZnVuY3Rpb24odCl7cmV0dXJuYCR7ZnVuY3Rpb24odCl7Y29uc3Qgbj10LnByb3RvY29sP2Ake3QucHJvdG9jb2x9OmA6IiIsZT10LnBvcnQ/YDoke3QucG9ydH1gOiIiO3JldHVybmAke259Ly8ke3QuaG9zdH0ke2V9JHt0LnBhdGg/YC8ke3QucGF0aH1gOiIifS9hcGkvYH0odCl9JHt0LnByb2plY3RJZH0vZW52ZWxvcGUvYH0od24pfT8ke2Z1bmN0aW9uKHQsbil7Y29uc3QgZT17c2VudHJ5X3ZlcnNpb246VnR9O3JldHVybiB0LnB1YmxpY0tleSYmKGUuc2VudHJ5X2tleT10LnB1YmxpY0tleSksbiYmKGUuc2VudHJ5X2NsaWVudD1gJHtuLm5hbWV9LyR7bi52ZXJzaW9ufWApLG5ldyBVUkxTZWFyY2hQYXJhbXMoZSkudG9TdHJpbmcoKX0od24sJG4pfWApfSk7YXN5bmMgZnVuY3Rpb24geG4oKXtpZih5bil7dm4oIlNlbmRpbmcgYWJub3JtYWwgc2Vzc2lvbiIpLFYoeW4se3N0YXR1czoiYWJub3JtYWwiLGFibm9ybWFsX21lY2hhbmlzbToiYW5yX2ZvcmVncm91bmQiLHJlbGVhc2U6Z24ucmVsZWFzZSxlbnZpcm9ubWVudDpnbi5lbnZpcm9ubWVudH0pO2NvbnN0IHQ9ZnVuY3Rpb24odCxuLGUscil7Y29uc3Qgbz16dChlKTtyZXR1cm4gTHQoe3NlbnRfYXQ6KG5ldyBEYXRlKS50b0lTT1N0cmluZygpLC4uLm8mJntzZGs6b30sLi4uISFyJiZuJiZ7ZHNuOkF0KG4pfX0sWyJhZ2dyZWdhdGVzImluIHQ/W3t0eXBlOiJzZXNzaW9ucyJ9LHRdOlt7dHlwZToic2Vzc2lvbiJ9LHQudG9KU09OKCldXSl9KHluLGduLmRzbixnbi5zZGtNZXRhZGF0YSxnbi50dW5uZWwpO3ZuKEpTT04uc3RyaW5naWZ5KHQpKSxhd2FpdCBFbi5zZW5kKHQpO3RyeXtlPy5wb3N0TWVzc2FnZSgic2Vzc2lvbi1lbmRlZCIpfWNhdGNoKHQpe319fWZ1bmN0aW9uIE5uKHQpe2lmKCF0KXJldHVybjtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0Lmxlbmd0aClyZXR1cm5bXTtjb25zdCBuPUFycmF5LmZyb20odCk7cmV0dXJuL3NlbnRyeVdyYXBwZWQvLnRlc3QoeChuKS5mdW5jdGlvbnx8IiIpJiZuLnBvcCgpLG4ucmV2ZXJzZSgpLEUudGVzdCh4KG4pLmZ1bmN0aW9ufHwiIikmJihuLnBvcCgpLEUudGVzdCh4KG4pLmZ1bmN0aW9ufHwiIikmJm4ucG9wKCkpLG4uc2xpY2UoMCxTKS5tYXAoKHQ9Pih7Li4udCxmaWxlbmFtZTp0LmZpbGVuYW1lfHx4KG4pLmZpbGVuYW1lLGZ1bmN0aW9uOnQuZnVuY3Rpb258fCR9KSkpfSh0KTtpZihnbi5hcHBSb290UGF0aClmb3IoY29uc3QgdCBvZiBuKXQuZmlsZW5hbWUmJih0LmZpbGVuYW1lPU10KHQuZmlsZW5hbWUsZ24uYXBwUm9vdFBhdGgpKTtyZXR1cm4gbn1hc3luYyBmdW5jdGlvbiBUbih0LG4pe2lmKGJuPj1nbi5tYXhBbnJFdmVudHMpcmV0dXJuO2JuKz0xLGF3YWl0IHhuKCksdm4oIlNlbmRpbmcgZXZlbnQiKTtjb25zdCBlPXtldmVudF9pZDp6KCksY29udGV4dHM6Z24uY29udGV4dHMscmVsZWFzZTpnbi5yZWxlYXNlLGVudmlyb25tZW50OmduLmVudmlyb25tZW50LGRpc3Q6Z24uZGlzdCxwbGF0Zm9ybToibm9kZSIsbGV2ZWw6ImVycm9yIixleGNlcHRpb246e3ZhbHVlczpbe3R5cGU6IkFwcGxpY2F0aW9uTm90UmVzcG9uZGluZyIsdmFsdWU6YEFwcGxpY2F0aW9uIE5vdCBSZXNwb25kaW5nIGZvciBhdCBsZWFzdCAke2duLmFuclRocmVzaG9sZH0gbXNgLHN0YWNrdHJhY2U6e2ZyYW1lczpObih0KX0sbWVjaGFuaXNtOnt0eXBlOiJBTlIifX1dfSx0YWdzOmduLnN0YXRpY1RhZ3N9O24mJmZ1bmN0aW9uKHQsbil7aWYoS3QodCxuKSwhdC5jb250ZXh0cz8udHJhY2Upe2NvbnN0e3RyYWNlSWQ6ZSxwYXJlbnRTcGFuSWQ6cixwcm9wYWdhdGlvblNwYW5JZDpvfT1uLnByb3BhZ2F0aW9uQ29udGV4dDt0LmNvbnRleHRzPXt0cmFjZTp7dHJhY2VfaWQ6ZSxzcGFuX2lkOm98fHEoKSxwYXJlbnRfc3Bhbl9pZDpyfSwuLi50LmNvbnRleHRzfX19KGUsbiksZnVuY3Rpb24odCl7aWYoMD09PU9iamVjdC5rZXlzKF9uKS5sZW5ndGgpcmV0dXJuO2NvbnN0IG49Z24uYXBwUm9vdFBhdGg/e306X247aWYoZ24uYXBwUm9vdFBhdGgpZm9yKGNvbnN0W3QsZV1vZiBPYmplY3QuZW50cmllcyhfbikpbltNdCh0LGduLmFwcFJvb3RQYXRoKV09ZTtjb25zdCBlPW5ldyBNYXA7Zm9yKGNvbnN0IHIgb2YgdC5leGNlcHRpb24/LnZhbHVlc3x8W10pZm9yKGNvbnN0IHQgb2Ygci5zdGFja3RyYWNlPy5mcmFtZXN8fFtdKXtjb25zdCByPXQuYWJzX3BhdGh8fHQuZmlsZW5hbWU7ciYmbltyXSYmZS5zZXQocixuW3JdKX1pZihlLnNpemU+MCl7Y29uc3Qgbj1bXTtmb3IoY29uc3RbdCxyXW9mIGUuZW50cmllcygpKW4ucHVzaCh7dHlwZToic291cmNlbWFwIixjb2RlX2ZpbGU6dCxkZWJ1Z19pZDpyfSk7dC5kZWJ1Z19tZXRhPXtpbWFnZXM6bn19fShlKTtjb25zdCByPUh0KGUsZ24uZHNuLGduLnNka01ldGFkYXRhLGduLnR1bm5lbCk7dm4oSlNPTi5zdHJpbmdpZnkocikpLGF3YWl0IEVuLnNlbmQociksYXdhaXQgRW4uZmx1c2goMmUzKSxibj49Z24ubWF4QW5yRXZlbnRzJiZzZXRUaW1lb3V0KCgoKT0+e3Byb2Nlc3MuZXhpdCgwKX0pLDVlMyl9bGV0IENuO2lmKHZuKCJTdGFydGVkIiksZ24uY2FwdHVyZVN0YWNrVHJhY2Upe3ZuKCJDb25uZWN0aW5nIHRvIGRlYnVnZ2VyIik7Y29uc3Qgbj1uZXcgdDtuLmNvbm5lY3RUb01haW5UaHJlYWQoKSx2bigiQ29ubmVjdGVkIHRvIGRlYnVnZ2VyIik7Y29uc3QgZT1uZXcgTWFwO24ub24oIkRlYnVnZ2VyLnNjcmlwdFBhcnNlZCIsKHQ9PntlLnNldCh0LnBhcmFtcy5zY3JpcHRJZCx0LnBhcmFtcy51cmwpfSkpLG4ub24oIkRlYnVnZ2VyLnBhdXNlZCIsKHQ9PntpZigib3RoZXIiPT09dC5wYXJhbXMucmVhc29uKXRyeXt2bigiRGVidWdnZXIgcGF1c2VkIik7Y29uc3Qgcz1bLi4udC5wYXJhbXMuY2FsbEZyYW1lc10saT1nbi5hcHBSb290UGF0aD9mdW5jdGlvbih0PShwcm9jZXNzLmFyZ3ZbMV0/cm4ocHJvY2Vzcy5hcmd2WzFdKTpwcm9jZXNzLmN3ZCgpKSxuPSJcXCI9PT1vKXtjb25zdCBlPW4/bW4odCk6dDtyZXR1cm4gdD0+e2lmKCF0KXJldHVybjtjb25zdCBvPW4/bW4odCk6dDtsZXR7ZGlyOnMsYmFzZTppLGV4dDpjfT1yLnBhcnNlKG8pOyIuanMiIT09YyYmIi5tanMiIT09YyYmIi5janMiIT09Y3x8KGk9aS5zbGljZSgwLC0xKmMubGVuZ3RoKSk7Y29uc3QgdT1kZWNvZGVVUklDb21wb25lbnQoaSk7c3x8KHM9Ii4iKTtjb25zdCBhPXMubGFzdEluZGV4T2YoIi9ub2RlX21vZHVsZXMiKTtpZihhPi0xKXJldHVybmAke3Muc2xpY2UoYSsxNCkucmVwbGFjZSgvXC8vZywiLiIpfToke3V9YDtpZihzLnN0YXJ0c1dpdGgoZSkpe2NvbnN0IHQ9cy5zbGljZShlLmxlbmd0aCsxKS5yZXBsYWNlKC9cLy9nLCIuIik7cmV0dXJuIHQ/YCR7dH06JHt1fWA6dX1yZXR1cm4gdX19KGduLmFwcFJvb3RQYXRoKTooKT0+e30sYz1zLm1hcCgodD0+ZnVuY3Rpb24odCxuLGUpe2NvbnN0IHI9bj9uLnJlcGxhY2UoL15maWxlOlwvXC8vLCIiKTp2b2lkIDAsbz10LmxvY2F0aW9uLmNvbHVtbk51bWJlcj90LmxvY2F0aW9uLmNvbHVtbk51bWJlcisxOnZvaWQgMCxzPXQubG9jYXRpb24ubGluZU51bWJlcj90LmxvY2F0aW9uLmxpbmVOdW1iZXIrMTp2b2lkIDA7cmV0dXJuIEooe2ZpbGVuYW1lOnIsbW9kdWxlOmUociksZnVuY3Rpb246dC5mdW5jdGlvbk5hbWV8fCQsY29sbm86byxsaW5lbm86cyxpbl9hcHA6cj9vbihyKTp2b2lkIDB9KX0odCxlLmdldCh0LmxvY2F0aW9uLnNjcmlwdElkKSxpKSkpLHU9c2V0VGltZW91dCgoKCk9PntUbihjKS50aGVuKG51bGwsKCgpPT57dm4oIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pKX0pLDVlMyk7bi5wb3N0KCJSdW50aW1lLmV2YWx1YXRlIix7ZXhwcmVzc2lvbjoiZ2xvYmFsLl9fU0VOVFJZX0dFVF9TQ09QRVNfXygpOyIsc2lsZW50OiEwLHJldHVybkJ5VmFsdWU6ITB9LCgodCxlKT0+e3QmJnZuKGBFcnJvciBleGVjdXRpbmcgc2NyaXB0OiAnJHt0Lm1lc3NhZ2V9J2ApLGNsZWFyVGltZW91dCh1KTtjb25zdCByPWU/LnJlc3VsdD9lLnJlc3VsdC52YWx1ZTp2b2lkIDA7bi5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxuLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSxUbihjLHIpLnRoZW4obnVsbCwoKCk9Pnt2bigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSkpfSkpfWNhdGNoKHQpe3Rocm93IG4ucG9zdCgiRGVidWdnZXIucmVzdW1lIiksbi5wb3N0KCJEZWJ1Z2dlci5kaXNhYmxlIiksdH19KSksQ249KCk9Pnt0cnl7bi5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiLCgoKT0+e24ucG9zdCgiRGVidWdnZXIucGF1c2UiKX0pKX1jYXRjaCh0KXt9fX1jb25zdHtwb2xsOmpufT1mdW5jdGlvbih0LG4sZSxyKXtjb25zdCBvPXQoKTtsZXQgcz0hMSxpPSEwO3JldHVybiBzZXRJbnRlcnZhbCgoKCk9Pntjb25zdCB0PW8uZ2V0VGltZU1zKCk7ITE9PT1zJiZ0Pm4rZSYmKHM9ITAsaSYmcigpKSx0PG4rZSYmKHM9ITEpfSksMjApLHtwb2xsOigpPT57by5yZXNldCgpfSxlbmFibGVkOnQ9PntpPXR9fX0oKGZ1bmN0aW9uKCl7bGV0IHQ9cHJvY2Vzcy5ocnRpbWUoKTtyZXR1cm57Z2V0VGltZU1zOigpPT57Y29uc3RbbixlXT1wcm9jZXNzLmhydGltZSh0KTtyZXR1cm4gTWF0aC5mbG9vcigxZTMqbitlLzFlNil9LHJlc2V0OigpPT57dD1wcm9jZXNzLmhydGltZSgpfX19KSxnbi5wb2xsSW50ZXJ2YWwsZ24uYW5yVGhyZXNob2xkLChmdW5jdGlvbigpe3ZuKCJXYXRjaGRvZyB0aW1lb3V0IiksQ24/KHZuKCJQYXVzaW5nIGRlYnVnZ2VyIHRvIGNhcHR1cmUgc3RhY2sgdHJhY2UiKSxDbigpKToodm4oIkNhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UiKSxUbigpLnRoZW4obnVsbCwoKCk9Pnt2bigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkIG9uIHdhdGNoZG9nIHRpbWVvdXQuIil9KSkpfSkpO2U/Lm9uKCJtZXNzYWdlIiwodD0+e3Quc2Vzc2lvbiYmKHluPUsodC5zZXNzaW9uKSksdC5kZWJ1Z0ltYWdlcyYmKF9uPXQuZGVidWdJbWFnZXMpLGpuKCl9KSk7';
13
13
 
14
14
  const DEFAULT_INTERVAL = 50;
15
15
  const DEFAULT_HANG_THRESHOLD = 5000;
@@ -6,6 +6,17 @@ const core$1 = require('@sentry/core');
6
6
  const debugBuild = require('../../debug-build.js');
7
7
  const utils = require('./utils.js');
8
8
 
9
+ // The reason this "before OTEL" integration even exists is due to timing reasons. We need to be able to register a
10
+ // `res.on('close')` handler **after** OTEL registers its own handler (which it uses to end spans), so that we can do
11
+ // something (ie. flush) after OTEL has ended a span for a request. If you think about it like an onion:
12
+ //
13
+ // (Sentry after OTEL instrumentation
14
+ // (OTEL instrumentation
15
+ // (Sentry before OTEL instrumentation
16
+ // (orig HTTP request handler))))
17
+ //
18
+ // registering an instrumentation before OTEL allows us to do this for incoming requests.
19
+
9
20
  /**
10
21
  * A Sentry specific http instrumentation that is applied before the otel instrumentation.
11
22
  */
@@ -67,47 +78,51 @@ class SentryHttpInstrumentationBeforeOtel extends instrumentation.Instrumentatio
67
78
  function patchResponseToFlushOnServerlessPlatforms(res) {
68
79
  // Freely extend this function with other platforms if necessary
69
80
  if (process.env.VERCEL) {
70
- let markOnEndDone = () => undefined;
71
- const onEndDonePromise = new Promise(res => {
72
- markOnEndDone = res;
73
- });
74
-
75
- res.on('close', () => {
76
- markOnEndDone();
77
- });
78
-
79
- // eslint-disable-next-line @typescript-eslint/unbound-method
80
- res.end = new Proxy(res.end, {
81
- apply(target, thisArg, argArray) {
82
- core$1.vercelWaitUntil(
83
- new Promise(finishWaitUntil => {
84
- // Define a timeout that unblocks the lambda just to be safe so we're not indefinitely keeping it alive, exploding server bills
85
- const timeout = setTimeout(() => {
86
- finishWaitUntil();
87
- }, 2000);
88
-
89
- onEndDonePromise
90
- .then(() => {
91
- debugBuild.DEBUG_BUILD && core$1.logger.log('Flushing events before Vercel Lambda freeze');
92
- return core$1.flush(2000);
93
- })
94
- .then(
95
- () => {
96
- clearTimeout(timeout);
97
- finishWaitUntil();
98
- },
99
- e => {
100
- clearTimeout(timeout);
101
- debugBuild.DEBUG_BUILD && core$1.logger.log('Error while flushing events for Vercel:\n', e);
102
- finishWaitUntil();
103
- },
104
- );
105
- }),
106
- );
107
-
108
- return target.apply(thisArg, argArray);
109
- },
110
- });
81
+ // In some cases res.end does not seem to be defined leading to errors if passed to Proxy
82
+ // https://github.com/getsentry/sentry-javascript/issues/15759
83
+ if (typeof res.end === 'function') {
84
+ let markOnEndDone = () => undefined;
85
+ const onEndDonePromise = new Promise(res => {
86
+ markOnEndDone = res;
87
+ });
88
+
89
+ res.on('close', () => {
90
+ markOnEndDone();
91
+ });
92
+
93
+ // eslint-disable-next-line @typescript-eslint/unbound-method
94
+ res.end = new Proxy(res.end, {
95
+ apply(target, thisArg, argArray) {
96
+ core$1.vercelWaitUntil(
97
+ new Promise(finishWaitUntil => {
98
+ // Define a timeout that unblocks the lambda just to be safe so we're not indefinitely keeping it alive, exploding server bills
99
+ const timeout = setTimeout(() => {
100
+ finishWaitUntil();
101
+ }, 2000);
102
+
103
+ onEndDonePromise
104
+ .then(() => {
105
+ debugBuild.DEBUG_BUILD && core$1.logger.log('Flushing events before Vercel Lambda freeze');
106
+ return core$1.flush(2000);
107
+ })
108
+ .then(
109
+ () => {
110
+ clearTimeout(timeout);
111
+ finishWaitUntil();
112
+ },
113
+ e => {
114
+ clearTimeout(timeout);
115
+ debugBuild.DEBUG_BUILD && core$1.logger.log('Error while flushing events for Vercel:\n', e);
116
+ finishWaitUntil();
117
+ },
118
+ );
119
+ }),
120
+ );
121
+
122
+ return target.apply(thisArg, argArray);
123
+ },
124
+ });
125
+ }
111
126
  }
112
127
  }
113
128
 
@@ -1 +1 @@
1
- {"version":3,"file":"SentryHttpInstrumentationBeforeOtel.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentationBeforeOtel.ts"],"sourcesContent":["import { VERSION } from '@opentelemetry/core';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { flush, logger, vercelWaitUntil } from '@sentry/core';\nimport type * as http from 'node:http';\nimport type * as https from 'node:https';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { stealthWrap } from './utils';\n\ntype Http = typeof http;\ntype Https = typeof https;\n\n/**\n * A Sentry specific http instrumentation that is applied before the otel instrumentation.\n */\nexport class SentryHttpInstrumentationBeforeOtel extends InstrumentationBase {\n public constructor() {\n super('@sentry/instrumentation-http-before-otel', VERSION, {});\n }\n\n // eslint-disable-next-line jsdoc/require-jsdoc\n public init(): [InstrumentationNodeModuleDefinition, InstrumentationNodeModuleDefinition] {\n return [this._getHttpsInstrumentation(), this._getHttpInstrumentation()];\n }\n\n /** Get the instrumentation for the http module. */\n private _getHttpInstrumentation(): InstrumentationNodeModuleDefinition {\n return new InstrumentationNodeModuleDefinition('http', ['*'], (moduleExports: Http): Http => {\n // Patch incoming requests\n stealthWrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction());\n\n return moduleExports;\n });\n }\n\n /** Get the instrumentation for the https module. */\n private _getHttpsInstrumentation(): InstrumentationNodeModuleDefinition {\n return new InstrumentationNodeModuleDefinition('https', ['*'], (moduleExports: Https): Https => {\n // Patch incoming requests\n stealthWrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction());\n\n return moduleExports;\n });\n }\n\n /**\n * Patch the incoming request function for request isolation.\n */\n private _getPatchIncomingRequestFunction(): (\n original: (event: string, ...args: unknown[]) => boolean,\n ) => (this: unknown, event: string, ...args: unknown[]) => boolean {\n return (\n original: (event: string, ...args: unknown[]) => boolean,\n ): ((this: unknown, event: string, ...args: unknown[]) => boolean) => {\n return function incomingRequest(this: unknown, ...args: [event: string, ...args: unknown[]]): boolean {\n // Only traces request events\n if (args[0] !== 'request') {\n return original.apply(this, args);\n }\n\n const response = args[1] as http.OutgoingMessage;\n\n patchResponseToFlushOnServerlessPlatforms(response);\n\n return original.apply(this, args);\n };\n };\n }\n}\n\nfunction patchResponseToFlushOnServerlessPlatforms(res: http.OutgoingMessage): void {\n // Freely extend this function with other platforms if necessary\n if (process.env.VERCEL) {\n let markOnEndDone = (): void => undefined;\n const onEndDonePromise = new Promise<void>(res => {\n markOnEndDone = res;\n });\n\n res.on('close', () => {\n markOnEndDone();\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n res.end = new Proxy(res.end, {\n apply(target, thisArg, argArray) {\n vercelWaitUntil(\n new Promise<void>(finishWaitUntil => {\n // Define a timeout that unblocks the lambda just to be safe so we're not indefinitely keeping it alive, exploding server bills\n const timeout = setTimeout(() => {\n finishWaitUntil();\n }, 2000);\n\n onEndDonePromise\n .then(() => {\n DEBUG_BUILD && logger.log('Flushing events before Vercel Lambda freeze');\n return flush(2000);\n })\n .then(\n () => {\n clearTimeout(timeout);\n finishWaitUntil();\n },\n e => {\n clearTimeout(timeout);\n DEBUG_BUILD && logger.log('Error while flushing events for Vercel:\\n', e);\n finishWaitUntil();\n },\n );\n }),\n );\n\n return target.apply(thisArg, argArray);\n },\n });\n }\n}\n"],"names":["InstrumentationBase","VERSION","InstrumentationNodeModuleDefinition","stealthWrap","vercelWaitUntil","DEBUG_BUILD","logger","flush"],"mappings":"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"}
1
+ {"version":3,"file":"SentryHttpInstrumentationBeforeOtel.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentationBeforeOtel.ts"],"sourcesContent":["import { VERSION } from '@opentelemetry/core';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { flush, logger, vercelWaitUntil } from '@sentry/core';\nimport type * as http from 'node:http';\nimport type * as https from 'node:https';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { stealthWrap } from './utils';\n\ntype Http = typeof http;\ntype Https = typeof https;\n\n// The reason this \"before OTEL\" integration even exists is due to timing reasons. We need to be able to register a\n// `res.on('close')` handler **after** OTEL registers its own handler (which it uses to end spans), so that we can do\n// something (ie. flush) after OTEL has ended a span for a request. If you think about it like an onion:\n//\n// (Sentry after OTEL instrumentation\n// (OTEL instrumentation\n// (Sentry before OTEL instrumentation\n// (orig HTTP request handler))))\n//\n// registering an instrumentation before OTEL allows us to do this for incoming requests.\n\n/**\n * A Sentry specific http instrumentation that is applied before the otel instrumentation.\n */\nexport class SentryHttpInstrumentationBeforeOtel extends InstrumentationBase {\n public constructor() {\n super('@sentry/instrumentation-http-before-otel', VERSION, {});\n }\n\n // eslint-disable-next-line jsdoc/require-jsdoc\n public init(): [InstrumentationNodeModuleDefinition, InstrumentationNodeModuleDefinition] {\n return [this._getHttpsInstrumentation(), this._getHttpInstrumentation()];\n }\n\n /** Get the instrumentation for the http module. */\n private _getHttpInstrumentation(): InstrumentationNodeModuleDefinition {\n return new InstrumentationNodeModuleDefinition('http', ['*'], (moduleExports: Http): Http => {\n // Patch incoming requests\n stealthWrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction());\n\n return moduleExports;\n });\n }\n\n /** Get the instrumentation for the https module. */\n private _getHttpsInstrumentation(): InstrumentationNodeModuleDefinition {\n return new InstrumentationNodeModuleDefinition('https', ['*'], (moduleExports: Https): Https => {\n // Patch incoming requests\n stealthWrap(moduleExports.Server.prototype, 'emit', this._getPatchIncomingRequestFunction());\n\n return moduleExports;\n });\n }\n\n /**\n * Patch the incoming request function for request isolation.\n */\n private _getPatchIncomingRequestFunction(): (\n original: (event: string, ...args: unknown[]) => boolean,\n ) => (this: unknown, event: string, ...args: unknown[]) => boolean {\n return (\n original: (event: string, ...args: unknown[]) => boolean,\n ): ((this: unknown, event: string, ...args: unknown[]) => boolean) => {\n return function incomingRequest(this: unknown, ...args: [event: string, ...args: unknown[]]): boolean {\n // Only traces request events\n if (args[0] !== 'request') {\n return original.apply(this, args);\n }\n\n const response = args[1] as http.OutgoingMessage;\n\n patchResponseToFlushOnServerlessPlatforms(response);\n\n return original.apply(this, args);\n };\n };\n }\n}\n\nfunction patchResponseToFlushOnServerlessPlatforms(res: http.OutgoingMessage): void {\n // Freely extend this function with other platforms if necessary\n if (process.env.VERCEL) {\n // In some cases res.end does not seem to be defined leading to errors if passed to Proxy\n // https://github.com/getsentry/sentry-javascript/issues/15759\n if (typeof res.end === 'function') {\n let markOnEndDone = (): void => undefined;\n const onEndDonePromise = new Promise<void>(res => {\n markOnEndDone = res;\n });\n\n res.on('close', () => {\n markOnEndDone();\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n res.end = new Proxy(res.end, {\n apply(target, thisArg, argArray) {\n vercelWaitUntil(\n new Promise<void>(finishWaitUntil => {\n // Define a timeout that unblocks the lambda just to be safe so we're not indefinitely keeping it alive, exploding server bills\n const timeout = setTimeout(() => {\n finishWaitUntil();\n }, 2000);\n\n onEndDonePromise\n .then(() => {\n DEBUG_BUILD && logger.log('Flushing events before Vercel Lambda freeze');\n return flush(2000);\n })\n .then(\n () => {\n clearTimeout(timeout);\n finishWaitUntil();\n },\n e => {\n clearTimeout(timeout);\n DEBUG_BUILD && logger.log('Error while flushing events for Vercel:\\n', e);\n finishWaitUntil();\n },\n );\n }),\n );\n\n return target.apply(thisArg, argArray);\n },\n });\n }\n }\n}\n"],"names":["InstrumentationBase","VERSION","InstrumentationNodeModuleDefinition","stealthWrap","vercelWaitUntil","DEBUG_BUILD","logger","flush"],"mappings":"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"}
@@ -56,7 +56,17 @@ const httpIntegration = core.defineIntegration((options = {}) => {
56
56
  return {
57
57
  name: INTEGRATION_NAME,
58
58
  setupOnce() {
59
- instrumentSentryHttpBeforeOtel();
59
+ // Below, we instrument the Node.js HTTP API three times. 2 times Sentry-specific, 1 time OTEL specific.
60
+ // Due to timing reasons, we sometimes need to apply Sentry instrumentation _before_ we apply the OTEL
61
+ // instrumentation (e.g. to flush on serverless platforms), and sometimes we need to apply Sentry instrumentation
62
+ // _after_ we apply OTEL instrumentation (e.g. for isolation scope handling and breadcrumbs).
63
+
64
+ // This is Sentry-specific instrumentation that is applied _before_ any OTEL instrumentation.
65
+ if (process.env.VERCEL) {
66
+ // Currently this instrumentation only does something when deployed on Vercel, so to save some overhead, we short circuit adding it here only for Vercel.
67
+ // If it's functionality is extended in the future, feel free to remove the if statement and this comment.
68
+ instrumentSentryHttpBeforeOtel();
69
+ }
60
70
 
61
71
  const instrumentSpans = _shouldInstrumentSpans(options, core.getClient()?.getOptions());
62
72
 
@@ -66,9 +76,7 @@ const httpIntegration = core.defineIntegration((options = {}) => {
66
76
  instrumentOtelHttp(instrumentationConfig);
67
77
  }
68
78
 
69
- // This is the Sentry-specific instrumentation that isolates requests & creates breadcrumbs
70
- // Note that this _has_ to be wrapped after the OTEL instrumentation,
71
- // otherwise the isolation will not work correctly
79
+ // This is Sentry-specific instrumentation that is applied _after_ any OTEL instrumentation.
72
80
  instrumentSentryHttp({
73
81
  ...options,
74
82
  // If spans are not instrumented, it means the HttpInstrumentation has not been added
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/integrations/http/index.ts"],"sourcesContent":["import type { ClientRequest, IncomingMessage, RequestOptions, ServerResponse } from 'node:http';\nimport { diag } from '@opentelemetry/api';\nimport type { HttpInstrumentationConfig } from '@opentelemetry/instrumentation-http';\nimport { HttpInstrumentation } from '@opentelemetry/instrumentation-http';\nimport type { Span } from '@sentry/core';\nimport { defineIntegration, getClient } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport type { NodeClient } from '../../sdk/client';\nimport type { HTTPModuleRequestIncomingMessage } from '../../transports/http-module';\nimport type { NodeClientOptions } from '../../types';\nimport { addOriginToSpan } from '../../utils/addOriginToSpan';\nimport { getRequestUrl } from '../../utils/getRequestUrl';\nimport type { SentryHttpInstrumentationOptions } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentation } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentationBeforeOtel } from './SentryHttpInstrumentationBeforeOtel';\n\nconst INTEGRATION_NAME = 'Http';\n\nconst INSTRUMENTATION_NAME = '@opentelemetry_sentry-patched/instrumentation-http';\n\ninterface HttpOptions {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * If set to false, do not emit any spans.\n * This will ensure that the default HttpInstrumentation from OpenTelemetry is not setup,\n * only the Sentry-specific instrumentation for request isolation is applied.\n *\n * If `skipOpenTelemetrySetup: true` is configured, this defaults to `false`, otherwise it defaults to `true`.\n */\n spans?: boolean;\n\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n *\n * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * For example: `'https://someService.com/users/details?id=123'`\n *\n * The `request` param contains the original {@type RequestOptions} object used to make the outgoing request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: IncomingMessage) => boolean;\n\n /**\n * If true, do not generate spans for incoming requests at all.\n * This is used by Remix to avoid generating spans for incoming requests, as it generates its own spans.\n */\n disableIncomingRequestSpans?: boolean;\n\n /**\n * Additional instrumentation options that are passed to the underlying HttpInstrumentation.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: ClientRequest | HTTPModuleRequestIncomingMessage) => void;\n responseHook?: (span: Span, response: HTTPModuleRequestIncomingMessage | ServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: ClientRequest | HTTPModuleRequestIncomingMessage,\n response: HTTPModuleRequestIncomingMessage | ServerResponse,\n ) => void;\n\n /**\n * You can pass any configuration through to the underlying instrumentation.\n * Note that there are no semver guarantees for this!\n */\n _experimentalConfig?: ConstructorParameters<typeof HttpInstrumentation>[0];\n };\n}\n\nconst instrumentSentryHttpBeforeOtel = generateInstrumentOnce(`${INTEGRATION_NAME}.sentry-before-otel`, () => {\n return new SentryHttpInstrumentationBeforeOtel();\n});\n\nconst instrumentSentryHttp = generateInstrumentOnce<SentryHttpInstrumentationOptions>(\n `${INTEGRATION_NAME}.sentry`,\n options => {\n return new SentryHttpInstrumentation(options);\n },\n);\n\nexport const instrumentOtelHttp = generateInstrumentOnce<HttpInstrumentationConfig>(INTEGRATION_NAME, config => {\n const instrumentation = new HttpInstrumentation(config);\n\n // We want to update the logger namespace so we can better identify what is happening here\n try {\n instrumentation['_diag'] = diag.createComponentLogger({\n namespace: INSTRUMENTATION_NAME,\n });\n // @ts-expect-error We are writing a read-only property here...\n instrumentation.instrumentationName = INSTRUMENTATION_NAME;\n } catch {\n // ignore errors here...\n }\n\n return instrumentation;\n});\n\n/** Exported only for tests. */\nexport function _shouldInstrumentSpans(options: HttpOptions, clientOptions: Partial<NodeClientOptions> = {}): boolean {\n // If `spans` is passed in, it takes precedence\n // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true`\n return typeof options.spans === 'boolean' ? options.spans : !clientOptions.skipOpenTelemetrySetup;\n}\n\n/**\n * The http integration instruments Node's internal http and https modules.\n * It creates breadcrumbs and spans for outgoing HTTP requests which will be attached to the currently active span.\n */\nexport const httpIntegration = defineIntegration((options: HttpOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentSentryHttpBeforeOtel();\n\n const instrumentSpans = _shouldInstrumentSpans(options, getClient<NodeClient>()?.getOptions());\n\n // This is the \"regular\" OTEL instrumentation that emits spans\n if (instrumentSpans) {\n const instrumentationConfig = getConfigWithDefaults(options);\n instrumentOtelHttp(instrumentationConfig);\n }\n\n // This is the Sentry-specific instrumentation that isolates requests & creates breadcrumbs\n // Note that this _has_ to be wrapped after the OTEL instrumentation,\n // otherwise the isolation will not work correctly\n instrumentSentryHttp({\n ...options,\n // If spans are not instrumented, it means the HttpInstrumentation has not been added\n // In that case, we want to handle incoming trace extraction ourselves\n extractIncomingTraceFromHeader: !instrumentSpans,\n });\n },\n };\n});\n\n/**\n * Determines if @param req is a ClientRequest, meaning the request was created within the express app\n * and it's an outgoing request.\n * Checking for properties instead of using `instanceOf` to avoid importing the request classes.\n */\nfunction _isClientRequest(req: ClientRequest | HTTPModuleRequestIncomingMessage): req is ClientRequest {\n return 'outputData' in req && 'outputSize' in req && !('client' in req) && !('statusCode' in req);\n}\n\n/**\n * Detects if an incoming request is a prefetch request.\n */\nfunction isKnownPrefetchRequest(req: HTTPModuleRequestIncomingMessage): boolean {\n // Currently only handles Next.js prefetch requests but may check other frameworks in the future.\n return req.headers['next-router-prefetch'] === '1';\n}\n\nfunction getConfigWithDefaults(options: Partial<HttpOptions> = {}): HttpInstrumentationConfig {\n const instrumentationConfig = {\n ...options.instrumentation?._experimentalConfig,\n\n disableIncomingRequestInstrumentation: options.disableIncomingRequestSpans,\n\n ignoreOutgoingRequestHook: request => {\n const url = getRequestUrl(request);\n\n if (!url) {\n return false;\n }\n\n const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;\n if (_ignoreOutgoingRequests?.(url, request)) {\n return true;\n }\n\n return false;\n },\n\n ignoreIncomingRequestHook: request => {\n // request.url is the only property that holds any information about the url\n // it only consists of the URL path and query string (if any)\n const urlPath = request.url;\n\n const method = request.method?.toUpperCase();\n // We do not capture OPTIONS/HEAD requests as transactions\n if (method === 'OPTIONS' || method === 'HEAD') {\n return true;\n }\n\n const _ignoreIncomingRequests = options.ignoreIncomingRequests;\n if (urlPath && _ignoreIncomingRequests?.(urlPath, request)) {\n return true;\n }\n\n return false;\n },\n\n requireParentforOutgoingSpans: false,\n requireParentforIncomingSpans: false,\n requestHook: (span, req) => {\n addOriginToSpan(span, 'auto.http.otel.http');\n if (!_isClientRequest(req) && isKnownPrefetchRequest(req)) {\n span.setAttribute('sentry.http.prefetch', true);\n }\n\n options.instrumentation?.requestHook?.(span, req);\n },\n responseHook: (span, res) => {\n options.instrumentation?.responseHook?.(span, res);\n },\n applyCustomAttributesOnSpan: (\n span: Span,\n request: ClientRequest | HTTPModuleRequestIncomingMessage,\n response: HTTPModuleRequestIncomingMessage | ServerResponse,\n ) => {\n options.instrumentation?.applyCustomAttributesOnSpan?.(span, request, response);\n },\n } satisfies HttpInstrumentationConfig;\n\n return instrumentationConfig;\n}\n"],"names":["generateInstrumentOnce","SentryHttpInstrumentationBeforeOtel","SentryHttpInstrumentation","HttpInstrumentation","diag","defineIntegration","getClient","getRequestUrl","addOriginToSpan"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/integrations/http/index.ts"],"sourcesContent":["import type { ClientRequest, IncomingMessage, RequestOptions, ServerResponse } from 'node:http';\nimport { diag } from '@opentelemetry/api';\nimport type { HttpInstrumentationConfig } from '@opentelemetry/instrumentation-http';\nimport { HttpInstrumentation } from '@opentelemetry/instrumentation-http';\nimport type { Span } from '@sentry/core';\nimport { defineIntegration, getClient } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport type { NodeClient } from '../../sdk/client';\nimport type { HTTPModuleRequestIncomingMessage } from '../../transports/http-module';\nimport type { NodeClientOptions } from '../../types';\nimport { addOriginToSpan } from '../../utils/addOriginToSpan';\nimport { getRequestUrl } from '../../utils/getRequestUrl';\nimport type { SentryHttpInstrumentationOptions } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentation } from './SentryHttpInstrumentation';\nimport { SentryHttpInstrumentationBeforeOtel } from './SentryHttpInstrumentationBeforeOtel';\n\nconst INTEGRATION_NAME = 'Http';\n\nconst INSTRUMENTATION_NAME = '@opentelemetry_sentry-patched/instrumentation-http';\n\ninterface HttpOptions {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * If set to false, do not emit any spans.\n * This will ensure that the default HttpInstrumentation from OpenTelemetry is not setup,\n * only the Sentry-specific instrumentation for request isolation is applied.\n *\n * If `skipOpenTelemetrySetup: true` is configured, this defaults to `false`, otherwise it defaults to `true`.\n */\n spans?: boolean;\n\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n trackIncomingRequestsAsSessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n *\n * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * For example: `'https://someService.com/users/details?id=123'`\n *\n * The `request` param contains the original {@type RequestOptions} object used to make the outgoing request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Do not capture spans for incoming HTTP requests to URLs where the given callback returns `true`.\n * Spans will be non recording if tracing is disabled.\n *\n * The `urlPath` param consists of the URL path and query string (if any) of the incoming request.\n * For example: `'/users/details?id=123'`\n *\n * The `request` param contains the original {@type IncomingMessage} object of the incoming request.\n * You can use it to filter on additional properties like method, headers, etc.\n */\n ignoreIncomingRequests?: (urlPath: string, request: IncomingMessage) => boolean;\n\n /**\n * If true, do not generate spans for incoming requests at all.\n * This is used by Remix to avoid generating spans for incoming requests, as it generates its own spans.\n */\n disableIncomingRequestSpans?: boolean;\n\n /**\n * Additional instrumentation options that are passed to the underlying HttpInstrumentation.\n */\n instrumentation?: {\n requestHook?: (span: Span, req: ClientRequest | HTTPModuleRequestIncomingMessage) => void;\n responseHook?: (span: Span, response: HTTPModuleRequestIncomingMessage | ServerResponse) => void;\n applyCustomAttributesOnSpan?: (\n span: Span,\n request: ClientRequest | HTTPModuleRequestIncomingMessage,\n response: HTTPModuleRequestIncomingMessage | ServerResponse,\n ) => void;\n\n /**\n * You can pass any configuration through to the underlying instrumentation.\n * Note that there are no semver guarantees for this!\n */\n _experimentalConfig?: ConstructorParameters<typeof HttpInstrumentation>[0];\n };\n}\n\nconst instrumentSentryHttpBeforeOtel = generateInstrumentOnce(`${INTEGRATION_NAME}.sentry-before-otel`, () => {\n return new SentryHttpInstrumentationBeforeOtel();\n});\n\nconst instrumentSentryHttp = generateInstrumentOnce<SentryHttpInstrumentationOptions>(\n `${INTEGRATION_NAME}.sentry`,\n options => {\n return new SentryHttpInstrumentation(options);\n },\n);\n\nexport const instrumentOtelHttp = generateInstrumentOnce<HttpInstrumentationConfig>(INTEGRATION_NAME, config => {\n const instrumentation = new HttpInstrumentation(config);\n\n // We want to update the logger namespace so we can better identify what is happening here\n try {\n instrumentation['_diag'] = diag.createComponentLogger({\n namespace: INSTRUMENTATION_NAME,\n });\n // @ts-expect-error We are writing a read-only property here...\n instrumentation.instrumentationName = INSTRUMENTATION_NAME;\n } catch {\n // ignore errors here...\n }\n\n return instrumentation;\n});\n\n/** Exported only for tests. */\nexport function _shouldInstrumentSpans(options: HttpOptions, clientOptions: Partial<NodeClientOptions> = {}): boolean {\n // If `spans` is passed in, it takes precedence\n // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true`\n return typeof options.spans === 'boolean' ? options.spans : !clientOptions.skipOpenTelemetrySetup;\n}\n\n/**\n * The http integration instruments Node's internal http and https modules.\n * It creates breadcrumbs and spans for outgoing HTTP requests which will be attached to the currently active span.\n */\nexport const httpIntegration = defineIntegration((options: HttpOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // Below, we instrument the Node.js HTTP API three times. 2 times Sentry-specific, 1 time OTEL specific.\n // Due to timing reasons, we sometimes need to apply Sentry instrumentation _before_ we apply the OTEL\n // instrumentation (e.g. to flush on serverless platforms), and sometimes we need to apply Sentry instrumentation\n // _after_ we apply OTEL instrumentation (e.g. for isolation scope handling and breadcrumbs).\n\n // This is Sentry-specific instrumentation that is applied _before_ any OTEL instrumentation.\n if (process.env.VERCEL) {\n // Currently this instrumentation only does something when deployed on Vercel, so to save some overhead, we short circuit adding it here only for Vercel.\n // If it's functionality is extended in the future, feel free to remove the if statement and this comment.\n instrumentSentryHttpBeforeOtel();\n }\n\n const instrumentSpans = _shouldInstrumentSpans(options, getClient<NodeClient>()?.getOptions());\n\n // This is the \"regular\" OTEL instrumentation that emits spans\n if (instrumentSpans) {\n const instrumentationConfig = getConfigWithDefaults(options);\n instrumentOtelHttp(instrumentationConfig);\n }\n\n // This is Sentry-specific instrumentation that is applied _after_ any OTEL instrumentation.\n instrumentSentryHttp({\n ...options,\n // If spans are not instrumented, it means the HttpInstrumentation has not been added\n // In that case, we want to handle incoming trace extraction ourselves\n extractIncomingTraceFromHeader: !instrumentSpans,\n });\n },\n };\n});\n\n/**\n * Determines if @param req is a ClientRequest, meaning the request was created within the express app\n * and it's an outgoing request.\n * Checking for properties instead of using `instanceOf` to avoid importing the request classes.\n */\nfunction _isClientRequest(req: ClientRequest | HTTPModuleRequestIncomingMessage): req is ClientRequest {\n return 'outputData' in req && 'outputSize' in req && !('client' in req) && !('statusCode' in req);\n}\n\n/**\n * Detects if an incoming request is a prefetch request.\n */\nfunction isKnownPrefetchRequest(req: HTTPModuleRequestIncomingMessage): boolean {\n // Currently only handles Next.js prefetch requests but may check other frameworks in the future.\n return req.headers['next-router-prefetch'] === '1';\n}\n\nfunction getConfigWithDefaults(options: Partial<HttpOptions> = {}): HttpInstrumentationConfig {\n const instrumentationConfig = {\n ...options.instrumentation?._experimentalConfig,\n\n disableIncomingRequestInstrumentation: options.disableIncomingRequestSpans,\n\n ignoreOutgoingRequestHook: request => {\n const url = getRequestUrl(request);\n\n if (!url) {\n return false;\n }\n\n const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;\n if (_ignoreOutgoingRequests?.(url, request)) {\n return true;\n }\n\n return false;\n },\n\n ignoreIncomingRequestHook: request => {\n // request.url is the only property that holds any information about the url\n // it only consists of the URL path and query string (if any)\n const urlPath = request.url;\n\n const method = request.method?.toUpperCase();\n // We do not capture OPTIONS/HEAD requests as transactions\n if (method === 'OPTIONS' || method === 'HEAD') {\n return true;\n }\n\n const _ignoreIncomingRequests = options.ignoreIncomingRequests;\n if (urlPath && _ignoreIncomingRequests?.(urlPath, request)) {\n return true;\n }\n\n return false;\n },\n\n requireParentforOutgoingSpans: false,\n requireParentforIncomingSpans: false,\n requestHook: (span, req) => {\n addOriginToSpan(span, 'auto.http.otel.http');\n if (!_isClientRequest(req) && isKnownPrefetchRequest(req)) {\n span.setAttribute('sentry.http.prefetch', true);\n }\n\n options.instrumentation?.requestHook?.(span, req);\n },\n responseHook: (span, res) => {\n options.instrumentation?.responseHook?.(span, res);\n },\n applyCustomAttributesOnSpan: (\n span: Span,\n request: ClientRequest | HTTPModuleRequestIncomingMessage,\n response: HTTPModuleRequestIncomingMessage | ServerResponse,\n ) => {\n options.instrumentation?.applyCustomAttributesOnSpan?.(span, request, response);\n },\n } satisfies HttpInstrumentationConfig;\n\n return instrumentationConfig;\n}\n"],"names":["generateInstrumentOnce","SentryHttpInstrumentationBeforeOtel","SentryHttpInstrumentation","HttpInstrumentation","diag","defineIntegration","getClient","getRequestUrl","addOriginToSpan"],"mappings":"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"}
@@ -6,7 +6,7 @@ const debug = require('../../utils/debug.js');
6
6
  const common = require('./common.js');
7
7
 
8
8
  // This string is a placeholder that gets overwritten with the worker code.
9
- const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZSA5LjcuMCAoNTI2ZWQ4ZSkgfCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0ICovCmltcG9ydHtTZXNzaW9uIGFzIGV9ZnJvbSJub2RlOmluc3BlY3Rvci9wcm9taXNlcyI7aW1wb3J0e3dvcmtlckRhdGEgYXMgdH1mcm9tIm5vZGU6d29ya2VyX3RocmVhZHMiO2NvbnN0IG49Z2xvYmFsVGhpcyxvPSJ1bmRlZmluZWQiPT10eXBlb2YgX19TRU5UUllfREVCVUdfX3x8X19TRU5UUllfREVCVUdfXyxpPSI5LjcuMCI7Y29uc3QgYT1bImRlYnVnIiwiaW5mbyIsIndhcm4iLCJlcnJvciIsImxvZyIsImFzc2VydCIsInRyYWNlIl0scz17fTtmdW5jdGlvbiBjKGUpe2lmKCEoImNvbnNvbGUiaW4gbikpcmV0dXJuIGUoKTtjb25zdCB0PW4uY29uc29sZSxvPXt9LGk9T2JqZWN0LmtleXMocyk7aS5mb3JFYWNoKChlPT57Y29uc3Qgbj1zW2VdO29bZV09dFtlXSx0W2VdPW59KSk7dHJ5e3JldHVybiBlKCl9ZmluYWxseXtpLmZvckVhY2goKGU9Pnt0W2VdPW9bZV19KSl9fSFmdW5jdGlvbihlLHQsbz1uKXtjb25zdCBhPW8uX19TRU5UUllfXz1vLl9fU0VOVFJZX198fHt9LHM9YVtpXT1hW2ldfHx7fTtzW2VdfHwoc1tlXT10KCkpfSgibG9nZ2VyIiwoZnVuY3Rpb24oKXtsZXQgZT0hMTtjb25zdCB0PXtlbmFibGU6KCk9PntlPSEwfSxkaXNhYmxlOigpPT57ZT0hMX0saXNFbmFibGVkOigpPT5lfTtyZXR1cm4gbz9hLmZvckVhY2goKG89Pnt0W29dPSguLi50KT0+e2UmJmMoKCgpPT57bi5jb25zb2xlW29dKGBTZW50cnkgTG9nZ2VyIFske299XTpgLC4uLnQpfSkpfX0pKTphLmZvckVhY2goKGU9Pnt0W2VdPSgpPT57fX0pKSx0fSkpO2NvbnN0IHI9Il9fU0VOVFJZX0VSUk9SX0xPQ0FMX1ZBUklBQkxFU19fIjtjb25zdCB1PXQ7ZnVuY3Rpb24gbCguLi5lKXt1LmRlYnVnJiZjKCgoKT0+Y29uc29sZS5sb2coIltMb2NhbFZhcmlhYmxlcyBXb3JrZXJdIiwuLi5lKSkpfWFzeW5jIGZ1bmN0aW9uIGYoZSx0LG4sbyl7Y29uc3QgaT1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO29bbl09aS5yZXN1bHQuZmlsdGVyKChlPT4ibGVuZ3RoIiE9PWUubmFtZSYmIWlzTmFOKHBhcnNlSW50KGUubmFtZSwxMCkpKSkuc29ydCgoKGUsdCk9PnBhcnNlSW50KGUubmFtZSwxMCktcGFyc2VJbnQodC5uYW1lLDEwKSkpLm1hcCgoZT0+ZS52YWx1ZT8udmFsdWUpKX1hc3luYyBmdW5jdGlvbiBnKGUsdCxuLG8pe2NvbnN0IGk9YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KTtvW25dPWkucmVzdWx0Lm1hcCgoZT0+W2UubmFtZSxlLnZhbHVlPy52YWx1ZV0pKS5yZWR1Y2UoKChlLFt0LG5dKT0+KGVbdF09bixlKSkse30pfWZ1bmN0aW9uIGQoZSx0KXtlLnZhbHVlJiYoInZhbHVlImluIGUudmFsdWU/dm9pZCAwPT09ZS52YWx1ZS52YWx1ZXx8bnVsbD09PWUudmFsdWUudmFsdWU/dFtlLm5hbWVdPWA8JHtlLnZhbHVlLnZhbHVlfT5gOnRbZS5uYW1lXT1lLnZhbHVlLnZhbHVlOiJkZXNjcmlwdGlvbiJpbiBlLnZhbHVlJiYiZnVuY3Rpb24iIT09ZS52YWx1ZS50eXBlP3RbZS5uYW1lXT1gPCR7ZS52YWx1ZS5kZXNjcmlwdGlvbn0+YDoidW5kZWZpbmVkIj09PWUudmFsdWUudHlwZSYmKHRbZS5uYW1lXT0iPHVuZGVmaW5lZD4iKSl9YXN5bmMgZnVuY3Rpb24gYihlLHQpe2NvbnN0IG49YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KSxvPXt9O2Zvcihjb25zdCB0IG9mIG4ucmVzdWx0KWlmKHQudmFsdWU/Lm9iamVjdElkJiYiQXJyYXkiPT09dC52YWx1ZS5jbGFzc05hbWUpe2NvbnN0IG49dC52YWx1ZS5vYmplY3RJZDthd2FpdCBmKGUsbix0Lm5hbWUsbyl9ZWxzZSBpZih0LnZhbHVlPy5vYmplY3RJZCYmIk9iamVjdCI9PT10LnZhbHVlLmNsYXNzTmFtZSl7Y29uc3Qgbj10LnZhbHVlLm9iamVjdElkO2F3YWl0IGcoZSxuLHQubmFtZSxvKX1lbHNlIHQudmFsdWUmJmQodCxvKTtyZXR1cm4gb31sZXQgcDsoYXN5bmMgZnVuY3Rpb24oKXtjb25zdCB0PW5ldyBlO3QuY29ubmVjdFRvTWFpblRocmVhZCgpLGwoIkNvbm5lY3RlZCB0byBtYWluIHRocmVhZCIpO2xldCBuPSExO3Qub24oIkRlYnVnZ2VyLnJlc3VtZWQiLCgoKT0+e249ITF9KSksdC5vbigiRGVidWdnZXIucGF1c2VkIiwoZT0+e249ITAsYXN5bmMgZnVuY3Rpb24oZSx7cmVhc29uOnQsZGF0YTp7b2JqZWN0SWQ6bn0sY2FsbEZyYW1lczpvfSl7aWYoImV4Y2VwdGlvbiIhPT10JiYicHJvbWlzZVJlamVjdGlvbiIhPT10KXJldHVybjtpZihwPy4oKSxudWxsPT1uKXJldHVybjtjb25zdCBpPVtdO2ZvcihsZXQgdD0wO3Q8by5sZW5ndGg7dCsrKXtjb25zdHtzY29wZUNoYWluOm4sZnVuY3Rpb25OYW1lOmEsdGhpczpzfT1vW3RdLGM9bi5maW5kKChlPT4ibG9jYWwiPT09ZS50eXBlKSkscj0iZ2xvYmFsIiE9PXMuY2xhc3NOYW1lJiZzLmNsYXNzTmFtZT9gJHtzLmNsYXNzTmFtZX0uJHthfWA6YTtpZih2b2lkIDA9PT1jPy5vYmplY3Qub2JqZWN0SWQpaVt0XT17ZnVuY3Rpb246cn07ZWxzZXtjb25zdCBuPWF3YWl0IGIoZSxjLm9iamVjdC5vYmplY3RJZCk7aVt0XT17ZnVuY3Rpb246cix2YXJzOm59fX1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuY2FsbEZ1bmN0aW9uT24iLHtmdW5jdGlvbkRlY2xhcmF0aW9uOmBmdW5jdGlvbigpIHsgdGhpcy4ke3J9ID0gdGhpcy4ke3J9IHx8ICR7SlNPTi5zdHJpbmdpZnkoaSl9OyB9YCxzaWxlbnQ6ITAsb2JqZWN0SWQ6bn0pLGF3YWl0IGUucG9zdCgiUnVudGltZS5yZWxlYXNlT2JqZWN0Iix7b2JqZWN0SWQ6bn0pfSh0LGUucGFyYW1zKS50aGVuKChhc3luYygpPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pLChhc3luYyBlPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pKX0pKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLmVuYWJsZSIpO2NvbnN0IG89ITEhPT11LmNhcHR1cmVBbGxFeGNlcHRpb25zO2lmKGF3YWl0IHQucG9zdCgiRGVidWdnZXIuc2V0UGF1c2VPbkV4Y2VwdGlvbnMiLHtzdGF0ZTpvPyJhbGwiOiJ1bmNhdWdodCJ9KSxvKXtjb25zdCBlPXUubWF4RXhjZXB0aW9uc1BlclNlY29uZHx8NTA7cD1mdW5jdGlvbihlLHQsbil7bGV0IG89MCxpPTUsYT0wO3JldHVybiBzZXRJbnRlcnZhbCgoKCk9PnswPT09YT9vPmUmJihpKj0yLG4oaSksaT44NjQwMCYmKGk9ODY0MDApLGE9aSk6KGEtPTEsMD09PWEmJnQoKSksbz0wfSksMWUzKS51bnJlZigpLCgpPT57bys9MX19KGUsKGFzeW5jKCk9PntsKCJSYXRlLWxpbWl0IGxpZnRlZC4iKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6ImFsbCJ9KX0pLChhc3luYyBlPT57bChgUmF0ZS1saW1pdCBleGNlZWRlZC4gRGlzYWJsaW5nIGNhcHR1cmluZyBvZiBjYXVnaHQgZXhjZXB0aW9ucyBmb3IgJHtlfSBzZWNvbmRzLmApLGF3YWl0IHQucG9zdCgiRGVidWdnZXIuc2V0UGF1c2VPbkV4Y2VwdGlvbnMiLHtzdGF0ZToidW5jYXVnaHQifSl9KSl9fSkoKS5jYXRjaCgoZT0+e2woIkZhaWxlZCB0byBzdGFydCBkZWJ1Z2dlciIsZSl9KSksc2V0SW50ZXJ2YWwoKCgpPT57fSksMWU0KTs=';
9
+ const base64WorkerScript = 'LyohIEBzZW50cnkvbm9kZSA5LjkuMCAoNDNjNGM4MykgfCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0ICovCmltcG9ydHtTZXNzaW9uIGFzIGV9ZnJvbSJub2RlOmluc3BlY3Rvci9wcm9taXNlcyI7aW1wb3J0e3dvcmtlckRhdGEgYXMgdH1mcm9tIm5vZGU6d29ya2VyX3RocmVhZHMiO2NvbnN0IG49Z2xvYmFsVGhpcyxvPSJ1bmRlZmluZWQiPT10eXBlb2YgX19TRU5UUllfREVCVUdfX3x8X19TRU5UUllfREVCVUdfXyxpPSI5LjkuMCI7Y29uc3QgYT1bImRlYnVnIiwiaW5mbyIsIndhcm4iLCJlcnJvciIsImxvZyIsImFzc2VydCIsInRyYWNlIl0scz17fTtmdW5jdGlvbiBjKGUpe2lmKCEoImNvbnNvbGUiaW4gbikpcmV0dXJuIGUoKTtjb25zdCB0PW4uY29uc29sZSxvPXt9LGk9T2JqZWN0LmtleXMocyk7aS5mb3JFYWNoKChlPT57Y29uc3Qgbj1zW2VdO29bZV09dFtlXSx0W2VdPW59KSk7dHJ5e3JldHVybiBlKCl9ZmluYWxseXtpLmZvckVhY2goKGU9Pnt0W2VdPW9bZV19KSl9fSFmdW5jdGlvbihlLHQsbz1uKXtjb25zdCBhPW8uX19TRU5UUllfXz1vLl9fU0VOVFJZX198fHt9LHM9YVtpXT1hW2ldfHx7fTtzW2VdfHwoc1tlXT10KCkpfSgibG9nZ2VyIiwoZnVuY3Rpb24oKXtsZXQgZT0hMTtjb25zdCB0PXtlbmFibGU6KCk9PntlPSEwfSxkaXNhYmxlOigpPT57ZT0hMX0saXNFbmFibGVkOigpPT5lfTtyZXR1cm4gbz9hLmZvckVhY2goKG89Pnt0W29dPSguLi50KT0+e2UmJmMoKCgpPT57bi5jb25zb2xlW29dKGBTZW50cnkgTG9nZ2VyIFske299XTpgLC4uLnQpfSkpfX0pKTphLmZvckVhY2goKGU9Pnt0W2VdPSgpPT57fX0pKSx0fSkpO2NvbnN0IHI9Il9fU0VOVFJZX0VSUk9SX0xPQ0FMX1ZBUklBQkxFU19fIjtjb25zdCB1PXQ7ZnVuY3Rpb24gbCguLi5lKXt1LmRlYnVnJiZjKCgoKT0+Y29uc29sZS5sb2coIltMb2NhbFZhcmlhYmxlcyBXb3JrZXJdIiwuLi5lKSkpfWFzeW5jIGZ1bmN0aW9uIGYoZSx0LG4sbyl7Y29uc3QgaT1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO29bbl09aS5yZXN1bHQuZmlsdGVyKChlPT4ibGVuZ3RoIiE9PWUubmFtZSYmIWlzTmFOKHBhcnNlSW50KGUubmFtZSwxMCkpKSkuc29ydCgoKGUsdCk9PnBhcnNlSW50KGUubmFtZSwxMCktcGFyc2VJbnQodC5uYW1lLDEwKSkpLm1hcCgoZT0+ZS52YWx1ZT8udmFsdWUpKX1hc3luYyBmdW5jdGlvbiBnKGUsdCxuLG8pe2NvbnN0IGk9YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KTtvW25dPWkucmVzdWx0Lm1hcCgoZT0+W2UubmFtZSxlLnZhbHVlPy52YWx1ZV0pKS5yZWR1Y2UoKChlLFt0LG5dKT0+KGVbdF09bixlKSkse30pfWZ1bmN0aW9uIGQoZSx0KXtlLnZhbHVlJiYoInZhbHVlImluIGUudmFsdWU/dm9pZCAwPT09ZS52YWx1ZS52YWx1ZXx8bnVsbD09PWUudmFsdWUudmFsdWU/dFtlLm5hbWVdPWA8JHtlLnZhbHVlLnZhbHVlfT5gOnRbZS5uYW1lXT1lLnZhbHVlLnZhbHVlOiJkZXNjcmlwdGlvbiJpbiBlLnZhbHVlJiYiZnVuY3Rpb24iIT09ZS52YWx1ZS50eXBlP3RbZS5uYW1lXT1gPCR7ZS52YWx1ZS5kZXNjcmlwdGlvbn0+YDoidW5kZWZpbmVkIj09PWUudmFsdWUudHlwZSYmKHRbZS5uYW1lXT0iPHVuZGVmaW5lZD4iKSl9YXN5bmMgZnVuY3Rpb24gYihlLHQpe2NvbnN0IG49YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KSxvPXt9O2Zvcihjb25zdCB0IG9mIG4ucmVzdWx0KWlmKHQudmFsdWU/Lm9iamVjdElkJiYiQXJyYXkiPT09dC52YWx1ZS5jbGFzc05hbWUpe2NvbnN0IG49dC52YWx1ZS5vYmplY3RJZDthd2FpdCBmKGUsbix0Lm5hbWUsbyl9ZWxzZSBpZih0LnZhbHVlPy5vYmplY3RJZCYmIk9iamVjdCI9PT10LnZhbHVlLmNsYXNzTmFtZSl7Y29uc3Qgbj10LnZhbHVlLm9iamVjdElkO2F3YWl0IGcoZSxuLHQubmFtZSxvKX1lbHNlIHQudmFsdWUmJmQodCxvKTtyZXR1cm4gb31sZXQgcDsoYXN5bmMgZnVuY3Rpb24oKXtjb25zdCB0PW5ldyBlO3QuY29ubmVjdFRvTWFpblRocmVhZCgpLGwoIkNvbm5lY3RlZCB0byBtYWluIHRocmVhZCIpO2xldCBuPSExO3Qub24oIkRlYnVnZ2VyLnJlc3VtZWQiLCgoKT0+e249ITF9KSksdC5vbigiRGVidWdnZXIucGF1c2VkIiwoZT0+e249ITAsYXN5bmMgZnVuY3Rpb24oZSx7cmVhc29uOnQsZGF0YTp7b2JqZWN0SWQ6bn0sY2FsbEZyYW1lczpvfSl7aWYoImV4Y2VwdGlvbiIhPT10JiYicHJvbWlzZVJlamVjdGlvbiIhPT10KXJldHVybjtpZihwPy4oKSxudWxsPT1uKXJldHVybjtjb25zdCBpPVtdO2ZvcihsZXQgdD0wO3Q8by5sZW5ndGg7dCsrKXtjb25zdHtzY29wZUNoYWluOm4sZnVuY3Rpb25OYW1lOmEsdGhpczpzfT1vW3RdLGM9bi5maW5kKChlPT4ibG9jYWwiPT09ZS50eXBlKSkscj0iZ2xvYmFsIiE9PXMuY2xhc3NOYW1lJiZzLmNsYXNzTmFtZT9gJHtzLmNsYXNzTmFtZX0uJHthfWA6YTtpZih2b2lkIDA9PT1jPy5vYmplY3Qub2JqZWN0SWQpaVt0XT17ZnVuY3Rpb246cn07ZWxzZXtjb25zdCBuPWF3YWl0IGIoZSxjLm9iamVjdC5vYmplY3RJZCk7aVt0XT17ZnVuY3Rpb246cix2YXJzOm59fX1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuY2FsbEZ1bmN0aW9uT24iLHtmdW5jdGlvbkRlY2xhcmF0aW9uOmBmdW5jdGlvbigpIHsgdGhpcy4ke3J9ID0gdGhpcy4ke3J9IHx8ICR7SlNPTi5zdHJpbmdpZnkoaSl9OyB9YCxzaWxlbnQ6ITAsb2JqZWN0SWQ6bn0pLGF3YWl0IGUucG9zdCgiUnVudGltZS5yZWxlYXNlT2JqZWN0Iix7b2JqZWN0SWQ6bn0pfSh0LGUucGFyYW1zKS50aGVuKChhc3luYygpPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pLChhc3luYyBlPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pKX0pKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLmVuYWJsZSIpO2NvbnN0IG89ITEhPT11LmNhcHR1cmVBbGxFeGNlcHRpb25zO2lmKGF3YWl0IHQucG9zdCgiRGVidWdnZXIuc2V0UGF1c2VPbkV4Y2VwdGlvbnMiLHtzdGF0ZTpvPyJhbGwiOiJ1bmNhdWdodCJ9KSxvKXtjb25zdCBlPXUubWF4RXhjZXB0aW9uc1BlclNlY29uZHx8NTA7cD1mdW5jdGlvbihlLHQsbil7bGV0IG89MCxpPTUsYT0wO3JldHVybiBzZXRJbnRlcnZhbCgoKCk9PnswPT09YT9vPmUmJihpKj0yLG4oaSksaT44NjQwMCYmKGk9ODY0MDApLGE9aSk6KGEtPTEsMD09PWEmJnQoKSksbz0wfSksMWUzKS51bnJlZigpLCgpPT57bys9MX19KGUsKGFzeW5jKCk9PntsKCJSYXRlLWxpbWl0IGxpZnRlZC4iKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6ImFsbCJ9KX0pLChhc3luYyBlPT57bChgUmF0ZS1saW1pdCBleGNlZWRlZC4gRGlzYWJsaW5nIGNhcHR1cmluZyBvZiBjYXVnaHQgZXhjZXB0aW9ucyBmb3IgJHtlfSBzZWNvbmRzLmApLGF3YWl0IHQucG9zdCgiRGVidWdnZXIuc2V0UGF1c2VPbkV4Y2VwdGlvbnMiLHtzdGF0ZToidW5jYXVnaHQifSl9KSl9fSkoKS5jYXRjaCgoZT0+e2woIkZhaWxlZCB0byBzdGFydCBkZWJ1Z2dlciIsZSl9KSksc2V0SW50ZXJ2YWwoKCgpPT57fSksMWU0KTs=';
10
10
 
11
11
  function log(...args) {
12
12
  core.logger.log('[LocalVariables]', ...args);
@@ -5,7 +5,13 @@ const core = require('@sentry/core');
5
5
  const instrument = require('../../otel/instrument.js');
6
6
  const ensureIsWrapped = require('../../utils/ensureIsWrapped.js');
7
7
 
8
- // We inline the types we care about here
8
+ /**
9
+ * Minimal request type containing properties around route information.
10
+ * Works for Fastify 3, 4 and presumably 5.
11
+ *
12
+ * Based on https://github.com/fastify/fastify/blob/ce3811f5f718be278bbcd4392c615d64230065a6/types/request.d.ts
13
+ */
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
15
 
10
16
  const INTEGRATION_NAME = 'Fastify';
11
17
 
@@ -47,10 +53,22 @@ const _fastifyIntegration = (() => {
47
53
  */
48
54
  const fastifyIntegration = core.defineIntegration(_fastifyIntegration);
49
55
 
56
+ /**
57
+ * Default function to determine if an error should be sent to Sentry
58
+ *
59
+ * 3xx and 4xx errors are not sent by default.
60
+ */
61
+ function defaultShouldHandleError(_error, _request, reply) {
62
+ const statusCode = reply.statusCode;
63
+ // 3xx and 4xx errors are not sent by default.
64
+ return statusCode >= 500 || statusCode <= 299;
65
+ }
66
+
50
67
  /**
51
68
  * Add an Fastify error handler to capture errors to Sentry.
52
69
  *
53
70
  * @param fastify The Fastify instance to which to add the error handler
71
+ * @param options Configuration options for the handler
54
72
  *
55
73
  * @example
56
74
  * ```javascript
@@ -66,23 +84,25 @@ const fastifyIntegration = core.defineIntegration(_fastifyIntegration);
66
84
  * app.listen({ port: 3000 });
67
85
  * ```
68
86
  */
69
- function setupFastifyErrorHandler(fastify) {
87
+ function setupFastifyErrorHandler(fastify, options) {
88
+ const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError;
89
+
70
90
  const plugin = Object.assign(
71
91
  function (fastify, _options, done) {
72
- fastify.addHook('onError', async (_request, _reply, error) => {
73
- core.captureException(error);
92
+ fastify.addHook('onError', async (request, reply, error) => {
93
+ if (shouldHandleError(error, request, reply)) {
94
+ core.captureException(error);
95
+ }
74
96
  });
75
97
 
76
98
  // registering `onRequest` hook here instead of using Otel `onRequest` callback b/c `onRequest` hook
77
99
  // is ironically called in the fastify `preHandler` hook which is called later in the lifecycle:
78
100
  // https://fastify.dev/docs/latest/Reference/Lifecycle/
79
101
  fastify.addHook('onRequest', async (request, _reply) => {
80
- const reqWithRouteInfo = request ;
81
-
82
102
  // Taken from Otel Fastify instrumentation:
83
103
  // https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-fastify/src/instrumentation.ts#L94-L96
84
- const routeName = reqWithRouteInfo.routeOptions?.url || reqWithRouteInfo.routerPath;
85
- const method = reqWithRouteInfo.method || 'GET';
104
+ const routeName = request.routeOptions?.url || request.routerPath;
105
+ const method = request.method || 'GET';
86
106
 
87
107
  core.getIsolationScope().setTransactionName(`${method} ${routeName}`);
88
108
  });