LoggerController.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.logLevels = exports.default = exports.LoggerController = exports.LogOrder = exports.LogLevel = void 0;
  6. var _node = require("parse/node");
  7. var _AdaptableController = _interopRequireDefault(require("./AdaptableController"));
  8. var _LoggerAdapter = require("../Adapters/Logger/LoggerAdapter");
  9. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  10. const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;
  11. const LOG_STRING_TRUNCATE_LENGTH = 1000;
  12. const truncationMarker = '... (truncated)';
  13. const LogLevel = exports.LogLevel = {
  14. INFO: 'info',
  15. ERROR: 'error'
  16. };
  17. const LogOrder = exports.LogOrder = {
  18. DESCENDING: 'desc',
  19. ASCENDING: 'asc'
  20. };
  21. const logLevels = exports.logLevels = ['error', 'warn', 'info', 'debug', 'verbose', 'silly', 'silent'];
  22. class LoggerController extends _AdaptableController.default {
  23. constructor(adapter, appId, options = {
  24. logLevel: 'info'
  25. }) {
  26. super(adapter, appId, options);
  27. let level = 'info';
  28. if (options.verbose) {
  29. level = 'verbose';
  30. }
  31. if (options.logLevel) {
  32. level = options.logLevel;
  33. }
  34. const index = logLevels.indexOf(level); // info by default
  35. logLevels.forEach((level, levelIndex) => {
  36. if (levelIndex > index) {
  37. // silence the levels that are > maxIndex
  38. this[level] = () => {};
  39. }
  40. });
  41. }
  42. maskSensitiveUrl(path) {
  43. const urlString = 'http://localhost' + path; // prepend dummy string to make a real URL
  44. const urlObj = new URL(urlString);
  45. const query = urlObj.searchParams;
  46. let sanitizedQuery = '?';
  47. for (const [key, value] of query) {
  48. if (key !== 'password') {
  49. // normal value
  50. sanitizedQuery += key + '=' + value + '&';
  51. } else {
  52. // password value, redact it
  53. sanitizedQuery += key + '=' + '********' + '&';
  54. }
  55. }
  56. // trim last character, ? or &
  57. sanitizedQuery = sanitizedQuery.slice(0, -1);
  58. // return original path name with sanitized params attached
  59. return urlObj.pathname + sanitizedQuery;
  60. }
  61. maskSensitive(argArray) {
  62. return argArray.map(e => {
  63. if (!e) {
  64. return e;
  65. }
  66. if (typeof e === 'string') {
  67. return e.replace(/(password".?:.?")[^"]*"/g, '$1********"');
  68. }
  69. // else it is an object...
  70. // check the url
  71. if (e.url) {
  72. // for strings
  73. if (typeof e.url === 'string') {
  74. e.url = this.maskSensitiveUrl(e.url);
  75. } else if (Array.isArray(e.url)) {
  76. // for strings in array
  77. e.url = e.url.map(item => {
  78. if (typeof item === 'string') {
  79. return this.maskSensitiveUrl(item);
  80. }
  81. return item;
  82. });
  83. }
  84. }
  85. if (e.body) {
  86. for (const key of Object.keys(e.body)) {
  87. if (key === 'password') {
  88. e.body[key] = '********';
  89. break;
  90. }
  91. }
  92. }
  93. if (e.params) {
  94. for (const key of Object.keys(e.params)) {
  95. if (key === 'password') {
  96. e.params[key] = '********';
  97. break;
  98. }
  99. }
  100. }
  101. return e;
  102. });
  103. }
  104. log(level, args) {
  105. // make the passed in arguments object an array with the spread operator
  106. args = this.maskSensitive([...args]);
  107. args = [].concat(level, args.map(arg => {
  108. if (typeof arg === 'function') {
  109. return arg();
  110. }
  111. return arg;
  112. }));
  113. this.adapter.log.apply(this.adapter, args);
  114. }
  115. info() {
  116. return this.log('info', arguments);
  117. }
  118. error() {
  119. return this.log('error', arguments);
  120. }
  121. warn() {
  122. return this.log('warn', arguments);
  123. }
  124. verbose() {
  125. return this.log('verbose', arguments);
  126. }
  127. debug() {
  128. return this.log('debug', arguments);
  129. }
  130. silly() {
  131. return this.log('silly', arguments);
  132. }
  133. logRequest({
  134. method,
  135. url,
  136. headers,
  137. body
  138. }) {
  139. this.verbose(() => {
  140. const stringifiedBody = JSON.stringify(body, null, 2);
  141. return `REQUEST for [${method}] ${url}: ${stringifiedBody}`;
  142. }, {
  143. method,
  144. url,
  145. headers,
  146. body
  147. });
  148. }
  149. logResponse({
  150. method,
  151. url,
  152. result
  153. }) {
  154. this.verbose(() => {
  155. const stringifiedResponse = JSON.stringify(result, null, 2);
  156. return `RESPONSE from [${method}] ${url}: ${stringifiedResponse}`;
  157. }, {
  158. result: result
  159. });
  160. }
  161. // check that date input is valid
  162. static validDateTime(date) {
  163. if (!date) {
  164. return null;
  165. }
  166. date = new Date(date);
  167. if (!isNaN(date.getTime())) {
  168. return date;
  169. }
  170. return null;
  171. }
  172. truncateLogMessage(string) {
  173. if (string && string.length > LOG_STRING_TRUNCATE_LENGTH) {
  174. const truncated = string.substring(0, LOG_STRING_TRUNCATE_LENGTH) + truncationMarker;
  175. return truncated;
  176. }
  177. return string;
  178. }
  179. static parseOptions(options = {}) {
  180. const from = LoggerController.validDateTime(options.from) || new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY);
  181. const until = LoggerController.validDateTime(options.until) || new Date();
  182. const size = Number(options.size) || 10;
  183. const order = options.order || LogOrder.DESCENDING;
  184. const level = options.level || LogLevel.INFO;
  185. return {
  186. from,
  187. until,
  188. size,
  189. order,
  190. level
  191. };
  192. }
  193. // Returns a promise for a {response} object.
  194. // query params:
  195. // level (optional) Level of logging you want to query for (info || error)
  196. // from (optional) Start time for the search. Defaults to 1 week ago.
  197. // until (optional) End time for the search. Defaults to current time.
  198. // order (optional) Direction of results returned, either “asc” or “desc”. Defaults to “desc”.
  199. // size (optional) Number of rows returned by search. Defaults to 10
  200. getLogs(options = {}) {
  201. if (!this.adapter) {
  202. throw new _node.Parse.Error(_node.Parse.Error.PUSH_MISCONFIGURED, 'Logger adapter is not available');
  203. }
  204. if (typeof this.adapter.query !== 'function') {
  205. throw new _node.Parse.Error(_node.Parse.Error.PUSH_MISCONFIGURED, 'Querying logs is not supported with this adapter');
  206. }
  207. options = LoggerController.parseOptions(options);
  208. return this.adapter.query(options);
  209. }
  210. expectedAdapterType() {
  211. return _LoggerAdapter.LoggerAdapter;
  212. }
  213. }
  214. exports.LoggerController = LoggerController;
  215. var _default = exports.default = LoggerController;
  216. //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbm9kZSIsInJlcXVpcmUiLCJfQWRhcHRhYmxlQ29udHJvbGxlciIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJfTG9nZ2VyQWRhcHRlciIsImUiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsIk1JTExJU0VDT05EU19JTl9BX0RBWSIsIkxPR19TVFJJTkdfVFJVTkNBVEVfTEVOR1RIIiwidHJ1bmNhdGlvbk1hcmtlciIsIkxvZ0xldmVsIiwiZXhwb3J0cyIsIklORk8iLCJFUlJPUiIsIkxvZ09yZGVyIiwiREVTQ0VORElORyIsIkFTQ0VORElORyIsImxvZ0xldmVscyIsIkxvZ2dlckNvbnRyb2xsZXIiLCJBZGFwdGFibGVDb250cm9sbGVyIiwiY29uc3RydWN0b3IiLCJhZGFwdGVyIiwiYXBwSWQiLCJvcHRpb25zIiwibG9nTGV2ZWwiLCJsZXZlbCIsInZlcmJvc2UiLCJpbmRleCIsImluZGV4T2YiLCJmb3JFYWNoIiwibGV2ZWxJbmRleCIsIm1hc2tTZW5zaXRpdmVVcmwiLCJwYXRoIiwidXJsU3RyaW5nIiwidXJsT2JqIiwiVVJMIiwicXVlcnkiLCJzZWFyY2hQYXJhbXMiLCJzYW5pdGl6ZWRRdWVyeSIsImtleSIsInZhbHVlIiwic2xpY2UiLCJwYXRobmFtZSIsIm1hc2tTZW5zaXRpdmUiLCJhcmdBcnJheSIsIm1hcCIsInJlcGxhY2UiLCJ1cmwiLCJBcnJheSIsImlzQXJyYXkiLCJpdGVtIiwiYm9keSIsIk9iamVjdCIsImtleXMiLCJwYXJhbXMiLCJsb2ciLCJhcmdzIiwiY29uY2F0IiwiYXJnIiwiYXBwbHkiLCJpbmZvIiwiYXJndW1lbnRzIiwiZXJyb3IiLCJ3YXJuIiwiZGVidWciLCJzaWxseSIsImxvZ1JlcXVlc3QiLCJtZXRob2QiLCJoZWFkZXJzIiwic3RyaW5naWZpZWRCb2R5IiwiSlNPTiIsInN0cmluZ2lmeSIsImxvZ1Jlc3BvbnNlIiwicmVzdWx0Iiwic3RyaW5naWZpZWRSZXNwb25zZSIsInZhbGlkRGF0ZVRpbWUiLCJkYXRlIiwiRGF0ZSIsImlzTmFOIiwiZ2V0VGltZSIsInRydW5jYXRlTG9nTWVzc2FnZSIsInN0cmluZyIsImxlbmd0aCIsInRydW5jYXRlZCIsInN1YnN0cmluZyIsInBhcnNlT3B0aW9ucyIsImZyb20iLCJub3ciLCJ1bnRpbCIsInNpemUiLCJOdW1iZXIiLCJvcmRlciIsImdldExvZ3MiLCJQYXJzZSIsIkVycm9yIiwiUFVTSF9NSVNDT05GSUdVUkVEIiwiZXhwZWN0ZWRBZGFwdGVyVHlwZSIsIkxvZ2dlckFkYXB0ZXIiLCJfZGVmYXVsdCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9Db250cm9sbGVycy9Mb2dnZXJDb250cm9sbGVyLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFBhcnNlIH0gZnJvbSAncGFyc2Uvbm9kZSc7XG5pbXBvcnQgQWRhcHRhYmxlQ29udHJvbGxlciBmcm9tICcuL0FkYXB0YWJsZUNvbnRyb2xsZXInO1xuaW1wb3J0IHsgTG9nZ2VyQWRhcHRlciB9IGZyb20gJy4uL0FkYXB0ZXJzL0xvZ2dlci9Mb2dnZXJBZGFwdGVyJztcblxuY29uc3QgTUlMTElTRUNPTkRTX0lOX0FfREFZID0gMjQgKiA2MCAqIDYwICogMTAwMDtcbmNvbnN0IExPR19TVFJJTkdfVFJVTkNBVEVfTEVOR1RIID0gMTAwMDtcbmNvbnN0IHRydW5jYXRpb25NYXJrZXIgPSAnLi4uICh0cnVuY2F0ZWQpJztcblxuZXhwb3J0IGNvbnN0IExvZ0xldmVsID0ge1xuICBJTkZPOiAnaW5mbycsXG4gIEVSUk9SOiAnZXJyb3InLFxufTtcblxuZXhwb3J0IGNvbnN0IExvZ09yZGVyID0ge1xuICBERVNDRU5ESU5HOiAnZGVzYycsXG4gIEFTQ0VORElORzogJ2FzYycsXG59O1xuXG5leHBvcnQgY29uc3QgbG9nTGV2ZWxzID0gWydlcnJvcicsICd3YXJuJywgJ2luZm8nLCAnZGVidWcnLCAndmVyYm9zZScsICdzaWxseScsICdzaWxlbnQnXTtcblxuZXhwb3J0IGNsYXNzIExvZ2dlckNvbnRyb2xsZXIgZXh0ZW5kcyBBZGFwdGFibGVDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoYWRhcHRlciwgYXBwSWQsIG9wdGlvbnMgPSB7IGxvZ0xldmVsOiAnaW5mbycgfSkge1xuICAgIHN1cGVyKGFkYXB0ZXIsIGFwcElkLCBvcHRpb25zKTtcbiAgICBsZXQgbGV2ZWwgPSAnaW5mbyc7XG4gICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgbGV2ZWwgPSAndmVyYm9zZSc7XG4gICAgfVxuICAgIGlmIChvcHRpb25zLmxvZ0xldmVsKSB7XG4gICAgICBsZXZlbCA9IG9wdGlvbnMubG9nTGV2ZWw7XG4gICAgfVxuICAgIGNvbnN0IGluZGV4ID0gbG9nTGV2ZWxzLmluZGV4T2YobGV2ZWwpOyAvLyBpbmZvIGJ5IGRlZmF1bHRcbiAgICBsb2dMZXZlbHMuZm9yRWFjaCgobGV2ZWwsIGxldmVsSW5kZXgpID0+IHtcbiAgICAgIGlmIChsZXZlbEluZGV4ID4gaW5kZXgpIHtcbiAgICAgICAgLy8gc2lsZW5jZSB0aGUgbGV2ZWxzIHRoYXQgYXJlID4gbWF4SW5kZXhcbiAgICAgICAgdGhpc1tsZXZlbF0gPSAoKSA9PiB7fTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIG1hc2tTZW5zaXRpdmVVcmwocGF0aCkge1xuICAgIGNvbnN0IHVybFN0cmluZyA9ICdodHRwOi8vbG9jYWxob3N0JyArIHBhdGg7IC8vIHByZXBlbmQgZHVtbXkgc3RyaW5nIHRvIG1ha2UgYSByZWFsIFVSTFxuICAgIGNvbnN0IHVybE9iaiA9IG5ldyBVUkwodXJsU3RyaW5nKTtcbiAgICBjb25zdCBxdWVyeSA9IHVybE9iai5zZWFyY2hQYXJhbXM7XG4gICAgbGV0IHNhbml0aXplZFF1ZXJ5ID0gJz8nO1xuXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgcXVlcnkpIHtcbiAgICAgIGlmIChrZXkgIT09ICdwYXNzd29yZCcpIHtcbiAgICAgICAgLy8gbm9ybWFsIHZhbHVlXG4gICAgICAgIHNhbml0aXplZFF1ZXJ5ICs9IGtleSArICc9JyArIHZhbHVlICsgJyYnO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gcGFzc3dvcmQgdmFsdWUsIHJlZGFjdCBpdFxuICAgICAgICBzYW5pdGl6ZWRRdWVyeSArPSBrZXkgKyAnPScgKyAnKioqKioqKionICsgJyYnO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRyaW0gbGFzdCBjaGFyYWN0ZXIsID8gb3IgJlxuICAgIHNhbml0aXplZFF1ZXJ5ID0gc2FuaXRpemVkUXVlcnkuc2xpY2UoMCwgLTEpO1xuXG4gICAgLy8gcmV0dXJuIG9yaWdpbmFsIHBhdGggbmFtZSB3aXRoIHNhbml0aXplZCBwYXJhbXMgYXR0YWNoZWRcbiAgICByZXR1cm4gdXJsT2JqLnBhdGhuYW1lICsgc2FuaXRpemVkUXVlcnk7XG4gIH1cblxuICBtYXNrU2Vuc2l0aXZlKGFyZ0FycmF5KSB7XG4gICAgcmV0dXJuIGFyZ0FycmF5Lm1hcChlID0+IHtcbiAgICAgIGlmICghZSkge1xuICAgICAgICByZXR1cm4gZTtcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiBlID09PSAnc3RyaW5nJykge1xuICAgICAgICByZXR1cm4gZS5yZXBsYWNlKC8ocGFzc3dvcmRcIi4/Oi4/XCIpW15cIl0qXCIvZywgJyQxKioqKioqKipcIicpO1xuICAgICAgfVxuICAgICAgLy8gZWxzZSBpdCBpcyBhbiBvYmplY3QuLi5cblxuICAgICAgLy8gY2hlY2sgdGhlIHVybFxuICAgICAgaWYgKGUudXJsKSB7XG4gICAgICAgIC8vIGZvciBzdHJpbmdzXG4gICAgICAgIGlmICh0eXBlb2YgZS51cmwgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgZS51cmwgPSB0aGlzLm1hc2tTZW5zaXRpdmVVcmwoZS51cmwpO1xuICAgICAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoZS51cmwpKSB7XG4gICAgICAgICAgLy8gZm9yIHN0cmluZ3MgaW4gYXJyYXlcbiAgICAgICAgICBlLnVybCA9IGUudXJsLm1hcChpdGVtID0+IHtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgaXRlbSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubWFza1NlbnNpdGl2ZVVybChpdGVtKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIGl0ZW07XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGUuYm9keSkge1xuICAgICAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyhlLmJvZHkpKSB7XG4gICAgICAgICAgaWYgKGtleSA9PT0gJ3Bhc3N3b3JkJykge1xuICAgICAgICAgICAgZS5ib2R5W2tleV0gPSAnKioqKioqKionO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChlLnBhcmFtcykge1xuICAgICAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyhlLnBhcmFtcykpIHtcbiAgICAgICAgICBpZiAoa2V5ID09PSAncGFzc3dvcmQnKSB7XG4gICAgICAgICAgICBlLnBhcmFtc1trZXldID0gJyoqKioqKioqJztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gZTtcbiAgICB9KTtcbiAgfVxuXG4gIGxvZyhsZXZlbCwgYXJncykge1xuICAgIC8vIG1ha2UgdGhlIHBhc3NlZCBpbiBhcmd1bWVudHMgb2JqZWN0IGFuIGFycmF5IHdpdGggdGhlIHNwcmVhZCBvcGVyYXRvclxuICAgIGFyZ3MgPSB0aGlzLm1hc2tTZW5zaXRpdmUoWy4uLmFyZ3NdKTtcbiAgICBhcmdzID0gW10uY29uY2F0KFxuICAgICAgbGV2ZWwsXG4gICAgICBhcmdzLm1hcChhcmcgPT4ge1xuICAgICAgICBpZiAodHlwZW9mIGFyZyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgIHJldHVybiBhcmcoKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXJnO1xuICAgICAgfSlcbiAgICApO1xuICAgIHRoaXMuYWRhcHRlci5sb2cuYXBwbHkodGhpcy5hZGFwdGVyLCBhcmdzKTtcbiAgfVxuXG4gIGluZm8oKSB7XG4gICAgcmV0dXJuIHRoaXMubG9nKCdpbmZvJywgYXJndW1lbnRzKTtcbiAgfVxuXG4gIGVycm9yKCkge1xuICAgIHJldHVybiB0aGlzLmxvZygnZXJyb3InLCBhcmd1bWVudHMpO1xuICB9XG5cbiAgd2FybigpIHtcbiAgICByZXR1cm4gdGhpcy5sb2coJ3dhcm4nLCBhcmd1bWVudHMpO1xuICB9XG5cbiAgdmVyYm9zZSgpIHtcbiAgICByZXR1cm4gdGhpcy5sb2coJ3ZlcmJvc2UnLCBhcmd1bWVudHMpO1xuICB9XG5cbiAgZGVidWcoKSB7XG4gICAgcmV0dXJuIHRoaXMubG9nKCdkZWJ1ZycsIGFyZ3VtZW50cyk7XG4gIH1cblxuICBzaWxseSgpIHtcbiAgICByZXR1cm4gdGhpcy5sb2coJ3NpbGx5JywgYXJndW1lbnRzKTtcbiAgfVxuXG4gIGxvZ1JlcXVlc3QoeyBtZXRob2QsIHVybCwgaGVhZGVycywgYm9keSB9KSB7XG4gICAgdGhpcy52ZXJib3NlKFxuICAgICAgKCkgPT4ge1xuICAgICAgICBjb25zdCBzdHJpbmdpZmllZEJvZHkgPSBKU09OLnN0cmluZ2lmeShib2R5LCBudWxsLCAyKTtcbiAgICAgICAgcmV0dXJuIGBSRVFVRVNUIGZvciBbJHttZXRob2R9XSAke3VybH06ICR7c3RyaW5naWZpZWRCb2R5fWA7XG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBtZXRob2QsXG4gICAgICAgIHVybCxcbiAgICAgICAgaGVhZGVycyxcbiAgICAgICAgYm9keSxcbiAgICAgIH1cbiAgICApO1xuICB9XG5cbiAgbG9nUmVzcG9uc2UoeyBtZXRob2QsIHVybCwgcmVzdWx0IH0pIHtcbiAgICB0aGlzLnZlcmJvc2UoXG4gICAgICAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHN0cmluZ2lmaWVkUmVzcG9uc2UgPSBKU09OLnN0cmluZ2lmeShyZXN1bHQsIG51bGwsIDIpO1xuICAgICAgICByZXR1cm4gYFJFU1BPTlNFIGZyb20gWyR7bWV0aG9kfV0gJHt1cmx9OiAke3N0cmluZ2lmaWVkUmVzcG9uc2V9YDtcbiAgICAgIH0sXG4gICAgICB7IHJlc3VsdDogcmVzdWx0IH1cbiAgICApO1xuICB9XG4gIC8vIGNoZWNrIHRoYXQgZGF0ZSBpbnB1dCBpcyB2YWxpZFxuICBzdGF0aWMgdmFsaWREYXRlVGltZShkYXRlKSB7XG4gICAgaWYgKCFkYXRlKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgZGF0ZSA9IG5ldyBEYXRlKGRhdGUpO1xuXG4gICAgaWYgKCFpc05hTihkYXRlLmdldFRpbWUoKSkpIHtcbiAgICAgIHJldHVybiBkYXRlO1xuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgdHJ1bmNhdGVMb2dNZXNzYWdlKHN0cmluZykge1xuICAgIGlmIChzdHJpbmcgJiYgc3RyaW5nLmxlbmd0aCA+IExPR19TVFJJTkdfVFJVTkNBVEVfTEVOR1RIKSB7XG4gICAgICBjb25zdCB0cnVuY2F0ZWQgPSBzdHJpbmcuc3Vic3RyaW5nKDAsIExPR19TVFJJTkdfVFJVTkNBVEVfTEVOR1RIKSArIHRydW5jYXRpb25NYXJrZXI7XG4gICAgICByZXR1cm4gdHJ1bmNhdGVkO1xuICAgIH1cblxuICAgIHJldHVybiBzdHJpbmc7XG4gIH1cblxuICBzdGF0aWMgcGFyc2VPcHRpb25zKG9wdGlvbnMgPSB7fSkge1xuICAgIGNvbnN0IGZyb20gPVxuICAgICAgTG9nZ2VyQ29udHJvbGxlci52YWxpZERhdGVUaW1lKG9wdGlvbnMuZnJvbSkgfHxcbiAgICAgIG5ldyBEYXRlKERhdGUubm93KCkgLSA3ICogTUlMTElTRUNPTkRTX0lOX0FfREFZKTtcbiAgICBjb25zdCB1bnRpbCA9IExvZ2dlckNvbnRyb2xsZXIudmFsaWREYXRlVGltZShvcHRpb25zLnVudGlsKSB8fCBuZXcgRGF0ZSgpO1xuICAgIGNvbnN0IHNpemUgPSBOdW1iZXIob3B0aW9ucy5zaXplKSB8fCAxMDtcbiAgICBjb25zdCBvcmRlciA9IG9wdGlvbnMub3JkZXIgfHwgTG9nT3JkZXIuREVTQ0VORElORztcbiAgICBjb25zdCBsZXZlbCA9IG9wdGlvbnMubGV2ZWwgfHwgTG9nTGV2ZWwuSU5GTztcblxuICAgIHJldHVybiB7XG4gICAgICBmcm9tLFxuICAgICAgdW50aWwsXG4gICAgICBzaXplLFxuICAgICAgb3JkZXIsXG4gICAgICBsZXZlbCxcbiAgICB9O1xuICB9XG5cbiAgLy8gUmV0dXJucyBhIHByb21pc2UgZm9yIGEge3Jlc3BvbnNlfSBvYmplY3QuXG4gIC8vIHF1ZXJ5IHBhcmFtczpcbiAgLy8gbGV2ZWwgKG9wdGlvbmFsKSBMZXZlbCBvZiBsb2dnaW5nIHlvdSB3YW50IHRvIHF1ZXJ5IGZvciAoaW5mbyB8fCBlcnJvcilcbiAgLy8gZnJvbSAob3B0aW9uYWwpIFN0YXJ0IHRpbWUgZm9yIHRoZSBzZWFyY2guIERlZmF1bHRzIHRvIDEgd2VlayBhZ28uXG4gIC8vIHVudGlsIChvcHRpb25hbCkgRW5kIHRpbWUgZm9yIHRoZSBzZWFyY2guIERlZmF1bHRzIHRvIGN1cnJlbnQgdGltZS5cbiAgLy8gb3JkZXIgKG9wdGlvbmFsKSBEaXJlY3Rpb24gb2YgcmVzdWx0cyByZXR1cm5lZCwgZWl0aGVyIOKAnGFzY+KAnSBvciDigJxkZXNj4oCdLiBEZWZhdWx0cyB0byDigJxkZXNj4oCdLlxuICAvLyBzaXplIChvcHRpb25hbCkgTnVtYmVyIG9mIHJvd3MgcmV0dXJuZWQgYnkgc2VhcmNoLiBEZWZhdWx0cyB0byAxMFxuICBnZXRMb2dzKG9wdGlvbnMgPSB7fSkge1xuICAgIGlmICghdGhpcy5hZGFwdGVyKSB7XG4gICAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuUFVTSF9NSVNDT05GSUdVUkVELCAnTG9nZ2VyIGFkYXB0ZXIgaXMgbm90IGF2YWlsYWJsZScpO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIHRoaXMuYWRhcHRlci5xdWVyeSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgICBQYXJzZS5FcnJvci5QVVNIX01JU0NPTkZJR1VSRUQsXG4gICAgICAgICdRdWVyeWluZyBsb2dzIGlzIG5vdCBzdXBwb3J0ZWQgd2l0aCB0aGlzIGFkYXB0ZXInXG4gICAgICApO1xuICAgIH1cbiAgICBvcHRpb25zID0gTG9nZ2VyQ29udHJvbGxlci5wYXJzZU9wdGlvbnMob3B0aW9ucyk7XG4gICAgcmV0dXJuIHRoaXMuYWRhcHRlci5xdWVyeShvcHRpb25zKTtcbiAgfVxuXG4gIGV4cGVjdGVkQWRhcHRlclR5cGUoKSB7XG4gICAgcmV0dXJuIExvZ2dlckFkYXB0ZXI7XG4gIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgTG9nZ2VyQ29udHJvbGxlcjtcbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsSUFBQUEsS0FBQSxHQUFBQyxPQUFBO0FBQ0EsSUFBQUMsb0JBQUEsR0FBQUMsc0JBQUEsQ0FBQUYsT0FBQTtBQUNBLElBQUFHLGNBQUEsR0FBQUgsT0FBQTtBQUFpRSxTQUFBRSx1QkFBQUUsQ0FBQSxXQUFBQSxDQUFBLElBQUFBLENBQUEsQ0FBQUMsVUFBQSxHQUFBRCxDQUFBLEtBQUFFLE9BQUEsRUFBQUYsQ0FBQTtBQUVqRSxNQUFNRyxxQkFBcUIsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0FBQ2pELE1BQU1DLDBCQUEwQixHQUFHLElBQUk7QUFDdkMsTUFBTUMsZ0JBQWdCLEdBQUcsaUJBQWlCO0FBRW5DLE1BQU1DLFFBQVEsR0FBQUMsT0FBQSxDQUFBRCxRQUFBLEdBQUc7RUFDdEJFLElBQUksRUFBRSxNQUFNO0VBQ1pDLEtBQUssRUFBRTtBQUNULENBQUM7QUFFTSxNQUFNQyxRQUFRLEdBQUFILE9BQUEsQ0FBQUcsUUFBQSxHQUFHO0VBQ3RCQyxVQUFVLEVBQUUsTUFBTTtFQUNsQkMsU0FBUyxFQUFFO0FBQ2IsQ0FBQztBQUVNLE1BQU1DLFNB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