@sentry/node 11.0.0-beta.1 → 11.0.0-rc.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.
- package/build/cjs/index.js +7 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/integrations/anr/index.js +12 -4
- package/build/cjs/integrations/anr/index.js.map +1 -1
- package/build/cjs/integrations/contextlines.js +1 -1
- package/build/cjs/integrations/contextlines.js.map +1 -1
- package/build/cjs/integrations/local-variables/local-variables-async.js +1 -1
- package/build/cjs/integrations/node-fetch/undici-instrumentation.js +4 -3
- package/build/cjs/integrations/node-fetch/undici-instrumentation.js.map +1 -1
- package/build/cjs/sdk/client.js +4 -0
- package/build/cjs/sdk/client.js.map +1 -1
- package/build/cjs/sdk/index.js +0 -4
- package/build/cjs/sdk/index.js.map +1 -1
- package/build/cjs/utils/errorhandling.js +6 -0
- package/build/cjs/utils/errorhandling.js.map +1 -1
- package/build/esm/index.js +1 -1
- package/build/esm/integrations/anr/index.js +12 -4
- package/build/esm/integrations/anr/index.js.map +1 -1
- package/build/esm/integrations/anr/worker-script.js +2 -2
- package/build/esm/integrations/contextlines.js +1 -1
- package/build/esm/integrations/contextlines.js.map +1 -1
- package/build/esm/integrations/local-variables/local-variables-async.js +1 -1
- package/build/esm/integrations/local-variables/worker-script.js +1 -1
- package/build/esm/integrations/node-fetch/undici-instrumentation.js +4 -3
- package/build/esm/integrations/node-fetch/undici-instrumentation.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/sdk/client.js +4 -0
- package/build/esm/sdk/client.js.map +1 -1
- package/build/esm/sdk/index.js +0 -4
- package/build/esm/sdk/index.js.map +1 -1
- package/build/esm/utils/errorhandling.js +6 -0
- package/build/esm/utils/errorhandling.js.map +1 -1
- package/build/types/index.d.ts +3 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/integrations/anr/index.d.ts +1 -0
- package/build/types/integrations/anr/index.d.ts.map +1 -1
- package/build/types/integrations/contextlines.d.ts +1 -1
- package/build/types/integrations/contextlines.d.ts.map +1 -1
- package/build/types/integrations/node-fetch/undici-instrumentation.d.ts.map +1 -1
- package/build/types/sdk/client.d.ts.map +1 -1
- package/build/types/sdk/index.d.ts.map +1 -1
- package/build/types/utils/errorhandling.d.ts.map +1 -1
- package/package.json +7 -7
package/build/cjs/index.js
CHANGED
|
@@ -49,15 +49,19 @@ const index = require('./integrations/anr/index.js');
|
|
|
49
49
|
exports.httpIntegration = index$5.httpIntegration;
|
|
50
50
|
exports.nativeNodeFetchIntegration = index$7.nativeNodeFetchIntegration;
|
|
51
51
|
exports.fsIntegration = index$2.fsIntegration;
|
|
52
|
+
exports.SentryMastraExporter = serverUtils.SentryMastraExporter;
|
|
52
53
|
exports.amqplibIntegration = serverUtils.amqplibIntegration;
|
|
53
54
|
exports.anthropicAIIntegration = serverUtils.anthropicAIIntegration;
|
|
55
|
+
exports.createFlueInstrumentation = serverUtils.createFlueInstrumentation;
|
|
54
56
|
exports.createLangChainCallbackHandler = serverUtils.createLangChainCallbackHandler;
|
|
55
57
|
exports.dataloaderIntegration = serverUtils.dataloaderIntegration;
|
|
58
|
+
exports.eveConversationHook = serverUtils.eveConversationHook;
|
|
56
59
|
exports.expressErrorHandler = serverUtils.expressErrorHandler;
|
|
57
60
|
exports.expressIntegration = serverUtils.expressIntegration;
|
|
58
61
|
exports.fastifyIntegration = serverUtils.fastifyIntegration;
|
|
59
62
|
exports.firebaseIntegration = serverUtils.firebaseIntegration;
|
|
60
63
|
exports.genericPoolIntegration = serverUtils.genericPoolIntegration;
|
|
64
|
+
exports.getInstrumentedModuleNames = serverUtils.getInstrumentedModuleNames;
|
|
61
65
|
exports.getOtlpTracesEndpoint = serverUtils.getOtlpTracesEndpoint;
|
|
62
66
|
exports.googleGenAIIntegration = serverUtils.googleGenAIIntegration;
|
|
63
67
|
exports.graphqlIntegration = serverUtils.graphqlIntegration;
|
|
@@ -65,6 +69,7 @@ exports.hapiIntegration = serverUtils.hapiIntegration;
|
|
|
65
69
|
exports.instrumentAnthropicAiClient = serverUtils.instrumentAnthropicAiClient;
|
|
66
70
|
exports.instrumentGoogleGenAIClient = serverUtils.instrumentGoogleGenAIClient;
|
|
67
71
|
exports.instrumentLangChainEmbeddings = serverUtils.instrumentLangChainEmbeddings;
|
|
72
|
+
exports.instrumentMistralAiClient = serverUtils.instrumentMistralAiClient;
|
|
68
73
|
exports.instrumentOpenAiClient = serverUtils.instrumentOpenAiClient;
|
|
69
74
|
exports.instrumentStateGraph = serverUtils.instrumentStateGraph;
|
|
70
75
|
exports.instrumentStateGraphCompile = serverUtils.instrumentStateGraphCompile;
|
|
@@ -74,6 +79,8 @@ exports.koaIntegration = serverUtils.koaIntegration;
|
|
|
74
79
|
exports.langChainIntegration = serverUtils.langChainIntegration;
|
|
75
80
|
exports.langGraphIntegration = serverUtils.langGraphIntegration;
|
|
76
81
|
exports.lruMemoizerIntegration = serverUtils.lruMemoizerIntegration;
|
|
82
|
+
exports.mastraIntegration = serverUtils.mastraIntegration;
|
|
83
|
+
exports.mistralAIIntegration = serverUtils.mistralAIIntegration;
|
|
77
84
|
exports.mongoIntegration = serverUtils.mongoIntegration;
|
|
78
85
|
exports.mongooseIntegration = serverUtils.mongooseIntegration;
|
|
79
86
|
exports.mysql2Integration = serverUtils.mysql2Integration;
|
package/build/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -6,7 +6,7 @@ const core = require('@sentry/core');
|
|
|
6
6
|
const debug = require('../../utils/debug.js');
|
|
7
7
|
|
|
8
8
|
const { isPromise } = util.types;
|
|
9
|
-
const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZSAxMS4wLjAtYmV0YS4xIChjYjIzOWUwKSB8IGh0dHBzOi8vZ2l0aHViLmNvbS9nZXRzZW50cnkvc2VudHJ5LWphdmFzY3JpcHQgKi8KaW1wb3J0e1Nlc3Npb24gYXMgdH1mcm9tIm5vZGU6aW5zcGVjdG9yIjtpbXBvcnR7d29ya2VyRGF0YSBhcyBlLHBhcmVudFBvcnQgYXMgbn1mcm9tIm5vZGU6d29ya2VyX3RocmVhZHMiO2ltcG9ydHtwb3NpeCBhcyByLHNlcCBhcyBvfWZyb20ibm9kZTpwYXRoIjtpbXBvcnQqYXMgcyBmcm9tIm5vZGU6aHR0cCI7aW1wb3J0KmFzIGkgZnJvbSJub2RlOmh0dHBzIjtpbXBvcnR7UmVhZGFibGUgYXMgY31mcm9tIm5vZGU6c3RyZWFtIjtpbXBvcnR7Y3JlYXRlR3ppcCBhcyB1fWZyb20ibm9kZTp6bGliIjtpbXBvcnQqYXMgYSBmcm9tIm5vZGU6bmV0IjtpbXBvcnQqYXMgZiBmcm9tIm5vZGU6dGxzIjtjb25zdCBoPSJ1bmRlZmluZWQiPT10eXBlb2YgX19TRU5UUllfREVCVUdfX3x8X19TRU5UUllfREVCVUdfXyxwPWdsb2JhbFRoaXMsbD0iMTEuMC4wLWJldGEuMSI7ZnVuY3Rpb24gZCgpe3JldHVybiBnKHApLHB9ZnVuY3Rpb24gZyh0KXtjb25zdCBlPXQuX19TRU5UUllfXz10Ll9fU0VOVFJZX198fHt9O3JldHVybiBlLnZlcnNpb249ZS52ZXJzaW9ufHxsLGVbbF09ZVtsXXx8e319ZnVuY3Rpb24gbSh0LGUsbj1wKXtjb25zdCByPW4uX19TRU5UUllfXz1uLl9fU0VOVFJZX198fHt9LG89cltsXT1yW2xdfHx7fTtyZXR1cm4gb1t0XXx8KG9bdF09ZSgpKX1jb25zdCB5PXt9O2Z1bmN0aW9uIGIodCl7aWYoISgiY29uc29sZSJpbiBwKSlyZXR1cm4gdCgpO2NvbnN0IGU9cC5jb25zb2xlLG49e30scj1PYmplY3Qua2V5cyh5KTtyLmZvckVhY2godD0+e2NvbnN0IHI9eVt0XTtuW3RdPWVbdF0sZVt0XT1yfSk7dHJ5e3JldHVybiB0KCl9ZmluYWxseXtyLmZvckVhY2godD0+e2VbdF09blt0XX0pfX1mdW5jdGlvbiB2KCl7cmV0dXJuIFMoKS5lbmFibGVkfWZ1bmN0aW9uIHcodCwuLi5lKXtoJiZ2KCkmJmIoKCk9PntwLmNvbnNvbGVbdF0oYFNlbnRyeSBMb2dnZXIgWyR7dH1dOmAsLi4uZSl9KX1mdW5jdGlvbiBTKCl7cmV0dXJuIGg/bSgibG9nZ2VyU2V0dGluZ3MiLCgpPT4oe2VuYWJsZWQ6ITF9KSk6e2VuYWJsZWQ6ITF9fWNvbnN0IF89e2VuYWJsZTpmdW5jdGlvbigpe1MoKS5lbmFibGVkPSEwfSxkaXNhYmxlOmZ1bmN0aW9uKCl7UygpLmVuYWJsZWQ9ITF9LGlzRW5hYmxlZDp2LGxvZzpmdW5jdGlvbiguLi50KXt3KCJsb2ciLC4uLnQpfSx3YXJuOmZ1bmN0aW9uKC4uLnQpe3coIndhcm4iLC4uLnQpfSxlcnJvcjpmdW5jdGlvbiguLi50KXt3KCJlcnJvciIsLi4udCl9fSwkPS9jYXB0dXJlTWVzc2FnZXxjYXB0dXJlRXhjZXB0aW9uLztmdW5jdGlvbiBFKHQpe3JldHVybiB0W3QubGVuZ3RoLTFdfHx7fX1jb25zdCB4PSI8YW5vbnltb3VzPiI7Y29uc3QgTj1PYmplY3QucHJvdG90eXBlLnRvU3RyaW5nO2Z1bmN0aW9uIEModCl7cmV0dXJuIGZ1bmN0aW9uKHQsZSl7cmV0dXJuIE4uY2FsbCh0KT09PWBbb2JqZWN0ICR7ZX1dYH0odCwiT2JqZWN0Iil9ZnVuY3Rpb24gaih0KXtyZXR1cm4gQm9vbGVhbih0Py50aGVuJiYiZnVuY3Rpb24iPT10eXBlb2YgdC50aGVuKX1mdW5jdGlvbiBPKHQsZSl7dHJ5e3JldHVybiB0IGluc3RhbmNlb2YgZX1jYXRjaHtyZXR1cm4hMX19ZnVuY3Rpb24gUCh0KXtpZihmdW5jdGlvbih0KXtzd2l0Y2goTi5jYWxsKHQpKXtjYXNlIltvYmplY3QgRXJyb3JdIjpjYXNlIltvYmplY3QgRXhjZXB0aW9uXSI6Y2FzZSJbb2JqZWN0IERPTUV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBXZWJBc3NlbWJseS5FeGNlcHRpb25dIjpyZXR1cm4hMDtkZWZhdWx0OnJldHVybiBPKHQsRXJyb3IpfX0odCkpcmV0dXJue21lc3NhZ2U6dC5tZXNzYWdlLG5hbWU6dC5uYW1lLHN0YWNrOnQuc3RhY2ssLi4uQSh0KX07aWYoZT10LCJ1bmRlZmluZWQiIT10eXBlb2YgRXZlbnQmJk8oZSxFdmVudCkpe2NvbnN0e3R5cGU6ZSx0YXJnZXQ6bixjdXJyZW50VGFyZ2V0OnIsZGV0YWlsOm99PXQ7cmV0dXJue3R5cGU6ZSx0YXJnZXQ6bixjdXJyZW50VGFyZ2V0OnIsLi4ubz97ZGV0YWlsOm99Ont9LC4uLkEodCl9fXZhciBlO3JldHVybiB0fWZ1bmN0aW9uIEEodCl7cmV0dXJuIm9iamVjdCI9PXR5cGVvZihlPXQpJiZudWxsIT09ZT9PYmplY3QuZnJvbUVudHJpZXMoT2JqZWN0LmVudHJpZXModCkpOnt9O3ZhciBlfWxldCBUO2Z1bmN0aW9uIGsodCl7aWYodm9pZCAwIT09VClyZXR1cm4gVD9UKHQpOnQoKTtjb25zdCBlPVN5bWJvbC5mb3IoIl9fU0VOVFJZX1NBRkVfUkFORE9NX0lEX1dSQVBQRVJfXyIpLG49cDtyZXR1cm4gZSBpbiBuJiYiZnVuY3Rpb24iPT10eXBlb2YgbltlXT8oVD1uW2VdLFQodCkpOihUPW51bGwsdCgpKX1mdW5jdGlvbiBSKCl7cmV0dXJuIGsoKCk9Pk1hdGgucmFuZG9tKCkpfWZ1bmN0aW9uIEkoKXtyZXR1cm4gaygoKT0+RGF0ZS5ub3coKSl9Y29uc3QgRD1TeW1ib2wuZm9yKCJzZW50cnkuc2tpcE5vcm1hbGl6YXRpb24iKSxVPVN5bWJvbC5mb3IoInNlbnRyeS5vdmVycmlkZU5vcm1hbGl6YXRpb25EZXB0aCIpO2Z1bmN0aW9uIEIodCxlPTEwMCxuPTEvMCl7dHJ5e3JldHVybiBMKCIiLHQsZSxuKX1jYXRjaCh0KXtyZXR1cm57RVJST1I6YCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9fX1mdW5jdGlvbiBMKHQsZSxuPTEvMCxyPTEvMCxvPWZ1bmN0aW9uKCl7Y29uc3QgdD1uZXcgV2Vha1NldDtmdW5jdGlvbiBlKGUpe3JldHVybiEhdC5oYXMoZSl8fCh0LmFkZChlKSwhMSl9ZnVuY3Rpb24gbihlKXt0LmRlbGV0ZShlKX1yZXR1cm5bZSxuXX0oKSl7Y29uc3RbcyxpXT1vO2lmKG51bGw9PWV8fFsiYm9vbGVhbiIsInN0cmluZyJdLmluY2x1ZGVzKHR5cGVvZiBlKXx8Im51bWJlciI9PXR5cGVvZiBlJiZOdW1iZXIuaXNGaW5pdGUoZSkpcmV0dXJuIGU7Y29uc3QgYz1mdW5jdGlvbih0LGUpe3RyeXtpZigidW5kZWZpbmVkIiE9dHlwZW9mIGdsb2JhbCYmZT09PWdsb2JhbClyZXR1cm4iW0dsb2JhbF0iO2lmKCJudW1iZXIiPT10eXBlb2YgZSYmIU51bWJlci5pc0Zpbml0ZShlKSlyZXR1cm5gWyR7ZX1dYDtpZigiZnVuY3Rpb24iPT10eXBlb2YgZSlyZXR1cm5gW0Z1bmN0aW9uOiAke2Z1bmN0aW9uKHQpe3RyeXtyZXR1cm4gdCYmImZ1bmN0aW9uIj09dHlwZW9mIHQmJnQubmFtZXx8eH1jYXRjaHtyZXR1cm4geH19KGUpfV1gO2lmKCJzeW1ib2wiPT10eXBlb2YgZSlyZXR1cm5gWyR7U3RyaW5nKGUpfV1gO2lmKCJiaWdpbnQiPT10eXBlb2YgZSlyZXR1cm5gW0JpZ0ludDogJHtTdHJpbmcoZSl9XWA7Y29uc3QgdD1mdW5jdGlvbih0KXtjb25zdCBlPU9iamVjdC5nZXRQcm90b3R5cGVPZih0KTtyZXR1cm4gZT8uY29uc3RydWN0b3I/ZS5jb25zdHJ1Y3Rvci5uYW1lOiJudWxsIHByb3RvdHlwZSJ9KGUpO3JldHVybmBbb2JqZWN0ICR7dH1dYH1jYXRjaCh0KXtyZXR1cm5gKipub24tc2VyaWFsaXphYmxlKiogKCR7dH0pYH19KDAsZSk7aWYoIWMuc3RhcnRzV2l0aCgiW29iamVjdCAiKSlyZXR1cm4gYztpZihmdW5jdGlvbih0KXtyZXR1cm4gQm9vbGVhbih0W0RdKX0oZSkpcmV0dXJuIGU7Y29uc3QgdT1mdW5jdGlvbih0KXtjb25zdCBlPXRbVV07cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlP2U6dm9pZCAwfShlKSxhPXZvaWQgMCE9PXU/dTpuO2lmKDA9PT1hKXJldHVybiBjLnJlcGxhY2UoIm9iamVjdCAiLCIiKTtpZihzKGUpKXJldHVybiJbQ2lyY3VsYXIgfl0iO2NvbnN0IGY9ZTtpZihmJiYiZnVuY3Rpb24iPT10eXBlb2YgZi50b0pTT04pdHJ5e3JldHVybiBMKCIiLGYudG9KU09OKCksYS0xLHIsbyl9Y2F0Y2h7fWNvbnN0IGg9QXJyYXkuaXNBcnJheShlKT9bXTp7fTtsZXQgcD0wO2NvbnN0IGw9UChlKTtmb3IoY29uc3QgdCBpbiBsKXtpZighT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGwsdCkpY29udGludWU7aWYocD49cil7aFt0XT0iW01heFByb3BlcnRpZXMgfl0iO2JyZWFrfWNvbnN0IGU9bFt0XTtoW3RdPUwodCxlLGEtMSxyLG8pLHArK31yZXR1cm4gaShlKSxofWZ1bmN0aW9uIE0odCxlKXtjb25zdCBuPWUucmVwbGFjZSgvXFwvZywiLyIpLnJlcGxhY2UoL1t8XFx7fSgpW1xdXiQrKj8uXS9nLCJcXCQmIik7bGV0IHI9dDt0cnl7cj1kZWNvZGVVUkkodCl9Y2F0Y2h7fXJldHVybiByLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC93ZWJwYWNrOlwvPy9nLCIiKS5yZXBsYWNlKG5ldyBSZWdFeHAoYChmaWxlOi8vKT8vKiR7bn0vKmAsImlnIiksImFwcDovLy8iKX1mdW5jdGlvbiB6KHQsZT0wKXtyZXR1cm4ic3RyaW5nIiE9dHlwZW9mIHR8fDA9PT1lfHx0Lmxlbmd0aDw9ZT90OmAke3Quc2xpY2UoMCxlKX0uLi5gfWxldCBKO2Z1bmN0aW9uIFcodD1mdW5jdGlvbigpe2NvbnN0IHQ9cDtyZXR1cm4gdC5jcnlwdG98fHQubXNDcnlwdG99KCkpe3RyeXtpZih0Py5yYW5kb21VVUlEKXJldHVybiBrKCgpPT50LnJhbmRvbVVVSUQoKSkucmVwbGFjZSgvLS9nLCIiKX1jYXRjaHt9cmV0dXJuIEp8fChKPSIxMDAwMDAwMDEwMDA0MDAwODAwMDEwMDAwMDAwMDAwMCIpLEoucmVwbGFjZSgvWzAxOF0vZyx0PT4odF4oMTYqUigpJjE1KT4+dC80KS50b1N0cmluZygxNikpfWZ1bmN0aW9uIEYoKXtyZXR1cm4gSSgpLzFlM31sZXQgRztmdW5jdGlvbiBZKCl7cmV0dXJuKEc/PyhHPWZ1bmN0aW9uKCl7Y29uc3R7cGVyZm9ybWFuY2U6dH09cDtpZighdD8ubm93fHwhdC50aW1lT3JpZ2luKXJldHVybiBGO2NvbnN0IGU9dC50aW1lT3JpZ2luO3JldHVybigpPT4oZStrKCgpPT50Lm5vdygpKSkvMWUzfSgpKSkoKX1mdW5jdGlvbiBIKHQpe2NvbnN0IGU9WSgpLG49e3NpZDpXKCksaW5pdDohMCx0aW1lc3RhbXA6ZSxzdGFydGVkOmUsZHVyYXRpb246MCxzdGF0dXM6Im9rIixlcnJvcnM6MCxpZ25vcmVEdXJhdGlvbjohMSx0b0pTT046KCk9PmZ1bmN0aW9uKHQpe3JldHVybntzaWQ6YCR7dC5zaWR9YCxpbml0OnQuaW5pdCxzdGFydGVkOm5ldyBEYXRlKDFlMyp0LnN0YXJ0ZWQpLnRvSVNPU3RyaW5nKCksdGltZXN0YW1wOm5ldyBEYXRlKDFlMyp0LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKSxzdGF0dXM6dC5zdGF0dXMsZXJyb3JzOnQuZXJyb3JzLGRpZDoibnVtYmVyIj09dHlwZW9mIHQuZGlkfHwic3RyaW5nIj09dHlwZW9mIHQuZGlkP2Ake3QuZGlkfWA6dm9pZCAwLGR1cmF0aW9uOnQuZHVyYXRpb24sYWJub3JtYWxfbWVjaGFuaXNtOnQuYWJub3JtYWxfbWVjaGFuaXNtLGF0dHJzOntyZWxlYXNlOnQucmVsZWFzZSxlbnZpcm9ubWVudDp0LmVudmlyb25tZW50LGlwX2FkZHJlc3M6dC5pcEFkZHJlc3MsdXNlcl9hZ2VudDp0LnVzZXJBZ2VudH19fShuKX07cmV0dXJuIHQmJloobix0KSxufWZ1bmN0aW9uIFoodCxlPXt9KXtpZihlLnVzZXImJighdC5pcEFkZHJlc3MmJmUudXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9ZS51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxlLmRpZHx8KHQuZGlkPWUudXNlci5pZHx8ZS51c2VyLmVtYWlsfHxlLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1lLnRpbWVzdGFtcHx8WSgpLGUuYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209ZS5hYm5vcm1hbF9tZWNoYW5pc20pLGUuaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPWUuaWdub3JlRHVyYXRpb24pLGUuc2lkJiYodC5zaWQ9MzI9PT1lLnNpZC5sZW5ndGg/ZS5zaWQ6VygpKSx2b2lkIDAhPT1lLmluaXQmJih0LmluaXQ9ZS5pbml0KSwhdC5kaWQmJmUuZGlkJiYodC5kaWQ9YCR7ZS5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBlLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9ZS5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBlLmR1cmF0aW9uKXQuZHVyYXRpb249ZS5kdXJhdGlvbjtlbHNle2NvbnN0IGU9dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249ZT49MD9lOjB9ZS5yZWxlYXNlJiYodC5yZWxlYXNlPWUucmVsZWFzZSksZS5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9ZS5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZlLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPWUuaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJmUudXNlckFnZW50JiYodC51c2VyQWdlbnQ9ZS51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2YgZS5lcnJvcnMmJih0LmVycm9ycz1lLmVycm9ycyksZS5zdGF0dXMmJih0LnN0YXR1cz1lLnN0YXR1cyl9ZnVuY3Rpb24gcSh0LGUsbj0yKXtpZighZXx8Im9iamVjdCIhPXR5cGVvZiBlfHxuPD0wKXJldHVybiBlO2lmKHQmJjA9PT1PYmplY3Qua2V5cyhlKS5sZW5ndGgpcmV0dXJuIHQ7Y29uc3Qgcj17Li4udH07Zm9yKGNvbnN0IHQgaW4gZSlPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoZSx0KSYmKHJbdF09cShyW3RdLGVbdF0sbi0xKSk7cmV0dXJuIHJ9Y2xhc3MgS3tjb25zdHJ1Y3Rvcigpe3RoaXMudD0hMSx0aGlzLm89W10sdGhpcy5pPVtdLHRoaXMudT1bXSx0aGlzLmg9W10sdGhpcy5wPXt9LHRoaXMubD17fSx0aGlzLm09e30sdGhpcy52PXt9LHRoaXMuUz17fSx0aGlzLl89e30sZnVuY3Rpb24odCxlLG4pe3RyeXtPYmplY3QuZGVmaW5lUHJvcGVydHkodCxlLHt2YWx1ZTpuLHdyaXRhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMH0pfWNhdGNoe2gmJl8ubG9nKGBGYWlsZWQgdG8gYWRkIG5vbi1lbnVtZXJhYmxlIHByb3BlcnR5ICIke1N0cmluZyhlKX0iIHRvIG9iamVjdGAsdCl9fSh0aGlzLCJyZWZzIix7fSksdGhpcy5OPXt0cmFjZUlkOlcoKSxzYW1wbGVSYW5kOlIoKX19Y2xvbmUoKXtjb25zdCB0PW5ldyBLO3JldHVybiB0LnU9Wy4uLnRoaXMudV0sdC5sPXsuLi50aGlzLmx9LHQubT17Li4udGhpcy5tfSx0LnY9ey4uLnRoaXMudn0sdC5TPXsuLi50aGlzLlN9LHRoaXMuUy5mbGFncyYmKHQuUy5mbGFncz17dmFsdWVzOlsuLi50aGlzLlMuZmxhZ3MudmFsdWVzXX0pLHQucD10aGlzLnAsdC5DPXRoaXMuQyx0Lmo9dGhpcy5qLHQuTz10aGlzLk8sdC5QPXRoaXMuUCx0Lmk9Wy4uLnRoaXMuaV0sdC5oPVsuLi50aGlzLmhdLHQuXz17Li4udGhpcy5ffSx0Lk49ey4uLnRoaXMuTn0sdC5BPXRoaXMuQSx0LlQ9dGhpcy5ULHQuaz10aGlzLmssdC5yZWZzPXsuLi50aGlzLnJlZnN9LHR9c2V0Q2xpZW50KHQpe3RoaXMuQT10fXNldExhc3RFdmVudElkKHQpe3RoaXMuVD10fWdldENsaWVudCgpe3JldHVybiB0aGlzLkF9bGFzdEV2ZW50SWQoKXtyZXR1cm4gdGhpcy5UfWFkZFNjb3BlTGlzdGVuZXIodCl7dGhpcy5vLnB1c2godCl9YWRkRXZlbnRQcm9jZXNzb3IodCl7cmV0dXJuIHRoaXMuaS5wdXNoKHQpLHRoaXN9c2V0VXNlcih0KXtyZXR1cm4gdGhpcy5wPXR8fHtlbWFpbDp2b2lkIDAsaWQ6dm9pZCAwLGlwX2FkZHJlc3M6dm9pZCAwLHVzZXJuYW1lOnZvaWQgMH0sdGhpcy5qJiZaKHRoaXMuaix7dXNlcjp0fSksdGhpcy5SKCksdGhpc31nZXRVc2VyKCl7cmV0dXJuIHRoaXMucH1zZXRDb252ZXJzYXRpb25JZCh0KXtyZXR1cm4gdGhpcy5rPXR8fHZvaWQgMCx0aGlzLlIoKSx0aGlzfXNldFRhZ3ModCl7cmV0dXJuIHRoaXMubD17Li4udGhpcy5sLC4uLnR9LHRoaXMuUigpLHRoaXN9c2V0VGFnKHQsZSl7cmV0dXJuIHRoaXMuc2V0VGFncyh7W3RdOmV9KX1zZXRBdHRyaWJ1dGVzKHQpe3JldHVybiB0aGlzLm09ey4uLnRoaXMubSwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEF0dHJpYnV0ZSh0LGUpe3JldHVybiB0aGlzLnNldEF0dHJpYnV0ZXMoe1t0XTplfSl9cmVtb3ZlQXR0cmlidXRlKHQpe3JldHVybiB0IGluIHRoaXMubSYmKGRlbGV0ZSB0aGlzLm1bdF0sdGhpcy5SKCkpLHRoaXN9c2V0RXh0cmFzKHQpe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudiwuLi50fSx0aGlzLlIoKSx0aGlzfXNldEV4dHJhKHQsZSl7cmV0dXJuIHRoaXMudj17Li4udGhpcy52LFt0XTplfSx0aGlzLlIoKSx0aGlzfXNldEZpbmdlcnByaW50KHQpe3JldHVybiB0aGlzLlA9dCx0aGlzLlIoKSx0aGlzfXNldExldmVsKHQpe3JldHVybiB0aGlzLkM9dCx0aGlzLlIoKSx0aGlzfXNldFRyYW5zYWN0aW9uTmFtZSh0KXtyZXR1cm4gdGhpcy5PPXQsdGhpcy5SKCksdGhpc31zZXRDb250ZXh0KHQsZSl7cmV0dXJuIG51bGw9PT1lP2RlbGV0ZSB0aGlzLlNbdF06dGhpcy5TW3RdPWUsdGhpcy5SKCksdGhpc31zZXRTZXNzaW9uKHQpe3JldHVybiB0P3RoaXMuaj10OmRlbGV0ZSB0aGlzLmosdGhpcy5SKCksdGhpc31nZXRTZXNzaW9uKCl7cmV0dXJuIHRoaXMuan11cGRhdGUodCl7aWYoIXQpcmV0dXJuIHRoaXM7Y29uc3QgZT0iZnVuY3Rpb24iPT10eXBlb2YgdD90KHRoaXMpOnQsbj1lIGluc3RhbmNlb2YgSz9lLmdldFNjb3BlRGF0YSgpOkMoZSk/dDp2b2lkIDAse3RhZ3M6cixhdHRyaWJ1dGVzOm8sZXh0cmE6cyx1c2VyOmksY29udGV4dHM6YyxsZXZlbDp1LGZpbmdlcnByaW50OmE9W10scHJvcGFnYXRpb25Db250ZXh0OmYsY29udmVyc2F0aW9uSWQ6aH09bnx8e307cmV0dXJuIHRoaXMubD17Li4udGhpcy5sLC4uLnJ9LHRoaXMubT17Li4udGhpcy5tLC4uLm99LHRoaXMudj17Li4udGhpcy52LC4uLnN9LHRoaXMuUz17Li4udGhpcy5TLC4uLmN9LGkmJk9iamVjdC5rZXlzKGkpLmxlbmd0aCYmKHRoaXMucD1pKSx1JiYodGhpcy5DPXUpLGEubGVuZ3RoJiYodGhpcy5QPWEpLGYmJih0aGlzLk49ZiksaCYmKHRoaXMuaz1oKSx0aGlzfWFkZEJyZWFkY3J1bWIodCxlKXtjb25zdCBuPSJudW1iZXIiPT10eXBlb2YgZT9lOjEwMDtpZihuPD0wKXJldHVybiB0aGlzO2NvbnN0IHI9e3RpbWVzdGFtcDpGKCksLi4udCxtZXNzYWdlOnQubWVzc2FnZT96KHQubWVzc2FnZSwyMDQ4KTp0Lm1lc3NhZ2V9O3JldHVybiB0aGlzLnUucHVzaChyKSx0aGlzLnUubGVuZ3RoPm4mJih0aGlzLnU9dGhpcy51LnNsaWNlKC1uKSx0aGlzLkE/LnJlY29yZERyb3BwZWRFdmVudCgiYnVmZmVyX292ZXJmbG93IiwibG9nX2l0ZW0iKSksdGhpcy5SKCksdGhpc31nZXRMYXN0QnJlYWRjcnVtYigpe3JldHVybiB0aGlzLnVbdGhpcy51Lmxlbmd0aC0xXX1jbGVhckJyZWFkY3J1bWJzKCl7cmV0dXJuIHRoaXMudT1bXSx0aGlzLlIoKSx0aGlzfWFkZEF0dGFjaG1lbnQodCl7cmV0dXJuIHRoaXMuaC5wdXNoKHQpLHRoaXN9Y2xlYXJBdHRhY2htZW50cygpe3JldHVybiB0aGlzLmg9W10sdGhpc31nZXRTY29wZURhdGEoKXtyZXR1cm57YnJlYWRjcnVtYnM6dGhpcy51LGF0dGFjaG1lbnRzOnRoaXMuaCxjb250ZXh0czp0aGlzLlMsdGFnczp0aGlzLmwsYXR0cmlidXRlczp0aGlzLm0sZXh0cmE6dGhpcy52LHVzZXI6dGhpcy5wLGxldmVsOnRoaXMuQyxmaW5nZXJwcmludDp0aGlzLlB8fFtdLGV2ZW50UHJvY2Vzc29yczp0aGlzLmkscHJvcGFnYXRpb25Db250ZXh0OnRoaXMuTixzZGtQcm9jZXNzaW5nTWV0YWRhdGE6dGhpcy5fLHRyYW5zYWN0aW9uTmFtZTp0aGlzLk8sY29udmVyc2F0aW9uSWQ6dGhpcy5rfX1zZXRTREtQcm9jZXNzaW5nTWV0YWRhdGEodCl7cmV0dXJuIHRoaXMuXz1xKHRoaXMuXyx0LDIpLHRoaXN9c2V0UHJvcGFnYXRpb25Db250ZXh0KHQpe3JldHVybiB0aGlzLk49dCx0aGlzfWdldFByb3BhZ2F0aW9uQ29udGV4dCgpe3JldHVybiB0aGlzLk59Y2FwdHVyZUV4Y2VwdGlvbih0LGUpe2NvbnN0IG49ZT8uZXZlbnRfaWR8fFcoKTtpZighdGhpcy5BKXJldHVybiBoJiZfLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBleGNlcHRpb24hIiksbjtjb25zdCByPW5ldyBFcnJvcigiU2VudHJ5IHN5bnRoZXRpY0V4Y2VwdGlvbiIpO3JldHVybiB0aGlzLkEuY2FwdHVyZUV4Y2VwdGlvbih0LHtvcmlnaW5hbEV4Y2VwdGlvbjp0LHN5bnRoZXRpY0V4Y2VwdGlvbjpyLC4uLmUsZXZlbnRfaWQ6bn0sdGhpcyksbn1jYXB0dXJlTWVzc2FnZSh0LGUsbil7Y29uc3Qgcj1uPy5ldmVudF9pZHx8VygpO2lmKCF0aGlzLkEpcmV0dXJuIGgmJl8ud2FybigiTm8gY2xpZW50IGNvbmZpZ3VyZWQgb24gc2NvcGUgLSB3aWxsIG5vdCBjYXB0dXJlIG1lc3NhZ2UhIikscjtjb25zdCBvPW4/LnN5bnRoZXRpY0V4Y2VwdGlvbj8/bmV3IEVycm9yKHQpO3JldHVybiB0aGlzLkEuY2FwdHVyZU1lc3NhZ2UodCxlLHtvcmlnaW5hbEV4Y2VwdGlvbjp0LHN5bnRoZXRpY0V4Y2VwdGlvbjpvLC4uLm4sZXZlbnRfaWQ6cn0sdGhpcykscn1jYXB0dXJlRXZlbnQodCxlKXtjb25zdCBuPXQuZXZlbnRfaWR8fGU/LmV2ZW50X2lkfHxXKCk7cmV0dXJuIHRoaXMuQT8odGhpcy5BLmNhcHR1cmVFdmVudCh0LHsuLi5lLGV2ZW50X2lkOm59LHRoaXMpLG4pOihoJiZfLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBldmVudCEiKSxuKX1SKCl7dGhpcy50fHwodGhpcy50PSEwLHRoaXMuby5mb3JFYWNoKHQ9Pnt0KHRoaXMpfSksdGhpcy50PSExKX19Y29uc3QgVj0iX19TRU5UUllfU1VQUFJFU1NfVFJBQ0lOR19fIixRPXQ9PnQgaW5zdGFuY2VvZiBQcm9taXNlJiYhdFtYXSxYPVN5bWJvbCgiY2hhaW5lZCBQcm9taXNlTGlrZSIpLHR0PSh0LGUpPT57aWYoIWUpcmV0dXJuIHQ7bGV0IG49ITE7Zm9yKGNvbnN0IHIgaW4gdCl7aWYociBpbiBlKWNvbnRpbnVlO249ITA7Y29uc3Qgbz10W3JdOyJmdW5jdGlvbiI9PXR5cGVvZiBvP09iamVjdC5kZWZpbmVQcm9wZXJ0eShlLHIse3ZhbHVlOiguLi5lKT0+by5hcHBseSh0LGUpLGVudW1lcmFibGU6ITAsY29uZmlndXJhYmxlOiEwLHdyaXRhYmxlOiEwfSk6ZVtyXT1vfXJldHVybiBuJiZPYmplY3QuYXNzaWduKGUse1tYXTohMH0pLGV9O2NsYXNzIGV0e2NvbnN0cnVjdG9yKHQsZSl7bGV0IG4scjtuPXR8fG5ldyBLLHI9ZXx8bmV3IEssdGhpcy5JPVt7c2NvcGU6bn1dLHRoaXMuRD1yfXdpdGhTY29wZSh0KXtjb25zdCBlPXRoaXMuVSgpO2xldCBuO3RyeXtuPXQoZSl9Y2F0Y2godCl7dGhyb3cgdGhpcy5CKCksdH1yZXR1cm4gaihuKT8oKHQsZSxuKT0+e2NvbnN0IHI9dC50aGVuKHQ9PihlKHQpLHQpLHQ9Pnt0aHJvdyBuKHQpLHR9KTtyZXR1cm4gUShyKSYmUSh0KT9yOnR0KHQscil9KShuLCgpPT50aGlzLkIoKSwoKT0+dGhpcy5CKCkpOih0aGlzLkIoKSxuKX1nZXRDbGllbnQoKXtyZXR1cm4gdGhpcy5nZXRTdGFja1RvcCgpLmNsaWVudH1nZXRTY29wZSgpe3JldHVybiB0aGlzLmdldFN0YWNrVG9wKCkuc2NvcGV9Z2V0SXNvbGF0aW9uU2NvcGUoKXtyZXR1cm4gdGhpcy5EfWdldFN0YWNrVG9wKCl7cmV0dXJuIHRoaXMuSVt0aGlzLkkubGVuZ3RoLTFdfVUoKXtjb25zdCB0PXRoaXMuZ2V0U2NvcGUoKS5jbG9uZSgpO3JldHVybiB0aGlzLkkucHVzaCh7Y2xpZW50OnRoaXMuZ2V0Q2xpZW50KCksc2NvcGU6dH0pLHR9Qigpe3JldHVybiEodGhpcy5JLmxlbmd0aDw9MSkmJiEhdGhpcy5JLnBvcCgpfX1mdW5jdGlvbiBudCgpe2NvbnN0IHQ9ZyhkKCkpO3JldHVybiB0LnN0YWNrPXQuc3RhY2t8fG5ldyBldChtKCJkZWZhdWx0Q3VycmVudFNjb3BlIiwoKT0+bmV3IEspLG0oImRlZmF1bHRJc29sYXRpb25TY29wZSIsKCk9Pm5ldyBLKSl9ZnVuY3Rpb24gcnQodCl7cmV0dXJuIG50KCkud2l0aFNjb3BlKHQpfWZ1bmN0aW9uIG90KHQsZSl7Y29uc3Qgbj1udCgpO3JldHVybiBuLndpdGhTY29wZSgoKT0+KG4uZ2V0U3RhY2tUb3AoKS5zY29wZT10LGUodCkpKX1mdW5jdGlvbiBzdCh0KXtyZXR1cm4gbnQoKS53aXRoU2NvcGUoKCk9PnQobnQoKS5nZXRJc29sYXRpb25TY29wZSgpKSl9ZnVuY3Rpb24gaXQodCl7cmV0dXJuIHJ0KGU9PntlLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1ZdOiEwfSk7Y29uc3Qgbj10KCk7cmV0dXJuIGUuc2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHtbVl06dm9pZCAwfSksbn0pfWZ1bmN0aW9uIGN0KHQpe2NvbnN0IGU9Zyh0KTtyZXR1cm4gZS5hY3M/ZS5hY3M6e3N1cHByZXNzVHJhY2luZzppdCx3aXRoSXNvbGF0aW9uU2NvcGU6c3Qsd2l0aFNjb3BlOnJ0LHdpdGhTZXRTY29wZTpvdCx3aXRoU2V0SXNvbGF0aW9uU2NvcGU6KHQsZSk9PnN0KGUpLGdldEN1cnJlbnRTY29wZTooKT0+bnQoKS5nZXRTY29wZSgpLGdldElzb2xhdGlvblNjb3BlOigpPT5udCgpLmdldElzb2xhdGlvblNjb3BlKCl9fWZ1bmN0aW9uIHV0KHQsZT0hMSl7Y29uc3R7aG9zdDpuLHBhdGg6cixwYXNzOm8scG9ydDpzLHByb2plY3RJZDppLHByb3RvY29sOmMscHVibGljS2V5OnV9PXQ7cmV0dXJuYCR7Y306Ly8ke3V9JHtlJiZvP2A6JHtvfWA6IiJ9QCR7bn0ke3M/YDoke3N9YDoiIn0vJHtyP2Ake3J9L2A6cn0ke2l9YH1mdW5jdGlvbiBhdCh0LGUpe2NvbnN0e2ZpbmdlcnByaW50Om4sYnJlYWRjcnVtYnM6cixzZGtQcm9jZXNzaW5nTWV0YWRhdGE6b309ZTshZnVuY3Rpb24odCxlKXtjb25zdHtleHRyYTpuLHRhZ3M6cix1c2VyOm8sY29udGV4dHM6cyxsZXZlbDppLHRyYW5zYWN0aW9uTmFtZTpjfT1lO09iamVjdC5rZXlzKG4pLmxlbmd0aCYmKHQuZXh0cmE9ey4uLm4sLi4udC5leHRyYX0pO09iamVjdC5rZXlzKHIpLmxlbmd0aCYmKHQudGFncz17Li4uciwuLi50LnRhZ3N9KTtPYmplY3Qua2V5cyhvKS5sZW5ndGgmJih0LnVzZXI9ey4uLm8sLi4udC51c2VyfSk7T2JqZWN0LmtleXMocykubGVuZ3RoJiYodC5jb250ZXh0cz17Li4ucywuLi50LmNvbnRleHRzfSk7aSYmKHQubGV2ZWw9aSk7YyYmInRyYW5zYWN0aW9uIiE9PXQudHlwZSYmKHQudHJhbnNhY3Rpb249Yyl9KHQsZSksZnVuY3Rpb24odCxlKXt0LmZpbmdlcnByaW50PXQuZmluZ2VycHJpbnQ/QXJyYXkuaXNBcnJheSh0LmZpbmdlcnByaW50KT90LmZpbmdlcnByaW50Olt0LmZpbmdlcnByaW50XTpbXSxlJiYodC5maW5nZXJwcmludD10LmZpbmdlcnByaW50LmNvbmNhdChlKSk7dC5maW5nZXJwcmludC5sZW5ndGh8fGRlbGV0ZSB0LmZpbmdlcnByaW50fSh0LG4pLGZ1bmN0aW9uKHQsZSl7Y29uc3Qgbj1bLi4udC5icmVhZGNydW1ic3x8W10sLi4uZV07dC5icmVhZGNydW1icz1uLmxlbmd0aD9uOnZvaWQgMH0odCxyKSxmdW5jdGlvbih0LGUpe3Quc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXsuLi50LnNka1Byb2Nlc3NpbmdNZXRhZGF0YSwuLi5lfX0odCxvKX1mdW5jdGlvbiBmdCh0LGU9W10pe3JldHVyblt0LGVdfWZ1bmN0aW9uIGh0KHQsZSl7Y29uc3Qgbj10WzFdO2Zvcihjb25zdCB0IG9mIG4pe2lmKGUodCx0WzBdLnR5cGUpKXJldHVybiEwfXJldHVybiExfWZ1bmN0aW9uIHB0KHQpe2NvbnN0IGU9ZyhwKTtyZXR1cm4gZS5lbmNvZGVQb2x5ZmlsbD9lLmVuY29kZVBvbHlmaWxsKHQpOihuZXcgVGV4dEVuY29kZXIpLmVuY29kZSh0KX1mdW5jdGlvbiBsdCh0KXtjb25zdFtlLG5dPXQ7bGV0IHI9SlNPTi5zdHJpbmdpZnkoZSk7ZnVuY3Rpb24gbyh0KXsic3RyaW5nIj09dHlwZW9mIHI/cj0ic3RyaW5nIj09dHlwZW9mIHQ/cit0OltwdChyKSx0XTpyLnB1c2goInN0cmluZyI9PXR5cGVvZiB0P3B0KHQpOnQpfWZvcihjb25zdCB0IG9mIG4pe2NvbnN0W2Usbl09dDtpZihvKGBcbiR7SlNPTi5zdHJpbmdpZnkoZSl9XG5gKSwic3RyaW5nIj09dHlwZW9mIG58fG4gaW5zdGFuY2VvZiBVaW50OEFycmF5KW8obik7ZWxzZXtsZXQgdDt0cnl7dD1KU09OLnN0cmluZ2lmeShuKX1jYXRjaHt0PUpTT04uc3RyaW5naWZ5KEIobikpfW8odCl9fXJldHVybiJzdHJpbmciPT10eXBlb2Ygcj9yOmZ1bmN0aW9uKHQpe2NvbnN0IGU9dC5yZWR1Y2UoKHQsZSk9PnQrZS5sZW5ndGgsMCksbj1uZXcgVWludDhBcnJheShlKTtsZXQgcj0wO2Zvcihjb25zdCBlIG9mIHQpbi5zZXQoZSxyKSxyKz1lLmxlbmd0aDtyZXR1cm4gbn0ocil9Y29uc3QgZHQ9e3Nlc3Npb25zOiJzZXNzaW9uIixldmVudDoiZXJyb3IiLGNsaWVudF9yZXBvcnQ6ImludGVybmFsIix1c2VyX3JlcG9ydDoiZGVmYXVsdCIscHJvZmlsZV9jaHVuazoicHJvZmlsZSIscmVwbGF5X2V2ZW50OiJyZXBsYXkiLHJlcGxheV9yZWNvcmRpbmc6InJlcGxheSIsY2hlY2tfaW46Im1vbml0b3IiLHJhd19zZWN1cml0eToic2VjdXJpdHkiLGxvZzoibG9nX2l0ZW0iLHRyYWNlX21ldHJpYzoibWV0cmljIn07ZnVuY3Rpb24gZ3QodCl7cmV0dXJuIGZ1bmN0aW9uKHQpe3JldHVybiB0IGluIGR0fSh0KT9kdFt0XTp0fWZ1bmN0aW9uIG10KHQpe2lmKCF0Py5zZGspcmV0dXJuO2NvbnN0e25hbWU6ZSx2ZXJzaW9uOm59PXQuc2RrO3JldHVybntuYW1lOmUsdmVyc2lvbjpufX1mdW5jdGlvbiB5dCh0KXtjb25zdCBlPWN0KGQoKSk7cmV0dXJuIGUuc3VwcHJlc3NUcmFjaW5nP2Uuc3VwcHJlc3NUcmFjaW5nKHQpOmZ1bmN0aW9uKC4uLnQpe2NvbnN0IGU9Y3QoZCgpKTtpZigyPT09dC5sZW5ndGgpe2NvbnN0W24scl09dDtyZXR1cm4gbj9lLndpdGhTZXRTY29wZShuLHIpOmUud2l0aFNjb3BlKHIpfXJldHVybiBlLndpdGhTY29wZSh0WzBdKX0oZT0+KGUuc2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHtbVl06ITB9KSx0KCkpKX1mdW5jdGlvbiBidCh0LGUsbixyKXtjb25zdCBvPW10KG4pLHM9dC50eXBlJiYicmVwbGF5X2V2ZW50IiE9PXQudHlwZT90LnR5cGU6ImV2ZW50IjshZnVuY3Rpb24odCxlKXtpZighZSlyZXR1cm4gdDtjb25zdCBuPXQuc2RrfHx7fTt0LnNkaz17Li4ubixuYW1lOm4ubmFtZXx8ZS5uYW1lLHZlcnNpb246bi52ZXJzaW9ufHxlLnZlcnNpb24saW50ZWdyYXRpb25zOlsuLi50LnNkaz8uaW50ZWdyYXRpb25zfHxbXSwuLi5lLmludGVncmF0aW9uc3x8W11dLHBhY2thZ2VzOlsuLi50LnNkaz8ucGFja2FnZXN8fFtdLC4uLmUucGFja2FnZXN8fFtdXSxzZXR0aW5nczp0LnNkaz8uc2V0dGluZ3N8fGUuc2V0dGluZ3M/ey4uLnQuc2RrPy5zZXR0aW5ncywuLi5lLnNldHRpbmdzfTp2b2lkIDB9fSh0LG4/LnNkayk7Y29uc3QgaT1mdW5jdGlvbih0LGUsbixyKXtjb25zdCBvPXQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhPy5keW5hbWljU2FtcGxpbmdDb250ZXh0O3JldHVybntldmVudF9pZDp0LmV2ZW50X2lkLHNlbnRfYXQ6bmV3IERhdGUoSSgpKS50b0lTT1N0cmluZygpLC4uLmUmJntzZGs6ZX0sLi4uISFuJiZyJiZ7ZHNuOnV0KHIpfSwuLi5vJiZ7dHJhY2U6b319fSh0LG8scixlKTtkZWxldGUgdC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE7cmV0dXJuIGZ0KGksW1t7dHlwZTpzfSx0XV0pfWNsYXNzIHZ0e2NvbnN0cnVjdG9yKHQpe3RoaXMuTD0wLHRoaXMuTT1bXSx0aGlzLkoodCl9dGhlbih0LGUpe3JldHVybiBuZXcgdnQoKG4scik9Pnt0aGlzLk0ucHVzaChbITEsZT0+e2lmKHQpdHJ5e24odChlKSl9Y2F0Y2godCl7cih0KX1lbHNlIG4oZSl9LHQ9PntpZihlKXRyeXtuKGUodCkpfWNhdGNoKHQpe3IodCl9ZWxzZSByKHQpfV0pLHRoaXMuVygpfSl9Y2F0Y2godCl7cmV0dXJuIHRoaXMudGhlbih0PT50LHQpfWZpbmFsbHkodCl7cmV0dXJuIG5ldyB2dCgoZSxuKT0+e2xldCByLG87cmV0dXJuIHRoaXMudGhlbihlPT57bz0hMSxyPWUsdCYmdCgpfSxlPT57bz0hMCxyPWUsdCYmdCgpfSkudGhlbigoKT0+e28/bihyKTplKHIpfSl9KX1XKCl7aWYoMD09PXRoaXMuTClyZXR1cm47Y29uc3QgdD10aGlzLk0uc2xpY2UoKTt0aGlzLk09W10sdC5mb3JFYWNoKHQ9Pnt0WzBdfHwoMT09PXRoaXMuTCYmdFsxXSh0aGlzLkYpLDI9PT10aGlzLkwmJnRbMl0odGhpcy5GKSx0WzBdPSEwKX0pfUoodCl7Y29uc3QgZT0odCxlKT0+ezA9PT10aGlzLkwmJihqKGUpP2UudGhlbihuLHIpOih0aGlzLkw9dCx0aGlzLkY9ZSx0aGlzLlcoKSkpfSxuPXQ9PntlKDEsdCl9LHI9dD0+e2UoMix0KX07dHJ5e3QobixyKX1jYXRjaCh0KXtyKHQpfX19Y29uc3Qgd3Q9U3ltYm9sLmZvcigiU2VudHJ5QnVmZmVyRnVsbEVycm9yIik7ZnVuY3Rpb24gU3QodD0xMDApe2NvbnN0IGU9bmV3IFNldDtmdW5jdGlvbiBuKHQpe2UuZGVsZXRlKHQpfXJldHVybntnZXQgJCgpe3JldHVybiBBcnJheS5mcm9tKGUpfSxhZGQ6ZnVuY3Rpb24ocil7aWYoIShlLnNpemU8dCkpcmV0dXJuIG89d3QsbmV3IHZ0KCh0LGUpPT57ZShvKX0pO3ZhciBvO2NvbnN0IHM9cigpO3JldHVybiBlLmFkZChzKSxzLnRoZW4oKCk9Pm4ocyksKCk9Pm4ocykpLHN9LGRyYWluOmZ1bmN0aW9uKHQpe2lmKCFlLnNpemUpcmV0dXJuIG49ITAsbmV3IHZ0KHQ9Pnt0KG4pfSk7dmFyIG47Y29uc3Qgcj1Qcm9taXNlLmFsbFNldHRsZWQoQXJyYXkuZnJvbShlKSkudGhlbigoKT0+ITApO2lmKCF0KXJldHVybiByO2NvbnN0IG89W3IsbmV3IFByb21pc2UoZT0+e3JldHVybiJvYmplY3QiPT10eXBlb2Yobj1zZXRUaW1lb3V0KCgpPT5lKCExKSx0KSkmJiJmdW5jdGlvbiI9PXR5cGVvZiBuLnVucmVmJiZuLnVucmVmKCksbjt2YXIgbn0pXTtyZXR1cm4gUHJvbWlzZS5yYWNlKG8pfX19ZnVuY3Rpb24gX3QodCx7c3RhdHVzQ29kZTplLGhlYWRlcnM6bn0scj1JKCkpe2NvbnN0IG89ey4uLnR9LHM9bj8uWyJ4LXNlbnRyeS1yYXRlLWxpbWl0cyJdLGk9bj8uWyJyZXRyeS1hZnRlciJdO2lmKHMpZm9yKGNvbnN0IHQgb2Ygcy50cmltKCkuc3BsaXQoIiwiKSl7Y29uc3RbZSxuLCwsc109dC5zcGxpdCgiOiIsNSksaT1wYXJzZUludChlLDEwKSxjPTFlMyooaXNOYU4oaSk/NjA6aSk7aWYobilmb3IoY29uc3QgdCBvZiBuLnNwbGl0KCI7IikpIm1ldHJpY19idWNrZXQiPT09dCYmcyYmIXMuc3BsaXQoIjsiKS5pbmNsdWRlcygiY3VzdG9tIil8fChvW3RdPXIrYyk7ZWxzZSBvLmFsbD1yK2N9ZWxzZSBpP28uYWxsPXIrZnVuY3Rpb24odCxlPUkoKSl7Y29uc3Qgbj1wYXJzZUludChgJHt0fWAsMTApO2lmKCFpc05hTihuKSlyZXR1cm4gMWUzKm47Y29uc3Qgcj1EYXRlLnBhcnNlKGAke3R9YCk7cmV0dXJuIGlzTmFOKHIpPzZlNDpyLWV9KGkscik6NDI5PT09ZSYmKG8uYWxsPXIrNmU0KTtyZXR1cm4gb31mdW5jdGlvbiAkdCh0LGUsbj1TdCh0LmJ1ZmZlclNpemV8fDY0KSl7bGV0IHI9e307cmV0dXJue3NlbmQ6ZnVuY3Rpb24odCl7Y29uc3Qgbz1bXTtpZihodCh0LCh0LGUpPT57Y29uc3Qgbj1ndChlKTsoZnVuY3Rpb24odCxlLG49SSgpKXtyZXR1cm4gZnVuY3Rpb24odCxlKXtyZXR1cm4gdFtlXXx8dC5hbGx8fDB9KHQsZSk+bn0pKHIsbil8fG8ucHVzaCh0KX0pLDA9PT1vLmxlbmd0aClyZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHt9KTtjb25zdCBzPWZ0KHRbMF0sbyksaT10PT57IWZ1bmN0aW9uKHQsZSl7cmV0dXJuIGh0KHQsKHQsbik9PmUuaW5jbHVkZXMobikpfShzLFsiY2xpZW50X3JlcG9ydCJdKT9odChzLCh0LGUpPT57fSk6aCYmXy53YXJuKGBEcm9wcGluZyBjbGllbnQgcmVwb3J0LiBXaWxsIG5vdCBzZW5kIG91dGNvbWVzIChyZWFzb246ICR7dH0pLmApfTtyZXR1cm4gbi5hZGQoKCk9PmUoe2JvZHk6bHQocyl9KS50aGVuKHQ9PjQxMz09PXQuc3RhdHVzQ29kZT8oaCYmXy5lcnJvcigiU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlIDQxMy4gRW52ZWxvcGUgd2FzIGRpc2NhcmRlZCBkdWUgdG8gZXhjZWVkaW5nIHNpemUgbGltaXRzLiIpLGkoInNlbmRfZXJyb3IiKSx0KTooaCYmdm9pZCAwIT09dC5zdGF0dXNDb2RlJiYodC5zdGF0dXNDb2RlPDIwMHx8dC5zdGF0dXNDb2RlPj0zMDApJiZfLndhcm4oYFNlbnRyeSByZXNwb25kZWQgd2l0aCBzdGF0dXMgY29kZSAke3Quc3RhdHVzQ29kZX0gdG8gc2VudCBldmVudC5gKSxyPV90KHIsdCksdCksdD0+e3Rocm93IGkoIm5ldHdvcmtfZXJyb3IiKSxoJiZfLmVycm9yKCJFbmNvdW50ZXJlZCBlcnJvciBydW5uaW5nIHRyYW5zcG9ydCByZXF1ZXN0OiIsdCksdH0pKS50aGVuKHQ9PnQsdD0+e2lmKHQ9PT13dClyZXR1cm4gaCYmXy5lcnJvcigiU2tpcHBlZCBzZW5kaW5nIGV2ZW50IGJlY2F1c2UgYnVmZmVyIGlzIGZ1bGwuIiksaSgicXVldWVfb3ZlcmZsb3ciKSxQcm9taXNlLnJlc29sdmUoe30pO3Rocm93IHR9KX0sZmx1c2g6dD0+bi5kcmFpbih0KX19Y29uc3QgRXQ9L14oXFMrOlxcfFwvPykoW1xzXFNdKj8pKCg/OlwuezEsMn18W14vXFxdKz98KShcLlteLi9cXF0qfCkpKD86Wy9cXF0qKSQvO2Z1bmN0aW9uIHh0KHQpe2NvbnN0IGU9ZnVuY3Rpb24odCl7Y29uc3QgZT10Lmxlbmd0aD4xMDI0P2A8dHJ1bmNhdGVkPiR7dC5zbGljZSgtMTAyNCl9YDp0LG49RXQuZXhlYyhlKTtyZXR1cm4gbj9uLnNsaWNlKDEpOltdfSh0KSxuPWVbMF18fCIiO2xldCByPWVbMV07cmV0dXJuIG58fHI/KHImJihyPXIuc2xpY2UoMCxyLmxlbmd0aC0xKSksbityKToiLiJ9ZnVuY3Rpb24gTnQodCxlPSExKXtyZXR1cm4hKGV8fHQmJiF0LnN0YXJ0c1dpdGgoIi8iKSYmIXQubWF0Y2goL15bQS1aXTovKSYmIXQuc3RhcnRzV2l0aCgiLiIpJiYhdC5tYXRjaCgvXlthLXpBLVpdKFthLXpBLVowLTkuXC0rXSkqOlwvXC8vKSkmJnZvaWQgMCE9PXQmJiF0LmluY2x1ZGVzKCJub2RlX21vZHVsZXMvIil9dmFyIEN0O2NvbnN0IGp0PVN5bWJvbCgiQWdlbnRCYXNlSW50ZXJuYWxTdGF0ZSIpO2NsYXNzIE90IGV4dGVuZHMoQ3Q9cy5BZ2VudCxDdCl7Y29uc3RydWN0b3IodCl7c3VwZXIodCksdGhpc1tqdF09e319aXNTZWN1cmVFbmRwb2ludCh0KXtpZih0KXtpZigiYm9vbGVhbiI9PXR5cGVvZiB0LnNlY3VyZUVuZHBvaW50KXJldHVybiB0LnNlY3VyZUVuZHBvaW50O2lmKCJzdHJpbmciPT10eXBlb2YgdC5wcm90b2NvbClyZXR1cm4iaHR0cHM6Ij09PXQucHJvdG9jb2x9Y29uc3R7c3RhY2s6ZX09bmV3IEVycm9yO3JldHVybiJzdHJpbmciPT10eXBlb2YgZSYmZS5zcGxpdCgiXG4iKS5zb21lKHQ9Pi0xIT09dC5pbmRleE9mKCIoaHR0cHMuanM6Iil8fC0xIT09dC5pbmRleE9mKCJub2RlOmh0dHBzOiIpKX1jcmVhdGVTb2NrZXQodCxlLG4pe2NvbnN0IHI9ey4uLmUsc2VjdXJlRW5kcG9pbnQ6dGhpcy5pc1NlY3VyZUVuZHBvaW50KGUpfTtQcm9taXNlLnJlc29sdmUoKS50aGVuKCgpPT50aGlzLmNvbm5lY3QodCxyKSkudGhlbihvPT57aWYobyBpbnN0YW5jZW9mIHMuQWdlbnQpcmV0dXJuIG8uYWRkUmVxdWVzdCh0LHIpO3RoaXNbanRdLmN1cnJlbnRTb2NrZXQ9byxzdXBlci5jcmVhdGVTb2NrZXQodCxlLG4pfSxuKX1jcmVhdGVDb25uZWN0aW9uKCl7Y29uc3QgdD10aGlzW2p0XS5jdXJyZW50U29ja2V0O2lmKHRoaXNbanRdLmN1cnJlbnRTb2NrZXQ9dm9pZCAwLCF0KXRocm93IG5ldyBFcnJvcigiTm8gc29ja2V0IHdhcyByZXR1cm5lZCBpbiB0aGUgYGNvbm5lY3QoKWAgZnVuY3Rpb24iKTtyZXR1cm4gdH1nZXQgZGVmYXVsdFBvcnQoKXtyZXR1cm4gdGhpc1tqdF0uZGVmYXVsdFBvcnQ/PygiaHR0cHM6Ij09PXRoaXMucHJvdG9jb2w/NDQzOjgwKX1zZXQgZGVmYXVsdFBvcnQodCl7dGhpc1tqdF0mJih0aGlzW2p0XS5kZWZhdWx0UG9ydD10KX1nZXQgcHJvdG9jb2woKXtyZXR1cm4gdGhpc1tqdF0ucHJvdG9jb2w/Pyh0aGlzLmlzU2VjdXJlRW5kcG9pbnQoKT8iaHR0cHM6IjoiaHR0cDoiKX1zZXQgcHJvdG9jb2wodCl7dGhpc1tqdF0mJih0aGlzW2p0XS5wcm90b2NvbD10KX19ZnVuY3Rpb24gUHQoLi4udCl7Xy5sb2coIltodHRwcy1wcm94eS1hZ2VudDpwYXJzZS1wcm94eS1yZXNwb25zZV0iLC4uLnQpfWZ1bmN0aW9uIEF0KHQpe3JldHVybiBuZXcgUHJvbWlzZSgoZSxuKT0+e2xldCByPTA7Y29uc3Qgbz1bXTtmdW5jdGlvbiBzKCl7Y29uc3QgYz10LnJlYWQoKTtjP2Z1bmN0aW9uKGMpe28ucHVzaChjKSxyKz1jLmxlbmd0aDtjb25zdCB1PUJ1ZmZlci5jb25jYXQobyxyKSxhPXUuaW5kZXhPZigiXHJcblxyXG4iKTtpZigtMT09PWEpcmV0dXJuIFB0KCJoYXZlIG5vdCByZWNlaXZlZCBlbmQgb2YgSFRUUCBoZWFkZXJzIHlldC4uLiIpLHZvaWQgcygpO2NvbnN0IGY9dS5zdWJhcnJheSgwLGEpLnRvU3RyaW5nKCJhc2NpaSIpLnNwbGl0KCJcclxuIiksaD1mLnNoaWZ0KCk7aWYoIWgpcmV0dXJuIHQuZGVzdHJveSgpLG4obmV3IEVycm9yKCJObyBoZWFkZXIgcmVjZWl2ZWQgZnJvbSBwcm94eSBDT05ORUNUIHJlc3BvbnNlIikpO2NvbnN0IHA9aC5zcGxpdCgiICIpLGw9KyhwWzFdfHwwKSxkPXAuc2xpY2UoMikuam9pbigiICIpLGc9e307Zm9yKGNvbnN0IGUgb2YgZil7aWYoIWUpY29udGludWU7Y29uc3Qgcj1lLmluZGV4T2YoIjoiKTtpZigtMT09PXIpcmV0dXJuIHQuZGVzdHJveSgpLG4obmV3IEVycm9yKGBJbnZhbGlkIGhlYWRlciBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2U6ICIke2V9ImApKTtjb25zdCBvPWUuc2xpY2UoMCxyKS50b0xvd2VyQ2FzZSgpLHM9ZS5zbGljZShyKzEpLnRyaW1TdGFydCgpLGk9Z1tvXTsic3RyaW5nIj09dHlwZW9mIGk/Z1tvXT1baSxzXTpBcnJheS5pc0FycmF5KGkpP2kucHVzaChzKTpnW29dPXN9UHQoImdvdCBwcm94eSBzZXJ2ZXIgcmVzcG9uc2U6ICVvICVvIixoLGcpLGkoKSxlKHtjb25uZWN0OntzdGF0dXNDb2RlOmwsc3RhdHVzVGV4dDpkLGhlYWRlcnM6Z30sYnVmZmVyZWQ6dX0pfShjKTp0Lm9uY2UoInJlYWRhYmxlIixzKX1mdW5jdGlvbiBpKCl7dC5yZW1vdmVMaXN0ZW5lcigiZW5kIixjKSx0LnJlbW92ZUxpc3RlbmVyKCJlcnJvciIsdSksdC5yZW1vdmVMaXN0ZW5lcigicmVhZGFibGUiLHMpfWZ1bmN0aW9uIGMoKXtpKCksUHQoIm9uZW5kIiksbihuZXcgRXJyb3IoIlByb3h5IGNvbm5lY3Rpb24gZW5kZWQgYmVmb3JlIHJlY2VpdmluZyBDT05ORUNUIHJlc3BvbnNlIikpfWZ1bmN0aW9uIHUodCl7aSgpLFB0KCJvbmVycm9yICVvIix0KSxuKHQpfXQub24oImVycm9yIix1KSx0Lm9uKCJlbmQiLGMpLHMoKX0pfWZ1bmN0aW9uIFR0KC4uLnQpe18ubG9nKCJbaHR0cHMtcHJveHktYWdlbnRdIiwuLi50KX1jbGFzcyBrdCBleHRlbmRzIE90e2NvbnN0cnVjdG9yKHQsZSl7c3VwZXIoZSksdGhpcy5vcHRpb25zPXt9LHRoaXMucHJveHk9InN0cmluZyI9PXR5cGVvZiB0P25ldyBVUkwodCk6dCx0aGlzLnByb3h5SGVhZGVycz1lPy5oZWFkZXJzPz97fSxUdCgiQ3JlYXRpbmcgbmV3IEh0dHBzUHJveHlBZ2VudCBpbnN0YW5jZTogJW8iLHRoaXMucHJveHkuaHJlZik7Y29uc3Qgbj0odGhpcy5wcm94eS5ob3N0bmFtZXx8dGhpcy5wcm94eS5ob3N0KS5yZXBsYWNlKC9eXFt8XF0kL2csIiIpLHI9dGhpcy5wcm94eS5wb3J0P3BhcnNlSW50KHRoaXMucHJveHkucG9ydCwxMCk6Imh0dHBzOiI9PT10aGlzLnByb3h5LnByb3RvY29sPzQ0Mzo4MDt0aGlzLmNvbm5lY3RPcHRzPXtBTFBOUHJvdG9jb2xzOlsiaHR0cC8xLjEiXSwuLi5lP0l0KGUsImhlYWRlcnMiKTpudWxsLGhvc3Q6bixwb3J0OnJ9fWFzeW5jIGNvbm5lY3QodCxlKXtjb25zdHtwcm94eTpufT10aGlzO2lmKCFlLmhvc3QpdGhyb3cgbmV3IFR5cGVFcnJvcignTm8gImhvc3QiIHByb3ZpZGVkJyk7bGV0IHI7aWYoImh0dHBzOiI9PT1uLnByb3RvY29sKXtUdCgiQ3JlYXRpbmcgYHRscy5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cyk7Y29uc3QgdD10aGlzLmNvbm5lY3RPcHRzLnNlcnZlcm5hbWV8fHRoaXMuY29ubmVjdE9wdHMuaG9zdDtyPWYuY29ubmVjdCh7Li4udGhpcy5jb25uZWN0T3B0cyxzZXJ2ZXJuYW1lOnQmJmEuaXNJUCh0KT92b2lkIDA6dH0pfWVsc2UgVHQoIkNyZWF0aW5nIGBuZXQuU29ja2V0YDogJW8iLHRoaXMuY29ubmVjdE9wdHMpLHI9YS5jb25uZWN0KHRoaXMuY29ubmVjdE9wdHMpO2NvbnN0IG89ImZ1bmN0aW9uIj09dHlwZW9mIHRoaXMucHJveHlIZWFkZXJzP3RoaXMucHJveHlIZWFkZXJzKCk6ey4uLnRoaXMucHJveHlIZWFkZXJzfSxzPWEuaXNJUHY2KGUuaG9zdCk/YFske2UuaG9zdH1dYDplLmhvc3Q7bGV0IGk9YENPTk5FQ1QgJHtzfToke2UucG9ydH0gSFRUUC8xLjFcclxuYDtpZihuLnVzZXJuYW1lfHxuLnBhc3N3b3JkKXtjb25zdCB0PWAke2RlY29kZVVSSUNvbXBvbmVudChuLnVzZXJuYW1lKX06JHtkZWNvZGVVUklDb21wb25lbnQobi5wYXNzd29yZCl9YDtvWyJQcm94eS1BdXRob3JpemF0aW9uIl09YEJhc2ljICR7QnVmZmVyLmZyb20odCkudG9TdHJpbmcoImJhc2U2NCIpfWB9by5Ib3N0PWAke3N9OiR7ZS5wb3J0fWAsb1siUHJveHktQ29ubmVjdGlvbiJdfHwob1siUHJveHktQ29ubmVjdGlvbiJdPXRoaXMua2VlcEFsaXZlPyJLZWVwLUFsaXZlIjoiY2xvc2UiKTtmb3IoY29uc3QgdCBvZiBPYmplY3Qua2V5cyhvKSlpKz1gJHt0fTogJHtvW3RdfVxyXG5gO2NvbnN0IGM9QXQocik7ci53cml0ZShgJHtpfVxyXG5gKTtjb25zdHtjb25uZWN0OnUsYnVmZmVyZWQ6aH09YXdhaXQgYztpZih0LmVtaXQoInByb3h5Q29ubmVjdCIsdSksdGhpcy5lbWl0KCJwcm94eUNvbm5lY3QiLHUsdCksMjAwPT09dS5zdGF0dXNDb2RlKXtpZih0Lm9uY2UoInNvY2tldCIsUnQpLGUuc2VjdXJlRW5kcG9pbnQpe1R0KCJVcGdyYWRpbmcgc29ja2V0IGNvbm5lY3Rpb24gdG8gVExTIik7Y29uc3QgdD1lLnNlcnZlcm5hbWV8fGUuaG9zdDtyZXR1cm4gZi5jb25uZWN0KHsuLi5JdChlLCJob3N0IiwicGF0aCIsInBvcnQiKSxzb2NrZXQ6cixzZXJ2ZXJuYW1lOmEuaXNJUCh0KT92b2lkIDA6dH0pfXJldHVybiByfXIuZGVzdHJveSgpO2NvbnN0IHA9bmV3IGEuU29ja2V0KHt3cml0YWJsZTohMX0pO3JldHVybiBwLnJlYWRhYmxlPSEwLHQub25jZSgic29ja2V0Iix0PT57VHQoIlJlcGxheWluZyBwcm94eSBidWZmZXIgZm9yIGZhaWxlZCByZXF1ZXN0IiksdC5wdXNoKGgpLHQucHVzaChudWxsKX0pLHB9fWZ1bmN0aW9uIFJ0KHQpe3QucmVzdW1lKCl9ZnVuY3Rpb24gSXQodCwuLi5lKXtjb25zdCBuPXt9O2xldCByO2ZvcihyIGluIHQpZS5pbmNsdWRlcyhyKXx8KG5bcl09dFtyXSk7cmV0dXJuIG59a3QucHJvdG9jb2xzPVsiaHR0cCIsImh0dHBzIl07ZnVuY3Rpb24gRHQodCl7cmV0dXJuIHQucmVwbGFjZSgvXltBLVpdOi8sIiIpLnJlcGxhY2UoL1xcL2csIi8iKX1jb25zdCBVdD1lO2xldCBCdCxMdD0wLE10PXt9O2Z1bmN0aW9uIHp0KHQpe1V0LmRlYnVnJiZjb25zb2xlLmxvZyhgW0FOUiBXb3JrZXJdICR7dH1gKX12YXIgSnQsV3QsRnQ7Y29uc3QgR3Q9ZnVuY3Rpb24odCl7bGV0IGU7dHJ5e2U9bmV3IFVSTCh0LnVybCl9Y2F0Y2h7cmV0dXJuIGIoKCk9Pntjb25zb2xlLndhcm4oIltAc2VudHJ5L25vZGVdOiBJbnZhbGlkIGRzbiBvciB0dW5uZWwgb3B0aW9uLCB3aWxsIG5vdCBzZW5kIGFueSBldmVudHMuIFRoZSB0dW5uZWwgb3B0aW9uIG11c3QgYmUgYSBmdWxsIFVSTCB3aGVuIHVzZWQuIil9KSwkdCh0LCgpPT5Qcm9taXNlLnJlc29sdmUoe30pKX1jb25zdCBuPSJodHRwczoiPT09ZS5wcm90b2NvbCxyPWZ1bmN0aW9uKHQsZSl7Y29uc3R7bm9fcHJveHk6bn09cHJvY2Vzcy5lbnYscj1uPy5zcGxpdCgiLCIpLnNvbWUoZT0+dC5ob3N0LmVuZHNXaXRoKGUpfHx0Lmhvc3RuYW1lLmVuZHNXaXRoKGUpKTtyZXR1cm4gcj92b2lkIDA6ZX0oZSx0LnByb3h5fHwobj9wcm9jZXNzLmVudi5odHRwc19wcm94eTp2b2lkIDApfHxwcm9jZXNzLmVudi5odHRwX3Byb3h5KSxvPW4/aTpzLGE9dC5rZWVwQWxpdmU/PyEwLGY9cj9uZXcga3Qocik6bmV3IG8uQWdlbnQoe2tlZXBBbGl2ZTphLG1heFNvY2tldHM6MzAsdGltZW91dDoyZTN9KSxoPWZ1bmN0aW9uKHQsZSxuKXtjb25zdHtob3N0bmFtZTpyLHBhdGhuYW1lOm8scG9ydDpzLHByb3RvY29sOmksc2VhcmNoOmF9PW5ldyBVUkwodC51cmwpO3JldHVybiBmdW5jdGlvbihmKXtjb25zdCBoPXA9Pm5ldyBQcm9taXNlKChsLGQpPT57eXQoKCk9PntsZXQgZz1mdW5jdGlvbih0KXtyZXR1cm4gbmV3IGMoe3JlYWQoKXt0aGlzLnB1c2godCksdGhpcy5wdXNoKG51bGwpfX0pfShmLmJvZHkpO2NvbnN0IG09ey4uLnQuaGVhZGVyc307Zi5ib2R5Lmxlbmd0aD4zMjc2OCYmKG1bImNvbnRlbnQtZW5jb2RpbmciXT0iZ3ppcCIsZz1nLnBpcGUodSgpKSk7Y29uc3QgeT1yLnN0YXJ0c1dpdGgoIlsiKSxiPWUucmVxdWVzdCh7bWV0aG9kOiJQT1NUIixhZ2VudDpuLGhlYWRlcnM6bSxob3N0bmFtZTp5P3Iuc2xpY2UoMSwtMSk6cixwYXRoOmAke299JHthfWAscG9ydDpzLHByb3RvY29sOmksY2E6dC5jYUNlcnRzfSx0PT57dC5vbigiZGF0YSIsKCk9Pnt9KSx0Lm9uKCJlbmQiLCgpPT57fSksdC5zZXRFbmNvZGluZygidXRmOCIpO2NvbnN0IGU9dC5oZWFkZXJzWyJyZXRyeS1hZnRlciJdPz9udWxsLG49dC5oZWFkZXJzWyJ4LXNlbnRyeS1yYXRlLWxpbWl0cyJdPz9udWxsO2woe3N0YXR1c0NvZGU6dC5zdGF0dXNDb2RlLGhlYWRlcnM6eyJyZXRyeS1hZnRlciI6ZSwieC1zZW50cnktcmF0ZS1saW1pdHMiOkFycmF5LmlzQXJyYXkobik/blswXXx8bnVsbDpufX0pfSk7Yi5vbigiZXJyb3IiLHQ9PntwJiZiLnJldXNlZFNvY2tldCYmIkVDT05OUkVTRVQiPT09dC5jb2RlP2woaCghMSkpOmQodCl9KSxnLnBpcGUoYil9KX0pO3JldHVybiBoKCEwKX19KHQsdC5odHRwTW9kdWxlPz9vLGYpO3JldHVybiAkdCh0LGgpfSh7dXJsOihKdD1VdC5kc24sV3Q9VXQudHVubmVsLEZ0PVV0LnNka01ldGFkYXRhLnNkayxXdHx8YCR7ZnVuY3Rpb24odCl7cmV0dXJuYCR7ZnVuY3Rpb24odCl7Y29uc3QgZT10LnByb3RvY29sP2Ake3QucHJvdG9jb2x9OmA6IiIsbj10LnBvcnQ/YDoke3QucG9ydH1gOiIiO3JldHVybmAke2V9Ly8ke3QuaG9zdH0ke259JHt0LnBhdGg/YC8ke3QucGF0aH1gOiIifS9hcGkvYH0odCl9JHt0LnByb2plY3RJZH0vZW52ZWxvcGUvYH0oSnQpfT8ke2Z1bmN0aW9uKHQsZSl7Y29uc3Qgbj17c2VudHJ5X3ZlcnNpb246IjcifTtyZXR1cm4gdC5wdWJsaWNLZXkmJihuLnNlbnRyeV9rZXk9dC5wdWJsaWNLZXkpLGUmJihuLnNlbnRyeV9jbGllbnQ9YCR7ZS5uYW1lfS8ke2UudmVyc2lvbn1gKSxuZXcgVVJMU2VhcmNoUGFyYW1zKG4pLnRvU3RyaW5nKCl9KEp0LEZ0KX1gKX0pO2FzeW5jIGZ1bmN0aW9uIFl0KCl7aWYoQnQpe3p0KCJTZW5kaW5nIGFibm9ybWFsIHNlc3Npb24iKSxaKEJ0LHtzdGF0dXM6ImFibm9ybWFsIixhYm5vcm1hbF9tZWNoYW5pc206ImFucl9mb3JlZ3JvdW5kIixyZWxlYXNlOlV0LnJlbGVhc2UsZW52aXJvbm1lbnQ6VXQuZW52aXJvbm1lbnR9KTtjb25zdCB0PWZ1bmN0aW9uKHQsZSxuLHIpe2NvbnN0IG89bXQobik7cmV0dXJuIGZ0KHtzZW50X2F0Om5ldyBEYXRlKEkoKSkudG9JU09TdHJpbmcoKSwuLi5vJiZ7c2RrOm99LC4uLiEhciYmZSYme2Rzbjp1dChlKX19LFsiYWdncmVnYXRlcyJpbiB0P1t7dHlwZToic2Vzc2lvbnMifSx0XTpbe3R5cGU6InNlc3Npb24ifSx0LnRvSlNPTigpXV0pfShCdCxVdC5kc24sVXQuc2RrTWV0YWRhdGEsVXQudHVubmVsKTt6dChKU09OLnN0cmluZ2lmeSh0KSksYXdhaXQgR3Quc2VuZCh0KTt0cnl7bj8ucG9zdE1lc3NhZ2UoInNlc3Npb24tZW5kZWQiKX1jYXRjaHt9fX1mdW5jdGlvbiBIdCh0KXtpZighdClyZXR1cm47Y29uc3QgZT1mdW5jdGlvbih0KXtpZighdC5sZW5ndGgpcmV0dXJuW107Y29uc3QgZT1BcnJheS5mcm9tKHQpO3JldHVybi9zZW50cnlXcmFwcGVkLy50ZXN0KEUoZSkuZnVuY3Rpb258fCIiKSYmZS5wb3AoKSxlLnJldmVyc2UoKSwkLnRlc3QoRShlKS5mdW5jdGlvbnx8IiIpJiYoZS5wb3AoKSwkLnRlc3QoRShlKS5mdW5jdGlvbnx8IiIpJiZlLnBvcCgpKSxlLnNsaWNlKDAsNTApLm1hcCh0PT4oey4uLnQsZmlsZW5hbWU6dC5maWxlbmFtZXx8RShlKS5maWxlbmFtZSxmdW5jdGlvbjp0LmZ1bmN0aW9ufHwiPyJ9KSl9KHQpO2lmKFV0LmFwcFJvb3RQYXRoKWZvcihjb25zdCB0IG9mIGUpdC5maWxlbmFtZSYmKHQuZmlsZW5hbWU9TSh0LmZpbGVuYW1lLFV0LmFwcFJvb3RQYXRoKSk7cmV0dXJuIGV9YXN5bmMgZnVuY3Rpb24gWnQodCxlKXtpZihMdD49VXQubWF4QW5yRXZlbnRzKXJldHVybjtMdCs9MSxhd2FpdCBZdCgpLHp0KCJTZW5kaW5nIGV2ZW50Iik7Y29uc3Qgbj17ZXZlbnRfaWQ6VygpLGNvbnRleHRzOlV0LmNvbnRleHRzLHJlbGVhc2U6VXQucmVsZWFzZSxlbnZpcm9ubWVudDpVdC5lbnZpcm9ubWVudCxkaXN0OlV0LmRpc3QscGxhdGZvcm06Im5vZGUiLGxldmVsOiJlcnJvciIsZXhjZXB0aW9uOnt2YWx1ZXM6W3t0eXBlOiJBcHBsaWNhdGlvbk5vdFJlc3BvbmRpbmciLHZhbHVlOmBBcHBsaWNhdGlvbiBOb3QgUmVzcG9uZGluZyBmb3IgYXQgbGVhc3QgJHtVdC5hbnJUaHJlc2hvbGR9IG1zYCxzdGFja3RyYWNlOntmcmFtZXM6SHQodCl9LG1lY2hhbmlzbTp7dHlwZToiQU5SIn19XX0sdGFnczpVdC5zdGF0aWNUYWdzfTtlJiZmdW5jdGlvbih0LGUpe2lmKGF0KHQsZSksIXQuY29udGV4dHM/LnRyYWNlKXtjb25zdHt0cmFjZUlkOm4scGFyZW50U3BhbklkOnIscHJvcGFnYXRpb25TcGFuSWQ6b309ZS5wcm9wYWdhdGlvbkNvbnRleHQ7dC5jb250ZXh0cz17dHJhY2U6e3RyYWNlX2lkOm4sc3Bhbl9pZDpvfHxXKCkuc3Vic3RyaW5nKDE2KSxwYXJlbnRfc3Bhbl9pZDpyfSwuLi50LmNvbnRleHRzfX19KG4sZSksZnVuY3Rpb24odCl7aWYoMD09PU9iamVjdC5rZXlzKE10KS5sZW5ndGgpcmV0dXJuO2NvbnN0IGU9VXQuYXBwUm9vdFBhdGg/e306TXQ7aWYoVXQuYXBwUm9vdFBhdGgpZm9yKGNvbnN0W3Qsbl1vZiBPYmplY3QuZW50cmllcyhNdCkpZVtNKHQsVXQuYXBwUm9vdFBhdGgpXT1uO2NvbnN0IG49bmV3IE1hcDtmb3IoY29uc3QgciBvZiB0LmV4Y2VwdGlvbj8udmFsdWVzfHxbXSlmb3IoY29uc3QgdCBvZiByLnN0YWNrdHJhY2U/LmZyYW1lc3x8W10pe2NvbnN0IHI9dC5hYnNfcGF0aHx8dC5maWxlbmFtZTtyJiZlW3JdJiZuLnNldChyLGVbcl0pfWlmKG4uc2l6ZT4wKXtjb25zdCBlPVtdO2Zvcihjb25zdFt0LHJdb2Ygbi5lbnRyaWVzKCkpZS5wdXNoKHt0eXBlOiJzb3VyY2VtYXAiLGNvZGVfZmlsZTp0LGRlYnVnX2lkOnJ9KTt0LmRlYnVnX21ldGE9e2ltYWdlczplfX19KG4pO2NvbnN0IHI9YnQobixVdC5kc24sVXQuc2RrTWV0YWRhdGEsVXQudHVubmVsKTt6dChKU09OLnN0cmluZ2lmeShyKSksYXdhaXQgR3Quc2VuZChyKSxhd2FpdCBHdC5mbHVzaCgyZTMpLEx0Pj1VdC5tYXhBbnJFdmVudHMmJnNldFRpbWVvdXQoKCk9Pntwcm9jZXNzLmV4aXQoMCl9LDVlMyl9bGV0IHF0O2lmKHp0KCJTdGFydGVkIiksVXQuY2FwdHVyZVN0YWNrVHJhY2Upe3p0KCJDb25uZWN0aW5nIHRvIGRlYnVnZ2VyIik7Y29uc3QgZT1uZXcgdDtlLmNvbm5lY3RUb01haW5UaHJlYWQoKSx6dCgiQ29ubmVjdGVkIHRvIGRlYnVnZ2VyIik7Y29uc3Qgbj1uZXcgTWFwO2Uub24oIkRlYnVnZ2VyLnNjcmlwdFBhcnNlZCIsdD0+e24uc2V0KHQucGFyYW1zLnNjcmlwdElkLHQucGFyYW1zLnVybCl9KSxlLm9uKCJEZWJ1Z2dlci5wYXVzZWQiLHQ9PntpZigib3RoZXIiPT09dC5wYXJhbXMucmVhc29uKXRyeXt6dCgiRGVidWdnZXIgcGF1c2VkIik7Y29uc3Qgcz1bLi4udC5wYXJhbXMuY2FsbEZyYW1lc10saT1VdC5hcHBSb290UGF0aD9mdW5jdGlvbih0PShwcm9jZXNzLmFyZ3ZbMV0/eHQocHJvY2Vzcy5hcmd2WzFdKTpwcm9jZXNzLmN3ZCgpKSxlPSJcXCI9PT1vKXtjb25zdCBuPWU/RHQodCk6dDtyZXR1cm4gdD0+e2lmKCF0KXJldHVybjtjb25zdCBvPWU/RHQodCk6dDtsZXR7ZGlyOnMsYmFzZTppLGV4dDpjfT1yLnBhcnNlKG8pOyIuanMiIT09YyYmIi5tanMiIT09YyYmIi5janMiIT09Y3x8KGk9aS5zbGljZSgwLC0xKmMubGVuZ3RoKSk7Y29uc3QgdT1kZWNvZGVVUklDb21wb25lbnQoaSk7c3x8KHM9Ii4iKTtjb25zdCBhPXMubGFzdEluZGV4T2YoIi9ub2RlX21vZHVsZXMiKTtpZihhPi0xKXJldHVybmAke3Muc2xpY2UoYSsxNCkucmVwbGFjZSgvXC8vZywiLiIpfToke3V9YDtpZihzLnN0YXJ0c1dpdGgobikpe2NvbnN0IHQ9cy5zbGljZShuLmxlbmd0aCsxKS5yZXBsYWNlKC9cLy9nLCIuIik7cmV0dXJuIHQ/YCR7dH06JHt1fWA6dX1yZXR1cm4gdX19KFV0LmFwcFJvb3RQYXRoKTooKT0+e30sYz1zLm1hcCh0PT5mdW5jdGlvbih0LGUsbil7Y29uc3Qgcj1lP2UucmVwbGFjZSgvXmZpbGU6XC9cLy8sIiIpOnZvaWQgMCxvPXQubG9jYXRpb24uY29sdW1uTnVtYmVyP3QubG9jYXRpb24uY29sdW1uTnVtYmVyKzE6dm9pZCAwLHM9dC5sb2NhdGlvbi5saW5lTnVtYmVyP3QubG9jYXRpb24ubGluZU51bWJlcisxOnZvaWQgMDtyZXR1cm57ZmlsZW5hbWU6cixtb2R1bGU6bihyKSxmdW5jdGlvbjp0LmZ1bmN0aW9uTmFtZXx8Ij8iLGNvbG5vOm8sbGluZW5vOnMsaW5fYXBwOnI/TnQocik6dm9pZCAwfX0odCxuLmdldCh0LmxvY2F0aW9uLnNjcmlwdElkKSxpKSksdT1zZXRUaW1lb3V0KCgpPT57WnQoYykudGhlbihudWxsLCgpPT57enQoIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pfSw1ZTMpO2UucG9zdCgiUnVudGltZS5ldmFsdWF0ZSIse2V4cHJlc3Npb246Imdsb2JhbC5fX1NFTlRSWV9HRVRfU0NPUEVTX18oKTsiLHNpbGVudDohMCxyZXR1cm5CeVZhbHVlOiEwfSwodCxuKT0+e3QmJnp0KGBFcnJvciBleGVjdXRpbmcgc2NyaXB0OiAnJHt0Lm1lc3NhZ2V9J2ApLGNsZWFyVGltZW91dCh1KTtjb25zdCByPW4/LnJlc3VsdD9uLnJlc3VsdC52YWx1ZTp2b2lkIDA7ZS5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxlLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSxadChjLHIpLnRoZW4obnVsbCwoKT0+e3p0KCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQuIil9KX0pfWNhdGNoKHQpe3Rocm93IGUucG9zdCgiRGVidWdnZXIucmVzdW1lIiksZS5wb3N0KCJEZWJ1Z2dlci5kaXNhYmxlIiksdH19KSxxdD0oKT0+e3RyeXtlLnBvc3QoIkRlYnVnZ2VyLmVuYWJsZSIsKCk9PntlLnBvc3QoIkRlYnVnZ2VyLnBhdXNlIil9KX1jYXRjaHt9fX1jb25zdHtwb2xsOkt0fT1mdW5jdGlvbih0LGUsbixyKXtjb25zdCBvPXQoKTtsZXQgcz0hMSxpPSEwO3JldHVybiBzZXRJbnRlcnZhbCgoKT0+e2NvbnN0IHQ9by5nZXRUaW1lTXMoKTshMT09PXMmJnQ+ZStuJiYocz0hMCxpJiZyKCkpLHQ8ZStuJiYocz0hMSl9LDIwKSx7cG9sbDooKT0+e28ucmVzZXQoKX0sZW5hYmxlZDp0PT57aT10fX19KGZ1bmN0aW9uKCl7bGV0IHQ9cHJvY2Vzcy5ocnRpbWUoKTtyZXR1cm57Z2V0VGltZU1zOigpPT57Y29uc3RbZSxuXT1wcm9jZXNzLmhydGltZSh0KTtyZXR1cm4gTWF0aC5mbG9vcigxZTMqZStuLzFlNil9LHJlc2V0OigpPT57dD1wcm9jZXNzLmhydGltZSgpfX19LFV0LnBvbGxJbnRlcnZhbCxVdC5hbnJUaHJlc2hvbGQsZnVuY3Rpb24oKXt6dCgiV2F0Y2hkb2cgdGltZW91dCIpLHF0Pyh6dCgiUGF1c2luZyBkZWJ1Z2dlciB0byBjYXB0dXJlIHN0YWNrIHRyYWNlIikscXQoKSk6KHp0KCJDYXB0dXJpbmcgZXZlbnQgd2l0aG91dCBhIHN0YWNrIHRyYWNlIiksWnQoKS50aGVuKG51bGwsKCk9Pnt6dCgiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkIG9uIHdhdGNoZG9nIHRpbWVvdXQuIil9KSl9KTtuPy5vbigibWVzc2FnZSIsdD0+e3Quc2Vzc2lvbiYmKEJ0PUgodC5zZXNzaW9uKSksdC5kZWJ1Z0ltYWdlcyYmKE10PXQuZGVidWdJbWFnZXMpLEt0KCl9KTs=";
|
|
9
|
+
const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZSAxMS4wLjAtcmMuMCAoYjY1MjA5NykgfCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0ICovCmltcG9ydHtTZXNzaW9uIGFzIHR9ZnJvbSJub2RlOmluc3BlY3RvciI7aW1wb3J0e3dvcmtlckRhdGEgYXMgZSxwYXJlbnRQb3J0IGFzIG59ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtpbXBvcnR7cG9zaXggYXMgcixzZXAgYXMgb31mcm9tIm5vZGU6cGF0aCI7aW1wb3J0KmFzIHMgZnJvbSJub2RlOmh0dHAiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwcyI7aW1wb3J0e1JlYWRhYmxlIGFzIGN9ZnJvbSJub2RlOnN0cmVhbSI7aW1wb3J0e2NyZWF0ZUd6aXAgYXMgdX1mcm9tIm5vZGU6emxpYiI7aW1wb3J0KmFzIGEgZnJvbSJub2RlOm5ldCI7aW1wb3J0KmFzIGYgZnJvbSJub2RlOnRscyI7Y29uc3QgaD0idW5kZWZpbmVkIj09dHlwZW9mIF9fU0VOVFJZX0RFQlVHX198fF9fU0VOVFJZX0RFQlVHX18scD1nbG9iYWxUaGlzLGw9IjExLjAuMC1yYy4wIjtmdW5jdGlvbiBkKCl7cmV0dXJuIGcocCkscH1mdW5jdGlvbiBnKHQpe2NvbnN0IGU9dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIGUudmVyc2lvbj1lLnZlcnNpb258fGwsZVtsXT1lW2xdfHx7fX1mdW5jdGlvbiBtKHQsZSxuPXApe2NvbnN0IHI9bi5fX1NFTlRSWV9fPW4uX19TRU5UUllfX3x8e30sbz1yW2xdPXJbbF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1lKCkpfWNvbnN0IHk9e307ZnVuY3Rpb24gYih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3QgZT1wLmNvbnNvbGUsbj17fSxyPU9iamVjdC5rZXlzKHkpO3IuZm9yRWFjaCh0PT57Y29uc3Qgcj15W3RdO25bdF09ZVt0XSxlW3RdPXJ9KTt0cnl7cmV0dXJuIHQoKX1maW5hbGx5e3IuZm9yRWFjaCh0PT57ZVt0XT1uW3RdfSl9fWZ1bmN0aW9uIHYoKXtyZXR1cm4gUygpLmVuYWJsZWR9ZnVuY3Rpb24gdyh0LC4uLmUpe2gmJnYoKSYmYigoKT0+e3AuY29uc29sZVt0XShgU2VudHJ5IExvZ2dlciBbJHt0fV06YCwuLi5lKX0pfWZ1bmN0aW9uIFMoKXtyZXR1cm4gaD9tKCJsb2dnZXJTZXR0aW5ncyIsKCk9Pih7ZW5hYmxlZDohMX0pKTp7ZW5hYmxlZDohMX19Y29uc3QgXz17ZW5hYmxlOmZ1bmN0aW9uKCl7UygpLmVuYWJsZWQ9ITB9LGRpc2FibGU6ZnVuY3Rpb24oKXtTKCkuZW5hYmxlZD0hMX0saXNFbmFibGVkOnYsbG9nOmZ1bmN0aW9uKC4uLnQpe3coImxvZyIsLi4udCl9LHdhcm46ZnVuY3Rpb24oLi4udCl7dygid2FybiIsLi4udCl9LGVycm9yOmZ1bmN0aW9uKC4uLnQpe3coImVycm9yIiwuLi50KX19LCQ9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIEUodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IHg9Ijxhbm9ueW1vdXM+Ijtjb25zdCBOPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7ZnVuY3Rpb24gQyh0KXtyZXR1cm4gZnVuY3Rpb24odCxlKXtyZXR1cm4gTi5jYWxsKHQpPT09YFtvYmplY3QgJHtlfV1gfSh0LCJPYmplY3QiKX1mdW5jdGlvbiBqKHQpe3JldHVybiBCb29sZWFuKHQ/LnRoZW4mJiJmdW5jdGlvbiI9PXR5cGVvZiB0LnRoZW4pfWZ1bmN0aW9uIE8odCxlKXt0cnl7cmV0dXJuIHQgaW5zdGFuY2VvZiBlfWNhdGNoe3JldHVybiExfX1mdW5jdGlvbiBQKHQpe2lmKGZ1bmN0aW9uKHQpe3N3aXRjaChOLmNhbGwodCkpe2Nhc2UiW29iamVjdCBFcnJvcl0iOmNhc2UiW29iamVjdCBFeGNlcHRpb25dIjpjYXNlIltvYmplY3QgRE9NRXhjZXB0aW9uXSI6Y2FzZSJbb2JqZWN0IFdlYkFzc2VtYmx5LkV4Y2VwdGlvbl0iOnJldHVybiEwO2RlZmF1bHQ6cmV0dXJuIE8odCxFcnJvcil9fSh0KSlyZXR1cm57bWVzc2FnZTp0Lm1lc3NhZ2UsbmFtZTp0Lm5hbWUsc3RhY2s6dC5zdGFjaywuLi5rKHQpfTtpZihlPXQsInVuZGVmaW5lZCIhPXR5cGVvZiBFdmVudCYmTyhlLEV2ZW50KSl7Y29uc3R7dHlwZTplLHRhcmdldDpuLGN1cnJlbnRUYXJnZXQ6cixkZXRhaWw6b309dDtyZXR1cm57dHlwZTplLHRhcmdldDpuLGN1cnJlbnRUYXJnZXQ6ciwuLi5vP3tkZXRhaWw6b306e30sLi4uayh0KX19dmFyIGU7cmV0dXJuIHR9ZnVuY3Rpb24gayh0KXtyZXR1cm4ib2JqZWN0Ij09dHlwZW9mKGU9dCkmJm51bGwhPT1lP09iamVjdC5mcm9tRW50cmllcyhPYmplY3QuZW50cmllcyh0KSk6e307dmFyIGV9bGV0IEE7ZnVuY3Rpb24gVCh0KXtpZih2b2lkIDAhPT1BKXJldHVybiBBP0EodCk6dCgpO2NvbnN0IGU9U3ltYm9sLmZvcigiX19TRU5UUllfU0FGRV9SQU5ET01fSURfV1JBUFBFUl9fIiksbj1wO3JldHVybiBlIGluIG4mJiJmdW5jdGlvbiI9PXR5cGVvZiBuW2VdPyhBPW5bZV0sQSh0KSk6KEE9bnVsbCx0KCkpfWZ1bmN0aW9uIFIoKXtyZXR1cm4gVCgoKT0+TWF0aC5yYW5kb20oKSl9ZnVuY3Rpb24gSSgpe3JldHVybiBUKCgpPT5EYXRlLm5vdygpKX1jb25zdCBEPVN5bWJvbC5mb3IoInNlbnRyeS5za2lwTm9ybWFsaXphdGlvbiIpLFU9U3ltYm9sLmZvcigic2VudHJ5Lm92ZXJyaWRlTm9ybWFsaXphdGlvbkRlcHRoIik7ZnVuY3Rpb24gQih0LGU9MTAwLG49MS8wKXt0cnl7cmV0dXJuIEwoIiIsdCxlLG4pfWNhdGNoKHQpe3JldHVybntFUlJPUjpgKipub24tc2VyaWFsaXphYmxlKiogKCR7dH0pYH19fWZ1bmN0aW9uIEwodCxlLG49MS8wLHI9MS8wLG89ZnVuY3Rpb24oKXtjb25zdCB0PW5ldyBXZWFrU2V0O2Z1bmN0aW9uIGUoZSl7cmV0dXJuISF0LmhhcyhlKXx8KHQuYWRkKGUpLCExKX1mdW5jdGlvbiBuKGUpe3QuZGVsZXRlKGUpfXJldHVybltlLG5dfSgpKXtjb25zdFtzLGldPW87aWYobnVsbD09ZXx8WyJib29sZWFuIiwic3RyaW5nIl0uaW5jbHVkZXModHlwZW9mIGUpfHwibnVtYmVyIj09dHlwZW9mIGUmJk51bWJlci5pc0Zpbml0ZShlKSlyZXR1cm4gZTtjb25zdCBjPWZ1bmN0aW9uKHQsZSl7dHJ5e2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZ2xvYmFsJiZlPT09Z2xvYmFsKXJldHVybiJbR2xvYmFsXSI7aWYoIm51bWJlciI9PXR5cGVvZiBlJiYhTnVtYmVyLmlzRmluaXRlKGUpKXJldHVybmBbJHtlfV1gO2lmKCJmdW5jdGlvbiI9PXR5cGVvZiBlKXJldHVybmBbRnVuY3Rpb246ICR7ZnVuY3Rpb24odCl7dHJ5e3JldHVybiB0JiYiZnVuY3Rpb24iPT10eXBlb2YgdCYmdC5uYW1lfHx4fWNhdGNoe3JldHVybiB4fX0oZSl9XWA7aWYoInN5bWJvbCI9PXR5cGVvZiBlKXJldHVybmBbJHtTdHJpbmcoZSl9XWA7aWYoImJpZ2ludCI9PXR5cGVvZiBlKXJldHVybmBbQmlnSW50OiAke1N0cmluZyhlKX1dYDtjb25zdCB0PWZ1bmN0aW9uKHQpe2NvbnN0IGU9T2JqZWN0LmdldFByb3RvdHlwZU9mKHQpO3JldHVybiBlPy5jb25zdHJ1Y3Rvcj9lLmNvbnN0cnVjdG9yLm5hbWU6Im51bGwgcHJvdG90eXBlIn0oZSk7cmV0dXJuYFtvYmplY3QgJHt0fV1gfWNhdGNoKHQpe3JldHVybmAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHt0fSlgfX0oMCxlKTtpZighYy5zdGFydHNXaXRoKCJbb2JqZWN0ICIpKXJldHVybiBjO2lmKGZ1bmN0aW9uKHQpe3JldHVybiBCb29sZWFuKHRbRF0pfShlKSlyZXR1cm4gZTtjb25zdCB1PWZ1bmN0aW9uKHQpe2NvbnN0IGU9dFtVXTtyZXR1cm4ibnVtYmVyIj09dHlwZW9mIGU/ZTp2b2lkIDB9KGUpLGE9dm9pZCAwIT09dT91Om47aWYoMD09PWEpcmV0dXJuIGMucmVwbGFjZSgib2JqZWN0ICIsIiIpO2lmKHMoZSkpcmV0dXJuIltDaXJjdWxhciB+XSI7Y29uc3QgZj1lO2lmKGYmJiJmdW5jdGlvbiI9PXR5cGVvZiBmLnRvSlNPTil0cnl7cmV0dXJuIEwoIiIsZi50b0pTT04oKSxhLTEscixvKX1jYXRjaHt9Y29uc3QgaD1BcnJheS5pc0FycmF5KGUpP1tdOnt9O2xldCBwPTA7Y29uc3QgbD1QKGUpO2Zvcihjb25zdCB0IGluIGwpe2lmKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobCx0KSljb250aW51ZTtpZihwPj1yKXtoW3RdPSJbTWF4UHJvcGVydGllcyB+XSI7YnJlYWt9Y29uc3QgZT1sW3RdO2hbdF09TCh0LGUsYS0xLHIsbykscCsrfXJldHVybiBpKGUpLGh9ZnVuY3Rpb24gTSh0LGUpe2NvbnN0IG49ZS5yZXBsYWNlKC9cXC9nLCIvIikucmVwbGFjZSgvW3xcXHt9KClbXF1eJCsqPy5dL2csIlxcJCYiKTtsZXQgcj10O3RyeXtyPWRlY29kZVVSSSh0KX1jYXRjaHt9cmV0dXJuIHIucmVwbGFjZSgvXFwvZywiLyIpLnJlcGxhY2UoL3dlYnBhY2s6XC8/L2csIiIpLnJlcGxhY2UobmV3IFJlZ0V4cChgKGZpbGU6Ly8pPy8qJHtufS8qYCwiaWciKSwiYXBwOi8vLyIpfWZ1bmN0aW9uIHoodCxlPTApe3JldHVybiJzdHJpbmciIT10eXBlb2YgdHx8MD09PWV8fHQubGVuZ3RoPD1lP3Q6YCR7dC5zbGljZSgwLGUpfS4uLmB9bGV0IEo7ZnVuY3Rpb24gVyh0PWZ1bmN0aW9uKCl7Y29uc3QgdD1wO3JldHVybiB0LmNyeXB0b3x8dC5tc0NyeXB0b30oKSl7dHJ5e2lmKHQ/LnJhbmRvbVVVSUQpcmV0dXJuIFQoKCk9PnQucmFuZG9tVVVJRCgpKS5yZXBsYWNlKC8tL2csIiIpfWNhdGNoe31yZXR1cm4gSnx8KEo9IjEwMDAwMDAwMTAwMDQwMDA4MDAwMTAwMDAwMDAwMDAwIiksSi5yZXBsYWNlKC9bMDE4XS9nLHQ9Pih0XigxNipSKCkmMTUpPj50LzQpLnRvU3RyaW5nKDE2KSl9ZnVuY3Rpb24gRigpe3JldHVybiBJKCkvMWUzfWxldCBHO2Z1bmN0aW9uIFkoKXtyZXR1cm4oRz8/KEc9ZnVuY3Rpb24oKXtjb25zdHtwZXJmb3JtYW5jZTp0fT1wO2lmKCF0Py5ub3d8fCF0LnRpbWVPcmlnaW4pcmV0dXJuIEY7Y29uc3QgZT10LnRpbWVPcmlnaW47cmV0dXJuKCk9PihlK1QoKCk9PnQubm93KCkpKS8xZTN9KCkpKSgpfWZ1bmN0aW9uIEgodCl7Y29uc3QgZT1ZKCksbj17c2lkOlcoKSxpbml0OiEwLHRpbWVzdGFtcDplLHN0YXJ0ZWQ6ZSxkdXJhdGlvbjowLHN0YXR1czoib2siLGVycm9yczowLGlnbm9yZUR1cmF0aW9uOiExLHRvSlNPTjooKT0+ZnVuY3Rpb24odCl7cmV0dXJue3NpZDpgJHt0LnNpZH1gLGluaXQ6dC5pbml0LHN0YXJ0ZWQ6bmV3IERhdGUoMWUzKnQuc3RhcnRlZCkudG9JU09TdHJpbmcoKSx0aW1lc3RhbXA6bmV3IERhdGUoMWUzKnQudGltZXN0YW1wKS50b0lTT1N0cmluZygpLHN0YXR1czp0LnN0YXR1cyxlcnJvcnM6dC5lcnJvcnMsZGlkOiJudW1iZXIiPT10eXBlb2YgdC5kaWR8fCJzdHJpbmciPT10eXBlb2YgdC5kaWQ/YCR7dC5kaWR9YDp2b2lkIDAsZHVyYXRpb246dC5kdXJhdGlvbixhYm5vcm1hbF9tZWNoYW5pc206dC5hYm5vcm1hbF9tZWNoYW5pc20sYXR0cnM6e3JlbGVhc2U6dC5yZWxlYXNlLGVudmlyb25tZW50OnQuZW52aXJvbm1lbnQsaXBfYWRkcmVzczp0LmlwQWRkcmVzcyx1c2VyX2FnZW50OnQudXNlckFnZW50fX19KG4pfTtyZXR1cm4gdCYmWihuLHQpLG59ZnVuY3Rpb24gWih0LGU9e30pe2lmKGUudXNlciYmKCF0LmlwQWRkcmVzcyYmZS51c2VyLmlwX2FkZHJlc3MmJih0LmlwQWRkcmVzcz1lLnVzZXIuaXBfYWRkcmVzcyksdC5kaWR8fGUuZGlkfHwodC5kaWQ9ZS51c2VyLmlkfHxlLnVzZXIuZW1haWx8fGUudXNlci51c2VybmFtZSkpLHQudGltZXN0YW1wPWUudGltZXN0YW1wfHxZKCksZS5hYm5vcm1hbF9tZWNoYW5pc20mJih0LmFibm9ybWFsX21lY2hhbmlzbT1lLmFibm9ybWFsX21lY2hhbmlzbSksZS5pZ25vcmVEdXJhdGlvbiYmKHQuaWdub3JlRHVyYXRpb249ZS5pZ25vcmVEdXJhdGlvbiksZS5zaWQmJih0LnNpZD0zMj09PWUuc2lkLmxlbmd0aD9lLnNpZDpXKCkpLHZvaWQgMCE9PWUuaW5pdCYmKHQuaW5pdD1lLmluaXQpLCF0LmRpZCYmZS5kaWQmJih0LmRpZD1gJHtlLmRpZH1gKSwibnVtYmVyIj09dHlwZW9mIGUuc3RhcnRlZCYmKHQuc3RhcnRlZD1lLnN0YXJ0ZWQpLHQuaWdub3JlRHVyYXRpb24pdC5kdXJhdGlvbj12b2lkIDA7ZWxzZSBpZigibnVtYmVyIj09dHlwZW9mIGUuZHVyYXRpb24pdC5kdXJhdGlvbj1lLmR1cmF0aW9uO2Vsc2V7Y29uc3QgZT10LnRpbWVzdGFtcC10LnN0YXJ0ZWQ7dC5kdXJhdGlvbj1lPj0wP2U6MH1lLnJlbGVhc2UmJih0LnJlbGVhc2U9ZS5yZWxlYXNlKSxlLmVudmlyb25tZW50JiYodC5lbnZpcm9ubWVudD1lLmVudmlyb25tZW50KSwhdC5pcEFkZHJlc3MmJmUuaXBBZGRyZXNzJiYodC5pcEFkZHJlc3M9ZS5pcEFkZHJlc3MpLCF0LnVzZXJBZ2VudCYmZS51c2VyQWdlbnQmJih0LnVzZXJBZ2VudD1lLnVzZXJBZ2VudCksIm51bWJlciI9PXR5cGVvZiBlLmVycm9ycyYmKHQuZXJyb3JzPWUuZXJyb3JzKSxlLnN0YXR1cyYmKHQuc3RhdHVzPWUuc3RhdHVzKX1mdW5jdGlvbiBxKHQsZSxuPTIpe2lmKCFlfHwib2JqZWN0IiE9dHlwZW9mIGV8fG48PTApcmV0dXJuIGU7aWYodCYmMD09PU9iamVjdC5rZXlzKGUpLmxlbmd0aClyZXR1cm4gdDtjb25zdCByPXsuLi50fTtmb3IoY29uc3QgdCBpbiBlKU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChlLHQpJiYoclt0XT1xKHJbdF0sZVt0XSxuLTEpKTtyZXR1cm4gcn1jbGFzcyBLe2NvbnN0cnVjdG9yKCl7dGhpcy50PSExLHRoaXMubz1bXSx0aGlzLmk9W10sdGhpcy51PVtdLHRoaXMuaD1bXSx0aGlzLnA9e30sdGhpcy5sPXt9LHRoaXMubT17fSx0aGlzLnY9e30sdGhpcy5TPXt9LHRoaXMuXz17fSxmdW5jdGlvbih0LGUsbil7dHJ5e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LGUse3ZhbHVlOm4sd3JpdGFibGU6ITAsY29uZmlndXJhYmxlOiEwfSl9Y2F0Y2h7aCYmXy5sb2coYEZhaWxlZCB0byBhZGQgbm9uLWVudW1lcmFibGUgcHJvcGVydHkgIiR7U3RyaW5nKGUpfSIgdG8gb2JqZWN0YCx0KX19KHRoaXMsInJlZnMiLHt9KSx0aGlzLk49e3RyYWNlSWQ6VygpLHNhbXBsZVJhbmQ6UigpfX1jbG9uZSgpe2NvbnN0IHQ9bmV3IEs7cmV0dXJuIHQudT1bLi4udGhpcy51XSx0Lmw9ey4uLnRoaXMubH0sdC5tPXsuLi50aGlzLm19LHQudj17Li4udGhpcy52fSx0LlM9ey4uLnRoaXMuU30sdGhpcy5TLmZsYWdzJiYodC5TLmZsYWdzPXt2YWx1ZXM6Wy4uLnRoaXMuUy5mbGFncy52YWx1ZXNdfSksdC5wPXRoaXMucCx0LkM9dGhpcy5DLHQuaj10aGlzLmosdC5PPXRoaXMuTyx0LlA9dGhpcy5QLHQuaT1bLi4udGhpcy5pXSx0Lmg9Wy4uLnRoaXMuaF0sdC5fPXsuLi50aGlzLl99LHQuTj17Li4udGhpcy5OfSx0Lms9dGhpcy5rLHQuQT10aGlzLkEsdC5UPXRoaXMuVCx0LnJlZnM9ey4uLnRoaXMucmVmc30sdH1zZXRDbGllbnQodCl7dGhpcy5rPXR9c2V0TGFzdEV2ZW50SWQodCl7dGhpcy5BPXR9Z2V0Q2xpZW50KCl7cmV0dXJuIHRoaXMua31sYXN0RXZlbnRJZCgpe3JldHVybiB0aGlzLkF9YWRkU2NvcGVMaXN0ZW5lcih0KXt0aGlzLm8ucHVzaCh0KX1hZGRFdmVudFByb2Nlc3Nvcih0KXtyZXR1cm4gdGhpcy5pLnB1c2godCksdGhpc31zZXRVc2VyKHQpe3JldHVybiB0aGlzLnA9dHx8e2VtYWlsOnZvaWQgMCxpZDp2b2lkIDAsaXBfYWRkcmVzczp2b2lkIDAsdXNlcm5hbWU6dm9pZCAwfSx0aGlzLmomJloodGhpcy5qLHt1c2VyOnR9KSx0aGlzLlIoKSx0aGlzfWdldFVzZXIoKXtyZXR1cm4gdGhpcy5wfXNldENvbnZlcnNhdGlvbklkKHQpe3JldHVybiB0aGlzLlQ9dHx8dm9pZCAwLHRoaXMuUigpLHRoaXN9c2V0VGFncyh0KXtyZXR1cm4gdGhpcy5sPXsuLi50aGlzLmwsLi4udH0sdGhpcy5SKCksdGhpc31zZXRUYWcodCxlKXtyZXR1cm4gdGhpcy5zZXRUYWdzKHtbdF06ZX0pfXNldEF0dHJpYnV0ZXModCl7cmV0dXJuIHRoaXMubT17Li4udGhpcy5tLC4uLnR9LHRoaXMuUigpLHRoaXN9c2V0QXR0cmlidXRlKHQsZSl7cmV0dXJuIHRoaXMuc2V0QXR0cmlidXRlcyh7W3RdOmV9KX1yZW1vdmVBdHRyaWJ1dGUodCl7cmV0dXJuIHQgaW4gdGhpcy5tJiYoZGVsZXRlIHRoaXMubVt0XSx0aGlzLlIoKSksdGhpc31zZXRFeHRyYXModCl7cmV0dXJuIHRoaXMudj17Li4udGhpcy52LC4uLnR9LHRoaXMuUigpLHRoaXN9c2V0RXh0cmEodCxlKXtyZXR1cm4gdGhpcy52PXsuLi50aGlzLnYsW3RdOmV9LHRoaXMuUigpLHRoaXN9c2V0RmluZ2VycHJpbnQodCl7cmV0dXJuIHRoaXMuUD10LHRoaXMuUigpLHRoaXN9c2V0TGV2ZWwodCl7cmV0dXJuIHRoaXMuQz10LHRoaXMuUigpLHRoaXN9c2V0VHJhbnNhY3Rpb25OYW1lKHQpe3JldHVybiB0aGlzLk89dCx0aGlzLlIoKSx0aGlzfXNldENvbnRleHQodCxlKXtyZXR1cm4gbnVsbD09PWU/ZGVsZXRlIHRoaXMuU1t0XTp0aGlzLlNbdF09ZSx0aGlzLlIoKSx0aGlzfXNldFNlc3Npb24odCl7cmV0dXJuIHQ/dGhpcy5qPXQ6ZGVsZXRlIHRoaXMuaix0aGlzLlIoKSx0aGlzfWdldFNlc3Npb24oKXtyZXR1cm4gdGhpcy5qfXVwZGF0ZSh0KXtpZighdClyZXR1cm4gdGhpcztjb25zdCBlPSJmdW5jdGlvbiI9PXR5cGVvZiB0P3QodGhpcyk6dCxuPWUgaW5zdGFuY2VvZiBLP2UuZ2V0U2NvcGVEYXRhKCk6QyhlKT90OnZvaWQgMCx7dGFnczpyLGF0dHJpYnV0ZXM6byxleHRyYTpzLHVzZXI6aSxjb250ZXh0czpjLGxldmVsOnUsZmluZ2VycHJpbnQ6YT1bXSxwcm9wYWdhdGlvbkNvbnRleHQ6Zixjb252ZXJzYXRpb25JZDpofT1ufHx7fTtyZXR1cm4gdGhpcy5sPXsuLi50aGlzLmwsLi4ucn0sdGhpcy5tPXsuLi50aGlzLm0sLi4ub30sdGhpcy52PXsuLi50aGlzLnYsLi4uc30sdGhpcy5TPXsuLi50aGlzLlMsLi4uY30saSYmT2JqZWN0LmtleXMoaSkubGVuZ3RoJiYodGhpcy5wPWkpLHUmJih0aGlzLkM9dSksYS5sZW5ndGgmJih0aGlzLlA9YSksZiYmKHRoaXMuTj1mKSxoJiYodGhpcy5UPWgpLHRoaXN9YWRkQnJlYWRjcnVtYih0LGUpe2NvbnN0IG49Im51bWJlciI9PXR5cGVvZiBlP2U6MTAwO2lmKG48PTApcmV0dXJuIHRoaXM7Y29uc3Qgcj17dGltZXN0YW1wOkYoKSwuLi50LG1lc3NhZ2U6dC5tZXNzYWdlP3oodC5tZXNzYWdlLDIwNDgpOnQubWVzc2FnZX07cmV0dXJuIHRoaXMudS5wdXNoKHIpLHRoaXMudS5sZW5ndGg+biYmKHRoaXMudT10aGlzLnUuc2xpY2UoLW4pLHRoaXMuaz8ucmVjb3JkRHJvcHBlZEV2ZW50KCJidWZmZXJfb3ZlcmZsb3ciLCJsb2dfaXRlbSIpKSx0aGlzLlIoKSx0aGlzfWdldExhc3RCcmVhZGNydW1iKCl7cmV0dXJuIHRoaXMudVt0aGlzLnUubGVuZ3RoLTFdfWNsZWFyQnJlYWRjcnVtYnMoKXtyZXR1cm4gdGhpcy51PVtdLHRoaXMuUigpLHRoaXN9YWRkQXR0YWNobWVudCh0KXtyZXR1cm4gdGhpcy5oLnB1c2godCksdGhpc31jbGVhckF0dGFjaG1lbnRzKCl7cmV0dXJuIHRoaXMuaD1bXSx0aGlzfWdldFNjb3BlRGF0YSgpe3JldHVybnticmVhZGNydW1iczp0aGlzLnUsYXR0YWNobWVudHM6dGhpcy5oLGNvbnRleHRzOnRoaXMuUyx0YWdzOnRoaXMubCxhdHRyaWJ1dGVzOnRoaXMubSxleHRyYTp0aGlzLnYsdXNlcjp0aGlzLnAsbGV2ZWw6dGhpcy5DLGZpbmdlcnByaW50OnRoaXMuUHx8W10sZXZlbnRQcm9jZXNzb3JzOnRoaXMuaSxwcm9wYWdhdGlvbkNvbnRleHQ6dGhpcy5OLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTp0aGlzLl8sdHJhbnNhY3Rpb25OYW1lOnRoaXMuTyxjb252ZXJzYXRpb25JZDp0aGlzLlR9fXNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh0KXtyZXR1cm4gdGhpcy5fPXEodGhpcy5fLHQsMiksdGhpc31zZXRQcm9wYWdhdGlvbkNvbnRleHQodCl7cmV0dXJuIHRoaXMuTj10LHRoaXN9Z2V0UHJvcGFnYXRpb25Db250ZXh0KCl7cmV0dXJuIHRoaXMuTn1jYXB0dXJlRXhjZXB0aW9uKHQsZSl7Y29uc3Qgbj1lPy5ldmVudF9pZHx8VygpO2lmKCF0aGlzLmspcmV0dXJuIGgmJl8ud2FybigiTm8gY2xpZW50IGNvbmZpZ3VyZWQgb24gc2NvcGUgLSB3aWxsIG5vdCBjYXB0dXJlIGV4Y2VwdGlvbiEiKSxuO2NvbnN0IHI9bmV3IEVycm9yKCJTZW50cnkgc3ludGhldGljRXhjZXB0aW9uIik7cmV0dXJuIHRoaXMuay5jYXB0dXJlRXhjZXB0aW9uKHQse29yaWdpbmFsRXhjZXB0aW9uOnQsc3ludGhldGljRXhjZXB0aW9uOnIsLi4uZSxldmVudF9pZDpufSx0aGlzKSxufWNhcHR1cmVNZXNzYWdlKHQsZSxuKXtjb25zdCByPW4/LmV2ZW50X2lkfHxXKCk7aWYoIXRoaXMuaylyZXR1cm4gaCYmXy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgbWVzc2FnZSEiKSxyO2NvbnN0IG89bj8uc3ludGhldGljRXhjZXB0aW9uPz9uZXcgRXJyb3IodCk7cmV0dXJuIHRoaXMuay5jYXB0dXJlTWVzc2FnZSh0LGUse29yaWdpbmFsRXhjZXB0aW9uOnQsc3ludGhldGljRXhjZXB0aW9uOm8sLi4ubixldmVudF9pZDpyfSx0aGlzKSxyfWNhcHR1cmVFdmVudCh0LGUpe2NvbnN0IG49dC5ldmVudF9pZHx8ZT8uZXZlbnRfaWR8fFcoKTtyZXR1cm4gdGhpcy5rPyh0aGlzLmsuY2FwdHVyZUV2ZW50KHQsey4uLmUsZXZlbnRfaWQ6bn0sdGhpcyksbik6KGgmJl8ud2FybigiTm8gY2xpZW50IGNvbmZpZ3VyZWQgb24gc2NvcGUgLSB3aWxsIG5vdCBjYXB0dXJlIGV2ZW50ISIpLG4pfVIoKXt0aGlzLnR8fCh0aGlzLnQ9ITAsdGhpcy5vLmZvckVhY2godD0+e3QodGhpcyl9KSx0aGlzLnQ9ITEpfX1jb25zdCBWPSJfX1NFTlRSWV9TVVBQUkVTU19UUkFDSU5HX18iLFE9dD0+dCBpbnN0YW5jZW9mIFByb21pc2UmJiF0W1hdLFg9U3ltYm9sKCJjaGFpbmVkIFByb21pc2VMaWtlIiksdHQ9KHQsZSk9PntpZighZSlyZXR1cm4gdDtsZXQgbj0hMTtmb3IoY29uc3QgciBpbiB0KXtpZihyIGluIGUpY29udGludWU7bj0hMDtjb25zdCBvPXRbcl07ImZ1bmN0aW9uIj09dHlwZW9mIG8/T2JqZWN0LmRlZmluZVByb3BlcnR5KGUscix7dmFsdWU6KC4uLmUpPT5vLmFwcGx5KHQsZSksZW51bWVyYWJsZTohMCxjb25maWd1cmFibGU6ITAsd3JpdGFibGU6ITB9KTplW3JdPW99cmV0dXJuIG4mJk9iamVjdC5hc3NpZ24oZSx7W1hdOiEwfSksZX07Y2xhc3MgZXR7Y29uc3RydWN0b3IodCxlKXtsZXQgbixyO249dHx8bmV3IEsscj1lfHxuZXcgSyx0aGlzLkk9W3tzY29wZTpufV0sdGhpcy5EPXJ9d2l0aFNjb3BlKHQpe2NvbnN0IGU9dGhpcy5VKCk7bGV0IG47dHJ5e249dChlKX1jYXRjaCh0KXt0aHJvdyB0aGlzLkIoKSx0fXJldHVybiBqKG4pPygodCxlLG4pPT57Y29uc3Qgcj10LnRoZW4odD0+KGUodCksdCksdD0+e3Rocm93IG4odCksdH0pO3JldHVybiBRKHIpJiZRKHQpP3I6dHQodCxyKX0pKG4sKCk9PnRoaXMuQigpLCgpPT50aGlzLkIoKSk6KHRoaXMuQigpLG4pfWdldENsaWVudCgpe3JldHVybiB0aGlzLmdldFN0YWNrVG9wKCkuY2xpZW50fWdldFNjb3BlKCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5zY29wZX1nZXRJc29sYXRpb25TY29wZSgpe3JldHVybiB0aGlzLkR9Z2V0U3RhY2tUb3AoKXtyZXR1cm4gdGhpcy5JW3RoaXMuSS5sZW5ndGgtMV19VSgpe2NvbnN0IHQ9dGhpcy5nZXRTY29wZSgpLmNsb25lKCk7cmV0dXJuIHRoaXMuSS5wdXNoKHtjbGllbnQ6dGhpcy5nZXRDbGllbnQoKSxzY29wZTp0fSksdH1CKCl7cmV0dXJuISh0aGlzLkkubGVuZ3RoPD0xKSYmISF0aGlzLkkucG9wKCl9fWZ1bmN0aW9uIG50KCl7Y29uc3QgdD1nKGQoKSk7cmV0dXJuIHQuc3RhY2s9dC5zdGFja3x8bmV3IGV0KG0oImRlZmF1bHRDdXJyZW50U2NvcGUiLCgpPT5uZXcgSyksbSgiZGVmYXVsdElzb2xhdGlvblNjb3BlIiwoKT0+bmV3IEspKX1mdW5jdGlvbiBydCh0KXtyZXR1cm4gbnQoKS53aXRoU2NvcGUodCl9ZnVuY3Rpb24gb3QodCxlKXtjb25zdCBuPW50KCk7cmV0dXJuIG4ud2l0aFNjb3BlKCgpPT4obi5nZXRTdGFja1RvcCgpLnNjb3BlPXQsZSh0KSkpfWZ1bmN0aW9uIHN0KHQpe3JldHVybiBudCgpLndpdGhTY29wZSgoKT0+dChudCgpLmdldElzb2xhdGlvblNjb3BlKCkpKX1mdW5jdGlvbiBpdCh0KXtyZXR1cm4gcnQoZT0+e2Uuc2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHtbVl06ITB9KTtjb25zdCBuPXQoKTtyZXR1cm4gZS5zZXRTREtQcm9jZXNzaW5nTWV0YWRhdGEoe1tWXTp2b2lkIDB9KSxufSl9ZnVuY3Rpb24gY3QodCl7Y29uc3QgZT1nKHQpO3JldHVybiBlLmFjcz9lLmFjczp7c3VwcHJlc3NUcmFjaW5nOml0LHdpdGhJc29sYXRpb25TY29wZTpzdCx3aXRoU2NvcGU6cnQsd2l0aFNldFNjb3BlOm90LHdpdGhTZXRJc29sYXRpb25TY29wZToodCxlKT0+c3QoZSksZ2V0Q3VycmVudFNjb3BlOigpPT5udCgpLmdldFNjb3BlKCksZ2V0SXNvbGF0aW9uU2NvcGU6KCk9Pm50KCkuZ2V0SXNvbGF0aW9uU2NvcGUoKX19ZnVuY3Rpb24gdXQodCxlPSExKXtjb25zdHtob3N0Om4scGF0aDpyLHBhc3M6byxwb3J0OnMscHJvamVjdElkOmkscHJvdG9jb2w6YyxwdWJsaWNLZXk6dX09dDtyZXR1cm5gJHtjfTovLyR7dX0ke2UmJm8/YDoke299YDoiIn1AJHtufSR7cz9gOiR7c31gOiIifS8ke3I/YCR7cn0vYDpyfSR7aX1gfWZ1bmN0aW9uIGF0KHQsZSl7Y29uc3R7ZmluZ2VycHJpbnQ6bixicmVhZGNydW1iczpyLHNka1Byb2Nlc3NpbmdNZXRhZGF0YTpvfT1lOyFmdW5jdGlvbih0LGUpe2NvbnN0e2V4dHJhOm4sdGFnczpyLHVzZXI6byxjb250ZXh0czpzLGxldmVsOmksdHJhbnNhY3Rpb25OYW1lOmN9PWU7T2JqZWN0LmtleXMobikubGVuZ3RoJiYodC5leHRyYT17Li4ubiwuLi50LmV4dHJhfSk7T2JqZWN0LmtleXMocikubGVuZ3RoJiYodC50YWdzPXsuLi5yLC4uLnQudGFnc30pO09iamVjdC5rZXlzKG8pLmxlbmd0aCYmKHQudXNlcj17Li4ubywuLi50LnVzZXJ9KTtPYmplY3Qua2V5cyhzKS5sZW5ndGgmJih0LmNvbnRleHRzPXsuLi5zLC4uLnQuY29udGV4dHN9KTtpJiYodC5sZXZlbD1pKTtjJiYidHJhbnNhY3Rpb24iIT09dC50eXBlJiYodC50cmFuc2FjdGlvbj1jKX0odCxlKSxmdW5jdGlvbih0LGUpe3QuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludD9BcnJheS5pc0FycmF5KHQuZmluZ2VycHJpbnQpP3QuZmluZ2VycHJpbnQ6W3QuZmluZ2VycHJpbnRdOltdLGUmJih0LmZpbmdlcnByaW50PXQuZmluZ2VycHJpbnQuY29uY2F0KGUpKTt0LmZpbmdlcnByaW50Lmxlbmd0aHx8ZGVsZXRlIHQuZmluZ2VycHJpbnR9KHQsbiksZnVuY3Rpb24odCxlKXtjb25zdCBuPVsuLi50LmJyZWFkY3J1bWJzfHxbXSwuLi5lXTt0LmJyZWFkY3J1bWJzPW4ubGVuZ3RoP246dm9pZCAwfSh0LHIpLGZ1bmN0aW9uKHQsZSl7dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE9ey4uLnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhLC4uLmV9fSh0LG8pfWZ1bmN0aW9uIGZ0KHQsZT1bXSl7cmV0dXJuW3QsZV19ZnVuY3Rpb24gaHQodCxlKXtjb25zdCBuPXRbMV07Zm9yKGNvbnN0IHQgb2Ygbil7aWYoZSh0LHRbMF0udHlwZSkpcmV0dXJuITB9cmV0dXJuITF9ZnVuY3Rpb24gcHQodCl7Y29uc3QgZT1nKHApO3JldHVybiBlLmVuY29kZVBvbHlmaWxsP2UuZW5jb2RlUG9seWZpbGwodCk6KG5ldyBUZXh0RW5jb2RlcikuZW5jb2RlKHQpfWZ1bmN0aW9uIGx0KHQpe2NvbnN0W2Usbl09dDtsZXQgcj1KU09OLnN0cmluZ2lmeShlKTtmdW5jdGlvbiBvKHQpeyJzdHJpbmciPT10eXBlb2Ygcj9yPSJzdHJpbmciPT10eXBlb2YgdD9yK3Q6W3B0KHIpLHRdOnIucHVzaCgic3RyaW5nIj09dHlwZW9mIHQ/cHQodCk6dCl9Zm9yKGNvbnN0IHQgb2Ygbil7Y29uc3RbZSxuXT10O2lmKG8oYFxuJHtKU09OLnN0cmluZ2lmeShlKX1cbmApLCJzdHJpbmciPT10eXBlb2Ygbnx8biBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkpbyhuKTtlbHNle2xldCB0O3RyeXt0PUpTT04uc3RyaW5naWZ5KG4pfWNhdGNoe3Q9SlNPTi5zdHJpbmdpZnkoQihuKSl9byh0KX19cmV0dXJuInN0cmluZyI9PXR5cGVvZiByP3I6ZnVuY3Rpb24odCl7Y29uc3QgZT10LnJlZHVjZSgodCxlKT0+dCtlLmxlbmd0aCwwKSxuPW5ldyBVaW50OEFycmF5KGUpO2xldCByPTA7Zm9yKGNvbnN0IGUgb2YgdCluLnNldChlLHIpLHIrPWUubGVuZ3RoO3JldHVybiBufShyKX1jb25zdCBkdD17c2Vzc2lvbnM6InNlc3Npb24iLGV2ZW50OiJlcnJvciIsY2xpZW50X3JlcG9ydDoiaW50ZXJuYWwiLHVzZXJfcmVwb3J0OiJkZWZhdWx0Iixwcm9maWxlX2NodW5rOiJwcm9maWxlIixyZXBsYXlfZXZlbnQ6InJlcGxheSIscmVwbGF5X3JlY29yZGluZzoicmVwbGF5IixjaGVja19pbjoibW9uaXRvciIscmF3X3NlY3VyaXR5OiJzZWN1cml0eSIsbG9nOiJsb2dfaXRlbSIsdHJhY2VfbWV0cmljOiJtZXRyaWMifTtmdW5jdGlvbiBndCh0KXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIHQgaW4gZHR9KHQpP2R0W3RdOnR9ZnVuY3Rpb24gbXQodCl7aWYoIXQ/LnNkaylyZXR1cm47Y29uc3R7bmFtZTplLHZlcnNpb246bn09dC5zZGs7cmV0dXJue25hbWU6ZSx2ZXJzaW9uOm59fWZ1bmN0aW9uIHl0KHQpe2NvbnN0IGU9Y3QoZCgpKTtyZXR1cm4gZS5zdXBwcmVzc1RyYWNpbmc/ZS5zdXBwcmVzc1RyYWNpbmcodCk6ZnVuY3Rpb24oLi4udCl7Y29uc3QgZT1jdChkKCkpO2lmKDI9PT10Lmxlbmd0aCl7Y29uc3RbbixyXT10O3JldHVybiBuP2Uud2l0aFNldFNjb3BlKG4scik6ZS53aXRoU2NvcGUocil9cmV0dXJuIGUud2l0aFNjb3BlKHRbMF0pfShlPT4oZS5zZXRTREtQcm9jZXNzaW5nTWV0YWRhdGEoe1tWXTohMH0pLHQoKSkpfWZ1bmN0aW9uIGJ0KHQsZSxuLHIpe2NvbnN0IG89bXQobikscz10LnR5cGUmJiJyZXBsYXlfZXZlbnQiIT09dC50eXBlP3QudHlwZToiZXZlbnQiOyFmdW5jdGlvbih0LGUpe2lmKCFlKXJldHVybiB0O2NvbnN0IG49dC5zZGt8fHt9O3Quc2RrPXsuLi5uLG5hbWU6bi5uYW1lfHxlLm5hbWUsdmVyc2lvbjpuLnZlcnNpb258fGUudmVyc2lvbixpbnRlZ3JhdGlvbnM6Wy4uLnQuc2RrPy5pbnRlZ3JhdGlvbnN8fFtdLC4uLmUuaW50ZWdyYXRpb25zfHxbXV0scGFja2FnZXM6Wy4uLnQuc2RrPy5wYWNrYWdlc3x8W10sLi4uZS5wYWNrYWdlc3x8W11dLHNldHRpbmdzOnQuc2RrPy5zZXR0aW5nc3x8ZS5zZXR0aW5ncz97Li4udC5zZGs/LnNldHRpbmdzLC4uLmUuc2V0dGluZ3N9OnZvaWQgMH19KHQsbj8uc2RrKTtjb25zdCBpPWZ1bmN0aW9uKHQsZSxuLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDpuZXcgRGF0ZShJKCkpLnRvSVNPU3RyaW5nKCksLi4uZSYme3NkazplfSwuLi4hIW4mJnImJntkc246dXQocil9LC4uLm8mJnt0cmFjZTpvfX19KHQsbyxyLGUpO2RlbGV0ZSB0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YTtyZXR1cm4gZnQoaSxbW3t0eXBlOnN9LHRdXSl9Y2xhc3MgdnR7Y29uc3RydWN0b3IodCl7dGhpcy5MPTAsdGhpcy5NPVtdLHRoaXMuSih0KX10aGVuKHQsZSl7cmV0dXJuIG5ldyB2dCgobixyKT0+e3RoaXMuTS5wdXNoKFshMSxlPT57aWYodCl0cnl7bih0KGUpKX1jYXRjaCh0KXtyKHQpfWVsc2UgbihlKX0sdD0+e2lmKGUpdHJ5e24oZSh0KSl9Y2F0Y2godCl7cih0KX1lbHNlIHIodCl9XSksdGhpcy5XKCl9KX1jYXRjaCh0KXtyZXR1cm4gdGhpcy50aGVuKHQ9PnQsdCl9ZmluYWxseSh0KXtyZXR1cm4gbmV3IHZ0KChlLG4pPT57bGV0IHIsbztyZXR1cm4gdGhpcy50aGVuKGU9PntvPSExLHI9ZSx0JiZ0KCl9LGU9PntvPSEwLHI9ZSx0JiZ0KCl9KS50aGVuKCgpPT57bz9uKHIpOmUocil9KX0pfVcoKXtpZigwPT09dGhpcy5MKXJldHVybjtjb25zdCB0PXRoaXMuTS5zbGljZSgpO3RoaXMuTT1bXSx0LmZvckVhY2godD0+e3RbMF18fCgxPT09dGhpcy5MJiZ0WzFdKHRoaXMuRiksMj09PXRoaXMuTCYmdFsyXSh0aGlzLkYpLHRbMF09ITApfSl9Sih0KXtjb25zdCBlPSh0LGUpPT57MD09PXRoaXMuTCYmKGooZSk/ZS50aGVuKG4scik6KHRoaXMuTD10LHRoaXMuRj1lLHRoaXMuVygpKSl9LG49dD0+e2UoMSx0KX0scj10PT57ZSgyLHQpfTt0cnl7dChuLHIpfWNhdGNoKHQpe3IodCl9fX1jb25zdCB3dD1TeW1ib2wuZm9yKCJTZW50cnlCdWZmZXJGdWxsRXJyb3IiKTtmdW5jdGlvbiBTdCh0PTEwMCl7Y29uc3QgZT1uZXcgU2V0O2Z1bmN0aW9uIG4odCl7ZS5kZWxldGUodCl9cmV0dXJue2dldCAkKCl7cmV0dXJuIEFycmF5LmZyb20oZSl9LGFkZDpmdW5jdGlvbihyKXtpZighKGUuc2l6ZTx0KSlyZXR1cm4gbz13dCxuZXcgdnQoKHQsZSk9PntlKG8pfSk7dmFyIG87Y29uc3Qgcz1yKCk7cmV0dXJuIGUuYWRkKHMpLHMudGhlbigoKT0+bihzKSwoKT0+bihzKSksc30sZHJhaW46ZnVuY3Rpb24odCl7aWYoIWUuc2l6ZSlyZXR1cm4gbj0hMCxuZXcgdnQodD0+e3Qobil9KTt2YXIgbjtjb25zdCByPVByb21pc2UuYWxsU2V0dGxlZChBcnJheS5mcm9tKGUpKS50aGVuKCgpPT4hMCk7aWYoIXQpcmV0dXJuIHI7Y29uc3Qgbz1bcixuZXcgUHJvbWlzZShlPT57cmV0dXJuIm9iamVjdCI9PXR5cGVvZihuPXNldFRpbWVvdXQoKCk9PmUoITEpLHQpKSYmImZ1bmN0aW9uIj09dHlwZW9mIG4udW5yZWYmJm4udW5yZWYoKSxuO3ZhciBufSldO3JldHVybiBQcm9taXNlLnJhY2Uobyl9fX1mdW5jdGlvbiBfdCh0LHtzdGF0dXNDb2RlOmUsaGVhZGVyczpufSxyPUkoKSl7Y29uc3Qgbz17Li4udH0scz1uPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0saT1uPy5bInJldHJ5LWFmdGVyIl07aWYocylmb3IoY29uc3QgdCBvZiBzLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtlLG4sLCxzXT10LnNwbGl0KCI6Iiw1KSxpPXBhcnNlSW50KGUsMTApLGM9MWUzKihpc05hTihpKT82MDppKTtpZihuKWZvcihjb25zdCB0IG9mIG4uc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZzJiYhcy5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIGk/by5hbGw9citmdW5jdGlvbih0LGU9SSgpKXtjb25zdCBuPXBhcnNlSW50KGAke3R9YCwxMCk7aWYoIWlzTmFOKG4pKXJldHVybiAxZTMqbjtjb25zdCByPURhdGUucGFyc2UoYCR7dH1gKTtyZXR1cm4gaXNOYU4ocik/NmU0OnItZX0oaSxyKTo0Mjk9PT1lJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWZ1bmN0aW9uICR0KHQsZSxuPVN0KHQuYnVmZmVyU2l6ZXx8NjQpKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKGh0KHQsKHQsZSk9Pntjb25zdCBuPWd0KGUpOyhmdW5jdGlvbih0LGUsbj1JKCkpe3JldHVybiBmdW5jdGlvbih0LGUpe3JldHVybiB0W2VdfHx0LmFsbHx8MH0odCxlKT5ufSkocixuKXx8by5wdXNoKHQpfSksMD09PW8ubGVuZ3RoKXJldHVybiBQcm9taXNlLnJlc29sdmUoe30pO2NvbnN0IHM9ZnQodFswXSxvKSxpPXQ9PnshZnVuY3Rpb24odCxlKXtyZXR1cm4gaHQodCwodCxuKT0+ZS5pbmNsdWRlcyhuKSl9KHMsWyJjbGllbnRfcmVwb3J0Il0pP2h0KHMsKHQsZSk9Pnt9KTpoJiZfLndhcm4oYERyb3BwaW5nIGNsaWVudCByZXBvcnQuIFdpbGwgbm90IHNlbmQgb3V0Y29tZXMgKHJlYXNvbjogJHt0fSkuYCl9O3JldHVybiBuLmFkZCgoKT0+ZSh7Ym9keTpsdChzKX0pLnRoZW4odD0+NDEzPT09dC5zdGF0dXNDb2RlPyhoJiZfLmVycm9yKCJTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgNDEzLiBFbnZlbG9wZSB3YXMgZGlzY2FyZGVkIGR1ZSB0byBleGNlZWRpbmcgc2l6ZSBsaW1pdHMuIiksaSgic2VuZF9lcnJvciIpLHQpOihoJiZ2b2lkIDAhPT10LnN0YXR1c0NvZGUmJih0LnN0YXR1c0NvZGU8MjAwfHx0LnN0YXR1c0NvZGU+PTMwMCkmJl8ud2FybihgU2VudHJ5IHJlc3BvbmRlZCB3aXRoIHN0YXR1cyBjb2RlICR7dC5zdGF0dXNDb2RlfSB0byBzZW50IGV2ZW50LmApLHI9X3Qocix0KSx0KSx0PT57dGhyb3cgaSgibmV0d29ya19lcnJvciIpLGgmJl8uZXJyb3IoIkVuY291bnRlcmVkIGVycm9yIHJ1bm5pbmcgdHJhbnNwb3J0IHJlcXVlc3Q6Iix0KSx0fSkpLnRoZW4odD0+dCx0PT57aWYodD09PXd0KXJldHVybiBoJiZfLmVycm9yKCJTa2lwcGVkIHNlbmRpbmcgZXZlbnQgYmVjYXVzZSBidWZmZXIgaXMgZnVsbC4iKSxpKCJxdWV1ZV9vdmVyZmxvdyIpLFByb21pc2UucmVzb2x2ZSh7fSk7dGhyb3cgdH0pfSxmbHVzaDp0PT5uLmRyYWluKHQpfX1jb25zdCBFdD0vXihcUys6XFx8XC8/KShbXHNcU10qPykoKD86XC57MSwyfXxbXi9cXF0rP3wpKFwuW14uL1xcXSp8KSkoPzpbL1xcXSopJC87ZnVuY3Rpb24geHQodCl7Y29uc3QgZT1mdW5jdGlvbih0KXtjb25zdCBlPXQubGVuZ3RoPjEwMjQ/YDx0cnVuY2F0ZWQ+JHt0LnNsaWNlKC0xMDI0KX1gOnQsbj1FdC5leGVjKGUpO3JldHVybiBuP24uc2xpY2UoMSk6W119KHQpLG49ZVswXXx8IiI7bGV0IHI9ZVsxXTtyZXR1cm4gbnx8cj8ociYmKHI9ci5zbGljZSgwLHIubGVuZ3RoLTEpKSxuK3IpOiIuIn1mdW5jdGlvbiBOdCh0LGU9ITEpe3JldHVybiEoZXx8dCYmIXQuc3RhcnRzV2l0aCgiLyIpJiYhdC5tYXRjaCgvXltBLVpdOi8pJiYhdC5zdGFydHNXaXRoKCIuIikmJiF0Lm1hdGNoKC9eW2EtekEtWl0oW2EtekEtWjAtOS5cLStdKSo6XC9cLy8pKSYmdm9pZCAwIT09dCYmIXQuaW5jbHVkZXMoIm5vZGVfbW9kdWxlcy8iKX12YXIgQ3Q7Y29uc3QganQ9U3ltYm9sKCJBZ2VudEJhc2VJbnRlcm5hbFN0YXRlIik7Y2xhc3MgT3QgZXh0ZW5kcyhDdD1zLkFnZW50LEN0KXtjb25zdHJ1Y3Rvcih0KXtzdXBlcih0KSx0aGlzW2p0XT17fX1pc1NlY3VyZUVuZHBvaW50KHQpe2lmKHQpe2lmKCJib29sZWFuIj09dHlwZW9mIHQuc2VjdXJlRW5kcG9pbnQpcmV0dXJuIHQuc2VjdXJlRW5kcG9pbnQ7aWYoInN0cmluZyI9PXR5cGVvZiB0LnByb3RvY29sKXJldHVybiJodHRwczoiPT09dC5wcm90b2NvbH1jb25zdHtzdGFjazplfT1uZXcgRXJyb3I7cmV0dXJuInN0cmluZyI9PXR5cGVvZiBlJiZlLnNwbGl0KCJcbiIpLnNvbWUodD0+LTEhPT10LmluZGV4T2YoIihodHRwcy5qczoiKXx8LTEhPT10LmluZGV4T2YoIm5vZGU6aHR0cHM6IikpfWNyZWF0ZVNvY2tldCh0LGUsbil7Y29uc3Qgcj17Li4uZSxzZWN1cmVFbmRwb2ludDp0aGlzLmlzU2VjdXJlRW5kcG9pbnQoZSl9O1Byb21pc2UucmVzb2x2ZSgpLnRoZW4oKCk9PnRoaXMuY29ubmVjdCh0LHIpKS50aGVuKG89PntpZihvIGluc3RhbmNlb2Ygcy5BZ2VudClyZXR1cm4gby5hZGRSZXF1ZXN0KHQscik7dGhpc1tqdF0uY3VycmVudFNvY2tldD1vLHN1cGVyLmNyZWF0ZVNvY2tldCh0LGUsbil9LG4pfWNyZWF0ZUNvbm5lY3Rpb24oKXtjb25zdCB0PXRoaXNbanRdLmN1cnJlbnRTb2NrZXQ7aWYodGhpc1tqdF0uY3VycmVudFNvY2tldD12b2lkIDAsIXQpdGhyb3cgbmV3IEVycm9yKCJObyBzb2NrZXQgd2FzIHJldHVybmVkIGluIHRoZSBgY29ubmVjdCgpYCBmdW5jdGlvbiIpO3JldHVybiB0fWdldCBkZWZhdWx0UG9ydCgpe3JldHVybiB0aGlzW2p0XS5kZWZhdWx0UG9ydD8/KCJodHRwczoiPT09dGhpcy5wcm90b2NvbD80NDM6ODApfXNldCBkZWZhdWx0UG9ydCh0KXt0aGlzW2p0XSYmKHRoaXNbanRdLmRlZmF1bHRQb3J0PXQpfWdldCBwcm90b2NvbCgpe3JldHVybiB0aGlzW2p0XS5wcm90b2NvbD8/KHRoaXMuaXNTZWN1cmVFbmRwb2ludCgpPyJodHRwczoiOiJodHRwOiIpfXNldCBwcm90b2NvbCh0KXt0aGlzW2p0XSYmKHRoaXNbanRdLnByb3RvY29sPXQpfX1mdW5jdGlvbiBQdCguLi50KXtfLmxvZygiW2h0dHBzLXByb3h5LWFnZW50OnBhcnNlLXByb3h5LXJlc3BvbnNlXSIsLi4udCl9ZnVuY3Rpb24ga3QodCl7cmV0dXJuIG5ldyBQcm9taXNlKChlLG4pPT57bGV0IHI9MDtjb25zdCBvPVtdO2Z1bmN0aW9uIHMoKXtjb25zdCBjPXQucmVhZCgpO2M/ZnVuY3Rpb24oYyl7by5wdXNoKGMpLHIrPWMubGVuZ3RoO2NvbnN0IHU9QnVmZmVyLmNvbmNhdChvLHIpLGE9dS5pbmRleE9mKCJcclxuXHJcbiIpO2lmKC0xPT09YSlyZXR1cm4gUHQoImhhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uIiksdm9pZCBzKCk7Y29uc3QgZj11LnN1YmFycmF5KDAsYSkudG9TdHJpbmcoImFzY2lpIikuc3BsaXQoIlxyXG4iKSxoPWYuc2hpZnQoKTtpZighaClyZXR1cm4gdC5kZXN0cm95KCksbihuZXcgRXJyb3IoIk5vIGhlYWRlciByZWNlaXZlZCBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2UiKSk7Y29uc3QgcD1oLnNwbGl0KCIgIiksbD0rKHBbMV18fDApLGQ9cC5zbGljZSgyKS5qb2luKCIgIiksZz17fTtmb3IoY29uc3QgZSBvZiBmKXtpZighZSljb250aW51ZTtjb25zdCByPWUuaW5kZXhPZigiOiIpO2lmKC0xPT09cilyZXR1cm4gdC5kZXN0cm95KCksbihuZXcgRXJyb3IoYEludmFsaWQgaGVhZGVyIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZTogIiR7ZX0iYCkpO2NvbnN0IG89ZS5zbGljZSgwLHIpLnRvTG93ZXJDYXNlKCkscz1lLnNsaWNlKHIrMSkudHJpbVN0YXJ0KCksaT1nW29dOyJzdHJpbmciPT10eXBlb2YgaT9nW29dPVtpLHNdOkFycmF5LmlzQXJyYXkoaSk/aS5wdXNoKHMpOmdbb109c31QdCgiZ290IHByb3h5IHNlcnZlciByZXNwb25zZTogJW8gJW8iLGgsZyksaSgpLGUoe2Nvbm5lY3Q6e3N0YXR1c0NvZGU6bCxzdGF0dXNUZXh0OmQsaGVhZGVyczpnfSxidWZmZXJlZDp1fSl9KGMpOnQub25jZSgicmVhZGFibGUiLHMpfWZ1bmN0aW9uIGkoKXt0LnJlbW92ZUxpc3RlbmVyKCJlbmQiLGMpLHQucmVtb3ZlTGlzdGVuZXIoImVycm9yIix1KSx0LnJlbW92ZUxpc3RlbmVyKCJyZWFkYWJsZSIscyl9ZnVuY3Rpb24gYygpe2koKSxQdCgib25lbmQiKSxuKG5ldyBFcnJvcigiUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UiKSl9ZnVuY3Rpb24gdSh0KXtpKCksUHQoIm9uZXJyb3IgJW8iLHQpLG4odCl9dC5vbigiZXJyb3IiLHUpLHQub24oImVuZCIsYykscygpfSl9ZnVuY3Rpb24gQXQoLi4udCl7Xy5sb2coIltodHRwcy1wcm94eS1hZ2VudF0iLC4uLnQpfWNsYXNzIFR0IGV4dGVuZHMgT3R7Y29uc3RydWN0b3IodCxlKXtzdXBlcihlKSx0aGlzLm9wdGlvbnM9e30sdGhpcy5wcm94eT0ic3RyaW5nIj09dHlwZW9mIHQ/bmV3IFVSTCh0KTp0LHRoaXMucHJveHlIZWFkZXJzPWU/LmhlYWRlcnM/P3t9LEF0KCJDcmVhdGluZyBuZXcgSHR0cHNQcm94eUFnZW50IGluc3RhbmNlOiAlbyIsdGhpcy5wcm94eS5ocmVmKTtjb25zdCBuPSh0aGlzLnByb3h5Lmhvc3RuYW1lfHx0aGlzLnByb3h5Lmhvc3QpLnJlcGxhY2UoL15cW3xcXSQvZywiIikscj10aGlzLnByb3h5LnBvcnQ/cGFyc2VJbnQodGhpcy5wcm94eS5wb3J0LDEwKToiaHR0cHM6Ij09PXRoaXMucHJveHkucHJvdG9jb2w/NDQzOjgwO3RoaXMuY29ubmVjdE9wdHM9e0FMUE5Qcm90b2NvbHM6WyJodHRwLzEuMSJdLC4uLmU/SXQoZSwiaGVhZGVycyIpOm51bGwsaG9zdDpuLHBvcnQ6cn19YXN5bmMgY29ubmVjdCh0LGUpe2NvbnN0e3Byb3h5Om59PXRoaXM7aWYoIWUuaG9zdCl0aHJvdyBuZXcgVHlwZUVycm9yKCdObyAiaG9zdCIgcHJvdmlkZWQnKTtsZXQgcjtpZigiaHR0cHM6Ij09PW4ucHJvdG9jb2wpe0F0KCJDcmVhdGluZyBgdGxzLlNvY2tldGA6ICVvIix0aGlzLmNvbm5lY3RPcHRzKTtjb25zdCB0PXRoaXMuY29ubmVjdE9wdHMuc2VydmVybmFtZXx8dGhpcy5jb25uZWN0T3B0cy5ob3N0O3I9Zi5jb25uZWN0KHsuLi50aGlzLmNvbm5lY3RPcHRzLHNlcnZlcm5hbWU6dCYmYS5pc0lQKHQpP3ZvaWQgMDp0fSl9ZWxzZSBBdCgiQ3JlYXRpbmcgYG5ldC5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cykscj1hLmNvbm5lY3QodGhpcy5jb25uZWN0T3B0cyk7Y29uc3Qgbz0iZnVuY3Rpb24iPT10eXBlb2YgdGhpcy5wcm94eUhlYWRlcnM/dGhpcy5wcm94eUhlYWRlcnMoKTp7Li4udGhpcy5wcm94eUhlYWRlcnN9LHM9YS5pc0lQdjYoZS5ob3N0KT9gWyR7ZS5ob3N0fV1gOmUuaG9zdDtsZXQgaT1gQ09OTkVDVCAke3N9OiR7ZS5wb3J0fSBIVFRQLzEuMVxyXG5gO2lmKG4udXNlcm5hbWV8fG4ucGFzc3dvcmQpe2NvbnN0IHQ9YCR7ZGVjb2RlVVJJQ29tcG9uZW50KG4udXNlcm5hbWUpfToke2RlY29kZVVSSUNvbXBvbmVudChuLnBhc3N3b3JkKX1gO29bIlByb3h5LUF1dGhvcml6YXRpb24iXT1gQmFzaWMgJHtCdWZmZXIuZnJvbSh0KS50b1N0cmluZygiYmFzZTY0Iil9YH1vLkhvc3Q9YCR7c306JHtlLnBvcnR9YCxvWyJQcm94eS1Db25uZWN0aW9uIl18fChvWyJQcm94eS1Db25uZWN0aW9uIl09dGhpcy5rZWVwQWxpdmU/IktlZXAtQWxpdmUiOiJjbG9zZSIpO2Zvcihjb25zdCB0IG9mIE9iamVjdC5rZXlzKG8pKWkrPWAke3R9OiAke29bdF19XHJcbmA7Y29uc3QgYz1rdChyKTtyLndyaXRlKGAke2l9XHJcbmApO2NvbnN0e2Nvbm5lY3Q6dSxidWZmZXJlZDpofT1hd2FpdCBjO2lmKHQuZW1pdCgicHJveHlDb25uZWN0Iix1KSx0aGlzLmVtaXQoInByb3h5Q29ubmVjdCIsdSx0KSwyMDA9PT11LnN0YXR1c0NvZGUpe2lmKHQub25jZSgic29ja2V0IixSdCksZS5zZWN1cmVFbmRwb2ludCl7QXQoIlVwZ3JhZGluZyBzb2NrZXQgY29ubmVjdGlvbiB0byBUTFMiKTtjb25zdCB0PWUuc2VydmVybmFtZXx8ZS5ob3N0O3JldHVybiBmLmNvbm5lY3Qoey4uLkl0KGUsImhvc3QiLCJwYXRoIiwicG9ydCIpLHNvY2tldDpyLHNlcnZlcm5hbWU6YS5pc0lQKHQpP3ZvaWQgMDp0fSl9cmV0dXJuIHJ9ci5kZXN0cm95KCk7Y29uc3QgcD1uZXcgYS5Tb2NrZXQoe3dyaXRhYmxlOiExfSk7cmV0dXJuIHAucmVhZGFibGU9ITAsdC5vbmNlKCJzb2NrZXQiLHQ9PntBdCgiUmVwbGF5aW5nIHByb3h5IGJ1ZmZlciBmb3IgZmFpbGVkIHJlcXVlc3QiKSx0LnB1c2goaCksdC5wdXNoKG51bGwpfSkscH19ZnVuY3Rpb24gUnQodCl7dC5yZXN1bWUoKX1mdW5jdGlvbiBJdCh0LC4uLmUpe2NvbnN0IG49e307bGV0IHI7Zm9yKHIgaW4gdCllLmluY2x1ZGVzKHIpfHwobltyXT10W3JdKTtyZXR1cm4gbn1UdC5wcm90b2NvbHM9WyJodHRwIiwiaHR0cHMiXTtmdW5jdGlvbiBEdCh0KXtyZXR1cm4gdC5yZXBsYWNlKC9eW0EtWl06LywiIikucmVwbGFjZSgvXFwvZywiLyIpfWNvbnN0IFV0PWU7bGV0IEJ0LEx0PTAsTXQ9e307ZnVuY3Rpb24genQodCl7VXQuZGVidWcmJmNvbnNvbGUubG9nKGBbQU5SIFdvcmtlcl0gJHt0fWApfXZhciBKdCxXdCxGdDtjb25zdCBHdD1mdW5jdGlvbih0KXtsZXQgZTt0cnl7ZT1uZXcgVVJMKHQudXJsKX1jYXRjaHtyZXR1cm4gYigoKT0+e2NvbnNvbGUud2FybigiW0BzZW50cnkvbm9kZV06IEludmFsaWQgZHNuIG9yIHR1bm5lbCBvcHRpb24sIHdpbGwgbm90IHNlbmQgYW55IGV2ZW50cy4gVGhlIHR1bm5lbCBvcHRpb24gbXVzdCBiZSBhIGZ1bGwgVVJMIHdoZW4gdXNlZC4iKX0pLCR0KHQsKCk9PlByb21pc2UucmVzb2x2ZSh7fSkpfWNvbnN0IG49Imh0dHBzOiI9PT1lLnByb3RvY29sLHI9ZnVuY3Rpb24odCxlKXtjb25zdHtub19wcm94eTpufT1wcm9jZXNzLmVudixyPW4/LnNwbGl0KCIsIikuc29tZShlPT50Lmhvc3QuZW5kc1dpdGgoZSl8fHQuaG9zdG5hbWUuZW5kc1dpdGgoZSkpO3JldHVybiByP3ZvaWQgMDplfShlLHQucHJveHl8fChuP3Byb2Nlc3MuZW52Lmh0dHBzX3Byb3h5OnZvaWQgMCl8fHByb2Nlc3MuZW52Lmh0dHBfcHJveHkpLG89bj9pOnMsYT10LmtlZXBBbGl2ZT8/ITAsZj1yP25ldyBUdChyKTpuZXcgby5BZ2VudCh7a2VlcEFsaXZlOmEsbWF4U29ja2V0czozMCx0aW1lb3V0OjJlM30pLGg9ZnVuY3Rpb24odCxlLG4pe2NvbnN0e2hvc3RuYW1lOnIscGF0aG5hbWU6byxwb3J0OnMscHJvdG9jb2w6aSxzZWFyY2g6YX09bmV3IFVSTCh0LnVybCk7cmV0dXJuIGZ1bmN0aW9uKGYpe2NvbnN0IGg9cD0+bmV3IFByb21pc2UoKGwsZCk9Pnt5dCgoKT0+e2xldCBnPWZ1bmN0aW9uKHQpe3JldHVybiBuZXcgYyh7cmVhZCgpe3RoaXMucHVzaCh0KSx0aGlzLnB1c2gobnVsbCl9fSl9KGYuYm9keSk7Y29uc3QgbT17Li4udC5oZWFkZXJzfTtmLmJvZHkubGVuZ3RoPjMyNzY4JiYobVsiY29udGVudC1lbmNvZGluZyJdPSJnemlwIixnPWcucGlwZSh1KCkpKTtjb25zdCB5PXIuc3RhcnRzV2l0aCgiWyIpLGI9ZS5yZXF1ZXN0KHttZXRob2Q6IlBPU1QiLGFnZW50Om4saGVhZGVyczptLGhvc3RuYW1lOnk/ci5zbGljZSgxLC0xKTpyLHBhdGg6YCR7b30ke2F9YCxwb3J0OnMscHJvdG9jb2w6aSxjYTp0LmNhQ2VydHN9LHQ9Pnt0Lm9uKCJkYXRhIiwoKT0+e30pLHQub24oImVuZCIsKCk9Pnt9KSx0LnNldEVuY29kaW5nKCJ1dGY4Iik7Y29uc3QgZT10LmhlYWRlcnNbInJldHJ5LWFmdGVyIl0/P251bGwsbj10LmhlYWRlcnNbIngtc2VudHJ5LXJhdGUtbGltaXRzIl0/P251bGw7bCh7c3RhdHVzQ29kZTp0LnN0YXR1c0NvZGUsaGVhZGVyczp7InJldHJ5LWFmdGVyIjplLCJ4LXNlbnRyeS1yYXRlLWxpbWl0cyI6QXJyYXkuaXNBcnJheShuKT9uWzBdfHxudWxsOm59fSl9KTtiLm9uKCJlcnJvciIsdD0+e3AmJmIucmV1c2VkU29ja2V0JiYiRUNPTk5SRVNFVCI9PT10LmNvZGU/bChoKCExKSk6ZCh0KX0pLGcucGlwZShiKX0pfSk7cmV0dXJuIGgoITApfX0odCx0Lmh0dHBNb2R1bGU/P28sZik7cmV0dXJuICR0KHQsaCl9KHt1cmw6KEp0PVV0LmRzbixXdD1VdC50dW5uZWwsRnQ9VXQuc2RrTWV0YWRhdGEuc2RrLFd0fHxgJHtmdW5jdGlvbih0KXtyZXR1cm5gJHtmdW5jdGlvbih0KXtjb25zdCBlPXQucHJvdG9jb2w/YCR7dC5wcm90b2NvbH06YDoiIixuPXQucG9ydD9gOiR7dC5wb3J0fWA6IiI7cmV0dXJuYCR7ZX0vLyR7dC5ob3N0fSR7bn0ke3QucGF0aD9gLyR7dC5wYXRofWA6IiJ9L2FwaS9gfSh0KX0ke3QucHJvamVjdElkfS9lbnZlbG9wZS9gfShKdCl9PyR7ZnVuY3Rpb24odCxlKXtjb25zdCBuPXtzZW50cnlfdmVyc2lvbjoiNyJ9O3JldHVybiB0LnB1YmxpY0tleSYmKG4uc2VudHJ5X2tleT10LnB1YmxpY0tleSksZSYmKG4uc2VudHJ5X2NsaWVudD1gJHtlLm5hbWV9LyR7ZS52ZXJzaW9ufWApLG5ldyBVUkxTZWFyY2hQYXJhbXMobikudG9TdHJpbmcoKX0oSnQsRnQpfWApfSk7YXN5bmMgZnVuY3Rpb24gWXQoKXtpZihCdCl7enQoIlNlbmRpbmcgYWJub3JtYWwgc2Vzc2lvbiIpLFooQnQse3N0YXR1czoiYWJub3JtYWwiLGFibm9ybWFsX21lY2hhbmlzbToiYW5yX2ZvcmVncm91bmQiLHJlbGVhc2U6VXQucmVsZWFzZSxlbnZpcm9ubWVudDpVdC5lbnZpcm9ubWVudH0pO2NvbnN0IHQ9ZnVuY3Rpb24odCxlLG4scil7Y29uc3Qgbz1tdChuKTtyZXR1cm4gZnQoe3NlbnRfYXQ6bmV3IERhdGUoSSgpKS50b0lTT1N0cmluZygpLC4uLm8mJntzZGs6b30sLi4uISFyJiZlJiZ7ZHNuOnV0KGUpfX0sWyJhZ2dyZWdhdGVzImluIHQ/W3t0eXBlOiJzZXNzaW9ucyJ9LHRdOlt7dHlwZToic2Vzc2lvbiJ9LHQudG9KU09OKCldXSl9KEJ0LFV0LmRzbixVdC5zZGtNZXRhZGF0YSxVdC50dW5uZWwpO3p0KEpTT04uc3RyaW5naWZ5KHQpKSxhd2FpdCBHdC5zZW5kKHQpO3RyeXtuPy5wb3N0TWVzc2FnZSgic2Vzc2lvbi1lbmRlZCIpfWNhdGNoe319fWZ1bmN0aW9uIEh0KHQpe2lmKCF0KXJldHVybjtjb25zdCBlPWZ1bmN0aW9uKHQpe2lmKCF0Lmxlbmd0aClyZXR1cm5bXTtjb25zdCBlPUFycmF5LmZyb20odCk7cmV0dXJuL3NlbnRyeVdyYXBwZWQvLnRlc3QoRShlKS5mdW5jdGlvbnx8IiIpJiZlLnBvcCgpLGUucmV2ZXJzZSgpLCQudGVzdChFKGUpLmZ1bmN0aW9ufHwiIikmJihlLnBvcCgpLCQudGVzdChFKGUpLmZ1bmN0aW9ufHwiIikmJmUucG9wKCkpLGUuc2xpY2UoMCw1MCkubWFwKHQ9Pih7Li4udCxmaWxlbmFtZTp0LmZpbGVuYW1lfHxFKGUpLmZpbGVuYW1lLGZ1bmN0aW9uOnQuZnVuY3Rpb258fCI/In0pKX0odCk7aWYoVXQuYXBwUm9vdFBhdGgpZm9yKGNvbnN0IHQgb2YgZSl0LmZpbGVuYW1lJiYodC5maWxlbmFtZT1NKHQuZmlsZW5hbWUsVXQuYXBwUm9vdFBhdGgpKTtyZXR1cm4gZX1hc3luYyBmdW5jdGlvbiBadCh0LGUpe2lmKEx0Pj1VdC5tYXhBbnJFdmVudHMpcmV0dXJuO0x0Kz0xLGF3YWl0IFl0KCksenQoIlNlbmRpbmcgZXZlbnQiKTtjb25zdCBuPXtldmVudF9pZDpXKCksY29udGV4dHM6VXQuY29udGV4dHMscmVsZWFzZTpVdC5yZWxlYXNlLGVudmlyb25tZW50OlV0LmVudmlyb25tZW50LGRpc3Q6VXQuZGlzdCxwbGF0Zm9ybToibm9kZSIsbGV2ZWw6ImVycm9yIixleGNlcHRpb246e3ZhbHVlczpbe3R5cGU6IkFwcGxpY2F0aW9uTm90UmVzcG9uZGluZyIsdmFsdWU6YEFwcGxpY2F0aW9uIE5vdCBSZXNwb25kaW5nIGZvciBhdCBsZWFzdCAke1V0LmFuclRocmVzaG9sZH0gbXNgLHN0YWNrdHJhY2U6e2ZyYW1lczpIdCh0KX0sbWVjaGFuaXNtOnt0eXBlOiJBTlIifX1dfSx0YWdzOlV0LnN0YXRpY1RhZ3N9O2UmJmZ1bmN0aW9uKHQsZSl7aWYoYXQodCxlKSwhdC5jb250ZXh0cz8udHJhY2Upe2NvbnN0e3RyYWNlSWQ6bixwYXJlbnRTcGFuSWQ6cixwcm9wYWdhdGlvblNwYW5JZDpvfT1lLnByb3BhZ2F0aW9uQ29udGV4dDt0LmNvbnRleHRzPXt0cmFjZTp7dHJhY2VfaWQ6bixzcGFuX2lkOm98fFcoKS5zdWJzdHJpbmcoMTYpLHBhcmVudF9zcGFuX2lkOnJ9LC4uLnQuY29udGV4dHN9fX0obixlKSxmdW5jdGlvbih0KXtpZigwPT09T2JqZWN0LmtleXMoTXQpLmxlbmd0aClyZXR1cm47Y29uc3QgZT1VdC5hcHBSb290UGF0aD97fTpNdDtpZihVdC5hcHBSb290UGF0aClmb3IoY29uc3RbdCxuXW9mIE9iamVjdC5lbnRyaWVzKE10KSllW00odCxVdC5hcHBSb290UGF0aCldPW47Y29uc3Qgbj1uZXcgTWFwO2Zvcihjb25zdCByIG9mIHQuZXhjZXB0aW9uPy52YWx1ZXN8fFtdKWZvcihjb25zdCB0IG9mIHIuc3RhY2t0cmFjZT8uZnJhbWVzfHxbXSl7Y29uc3Qgcj10LmFic19wYXRofHx0LmZpbGVuYW1lO3ImJmVbcl0mJm4uc2V0KHIsZVtyXSl9aWYobi5zaXplPjApe2NvbnN0IGU9W107Zm9yKGNvbnN0W3Qscl1vZiBuLmVudHJpZXMoKSllLnB1c2goe3R5cGU6InNvdXJjZW1hcCIsY29kZV9maWxlOnQsZGVidWdfaWQ6cn0pO3QuZGVidWdfbWV0YT17aW1hZ2VzOmV9fX0obik7Y29uc3Qgcj1idChuLFV0LmRzbixVdC5zZGtNZXRhZGF0YSxVdC50dW5uZWwpO3p0KEpTT04uc3RyaW5naWZ5KHIpKSxhd2FpdCBHdC5zZW5kKHIpLGF3YWl0IEd0LmZsdXNoKDJlMyksTHQ+PVV0Lm1heEFuckV2ZW50cyYmc2V0VGltZW91dCgoKT0+e3Byb2Nlc3MuZXhpdCgwKX0sNWUzKX1sZXQgcXQ7aWYoenQoIlN0YXJ0ZWQiKSxVdC5jYXB0dXJlU3RhY2tUcmFjZSl7enQoIkNvbm5lY3RpbmcgdG8gZGVidWdnZXIiKTtjb25zdCBlPW5ldyB0O2UuY29ubmVjdFRvTWFpblRocmVhZCgpLHp0KCJDb25uZWN0ZWQgdG8gZGVidWdnZXIiKTtjb25zdCBuPW5ldyBNYXA7ZS5vbigiRGVidWdnZXIuc2NyaXB0UGFyc2VkIix0PT57bi5zZXQodC5wYXJhbXMuc2NyaXB0SWQsdC5wYXJhbXMudXJsKX0pLGUub24oIkRlYnVnZ2VyLnBhdXNlZCIsdD0+e2lmKCJvdGhlciI9PT10LnBhcmFtcy5yZWFzb24pdHJ5e3p0KCJEZWJ1Z2dlciBwYXVzZWQiKTtjb25zdCBzPVsuLi50LnBhcmFtcy5jYWxsRnJhbWVzXSxpPVV0LmFwcFJvb3RQYXRoP2Z1bmN0aW9uKHQ9KHByb2Nlc3MuYXJndlsxXT94dChwcm9jZXNzLmFyZ3ZbMV0pOnByb2Nlc3MuY3dkKCkpLGU9IlxcIj09PW8pe2NvbnN0IG49ZT9EdCh0KTp0O3JldHVybiB0PT57aWYoIXQpcmV0dXJuO2NvbnN0IG89ZT9EdCh0KTp0O2xldHtkaXI6cyxiYXNlOmksZXh0OmN9PXIucGFyc2Uobyk7Ii5qcyIhPT1jJiYiLm1qcyIhPT1jJiYiLmNqcyIhPT1jfHwoaT1pLnNsaWNlKDAsLTEqYy5sZW5ndGgpKTtjb25zdCB1PWRlY29kZVVSSUNvbXBvbmVudChpKTtzfHwocz0iLiIpO2NvbnN0IGE9cy5sYXN0SW5kZXhPZigiL25vZGVfbW9kdWxlcyIpO2lmKGE+LTEpcmV0dXJuYCR7cy5zbGljZShhKzE0KS5yZXBsYWNlKC9cLy9nLCIuIil9OiR7dX1gO2lmKHMuc3RhcnRzV2l0aChuKSl7Y29uc3QgdD1zLnNsaWNlKG4ubGVuZ3RoKzEpLnJlcGxhY2UoL1wvL2csIi4iKTtyZXR1cm4gdD9gJHt0fToke3V9YDp1fXJldHVybiB1fX0oVXQuYXBwUm9vdFBhdGgpOigpPT57fSxjPXMubWFwKHQ9PmZ1bmN0aW9uKHQsZSxuKXtjb25zdCByPWU/ZS5yZXBsYWNlKC9eZmlsZTpcL1wvLywiIik6dm9pZCAwLG89dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXI/dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXIrMTp2b2lkIDAscz10LmxvY2F0aW9uLmxpbmVOdW1iZXI/dC5sb2NhdGlvbi5saW5lTnVtYmVyKzE6dm9pZCAwO3JldHVybntmaWxlbmFtZTpyLG1vZHVsZTpuKHIpLGZ1bmN0aW9uOnQuZnVuY3Rpb25OYW1lfHwiPyIsY29sbm86byxsaW5lbm86cyxpbl9hcHA6cj9OdChyKTp2b2lkIDB9fSh0LG4uZ2V0KHQubG9jYXRpb24uc2NyaXB0SWQpLGkpKSx1PXNldFRpbWVvdXQoKCk9PntadChjKS50aGVuKG51bGwsKCk9Pnt6dCgiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSl9LDVlMyk7ZS5wb3N0KCJSdW50aW1lLmV2YWx1YXRlIix7ZXhwcmVzc2lvbjoiZ2xvYmFsLl9fU0VOVFJZX0dFVF9TQ09QRVNfXygpOyIsc2lsZW50OiEwLHJldHVybkJ5VmFsdWU6ITB9LCh0LG4pPT57dCYmenQoYEVycm9yIGV4ZWN1dGluZyBzY3JpcHQ6ICcke3QubWVzc2FnZX0nYCksY2xlYXJUaW1lb3V0KHUpO2NvbnN0IHI9bj8ucmVzdWx0P24ucmVzdWx0LnZhbHVlOnZvaWQgMDtlLnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpLGUucG9zdCgiRGVidWdnZXIuZGlzYWJsZSIpLFp0KGMscikudGhlbihudWxsLCgpPT57enQoIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pfSl9Y2F0Y2godCl7dGhyb3cgZS5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxlLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSx0fX0pLHF0PSgpPT57dHJ5e2UucG9zdCgiRGVidWdnZXIuZW5hYmxlIiwoKT0+e2UucG9zdCgiRGVidWdnZXIucGF1c2UiKX0pfWNhdGNoe319fWNvbnN0e3BvbGw6S3R9PWZ1bmN0aW9uKHQsZSxuLHIpe2NvbnN0IG89dCgpO2xldCBzPSExLGk9ITA7cmV0dXJuIHNldEludGVydmFsKCgpPT57Y29uc3QgdD1vLmdldFRpbWVNcygpOyExPT09cyYmdD5lK24mJihzPSEwLGkmJnIoKSksdDxlK24mJihzPSExKX0sMjApLHtwb2xsOigpPT57by5yZXNldCgpfSxlbmFibGVkOnQ9PntpPXR9fX0oZnVuY3Rpb24oKXtsZXQgdD1wcm9jZXNzLmhydGltZS5iaWdpbnQoKTtyZXR1cm57Z2V0VGltZU1zOigpPT5OdW1iZXIoKHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpLXQpLzEwMDAwMDBuKSxyZXNldDooKT0+e3Q9cHJvY2Vzcy5ocnRpbWUuYmlnaW50KCl9fX0sVXQucG9sbEludGVydmFsLFV0LmFuclRocmVzaG9sZCxmdW5jdGlvbigpe3p0KCJXYXRjaGRvZyB0aW1lb3V0IikscXQ/KHp0KCJQYXVzaW5nIGRlYnVnZ2VyIHRvIGNhcHR1cmUgc3RhY2sgdHJhY2UiKSxxdCgpKTooenQoIkNhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UiKSxadCgpLnRoZW4obnVsbCwoKT0+e3p0KCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQgb24gd2F0Y2hkb2cgdGltZW91dC4iKX0pKX0pO24/Lm9uKCJtZXNzYWdlIix0PT57dC5zZXNzaW9uJiYoQnQ9SCh0LnNlc3Npb24pKSx0LmRlYnVnSW1hZ2VzJiYoTXQ9dC5kZWJ1Z0ltYWdlcyksS3QoKX0pLG4/LnBvc3RNZXNzYWdlKCJ3b3JrZXItcmVhZHkiKTs=";
|
|
10
10
|
const DEFAULT_INTERVAL = 50;
|
|
11
11
|
const DEFAULT_HANG_THRESHOLD = 5e3;
|
|
12
12
|
function log(message, ...args) {
|
|
@@ -34,6 +34,7 @@ const INTEGRATION_NAME = "Anr";
|
|
|
34
34
|
const _anrIntegration = ((options = {}) => {
|
|
35
35
|
let worker;
|
|
36
36
|
let client;
|
|
37
|
+
let workerReady;
|
|
37
38
|
const gbl = globalWithScopeFetchFn();
|
|
38
39
|
gbl.__SENTRY_GET_SCOPES__ = getScopeData;
|
|
39
40
|
return {
|
|
@@ -42,18 +43,23 @@ const _anrIntegration = ((options = {}) => {
|
|
|
42
43
|
if (worker) {
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
const initializedClient = client;
|
|
47
|
+
if (initializedClient) {
|
|
48
|
+
workerReady = new Promise((resolve) => {
|
|
49
|
+
worker = _startWorker(initializedClient, options, resolve);
|
|
50
|
+
});
|
|
47
51
|
}
|
|
48
52
|
},
|
|
49
53
|
stopWorker: () => {
|
|
50
54
|
if (worker) {
|
|
55
|
+
workerReady = void 0;
|
|
51
56
|
worker.then((stop) => {
|
|
52
57
|
stop();
|
|
53
58
|
worker = void 0;
|
|
54
59
|
});
|
|
55
60
|
}
|
|
56
61
|
},
|
|
62
|
+
waitUntilWorkerReady: () => workerReady ?? Promise.resolve(),
|
|
57
63
|
async setup(initClient) {
|
|
58
64
|
client = initClient;
|
|
59
65
|
if (options.captureStackTrace && await debug.isDebuggerEnabled()) {
|
|
@@ -65,7 +71,7 @@ const _anrIntegration = ((options = {}) => {
|
|
|
65
71
|
};
|
|
66
72
|
});
|
|
67
73
|
const anrIntegration = core.defineIntegration(_anrIntegration);
|
|
68
|
-
async function _startWorker(client, integrationOptions) {
|
|
74
|
+
async function _startWorker(client, integrationOptions, onReady) {
|
|
69
75
|
const dsn = client.getDsn();
|
|
70
76
|
if (!dsn) {
|
|
71
77
|
return () => {
|
|
@@ -123,6 +129,8 @@ async function _startWorker(client, integrationOptions) {
|
|
|
123
129
|
if (msg === "session-ended") {
|
|
124
130
|
log("ANR event sent from ANR worker. Clearing session in this thread.");
|
|
125
131
|
core.getIsolationScope().setSession(void 0);
|
|
132
|
+
} else if (msg === "worker-ready") {
|
|
133
|
+
onReady?.();
|
|
126
134
|
}
|
|
127
135
|
});
|
|
128
136
|
worker.once("error", (err) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/integrations/anr/index.ts"],"sourcesContent":["import { types } from 'node:util';\nimport { Worker } from 'node:worker_threads';\nimport type { Contexts, Event, EventHint, Integration, IntegrationFn, ScopeData } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCombinedScopeData,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getIsolationScope,\n GLOBAL_OBJ,\n} from '@sentry/core';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { AnrIntegrationOptions, WorkerStartData } from './common';\n\nconst { isPromise } = types;\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###AnrWorkerScript###';\n\nconst DEFAULT_INTERVAL = 50;\nconst DEFAULT_HANG_THRESHOLD = 5000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[ANR] ${message}`, ...args);\n}\n\nfunction globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?: () => ScopeData } {\n return GLOBAL_OBJ;\n}\n\n/** Fetches merged scope data */\nfunction getScopeData(): ScopeData {\n const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope());\n\n // We remove attachments because they likely won't serialize well as json\n scope.attachments = [];\n // We can't serialize event processor functions\n scope.eventProcessors = [];\n\n return scope;\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: 'ANR' };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nconst INTEGRATION_NAME = 'Anr' as const;\n\ntype AnrInternal = { startWorker: () => void; stopWorker: () => void };\n\n// eslint-disable-next-line typescript/no-deprecated\nconst _anrIntegration = ((options: Partial<AnrIntegrationOptions> = {}) => {\n let worker: Promise<() => void> | undefined;\n let client: NodeClient | undefined;\n\n // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the\n // debugger when it pauses\n const gbl = globalWithScopeFetchFn();\n gbl.__SENTRY_GET_SCOPES__ = getScopeData;\n\n return {\n name: INTEGRATION_NAME,\n startWorker: () => {\n if (worker) {\n return;\n }\n\n if (client) {\n worker = _startWorker(client, options);\n }\n },\n stopWorker: () => {\n if (worker) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.then(stop => {\n stop();\n worker = undefined;\n });\n }\n },\n async setup(initClient: NodeClient) {\n client = initClient;\n\n if (options.captureStackTrace && (await isDebuggerEnabled())) {\n debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled');\n options.captureStackTrace = false;\n }\n\n // setImmediate is used to ensure that all other integrations have had their setup called first.\n // This allows us to call into all integrations to fetch the full context\n setImmediate(() => this.startWorker());\n },\n } as Integration & AnrInternal;\n}) satisfies IntegrationFn;\n\n// eslint-disable-next-line typescript/no-deprecated\ntype AnrReturn = (options?: Partial<AnrIntegrationOptions>) => Integration & AnrInternal;\n\n/**\n * Application Not Responding (ANR) integration for Node.js applications.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n *\n * Detects when the Node.js main thread event loop is blocked for more than the configured\n * threshold (5 seconds by default) and reports these as Sentry events.\n *\n * ANR detection uses a worker thread to monitor the event loop in the main app thread.\n * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default.\n * If the ANR worker does not receive a heartbeat message for the configured threshold duration,\n * it triggers an ANR event.\n *\n * - Node.js 16.17.0 or higher\n * - Only supported in the Node.js runtime (not browsers)\n * - Not supported for Node.js clusters\n *\n * Overhead should be minimal:\n * - Main thread: Only polling the ANR worker over IPC every 50ms\n * - Worker thread: Consumes around 10-20 MB of RAM\n * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking)\n *\n * @example\n * ```javascript\n * Sentry.init({\n * dsn: \"https://examplePublicKey@o0.ingest.sentry.io/0\",\n * integrations: [\n * Sentry.anrIntegration({\n * anrThreshold: 5000,\n * captureStackTrace: true,\n * pollInterval: 50,\n * }),\n * ],\n * });\n * ```\n */\nexport const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn;\n\n/**\n * Starts the ANR worker thread\n *\n * @returns A function to stop the worker\n */\nasync function _startWorker(\n client: NodeClient,\n // eslint-disable-next-line typescript/no-deprecated\n integrationOptions: Partial<AnrIntegrationOptions>,\n): Promise<() => void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n return () => {\n //\n };\n }\n\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL,\n anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD,\n captureStackTrace: !!integrationOptions.captureStackTrace,\n maxAnrEvents: integrationOptions.maxAnrEvents || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n if (options.captureStackTrace) {\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n const timer = setInterval(() => {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n // message the worker to tell it the main event loop is still running\n worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) });\n } catch {\n //\n }\n }, options.pollInterval);\n // Timer should not block exit\n timer.unref();\n\n worker.on('message', (msg: string) => {\n if (msg === 'session-ended') {\n log('ANR event sent from ANR worker. Clearing session in this thread.');\n getIsolationScope().setSession(undefined);\n }\n });\n\n worker.once('error', (err: Error) => {\n clearInterval(timer);\n log('ANR worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n clearInterval(timer);\n log('ANR worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n clearInterval(timer);\n };\n}\n\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T): T;\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Temporarily disables ANR detection for the duration of a callback function.\n *\n * This utility function allows you to disable ANR detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as AnrInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stopWorker();\n\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.startWorker());\n }\n\n integration.startWorker();\n return result;\n}\n"],"names":["types","debug","GLOBAL_OBJ","getCombinedScopeData","getIsolationScope","getCurrentScope","isDebuggerEnabled","defineIntegration","Worker","getFilenameToDebugIdMap","getClient"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/integrations/anr/index.ts"],"sourcesContent":["import { types } from 'node:util';\nimport { Worker } from 'node:worker_threads';\nimport type { Contexts, Event, EventHint, Integration, IntegrationFn, ScopeData } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCombinedScopeData,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getIsolationScope,\n GLOBAL_OBJ,\n} from '@sentry/core';\nimport type { NodeClient } from '../../sdk/client';\nimport { isDebuggerEnabled } from '../../utils/debug';\nimport type { AnrIntegrationOptions, WorkerStartData } from './common';\n\nconst { isPromise } = types;\n\n// This string is a placeholder that gets overwritten with the worker code.\nexport const base64WorkerScript = '###AnrWorkerScript###';\n\nconst DEFAULT_INTERVAL = 50;\nconst DEFAULT_HANG_THRESHOLD = 5000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[ANR] ${message}`, ...args);\n}\n\nfunction globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?: () => ScopeData } {\n return GLOBAL_OBJ;\n}\n\n/** Fetches merged scope data */\nfunction getScopeData(): ScopeData {\n const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope());\n\n // We remove attachments because they likely won't serialize well as json\n scope.attachments = [];\n // We can't serialize event processor functions\n scope.eventProcessors = [];\n\n return scope;\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: 'ANR' };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nconst INTEGRATION_NAME = 'Anr' as const;\n\ntype AnrInternal = {\n startWorker: () => void;\n stopWorker: () => void;\n waitUntilWorkerReady: () => Promise<void>;\n};\n\n// eslint-disable-next-line typescript/no-deprecated\nconst _anrIntegration = ((options: Partial<AnrIntegrationOptions> = {}) => {\n let worker: Promise<() => void> | undefined;\n let client: NodeClient | undefined;\n let workerReady: Promise<void> | undefined;\n\n // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the\n // debugger when it pauses\n const gbl = globalWithScopeFetchFn();\n gbl.__SENTRY_GET_SCOPES__ = getScopeData;\n\n return {\n name: INTEGRATION_NAME,\n startWorker: () => {\n if (worker) {\n return;\n }\n\n const initializedClient = client;\n if (initializedClient) {\n workerReady = new Promise<void>(resolve => {\n worker = _startWorker(initializedClient, options, resolve);\n });\n }\n },\n stopWorker: () => {\n if (worker) {\n workerReady = undefined;\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.then(stop => {\n stop();\n worker = undefined;\n });\n }\n },\n waitUntilWorkerReady: () => workerReady ?? Promise.resolve(),\n async setup(initClient: NodeClient) {\n client = initClient;\n\n if (options.captureStackTrace && (await isDebuggerEnabled())) {\n debug.warn('ANR captureStackTrace has been disabled because the debugger was already enabled');\n options.captureStackTrace = false;\n }\n\n // setImmediate is used to ensure that all other integrations have had their setup called first.\n // This allows us to call into all integrations to fetch the full context\n setImmediate(() => this.startWorker());\n },\n } as Integration & AnrInternal;\n}) satisfies IntegrationFn;\n\n// eslint-disable-next-line typescript/no-deprecated\ntype AnrReturn = (options?: Partial<AnrIntegrationOptions>) => Integration & AnrInternal;\n\n/**\n * Application Not Responding (ANR) integration for Node.js applications.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n *\n * Detects when the Node.js main thread event loop is blocked for more than the configured\n * threshold (5 seconds by default) and reports these as Sentry events.\n *\n * ANR detection uses a worker thread to monitor the event loop in the main app thread.\n * The main app thread sends a heartbeat message to the ANR worker thread every 50ms by default.\n * If the ANR worker does not receive a heartbeat message for the configured threshold duration,\n * it triggers an ANR event.\n *\n * - Node.js 16.17.0 or higher\n * - Only supported in the Node.js runtime (not browsers)\n * - Not supported for Node.js clusters\n *\n * Overhead should be minimal:\n * - Main thread: Only polling the ANR worker over IPC every 50ms\n * - Worker thread: Consumes around 10-20 MB of RAM\n * - When ANR detected: Brief pause in debugger to capture stack trace (negligible compared to the blocking)\n *\n * @example\n * ```javascript\n * Sentry.init({\n * dsn: \"https://examplePublicKey@o0.ingest.sentry.io/0\",\n * integrations: [\n * Sentry.anrIntegration({\n * anrThreshold: 5000,\n * captureStackTrace: true,\n * pollInterval: 50,\n * }),\n * ],\n * });\n * ```\n */\nexport const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn;\n\n/**\n * Starts the ANR worker thread\n *\n * @returns A function to stop the worker\n */\nasync function _startWorker(\n client: NodeClient,\n // eslint-disable-next-line typescript/no-deprecated\n integrationOptions: Partial<AnrIntegrationOptions>,\n onReady?: () => void,\n): Promise<() => void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n return () => {\n //\n };\n }\n\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL,\n anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD,\n captureStackTrace: !!integrationOptions.captureStackTrace,\n maxAnrEvents: integrationOptions.maxAnrEvents || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n if (options.captureStackTrace) {\n const inspector = await import('node:inspector');\n if (!inspector.url()) {\n inspector.open(0);\n }\n }\n\n const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {\n workerData: options,\n // We don't want any Node args to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n const timer = setInterval(() => {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n // message the worker to tell it the main event loop is still running\n worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) });\n } catch {\n //\n }\n }, options.pollInterval);\n // Timer should not block exit\n timer.unref();\n\n worker.on('message', (msg: string) => {\n if (msg === 'session-ended') {\n log('ANR event sent from ANR worker. Clearing session in this thread.');\n getIsolationScope().setSession(undefined);\n } else if (msg === 'worker-ready') {\n onReady?.();\n }\n });\n\n worker.once('error', (err: Error) => {\n clearInterval(timer);\n log('ANR worker error', err);\n });\n\n worker.once('exit', (code: number) => {\n clearInterval(timer);\n log('ANR worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n clearInterval(timer);\n };\n}\n\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T): T;\n/**\n * @see {@link disableBlockDetectionForCallback}\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Temporarily disables ANR detection for the duration of a callback function.\n *\n * This utility function allows you to disable ANR detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n *\n * @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead.\n */\nexport function disableAnrDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as AnrInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stopWorker();\n\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.startWorker());\n }\n\n integration.startWorker();\n return result;\n}\n"],"names":["types","debug","GLOBAL_OBJ","getCombinedScopeData","getIsolationScope","getCurrentScope","isDebuggerEnabled","defineIntegration","Worker","getFilenameToDebugIdMap","getClient"],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ const debugBuild = require('../debug-build.js');
|
|
|
7
7
|
|
|
8
8
|
const LRU_FILE_CONTENTS_CACHE = new core.LRUMap(10);
|
|
9
9
|
const LRU_FILE_CONTENTS_FS_READ_FAILED = new core.LRUMap(20);
|
|
10
|
-
const DEFAULT_LINES_OF_CONTEXT =
|
|
10
|
+
const DEFAULT_LINES_OF_CONTEXT = 5;
|
|
11
11
|
const INTEGRATION_NAME = "ContextLines";
|
|
12
12
|
const MAX_CONTEXTLINES_COLNO = 1e3;
|
|
13
13
|
const MAX_CONTEXTLINES_LINENO = 1e4;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextlines.js","sources":["../../../src/integrations/contextlines.ts"],"sourcesContent":["import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { debug, defineIntegration, LRUMap, snipLine } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\nconst LRU_FILE_CONTENTS_CACHE = new LRUMap<string, Record<number, string>>(10);\nconst LRU_FILE_CONTENTS_FS_READ_FAILED = new LRUMap<string, 1>(20);\nconst DEFAULT_LINES_OF_CONTEXT = 7;\nconst INTEGRATION_NAME = 'ContextLines' as const;\n// Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be\n// minified code while large lineno values are likely to be bundled code.\n// Exported for testing purposes.\nexport const MAX_CONTEXTLINES_COLNO: number = 1000;\nexport const MAX_CONTEXTLINES_LINENO: number = 10000;\n\n// TODO(v11): Use `dataCollection.frameContextLines` default (5)\ninterface ContextLinesOptions {\n /**\n * Sets the number of context lines for each frame when loading a file.\n * Defaults to 7.\n *\n * Set to 0 to disable loading and inclusion of source files.\n *\n * When set, this option takes precedence over `dataCollection.frameContextLines`.\n **/\n frameContextLines?: number;\n}\n\n/**\n * Exported for testing purposes.\n */\nexport function resetFileContentCache(): void {\n LRU_FILE_CONTENTS_CACHE.clear();\n}\n\n/**\n * Get or init map value\n */\nfunction emplace<T extends LRUMap<K, V>, K extends string, V>(map: T, key: K, contents: V): V {\n const value = map.get(key);\n\n if (value === undefined) {\n map.set(key, contents);\n return contents;\n }\n\n return value;\n}\n\n/**\n * Determines if context lines should be skipped for a file.\n * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source\n * - node: prefixed modules are part of the runtime and cannot be resolved to a file\n * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports\n */\nfunction shouldSkipContextLinesForFile(path: string): boolean {\n // Test the most common prefix and extension first. These are the ones we\n // are most likely to see in user applications and are the ones we can break out of first.\n if (path.startsWith('node:')) return true;\n if (path.endsWith('.min.js')) return true;\n if (path.endsWith('.min.cjs')) return true;\n if (path.endsWith('.min.mjs')) return true;\n if (path.startsWith('data:')) return true;\n return false;\n}\n\n/**\n * Determines if we should skip contextlines based off the max lineno and colno values.\n */\nfunction shouldSkipContextLinesForFrame(frame: StackFrame): boolean {\n if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;\n if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true;\n return false;\n}\n/**\n * Checks if we have all the contents that we need in the cache.\n */\nfunction rangeExistsInContentCache(file: string, range: ReadlineRange): boolean {\n const contents = LRU_FILE_CONTENTS_CACHE.get(file);\n if (contents === undefined) return false;\n\n for (let i = range[0]; i <= range[1]; i++) {\n if (contents[i] === undefined) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap,\n * the ranges are merged to create a single range.\n */\nfunction makeLineReaderRanges(lines: number[], linecontext: number): ReadlineRange[] {\n if (!lines.length) {\n return [];\n }\n\n let i = 0;\n const line = lines[0];\n\n if (typeof line !== 'number') {\n return [];\n }\n\n let current = makeContextRange(line, linecontext);\n const out: ReadlineRange[] = [];\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (i === lines.length - 1) {\n out.push(current);\n break;\n }\n\n // If the next line falls into the current range, extend the current range to lineno + linecontext.\n const next = lines[i + 1];\n if (typeof next !== 'number') {\n break;\n }\n if (next <= current[1]) {\n current[1] = next + linecontext;\n } else {\n out.push(current);\n current = makeContextRange(next, linecontext);\n }\n\n i++;\n }\n\n return out;\n}\n\n/**\n * Extracts lines from a file and stores them in a cache.\n */\nfunction getContextLinesFromFile(path: string, ranges: ReadlineRange[], output: Record<number, string>): Promise<void> {\n return new Promise((resolve, _reject) => {\n // It is important *not* to have any async code between createInterface and the 'line' event listener\n // as it will cause the 'line' event to\n // be emitted before the listener is attached.\n const stream = createReadStream(path);\n const lineReaded = createInterface({\n input: stream,\n });\n\n // We need to explicitly destroy the stream to prevent memory leaks,\n // removing the listeners on the readline interface is not enough.\n // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892\n function destroyStreamAndResolve(): void {\n stream.destroy();\n resolve();\n }\n\n // Init at zero and increment at the start of the loop because lines are 1 indexed.\n let lineNumber = 0;\n let currentRangeIndex = 0;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging.\n destroyStreamAndResolve();\n return;\n }\n let rangeStart = range[0];\n let rangeEnd = range[1];\n\n // We use this inside Promise.all, so we need to resolve the promise even if there is an error\n // to prevent Promise.all from short circuiting the rest.\n function onStreamError(e: Error): void {\n // Mark file path as failed to read and prevent multiple read attempts.\n LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);\n DEBUG_BUILD && debug.error(`Failed to read file: ${path}. Error: ${e}`);\n lineReaded.close();\n lineReaded.removeAllListeners();\n destroyStreamAndResolve();\n }\n\n // We need to handle the error event to prevent the process from crashing in < Node 16\n // https://github.com/nodejs/node/pull/31603\n stream.on('error', onStreamError);\n lineReaded.on('error', onStreamError);\n lineReaded.on('close', destroyStreamAndResolve);\n\n lineReaded.on('line', line => {\n lineNumber++;\n if (lineNumber < rangeStart) return;\n\n // !Warning: This mutates the cache by storing the snipped line into the cache.\n output[lineNumber] = snipLine(line, 0);\n\n if (lineNumber >= rangeEnd) {\n if (currentRangeIndex === ranges.length - 1) {\n // We need to close the file stream and remove listeners, else the reader will continue to run our listener;\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n currentRangeIndex++;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // This should never happen as it means we have a bug in the context.\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n rangeStart = range[0];\n rangeEnd = range[1];\n }\n });\n });\n}\n\n/**\n * Adds surrounding (context) lines of the line that an exception occurred on to the event.\n * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in\n * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple\n * failing reads from happening.\n */\n/* eslint-disable complexity */\nasync function addSourceContext(event: Event, contextLines: number): Promise<Event> {\n // keep a lookup map of which files we've already enqueued to read,\n // so we don't enqueue the same file multiple times which would cause multiple i/o reads\n const filesToLines: Record<string, number[]> = {};\n\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (!exception.stacktrace?.frames?.length) {\n continue;\n }\n\n // Maps preserve insertion order, so we iterate in reverse, starting at the\n // outermost frame and closer to where the exception has occurred (poor mans priority)\n for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {\n const frame: StackFrame | undefined = exception.stacktrace.frames[i];\n const filename = frame?.filename;\n\n if (\n !frame ||\n typeof filename !== 'string' ||\n typeof frame.lineno !== 'number' ||\n shouldSkipContextLinesForFile(filename) ||\n shouldSkipContextLinesForFrame(frame)\n ) {\n continue;\n }\n\n const filesToLinesOutput = filesToLines[filename];\n if (!filesToLinesOutput) filesToLines[filename] = [];\n // @ts-expect-error this is defined above\n filesToLines[filename].push(frame.lineno);\n }\n }\n }\n\n const files = Object.keys(filesToLines);\n if (files.length == 0) {\n return event;\n }\n\n const readlinePromises: Promise<void>[] = [];\n for (const file of files) {\n // If we failed to read this before, dont try reading it again.\n if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) {\n continue;\n }\n\n const filesToLineRanges = filesToLines[file];\n if (!filesToLineRanges) {\n continue;\n }\n\n // Sort ranges so that they are sorted by line increasing order and match how the file is read.\n filesToLineRanges.sort((a, b) => a - b);\n // Check if the contents are already in the cache and if we can avoid reading the file again.\n const ranges = makeLineReaderRanges(filesToLineRanges, contextLines);\n if (ranges.every(r => rangeExistsInContentCache(file, r))) {\n continue;\n }\n\n const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});\n readlinePromises.push(getContextLinesFromFile(file, ranges, cache));\n }\n\n // The promise rejections are caught in order to prevent them from short circuiting Promise.all\n await Promise.all(readlinePromises).catch(() => {\n DEBUG_BUILD && debug.log('Failed to read one or more source files and resolve context lines');\n });\n\n // Perform the same loop as above, but this time we can assume all files are in the cache\n // and attempt to add source context to frames.\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (exception.stacktrace?.frames && exception.stacktrace.frames.length > 0) {\n addSourceContextToFrames(exception.stacktrace.frames, contextLines, LRU_FILE_CONTENTS_CACHE);\n }\n }\n }\n\n return event;\n}\n/* eslint-enable complexity */\n\n/** Adds context lines to frames */\nfunction addSourceContextToFrames(\n frames: StackFrame[],\n contextLines: number,\n cache: LRUMap<string, Record<number, string>>,\n): void {\n for (const frame of frames) {\n // Only add context if we have a filename and it hasn't already been added\n if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') {\n const contents = cache.get(frame.filename);\n if (contents === undefined) {\n continue;\n }\n\n addContextToFrame(frame.lineno, frame, contextLines, contents);\n }\n }\n}\n\n/**\n * Clears the context lines from a frame, used to reset a frame to its original state\n * if we fail to resolve all context lines for it.\n */\nfunction clearLineContext(frame: StackFrame): void {\n delete frame.pre_context;\n delete frame.context_line;\n delete frame.post_context;\n}\n\n/**\n * Resolves context lines before and after the given line number and appends them to the frame;\n */\nexport function addContextToFrame(\n lineno: number,\n frame: StackFrame,\n contextLines: number,\n contents: Record<number, string> | undefined,\n): void {\n // When there is no line number in the frame, attaching context is nonsensical and will even break grouping.\n // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again.\n if (frame.lineno === undefined || contents === undefined) {\n DEBUG_BUILD && debug.error('Cannot resolve context for frame with no lineno or file contents');\n return;\n }\n\n frame.pre_context = [];\n for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) {\n // We always expect the start context as line numbers cannot be negative. If we dont find a line, then\n // something went wrong somewhere. Clear the context and return without adding any linecontext.\n const line = contents[i];\n if (line === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${i} in file ${frame.filename}`);\n return;\n }\n\n frame.pre_context.push(line);\n }\n\n // We should always have the context line. If we dont, something went wrong, so we clear the context and return\n // without adding any linecontext.\n if (contents[lineno] === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${lineno} in file ${frame.filename}`);\n return;\n }\n\n frame.context_line = contents[lineno];\n\n const end = makeRangeEnd(lineno, contextLines);\n frame.post_context = [];\n for (let i = lineno + 1; i <= end; i++) {\n // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could\n // just be that we reached the end of the file.\n const line = contents[i];\n if (line === undefined) {\n break;\n }\n frame.post_context.push(line);\n }\n}\n\n// Helper functions for generating line context ranges. They take a line number and the number of lines of context to\n// include before and after the line and generate an inclusive range of indices.\ntype ReadlineRange = [start: number, end: number];\n// Compute inclusive end context range\nfunction makeRangeStart(line: number, linecontext: number): number {\n return Math.max(1, line - linecontext);\n}\n// Compute inclusive start context range\nfunction makeRangeEnd(line: number, linecontext: number): number {\n return line + linecontext;\n}\n// Determine start and end indices for context range (inclusive);\nfunction makeContextRange(line: number, linecontext: number): [start: number, end: number] {\n return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)];\n}\n\n/** Exported only for tests, as a type-safe variant. */\nexport const _contextLinesIntegration = ((options: ContextLinesOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n processEvent(event, _hint, client) {\n const contextLines =\n options.frameContextLines ?? client?.getDataCollectionOptions().frameContextLines ?? DEFAULT_LINES_OF_CONTEXT;\n return addSourceContext(event, contextLines);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture the lines before and after the frame's context.\n */\nexport const contextLinesIntegration = defineIntegration(_contextLinesIntegration);\n"],"names":["LRUMap","createReadStream","createInterface","DEBUG_BUILD","debug","snipLine","range","defineIntegration"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"contextlines.js","sources":["../../../src/integrations/contextlines.ts"],"sourcesContent":["import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Event, IntegrationFn, StackFrame } from '@sentry/core';\nimport { debug, defineIntegration, LRUMap, snipLine } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\nconst LRU_FILE_CONTENTS_CACHE = new LRUMap<string, Record<number, string>>(10);\nconst LRU_FILE_CONTENTS_FS_READ_FAILED = new LRUMap<string, 1>(20);\nconst DEFAULT_LINES_OF_CONTEXT = 5;\nconst INTEGRATION_NAME = 'ContextLines' as const;\n// Determines the upper bound of lineno/colno that we will attempt to read. Large colno values are likely to be\n// minified code while large lineno values are likely to be bundled code.\n// Exported for testing purposes.\nexport const MAX_CONTEXTLINES_COLNO: number = 1000;\nexport const MAX_CONTEXTLINES_LINENO: number = 10000;\n\ninterface ContextLinesOptions {\n /**\n * Sets the number of context lines for each frame when loading a file.\n * Defaults to `dataCollection.frameContextLines` (5).\n *\n * Set to 0 to disable loading and inclusion of source files.\n *\n * When set, this option takes precedence over `dataCollection.frameContextLines`.\n **/\n frameContextLines?: number;\n}\n\n/**\n * Exported for testing purposes.\n */\nexport function resetFileContentCache(): void {\n LRU_FILE_CONTENTS_CACHE.clear();\n}\n\n/**\n * Get or init map value\n */\nfunction emplace<T extends LRUMap<K, V>, K extends string, V>(map: T, key: K, contents: V): V {\n const value = map.get(key);\n\n if (value === undefined) {\n map.set(key, contents);\n return contents;\n }\n\n return value;\n}\n\n/**\n * Determines if context lines should be skipped for a file.\n * - .min.(mjs|cjs|js) files are and not useful since they dont point to the original source\n * - node: prefixed modules are part of the runtime and cannot be resolved to a file\n * - data: skip json, wasm and inline js https://nodejs.org/api/esm.html#data-imports\n */\nfunction shouldSkipContextLinesForFile(path: string): boolean {\n // Test the most common prefix and extension first. These are the ones we\n // are most likely to see in user applications and are the ones we can break out of first.\n if (path.startsWith('node:')) return true;\n if (path.endsWith('.min.js')) return true;\n if (path.endsWith('.min.cjs')) return true;\n if (path.endsWith('.min.mjs')) return true;\n if (path.startsWith('data:')) return true;\n return false;\n}\n\n/**\n * Determines if we should skip contextlines based off the max lineno and colno values.\n */\nfunction shouldSkipContextLinesForFrame(frame: StackFrame): boolean {\n if (frame.lineno !== undefined && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;\n if (frame.colno !== undefined && frame.colno > MAX_CONTEXTLINES_COLNO) return true;\n return false;\n}\n/**\n * Checks if we have all the contents that we need in the cache.\n */\nfunction rangeExistsInContentCache(file: string, range: ReadlineRange): boolean {\n const contents = LRU_FILE_CONTENTS_CACHE.get(file);\n if (contents === undefined) return false;\n\n for (let i = range[0]; i <= range[1]; i++) {\n if (contents[i] === undefined) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Creates contiguous ranges of lines to read from a file. In the case where context lines overlap,\n * the ranges are merged to create a single range.\n */\nfunction makeLineReaderRanges(lines: number[], linecontext: number): ReadlineRange[] {\n if (!lines.length) {\n return [];\n }\n\n let i = 0;\n const line = lines[0];\n\n if (typeof line !== 'number') {\n return [];\n }\n\n let current = makeContextRange(line, linecontext);\n const out: ReadlineRange[] = [];\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (i === lines.length - 1) {\n out.push(current);\n break;\n }\n\n // If the next line falls into the current range, extend the current range to lineno + linecontext.\n const next = lines[i + 1];\n if (typeof next !== 'number') {\n break;\n }\n if (next <= current[1]) {\n current[1] = next + linecontext;\n } else {\n out.push(current);\n current = makeContextRange(next, linecontext);\n }\n\n i++;\n }\n\n return out;\n}\n\n/**\n * Extracts lines from a file and stores them in a cache.\n */\nfunction getContextLinesFromFile(path: string, ranges: ReadlineRange[], output: Record<number, string>): Promise<void> {\n return new Promise((resolve, _reject) => {\n // It is important *not* to have any async code between createInterface and the 'line' event listener\n // as it will cause the 'line' event to\n // be emitted before the listener is attached.\n const stream = createReadStream(path);\n const lineReaded = createInterface({\n input: stream,\n });\n\n // We need to explicitly destroy the stream to prevent memory leaks,\n // removing the listeners on the readline interface is not enough.\n // See: https://github.com/nodejs/node/issues/9002 and https://github.com/getsentry/sentry-javascript/issues/14892\n function destroyStreamAndResolve(): void {\n stream.destroy();\n resolve();\n }\n\n // Init at zero and increment at the start of the loop because lines are 1 indexed.\n let lineNumber = 0;\n let currentRangeIndex = 0;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // We should never reach this point, but if we do, we should resolve the promise to prevent it from hanging.\n destroyStreamAndResolve();\n return;\n }\n let rangeStart = range[0];\n let rangeEnd = range[1];\n\n // We use this inside Promise.all, so we need to resolve the promise even if there is an error\n // to prevent Promise.all from short circuiting the rest.\n function onStreamError(e: Error): void {\n // Mark file path as failed to read and prevent multiple read attempts.\n LRU_FILE_CONTENTS_FS_READ_FAILED.set(path, 1);\n DEBUG_BUILD && debug.error(`Failed to read file: ${path}. Error: ${e}`);\n lineReaded.close();\n lineReaded.removeAllListeners();\n destroyStreamAndResolve();\n }\n\n // We need to handle the error event to prevent the process from crashing in < Node 16\n // https://github.com/nodejs/node/pull/31603\n stream.on('error', onStreamError);\n lineReaded.on('error', onStreamError);\n lineReaded.on('close', destroyStreamAndResolve);\n\n lineReaded.on('line', line => {\n lineNumber++;\n if (lineNumber < rangeStart) return;\n\n // !Warning: This mutates the cache by storing the snipped line into the cache.\n output[lineNumber] = snipLine(line, 0);\n\n if (lineNumber >= rangeEnd) {\n if (currentRangeIndex === ranges.length - 1) {\n // We need to close the file stream and remove listeners, else the reader will continue to run our listener;\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n currentRangeIndex++;\n const range = ranges[currentRangeIndex];\n if (range === undefined) {\n // This should never happen as it means we have a bug in the context.\n lineReaded.close();\n lineReaded.removeAllListeners();\n return;\n }\n rangeStart = range[0];\n rangeEnd = range[1];\n }\n });\n });\n}\n\n/**\n * Adds surrounding (context) lines of the line that an exception occurred on to the event.\n * This is done by reading the file line by line and extracting the lines. The extracted lines are stored in\n * a cache to prevent multiple reads of the same file. Failures to read a file are similarly cached to prevent multiple\n * failing reads from happening.\n */\n/* eslint-disable complexity */\nasync function addSourceContext(event: Event, contextLines: number): Promise<Event> {\n // keep a lookup map of which files we've already enqueued to read,\n // so we don't enqueue the same file multiple times which would cause multiple i/o reads\n const filesToLines: Record<string, number[]> = {};\n\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (!exception.stacktrace?.frames?.length) {\n continue;\n }\n\n // Maps preserve insertion order, so we iterate in reverse, starting at the\n // outermost frame and closer to where the exception has occurred (poor mans priority)\n for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {\n const frame: StackFrame | undefined = exception.stacktrace.frames[i];\n const filename = frame?.filename;\n\n if (\n !frame ||\n typeof filename !== 'string' ||\n typeof frame.lineno !== 'number' ||\n shouldSkipContextLinesForFile(filename) ||\n shouldSkipContextLinesForFrame(frame)\n ) {\n continue;\n }\n\n const filesToLinesOutput = filesToLines[filename];\n if (!filesToLinesOutput) filesToLines[filename] = [];\n // @ts-expect-error this is defined above\n filesToLines[filename].push(frame.lineno);\n }\n }\n }\n\n const files = Object.keys(filesToLines);\n if (files.length == 0) {\n return event;\n }\n\n const readlinePromises: Promise<void>[] = [];\n for (const file of files) {\n // If we failed to read this before, dont try reading it again.\n if (LRU_FILE_CONTENTS_FS_READ_FAILED.get(file)) {\n continue;\n }\n\n const filesToLineRanges = filesToLines[file];\n if (!filesToLineRanges) {\n continue;\n }\n\n // Sort ranges so that they are sorted by line increasing order and match how the file is read.\n filesToLineRanges.sort((a, b) => a - b);\n // Check if the contents are already in the cache and if we can avoid reading the file again.\n const ranges = makeLineReaderRanges(filesToLineRanges, contextLines);\n if (ranges.every(r => rangeExistsInContentCache(file, r))) {\n continue;\n }\n\n const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});\n readlinePromises.push(getContextLinesFromFile(file, ranges, cache));\n }\n\n // The promise rejections are caught in order to prevent them from short circuiting Promise.all\n await Promise.all(readlinePromises).catch(() => {\n DEBUG_BUILD && debug.log('Failed to read one or more source files and resolve context lines');\n });\n\n // Perform the same loop as above, but this time we can assume all files are in the cache\n // and attempt to add source context to frames.\n if (contextLines > 0 && event.exception?.values) {\n for (const exception of event.exception.values) {\n if (exception.stacktrace?.frames && exception.stacktrace.frames.length > 0) {\n addSourceContextToFrames(exception.stacktrace.frames, contextLines, LRU_FILE_CONTENTS_CACHE);\n }\n }\n }\n\n return event;\n}\n/* eslint-enable complexity */\n\n/** Adds context lines to frames */\nfunction addSourceContextToFrames(\n frames: StackFrame[],\n contextLines: number,\n cache: LRUMap<string, Record<number, string>>,\n): void {\n for (const frame of frames) {\n // Only add context if we have a filename and it hasn't already been added\n if (frame.filename && frame.context_line === undefined && typeof frame.lineno === 'number') {\n const contents = cache.get(frame.filename);\n if (contents === undefined) {\n continue;\n }\n\n addContextToFrame(frame.lineno, frame, contextLines, contents);\n }\n }\n}\n\n/**\n * Clears the context lines from a frame, used to reset a frame to its original state\n * if we fail to resolve all context lines for it.\n */\nfunction clearLineContext(frame: StackFrame): void {\n delete frame.pre_context;\n delete frame.context_line;\n delete frame.post_context;\n}\n\n/**\n * Resolves context lines before and after the given line number and appends them to the frame;\n */\nexport function addContextToFrame(\n lineno: number,\n frame: StackFrame,\n contextLines: number,\n contents: Record<number, string> | undefined,\n): void {\n // When there is no line number in the frame, attaching context is nonsensical and will even break grouping.\n // We already check for lineno before calling this, but since StackFrame lineno ism optional, we check it again.\n if (frame.lineno === undefined || contents === undefined) {\n DEBUG_BUILD && debug.error('Cannot resolve context for frame with no lineno or file contents');\n return;\n }\n\n frame.pre_context = [];\n for (let i = makeRangeStart(lineno, contextLines); i < lineno; i++) {\n // We always expect the start context as line numbers cannot be negative. If we dont find a line, then\n // something went wrong somewhere. Clear the context and return without adding any linecontext.\n const line = contents[i];\n if (line === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${i} in file ${frame.filename}`);\n return;\n }\n\n frame.pre_context.push(line);\n }\n\n // We should always have the context line. If we dont, something went wrong, so we clear the context and return\n // without adding any linecontext.\n if (contents[lineno] === undefined) {\n clearLineContext(frame);\n DEBUG_BUILD && debug.error(`Could not find line ${lineno} in file ${frame.filename}`);\n return;\n }\n\n frame.context_line = contents[lineno];\n\n const end = makeRangeEnd(lineno, contextLines);\n frame.post_context = [];\n for (let i = lineno + 1; i <= end; i++) {\n // Since we dont track when the file ends, we cant clear the context if we dont find a line as it could\n // just be that we reached the end of the file.\n const line = contents[i];\n if (line === undefined) {\n break;\n }\n frame.post_context.push(line);\n }\n}\n\n// Helper functions for generating line context ranges. They take a line number and the number of lines of context to\n// include before and after the line and generate an inclusive range of indices.\ntype ReadlineRange = [start: number, end: number];\n// Compute inclusive end context range\nfunction makeRangeStart(line: number, linecontext: number): number {\n return Math.max(1, line - linecontext);\n}\n// Compute inclusive start context range\nfunction makeRangeEnd(line: number, linecontext: number): number {\n return line + linecontext;\n}\n// Determine start and end indices for context range (inclusive);\nfunction makeContextRange(line: number, linecontext: number): [start: number, end: number] {\n return [makeRangeStart(line, linecontext), makeRangeEnd(line, linecontext)];\n}\n\n/** Exported only for tests, as a type-safe variant. */\nexport const _contextLinesIntegration = ((options: ContextLinesOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n processEvent(event, _hint, client) {\n const contextLines =\n options.frameContextLines ?? client?.getDataCollectionOptions().frameContextLines ?? DEFAULT_LINES_OF_CONTEXT;\n return addSourceContext(event, contextLines);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Capture the lines before and after the frame's context.\n */\nexport const contextLinesIntegration = defineIntegration(_contextLinesIntegration);\n"],"names":["LRUMap","createReadStream","createInterface","DEBUG_BUILD","debug","snipLine","range","defineIntegration"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ const core = require('@sentry/core');
|
|
|
5
5
|
const debug = require('../../utils/debug.js');
|
|
6
6
|
const common = require('./common.js');
|
|
7
7
|
|
|
8
|
-
const base64WorkerScript = "
|
|
8
|
+
const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZSAxMS4wLjAtcmMuMCAoYjY1MjA5NykgfCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0ICovCmltcG9ydHtTZXNzaW9uIGFzIGV9ZnJvbSJub2RlOmluc3BlY3Rvci9wcm9taXNlcyI7aW1wb3J0e3dvcmtlckRhdGEgYXMgdH1mcm9tIm5vZGU6d29ya2VyX3RocmVhZHMiO2NvbnN0IG49Z2xvYmFsVGhpcyxpPXt9O2NvbnN0IG89Il9fU0VOVFJZX0VSUk9SX0xPQ0FMX1ZBUklBQkxFU19fIjtjb25zdCBhPXQ7ZnVuY3Rpb24gcyguLi5lKXthLmRlYnVnJiZmdW5jdGlvbihlKXtpZighKCJjb25zb2xlImluIG4pKXJldHVybiBlKCk7Y29uc3QgdD1uLmNvbnNvbGUsbz17fSxhPU9iamVjdC5rZXlzKGkpO2EuZm9yRWFjaChlPT57Y29uc3Qgbj1pW2VdO29bZV09dFtlXSx0W2VdPW59KTt0cnl7cmV0dXJuIGUoKX1maW5hbGx5e2EuZm9yRWFjaChlPT57dFtlXT1vW2VdfSl9fSgoKT0+Y29uc29sZS5sb2coIltMb2NhbFZhcmlhYmxlcyBXb3JrZXJdIiwuLi5lKSl9YXN5bmMgZnVuY3Rpb24gYyhlLHQsbixpKXtjb25zdCBvPWF3YWl0IGUucG9zdCgiUnVudGltZS5nZXRQcm9wZXJ0aWVzIix7b2JqZWN0SWQ6dCxvd25Qcm9wZXJ0aWVzOiEwfSk7aVtuXT1vLnJlc3VsdC5maWx0ZXIoZT0+Imxlbmd0aCIhPT1lLm5hbWUmJiFpc05hTihwYXJzZUludChlLm5hbWUsMTApKSkuc29ydCgoZSx0KT0+cGFyc2VJbnQoZS5uYW1lLDEwKS1wYXJzZUludCh0Lm5hbWUsMTApKS5tYXAoZT0+ZS52YWx1ZT8udmFsdWUpfWFzeW5jIGZ1bmN0aW9uIHIoZSx0LG4saSl7Y29uc3Qgbz1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO2lbbl09by5yZXN1bHQubWFwKGU9PltlLm5hbWUsZS52YWx1ZT8udmFsdWVdKS5yZWR1Y2UoKGUsW3Qsbl0pPT4oZVt0XT1uLGUpLHt9KX1mdW5jdGlvbiB1KGUsdCl7ZS52YWx1ZSYmKCJ2YWx1ZSJpbiBlLnZhbHVlP3ZvaWQgMD09PWUudmFsdWUudmFsdWV8fG51bGw9PT1lLnZhbHVlLnZhbHVlP3RbZS5uYW1lXT1gPCR7ZS52YWx1ZS52YWx1ZX0+YDp0W2UubmFtZV09ZS52YWx1ZS52YWx1ZToiZGVzY3JpcHRpb24iaW4gZS52YWx1ZSYmImZ1bmN0aW9uIiE9PWUudmFsdWUudHlwZT90W2UubmFtZV09YDwke2UudmFsdWUuZGVzY3JpcHRpb259PmA6InVuZGVmaW5lZCI9PT1lLnZhbHVlLnR5cGUmJih0W2UubmFtZV09Ijx1bmRlZmluZWQ+IikpfWFzeW5jIGZ1bmN0aW9uIGwoZSx0KXtjb25zdCBuPWF3YWl0IGUucG9zdCgiUnVudGltZS5nZXRQcm9wZXJ0aWVzIix7b2JqZWN0SWQ6dCxvd25Qcm9wZXJ0aWVzOiEwfSksaT17fTtmb3IoY29uc3QgdCBvZiBuLnJlc3VsdClpZih0LnZhbHVlPy5vYmplY3RJZCYmIkFycmF5Ij09PXQudmFsdWUuY2xhc3NOYW1lKXtjb25zdCBuPXQudmFsdWUub2JqZWN0SWQ7YXdhaXQgYyhlLG4sdC5uYW1lLGkpfWVsc2UgaWYodC52YWx1ZT8ub2JqZWN0SWQmJiJPYmplY3QiPT09dC52YWx1ZS5jbGFzc05hbWUpe2NvbnN0IG49dC52YWx1ZS5vYmplY3RJZDthd2FpdCByKGUsbix0Lm5hbWUsaSl9ZWxzZSB0LnZhbHVlJiZ1KHQsaSk7cmV0dXJuIGl9bGV0IGY7KGFzeW5jIGZ1bmN0aW9uKCl7Y29uc3QgdD1uZXcgZTt0LmNvbm5lY3RUb01haW5UaHJlYWQoKSxzKCJDb25uZWN0ZWQgdG8gbWFpbiB0aHJlYWQiKTtsZXQgbj0hMTt0Lm9uKCJEZWJ1Z2dlci5yZXN1bWVkIiwoKT0+e249ITF9KSx0Lm9uKCJEZWJ1Z2dlci5wYXVzZWQiLGU9PntuPSEwLGFzeW5jIGZ1bmN0aW9uKGUse3JlYXNvbjp0LGRhdGE6e29iamVjdElkOm59LGNhbGxGcmFtZXM6aX0pe2lmKCJleGNlcHRpb24iIT09dCYmInByb21pc2VSZWplY3Rpb24iIT09dClyZXR1cm47aWYoZj8uKCksbnVsbD09bilyZXR1cm47Y29uc3QgYT1bXTtmb3IobGV0IHQ9MDt0PGkubGVuZ3RoO3QrKyl7Y29uc3R7c2NvcGVDaGFpbjpuLGZ1bmN0aW9uTmFtZTpvLHRoaXM6c309aVt0XSxjPW4uZmluZChlPT4ibG9jYWwiPT09ZS50eXBlKSxyPSJnbG9iYWwiIT09cy5jbGFzc05hbWUmJnMuY2xhc3NOYW1lP2Ake3MuY2xhc3NOYW1lfS4ke299YDpvO2lmKHZvaWQgMD09PWM/Lm9iamVjdC5vYmplY3RJZClhW3RdPXtmdW5jdGlvbjpyfTtlbHNle2NvbnN0IG49YXdhaXQgbChlLGMub2JqZWN0Lm9iamVjdElkKTthW3RdPXtmdW5jdGlvbjpyLHZhcnM6bn19fWF3YWl0IGUucG9zdCgiUnVudGltZS5jYWxsRnVuY3Rpb25PbiIse2Z1bmN0aW9uRGVjbGFyYXRpb246YGZ1bmN0aW9uKCkgeyB0aGlzLiR7b30gPSB0aGlzLiR7b30gfHwgJHtKU09OLnN0cmluZ2lmeShhKX07IH1gLHNpbGVudDohMCxvYmplY3RJZDpufSksYXdhaXQgZS5wb3N0KCJSdW50aW1lLnJlbGVhc2VPYmplY3QiLHtvYmplY3RJZDpufSl9KHQsZS5wYXJhbXMpLnRoZW4oYXN5bmMoKT0+e24mJmF3YWl0IHQucG9zdCgiRGVidWdnZXIucmVzdW1lIil9LGFzeW5jIGU9PntuJiZhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpfSl9KSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLmVuYWJsZSIpO2NvbnN0IGk9ITEhPT1hLmNhcHR1cmVBbGxFeGNlcHRpb25zO2lmKGF3YWl0IHQucG9zdCgiRGVidWdnZXIuc2V0UGF1c2VPbkV4Y2VwdGlvbnMiLHtzdGF0ZTppPyJhbGwiOiJ1bmNhdWdodCJ9KSxpKXtjb25zdCBlPWEubWF4RXhjZXB0aW9uc1BlclNlY29uZHx8NTA7Zj1mdW5jdGlvbihlLHQsbil7bGV0IGk9MCxvPTUsYT0wO3JldHVybiBzZXRJbnRlcnZhbCgoKT0+ezA9PT1hP2k+ZSYmKG8qPTIsbihvKSxvPjg2NDAwJiYobz04NjQwMCksYT1vKTooYS09MSwwPT09YSYmdCgpKSxpPTB9LDFlMykudW5yZWYoKSwoKT0+e2krPTF9fShlLGFzeW5jKCk9PntzKCJSYXRlLWxpbWl0IGxpZnRlZC4iKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6ImFsbCJ9KX0sYXN5bmMgZT0+e3MoYFJhdGUtbGltaXQgZXhjZWVkZWQuIERpc2FibGluZyBjYXB0dXJpbmcgb2YgY2F1Z2h0IGV4Y2VwdGlvbnMgZm9yICR7ZX0gc2Vjb25kcy5gKSxhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6InVuY2F1Z2h0In0pfSl9fSkoKS5jYXRjaChlPT57cygiRmFpbGVkIHRvIHN0YXJ0IGRlYnVnZ2VyIixlKX0pLHNldEludGVydmFsKCgpPT57fSwxZTQpOw==";
|
|
9
9
|
function log(...args) {
|
|
10
10
|
core.debug.log("[LocalVariables]", ...args);
|
|
11
11
|
}
|
|
@@ -9,14 +9,15 @@ const op = require('@sentry/conventions/op');
|
|
|
9
9
|
const debugBuild = require('../../debug-build.js');
|
|
10
10
|
|
|
11
11
|
const ATTR_HTTP_REQUEST_METHOD_ORIGINAL = "http.request.method_original";
|
|
12
|
-
|
|
12
|
+
let _isInstrumented = false;
|
|
13
13
|
const spanFromReq = /* @__PURE__ */ new WeakMap();
|
|
14
14
|
const ignoreRequestMap = /* @__PURE__ */ new WeakMap();
|
|
15
15
|
const propagationDecisionMap = new core.LRUMap(100);
|
|
16
16
|
function instrumentUndici(config = {}) {
|
|
17
|
-
if (
|
|
17
|
+
if (_isInstrumented) {
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
+
_isInstrumented = true;
|
|
20
21
|
subscribeToChannel("undici:request:create", (message) => onRequestCreated(config, message));
|
|
21
22
|
subscribeToChannel(
|
|
22
23
|
"undici:client:sendHeaders",
|
|
@@ -27,7 +28,7 @@ function instrumentUndici(config = {}) {
|
|
|
27
28
|
subscribeToChannel("undici:request:error", (message) => onError(message));
|
|
28
29
|
}
|
|
29
30
|
function subscribeToChannel(diagnosticChannel, onMessage) {
|
|
30
|
-
|
|
31
|
+
diagch.subscribe?.(diagnosticChannel, onMessage);
|
|
31
32
|
}
|
|
32
33
|
function parseRequestHeaders(request) {
|
|
33
34
|
const result = /* @__PURE__ */ new Map();
|