@sentry/node 11.0.0 → 11.2.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/README.md +7 -80
- package/build/cjs/index.js +5 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/integrations/anr/index.js +2 -1
- package/build/cjs/integrations/anr/index.js.map +1 -1
- package/build/cjs/integrations/childProcess.js +2 -2
- package/build/cjs/integrations/childProcess.js.map +1 -1
- package/build/cjs/integrations/http/SentryHttpInstrumentation.js +2 -2
- package/build/cjs/integrations/http/SentryHttpInstrumentation.js.map +1 -1
- package/build/cjs/integrations/http/httpServerIntegration.js +2 -2
- package/build/cjs/integrations/http/httpServerIntegration.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 +2 -2
- package/build/cjs/integrations/node-fetch/undici-instrumentation.js.map +1 -1
- package/build/cjs/integrations/pino.js +2 -2
- package/build/cjs/integrations/pino.js.map +1 -1
- package/build/cjs/integrations/workerThreads.js +2 -2
- package/build/cjs/integrations/workerThreads.js.map +1 -1
- package/build/cjs/sdk/client.js +7 -0
- package/build/cjs/sdk/client.js.map +1 -1
- package/build/cjs/sdk/index.js +1 -0
- package/build/cjs/sdk/index.js.map +1 -1
- package/build/esm/index.js +1 -1
- package/build/esm/integrations/anr/index.js +2 -1
- package/build/esm/integrations/anr/index.js.map +1 -1
- package/build/esm/integrations/anr/worker-script.js +2 -2
- package/build/esm/integrations/childProcess.js +2 -2
- package/build/esm/integrations/childProcess.js.map +1 -1
- package/build/esm/integrations/http/SentryHttpInstrumentation.js +2 -2
- package/build/esm/integrations/http/SentryHttpInstrumentation.js.map +1 -1
- package/build/esm/integrations/http/httpServerIntegration.js +2 -2
- package/build/esm/integrations/http/httpServerIntegration.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 +2 -2
- package/build/esm/integrations/node-fetch/undici-instrumentation.js.map +1 -1
- package/build/esm/integrations/pino.js +2 -2
- package/build/esm/integrations/pino.js.map +1 -1
- package/build/esm/integrations/workerThreads.js +2 -2
- package/build/esm/integrations/workerThreads.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/sdk/client.js +8 -1
- package/build/esm/sdk/client.js.map +1 -1
- package/build/esm/sdk/index.js +2 -1
- package/build/esm/sdk/index.js.map +1 -1
- package/build/types/index.d.ts +2 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/integrations/anr/index.d.ts.map +1 -1
- package/build/types/integrations/http/SentryHttpInstrumentation.d.ts.map +1 -1
- package/build/types/integrations/http/httpServerIntegration.d.ts.map +1 -1
- package/build/types/integrations/pino.d.ts.map +1 -1
- package/build/types/sdk/client.d.ts +1 -0
- package/build/types/sdk/client.d.ts.map +1 -1
- package/build/types/sdk/index.d.ts.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -10,87 +10,14 @@
|
|
|
10
10
|
[](https://www.npmjs.com/package/@sentry/node)
|
|
11
11
|
[](https://www.npmjs.com/package/@sentry/node)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The official Sentry SDK for monitoring Node.js applications.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
npm install @sentry/node
|
|
15
|
+
## Documentation
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
```
|
|
17
|
+
- [Getting started](https://docs.sentry.io/platforms/javascript/guides/node/)
|
|
18
|
+
- [Configuration](https://docs.sentry.io/platforms/javascript/guides/node/configuration/)
|
|
21
19
|
|
|
22
|
-
##
|
|
20
|
+
## Support
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
You need to create a file named `instrument.js` that imports and initializes Sentry:
|
|
28
|
-
|
|
29
|
-
```js
|
|
30
|
-
// CJS Syntax
|
|
31
|
-
const Sentry = require('@sentry/node');
|
|
32
|
-
// ESM Syntax
|
|
33
|
-
import * as Sentry from '@sentry/node';
|
|
34
|
-
|
|
35
|
-
Sentry.init({
|
|
36
|
-
dsn: '__DSN__',
|
|
37
|
-
// ...
|
|
38
|
-
});
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
You need to require or import the `instrument.js` file before importing any other modules in your application. This is
|
|
42
|
-
necessary to ensure that Sentry can automatically instrument all modules in your application:
|
|
43
|
-
|
|
44
|
-
```js
|
|
45
|
-
// Import this first!
|
|
46
|
-
import './instrument';
|
|
47
|
-
|
|
48
|
-
// Now import other modules
|
|
49
|
-
import http from 'http';
|
|
50
|
-
|
|
51
|
-
// Your application code goes here
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### ESM Support
|
|
55
|
-
|
|
56
|
-
When running your application in ESM mode, you should use the Node.js
|
|
57
|
-
[`--import`](https://nodejs.org/api/cli.html#--importmodule) command line option to ensure that Sentry is loaded before
|
|
58
|
-
the application code is evaluated.
|
|
59
|
-
|
|
60
|
-
Adjust the Node.js call for your application to use the `--import` parameter and point it at `instrument.js`, which
|
|
61
|
-
contains your `Sentry.init`() code:
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
# Note: This is only available for Node v18.19.0 onwards.
|
|
65
|
-
node --import ./instrument.mjs app.mjs
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
If it is not possible for you to pass the `--import` flag to the Node.js binary, you can alternatively use the
|
|
69
|
-
`NODE_OPTIONS` environment variable as follows:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
NODE_OPTIONS="--import ./instrument.mjs" npm run start
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Bundling your server
|
|
76
|
-
|
|
77
|
-
`@sentry/node` installs its automatic (diagnostics-channel) instrumentation through a runtime module
|
|
78
|
-
hook that ships in `@sentry/server-runtime-injection` and is designed to run from `node_modules`. There are two
|
|
79
|
-
supported ways to keep auto-instrumentation working when you bundle your server:
|
|
80
|
-
|
|
81
|
-
1. **Keep `@sentry/server-runtime-injection` external** (do not inline it into the bundle) so the runtime hook
|
|
82
|
-
loads from `node_modules`. Most bundlers externalize `node_modules` for a Node target by default;
|
|
83
|
-
if yours inlines everything, mark `@sentry/server-runtime-injection` as external explicitly.
|
|
84
|
-
2. **Instrument at build time** with the Sentry bundler plugins (`@sentry/node/esbuild`,
|
|
85
|
-
`@sentry/node/webpack`, `@sentry/node/vite`, `@sentry/node/rollup`), which inject the
|
|
86
|
-
instrumentation into your bundled dependencies during the build. In this mode the runtime hook is
|
|
87
|
-
not needed.
|
|
88
|
-
|
|
89
|
-
If you bundle `@sentry/server-runtime-injection` **and** don't use the build-time plugin, its internal code
|
|
90
|
-
transformer is stripped and runtime auto-instrumentation is disabled. `@sentry/node` warns the
|
|
91
|
-
first time an instrumented dependency loads uninstrumented, so a build-time-instrumented app never
|
|
92
|
-
sees the warning.
|
|
93
|
-
|
|
94
|
-
## Links
|
|
95
|
-
|
|
96
|
-
- [Official SDK Docs](https://docs.sentry.io/quickstart/)
|
|
22
|
+
- [Report a bug](https://github.com/getsentry/sentry-javascript/issues/new/choose)
|
|
23
|
+
- [Contributing](https://github.com/getsentry/sentry-javascript/blob/develop/CONTRIBUTING.md)
|
package/build/cjs/index.js
CHANGED
|
@@ -68,6 +68,8 @@ exports.googleGenAIIntegration = serverUtils.googleGenAIIntegration;
|
|
|
68
68
|
exports.graphqlIntegration = serverUtils.graphqlIntegration;
|
|
69
69
|
exports.groqIntegration = serverUtils.groqIntegration;
|
|
70
70
|
exports.hapiIntegration = serverUtils.hapiIntegration;
|
|
71
|
+
exports.honoIntegration = serverUtils.honoIntegration;
|
|
72
|
+
exports.honoMiddleware = serverUtils.honoMiddleware;
|
|
71
73
|
exports.instrumentAnthropicAiClient = serverUtils.instrumentAnthropicAiClient;
|
|
72
74
|
exports.instrumentGoogleGenAIClient = serverUtils.instrumentGoogleGenAIClient;
|
|
73
75
|
exports.instrumentLangChainEmbeddings = serverUtils.instrumentLangChainEmbeddings;
|
|
@@ -75,6 +77,7 @@ exports.instrumentMistralAiClient = serverUtils.instrumentMistralAiClient;
|
|
|
75
77
|
exports.instrumentOpenAiClient = serverUtils.instrumentOpenAiClient;
|
|
76
78
|
exports.instrumentStateGraph = serverUtils.instrumentStateGraph;
|
|
77
79
|
exports.instrumentStateGraphCompile = serverUtils.instrumentStateGraphCompile;
|
|
80
|
+
exports.instrumentTypeSafeClient = serverUtils.instrumentTypeSafeClient;
|
|
78
81
|
exports.kafkaIntegration = serverUtils.kafkaIntegration;
|
|
79
82
|
exports.knexIntegration = serverUtils.knexIntegration;
|
|
80
83
|
exports.koaIntegration = serverUtils.koaIntegration;
|
|
@@ -82,6 +85,7 @@ exports.langChainIntegration = serverUtils.langChainIntegration;
|
|
|
82
85
|
exports.langGraphIntegration = serverUtils.langGraphIntegration;
|
|
83
86
|
exports.lruMemoizerIntegration = serverUtils.lruMemoizerIntegration;
|
|
84
87
|
exports.mastraIntegration = serverUtils.mastraIntegration;
|
|
88
|
+
exports.mcpServerIntegration = serverUtils.mcpServerIntegration;
|
|
85
89
|
exports.mistralAIIntegration = serverUtils.mistralAIIntegration;
|
|
86
90
|
exports.mongoIntegration = serverUtils.mongoIntegration;
|
|
87
91
|
exports.mongooseIntegration = serverUtils.mongooseIntegration;
|
|
@@ -97,6 +101,7 @@ exports.setupExpressErrorHandler = serverUtils.setupExpressErrorHandler;
|
|
|
97
101
|
exports.setupFastifyErrorHandler = serverUtils.setupFastifyErrorHandler;
|
|
98
102
|
exports.tediousIntegration = serverUtils.tediousIntegration;
|
|
99
103
|
exports.togetherAIIntegration = serverUtils.togetherAIIntegration;
|
|
104
|
+
exports.typesafeIntegration = serverUtils.typesafeIntegration;
|
|
100
105
|
exports.vercelAIIntegration = serverUtils.vercelAIIntegration;
|
|
101
106
|
exports.setupHapiErrorHandler = hapi.setupHapiErrorHandler;
|
|
102
107
|
exports.setupKoaErrorHandler = koa.setupKoaErrorHandler;
|
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 = "LyohIEBzZW50cnkvbm9kZSAxMS4wLjAgKDNlMDJjODcpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyB0fWZyb20ibm9kZTppbnNwZWN0b3IiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIGUscGFyZW50UG9ydCBhcyBufWZyb20ibm9kZTp3b3JrZXJfdGhyZWFkcyI7aW1wb3J0e3Bvc2l4IGFzIHIsc2VwIGFzIG99ZnJvbSJub2RlOnBhdGgiO2ltcG9ydCphcyBzIGZyb20ibm9kZTpodHRwIjtpbXBvcnQqYXMgaSBmcm9tIm5vZGU6aHR0cHMiO2ltcG9ydHtSZWFkYWJsZSBhcyBjfWZyb20ibm9kZTpzdHJlYW0iO2ltcG9ydHtjcmVhdGVHemlwIGFzIHV9ZnJvbSJub2RlOnpsaWIiO2ltcG9ydCphcyBhIGZyb20ibm9kZTpuZXQiO2ltcG9ydCphcyBmIGZyb20ibm9kZTp0bHMiO2NvbnN0IGg9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLHA9Z2xvYmFsVGhpcyxsPSIxMS4wLjAiO2Z1bmN0aW9uIGQoKXtyZXR1cm4gZyhwKSxwfWZ1bmN0aW9uIGcodCl7Y29uc3QgZT10Ll9fU0VOVFJZX189dC5fX1NFTlRSWV9ffHx7fTtyZXR1cm4gZS52ZXJzaW9uPWUudmVyc2lvbnx8bCxlW2xdPWVbbF18fHt9fWZ1bmN0aW9uIG0odCxlLG49cCl7Y29uc3Qgcj1uLl9fU0VOVFJZX189bi5fX1NFTlRSWV9ffHx7fSxvPXJbbF09cltsXXx8e307cmV0dXJuIG9bdF18fChvW3RdPWUoKSl9Y29uc3QgeT17fTtmdW5jdGlvbiBiKHQpe2lmKCEoImNvbnNvbGUiaW4gcCkpcmV0dXJuIHQoKTtjb25zdCBlPXAuY29uc29sZSxuPXt9LHI9T2JqZWN0LmtleXMoeSk7ci5mb3JFYWNoKHQ9Pntjb25zdCByPXlbdF07blt0XT1lW3RdLGVbdF09cn0pO3RyeXtyZXR1cm4gdCgpfWZpbmFsbHl7ci5mb3JFYWNoKHQ9PntlW3RdPW5bdF19KX19ZnVuY3Rpb24gdigpe3JldHVybiBTKCkuZW5hYmxlZH1mdW5jdGlvbiB3KHQsLi4uZSl7aCYmdigpJiZiKCgpPT57cC5jb25zb2xlW3RdKGBTZW50cnkgTG9nZ2VyIFske3R9XTpgLC4uLmUpfSl9ZnVuY3Rpb24gUygpe3JldHVybiBoP20oImxvZ2dlclNldHRpbmdzIiwoKT0+KHtlbmFibGVkOiExfSkpOntlbmFibGVkOiExfX1jb25zdCBfPXtlbmFibGU6ZnVuY3Rpb24oKXtTKCkuZW5hYmxlZD0hMH0sZGlzYWJsZTpmdW5jdGlvbigpe1MoKS5lbmFibGVkPSExfSxpc0VuYWJsZWQ6dixsb2c6ZnVuY3Rpb24oLi4udCl7dygibG9nIiwuLi50KX0sd2FybjpmdW5jdGlvbiguLi50KXt3KCJ3YXJuIiwuLi50KX0sZXJyb3I6ZnVuY3Rpb24oLi4udCl7dygiZXJyb3IiLC4uLnQpfX0sJD0vY2FwdHVyZU1lc3NhZ2V8Y2FwdHVyZUV4Y2VwdGlvbi87ZnVuY3Rpb24gRSh0KXtyZXR1cm4gdFt0Lmxlbmd0aC0xXXx8e319Y29uc3QgeD0iPGFub255bW91cz4iO2NvbnN0IE49T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZztmdW5jdGlvbiBDKHQpe3JldHVybiBmdW5jdGlvbih0LGUpe3JldHVybiBOLmNhbGwodCk9PT1gW29iamVjdCAke2V9XWB9KHQsIk9iamVjdCIpfWZ1bmN0aW9uIGoodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gTyh0LGUpe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIGV9Y2F0Y2h7cmV0dXJuITF9fWZ1bmN0aW9uIFAodCl7aWYoZnVuY3Rpb24odCl7c3dpdGNoKE4uY2FsbCh0KSl7Y2FzZSJbb2JqZWN0IEVycm9yXSI6Y2FzZSJbb2JqZWN0IEV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBET01FeGNlcHRpb25dIjpjYXNlIltvYmplY3QgV2ViQXNzZW1ibHkuRXhjZXB0aW9uXSI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4gTyh0LEVycm9yKX19KHQpKXJldHVybnttZXNzYWdlOnQubWVzc2FnZSxuYW1lOnQubmFtZSxzdGFjazp0LnN0YWNrLC4uLmsodCl9O2lmKGU9dCwidW5kZWZpbmVkIiE9dHlwZW9mIEV2ZW50JiZPKGUsRXZlbnQpKXtjb25zdHt0eXBlOmUsdGFyZ2V0Om4sY3VycmVudFRhcmdldDpyLGRldGFpbDpvfT10O3JldHVybnt0eXBlOmUsdGFyZ2V0Om4sY3VycmVudFRhcmdldDpyLC4uLm8/e2RldGFpbDpvfTp7fSwuLi5rKHQpfX12YXIgZTtyZXR1cm4gdH1mdW5jdGlvbiBrKHQpe3JldHVybiJvYmplY3QiPT10eXBlb2YoZT10KSYmbnVsbCE9PWU/T2JqZWN0LmZyb21FbnRyaWVzKE9iamVjdC5lbnRyaWVzKHQpKTp7fTt2YXIgZX1sZXQgQTtmdW5jdGlvbiBUKHQpe2lmKHZvaWQgMCE9PUEpcmV0dXJuIEE/QSh0KTp0KCk7Y29uc3QgZT1TeW1ib2wuZm9yKCJfX1NFTlRSWV9TQUZFX1JBTkRPTV9JRF9XUkFQUEVSX18iKSxuPXA7cmV0dXJuIGUgaW4gbiYmImZ1bmN0aW9uIj09dHlwZW9mIG5bZV0/KEE9bltlXSxBKHQpKTooQT1udWxsLHQoKSl9ZnVuY3Rpb24gUigpe3JldHVybiBUKCgpPT5NYXRoLnJhbmRvbSgpKX1mdW5jdGlvbiBJKCl7cmV0dXJuIFQoKCk9PkRhdGUubm93KCkpfWNvbnN0IEQ9U3ltYm9sLmZvcigic2VudHJ5LnNraXBOb3JtYWxpemF0aW9uIiksVT1TeW1ib2wuZm9yKCJzZW50cnkub3ZlcnJpZGVOb3JtYWxpemF0aW9uRGVwdGgiKTtmdW5jdGlvbiBCKHQsZT0xMDAsbj0xLzApe3RyeXtyZXR1cm4gTCgiIix0LGUsbil9Y2F0Y2godCl7cmV0dXJue0VSUk9SOmAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHt0fSlgfX19ZnVuY3Rpb24gTCh0LGUsbj0xLzAscj0xLzAsbz1mdW5jdGlvbigpe2NvbnN0IHQ9bmV3IFdlYWtTZXQ7ZnVuY3Rpb24gZShlKXtyZXR1cm4hIXQuaGFzKGUpfHwodC5hZGQoZSksITEpfWZ1bmN0aW9uIG4oZSl7dC5kZWxldGUoZSl9cmV0dXJuW2Usbl19KCkpe2NvbnN0W3MsaV09bztpZihudWxsPT1lfHxbImJvb2xlYW4iLCJzdHJpbmciXS5pbmNsdWRlcyh0eXBlb2YgZSl8fCJudW1iZXIiPT10eXBlb2YgZSYmTnVtYmVyLmlzRmluaXRlKGUpKXJldHVybiBlO2NvbnN0IGM9ZnVuY3Rpb24odCxlKXt0cnl7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiBnbG9iYWwmJmU9PT1nbG9iYWwpcmV0dXJuIltHbG9iYWxdIjtpZigibnVtYmVyIj09dHlwZW9mIGUmJiFOdW1iZXIuaXNGaW5pdGUoZSkpcmV0dXJuYFske2V9XWA7aWYoImZ1bmN0aW9uIj09dHlwZW9mIGUpcmV0dXJuYFtGdW5jdGlvbjogJHtmdW5jdGlvbih0KXt0cnl7cmV0dXJuIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0JiZ0Lm5hbWV8fHh9Y2F0Y2h7cmV0dXJuIHh9fShlKX1dYDtpZigic3ltYm9sIj09dHlwZW9mIGUpcmV0dXJuYFske1N0cmluZyhlKX1dYDtpZigiYmlnaW50Ij09dHlwZW9mIGUpcmV0dXJuYFtCaWdJbnQ6ICR7U3RyaW5nKGUpfV1gO2NvbnN0IHQ9ZnVuY3Rpb24odCl7Y29uc3QgZT1PYmplY3QuZ2V0UHJvdG90eXBlT2YodCk7cmV0dXJuIGU/LmNvbnN0cnVjdG9yP2UuY29uc3RydWN0b3IubmFtZToibnVsbCBwcm90b3R5cGUifShlKTtyZXR1cm5gW29iamVjdCAke3R9XWB9Y2F0Y2godCl7cmV0dXJuYCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9fSgwLGUpO2lmKCFjLnN0YXJ0c1dpdGgoIltvYmplY3QgIikpcmV0dXJuIGM7aWYoZnVuY3Rpb24odCl7cmV0dXJuIEJvb2xlYW4odFtEXSl9KGUpKXJldHVybiBlO2NvbnN0IHU9ZnVuY3Rpb24odCl7Y29uc3QgZT10W1VdO3JldHVybiJudW1iZXIiPT10eXBlb2YgZT9lOnZvaWQgMH0oZSksYT12b2lkIDAhPT11P3U6bjtpZigwPT09YSlyZXR1cm4gYy5yZXBsYWNlKCJvYmplY3QgIiwiIik7aWYocyhlKSlyZXR1cm4iW0NpcmN1bGFyIH5dIjtjb25zdCBmPWU7aWYoZiYmImZ1bmN0aW9uIj09dHlwZW9mIGYudG9KU09OKXRyeXtyZXR1cm4gTCgiIixmLnRvSlNPTigpLGEtMSxyLG8pfWNhdGNoe31jb25zdCBoPUFycmF5LmlzQXJyYXkoZSk/W106e307bGV0IHA9MDtjb25zdCBsPVAoZSk7Zm9yKGNvbnN0IHQgaW4gbCl7aWYoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChsLHQpKWNvbnRpbnVlO2lmKHA+PXIpe2hbdF09IltNYXhQcm9wZXJ0aWVzIH5dIjticmVha31jb25zdCBlPWxbdF07aFt0XT1MKHQsZSxhLTEscixvKSxwKyt9cmV0dXJuIGkoZSksaH1mdW5jdGlvbiBNKHQsZSl7Y29uc3Qgbj1lLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC9bfFxce30oKVtcXV4kKyo/Ll0vZywiXFwkJiIpO2xldCByPXQ7dHJ5e3I9ZGVjb2RlVVJJKHQpfWNhdGNoe31yZXR1cm4gci5yZXBsYWNlKC9cXC9nLCIvIikucmVwbGFjZSgvd2VicGFjazpcLz8vZywiIikucmVwbGFjZShuZXcgUmVnRXhwKGAoZmlsZTovLyk/Lyoke259LypgLCJpZyIpLCJhcHA6Ly8vIil9ZnVuY3Rpb24geih0LGU9MCl7cmV0dXJuInN0cmluZyIhPXR5cGVvZiB0fHwwPT09ZXx8dC5sZW5ndGg8PWU/dDpgJHt0LnNsaWNlKDAsZSl9Li4uYH1sZXQgSjtmdW5jdGlvbiBXKHQ9ZnVuY3Rpb24oKXtjb25zdCB0PXA7cmV0dXJuIHQuY3J5cHRvfHx0Lm1zQ3J5cHRvfSgpKXt0cnl7aWYodD8ucmFuZG9tVVVJRClyZXR1cm4gVCgoKT0+dC5yYW5kb21VVUlEKCkpLnJlcGxhY2UoLy0vZywiIil9Y2F0Y2h7fXJldHVybiBKfHwoSj0iMTAwMDAwMDAxMDAwNDAwMDgwMDAxMDAwMDAwMDAwMDAiKSxKLnJlcGxhY2UoL1swMThdL2csdD0+KHReKDE2KlIoKSYxNSk+PnQvNCkudG9TdHJpbmcoMTYpKX1mdW5jdGlvbiBGKCl7cmV0dXJuIEkoKS8xZTN9bGV0IEc7ZnVuY3Rpb24gWSgpe3JldHVybihHPz8oRz1mdW5jdGlvbigpe2NvbnN0e3BlcmZvcm1hbmNlOnR9PXA7aWYoIXQ/Lm5vd3x8IXQudGltZU9yaWdpbilyZXR1cm4gRjtjb25zdCBlPXQudGltZU9yaWdpbjtyZXR1cm4oKT0+KGUrVCgoKT0+dC5ub3coKSkpLzFlM30oKSkpKCl9ZnVuY3Rpb24gSCh0KXtjb25zdCBlPVkoKSxuPXtzaWQ6VygpLGluaXQ6ITAsdGltZXN0YW1wOmUsc3RhcnRlZDplLGR1cmF0aW9uOjAsc3RhdHVzOiJvayIsZXJyb3JzOjAsaWdub3JlRHVyYXRpb246ITEsdG9KU09OOigpPT5mdW5jdGlvbih0KXtyZXR1cm57c2lkOmAke3Quc2lkfWAsaW5pdDp0LmluaXQsc3RhcnRlZDpuZXcgRGF0ZSgxZTMqdC5zdGFydGVkKS50b0lTT1N0cmluZygpLHRpbWVzdGFtcDpuZXcgRGF0ZSgxZTMqdC50aW1lc3RhbXApLnRvSVNPU3RyaW5nKCksc3RhdHVzOnQuc3RhdHVzLGVycm9yczp0LmVycm9ycyxkaWQ6Im51bWJlciI9PXR5cGVvZiB0LmRpZHx8InN0cmluZyI9PXR5cGVvZiB0LmRpZD9gJHt0LmRpZH1gOnZvaWQgMCxkdXJhdGlvbjp0LmR1cmF0aW9uLGFibm9ybWFsX21lY2hhbmlzbTp0LmFibm9ybWFsX21lY2hhbmlzbSxhdHRyczp7cmVsZWFzZTp0LnJlbGVhc2UsZW52aXJvbm1lbnQ6dC5lbnZpcm9ubWVudCxpcF9hZGRyZXNzOnQuaXBBZGRyZXNzLHVzZXJfYWdlbnQ6dC51c2VyQWdlbnR9fX0obil9O3JldHVybiB0JiZaKG4sdCksbn1mdW5jdGlvbiBaKHQsZT17fSl7aWYoZS51c2VyJiYoIXQuaXBBZGRyZXNzJiZlLnVzZXIuaXBfYWRkcmVzcyYmKHQuaXBBZGRyZXNzPWUudXNlci5pcF9hZGRyZXNzKSx0LmRpZHx8ZS5kaWR8fCh0LmRpZD1lLnVzZXIuaWR8fGUudXNlci5lbWFpbHx8ZS51c2VyLnVzZXJuYW1lKSksdC50aW1lc3RhbXA9ZS50aW1lc3RhbXB8fFkoKSxlLmFibm9ybWFsX21lY2hhbmlzbSYmKHQuYWJub3JtYWxfbWVjaGFuaXNtPWUuYWJub3JtYWxfbWVjaGFuaXNtKSxlLmlnbm9yZUR1cmF0aW9uJiYodC5pZ25vcmVEdXJhdGlvbj1lLmlnbm9yZUR1cmF0aW9uKSxlLnNpZCYmKHQuc2lkPTMyPT09ZS5zaWQubGVuZ3RoP2Uuc2lkOlcoKSksdm9pZCAwIT09ZS5pbml0JiYodC5pbml0PWUuaW5pdCksIXQuZGlkJiZlLmRpZCYmKHQuZGlkPWAke2UuZGlkfWApLCJudW1iZXIiPT10eXBlb2YgZS5zdGFydGVkJiYodC5zdGFydGVkPWUuc3RhcnRlZCksdC5pZ25vcmVEdXJhdGlvbil0LmR1cmF0aW9uPXZvaWQgMDtlbHNlIGlmKCJudW1iZXIiPT10eXBlb2YgZS5kdXJhdGlvbil0LmR1cmF0aW9uPWUuZHVyYXRpb247ZWxzZXtjb25zdCBlPXQudGltZXN0YW1wLXQuc3RhcnRlZDt0LmR1cmF0aW9uPWU+PTA/ZTowfWUucmVsZWFzZSYmKHQucmVsZWFzZT1lLnJlbGVhc2UpLGUuZW52aXJvbm1lbnQmJih0LmVudmlyb25tZW50PWUuZW52aXJvbm1lbnQpLCF0LmlwQWRkcmVzcyYmZS5pcEFkZHJlc3MmJih0LmlwQWRkcmVzcz1lLmlwQWRkcmVzcyksIXQudXNlckFnZW50JiZlLnVzZXJBZ2VudCYmKHQudXNlckFnZW50PWUudXNlckFnZW50KSwibnVtYmVyIj09dHlwZW9mIGUuZXJyb3JzJiYodC5lcnJvcnM9ZS5lcnJvcnMpLGUuc3RhdHVzJiYodC5zdGF0dXM9ZS5zdGF0dXMpfWZ1bmN0aW9uIHEodCxlLG49Mil7aWYoIWV8fCJvYmplY3QiIT10eXBlb2YgZXx8bjw9MClyZXR1cm4gZTtpZih0JiYwPT09T2JqZWN0LmtleXMoZSkubGVuZ3RoKXJldHVybiB0O2NvbnN0IHI9ey4uLnR9O2Zvcihjb25zdCB0IGluIGUpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGUsdCkmJihyW3RdPXEoclt0XSxlW3RdLG4tMSkpO3JldHVybiByfWNsYXNzIEt7Y29uc3RydWN0b3IoKXt0aGlzLnQ9ITEsdGhpcy5vPVtdLHRoaXMuaT1bXSx0aGlzLnU9W10sdGhpcy5oPVtdLHRoaXMucD17fSx0aGlzLmw9e30sdGhpcy5tPXt9LHRoaXMudj17fSx0aGlzLlM9e30sdGhpcy5fPXt9LGZ1bmN0aW9uKHQsZSxuKXt0cnl7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsZSx7dmFsdWU6bix3cml0YWJsZTohMCxjb25maWd1cmFibGU6ITB9KX1jYXRjaHtoJiZfLmxvZyhgRmFpbGVkIHRvIGFkZCBub24tZW51bWVyYWJsZSBwcm9wZXJ0eSAiJHtTdHJpbmcoZSl9IiB0byBvYmplY3RgLHQpfX0odGhpcywicmVmcyIse30pLHRoaXMuTj17dHJhY2VJZDpXKCksc2FtcGxlUmFuZDpSKCl9fWNsb25lKCl7Y29uc3QgdD1uZXcgSztyZXR1cm4gdC51PVsuLi50aGlzLnVdLHQubD17Li4udGhpcy5sfSx0Lm09ey4uLnRoaXMubX0sdC52PXsuLi50aGlzLnZ9LHQuUz17Li4udGhpcy5TfSx0aGlzLlMuZmxhZ3MmJih0LlMuZmxhZ3M9e3ZhbHVlczpbLi4udGhpcy5TLmZsYWdzLnZhbHVlc119KSx0LnA9dGhpcy5wLHQuQz10aGlzLkMsdC5qPXRoaXMuaix0Lk89dGhpcy5PLHQuUD10aGlzLlAsdC5pPVsuLi50aGlzLmldLHQuaD1bLi4udGhpcy5oXSx0Ll89ey4uLnRoaXMuX30sdC5OPXsuLi50aGlzLk59LHQuaz10aGlzLmssdC5BPXRoaXMuQSx0LlQ9dGhpcy5ULHQucmVmcz17Li4udGhpcy5yZWZzfSx0fXNldENsaWVudCh0KXt0aGlzLms9dH1zZXRMYXN0RXZlbnRJZCh0KXt0aGlzLkE9dH1nZXRDbGllbnQoKXtyZXR1cm4gdGhpcy5rfWxhc3RFdmVudElkKCl7cmV0dXJuIHRoaXMuQX1hZGRTY29wZUxpc3RlbmVyKHQpe3RoaXMuby5wdXNoKHQpfWFkZEV2ZW50UHJvY2Vzc29yKHQpe3JldHVybiB0aGlzLmkucHVzaCh0KSx0aGlzfXNldFVzZXIodCl7cmV0dXJuIHRoaXMucD10fHx7ZW1haWw6dm9pZCAwLGlkOnZvaWQgMCxpcF9hZGRyZXNzOnZvaWQgMCx1c2VybmFtZTp2b2lkIDB9LHRoaXMuaiYmWih0aGlzLmose3VzZXI6dH0pLHRoaXMuUigpLHRoaXN9Z2V0VXNlcigpe3JldHVybiB0aGlzLnB9c2V0Q29udmVyc2F0aW9uSWQodCl7cmV0dXJuIHRoaXMuVD10fHx2b2lkIDAsdGhpcy5SKCksdGhpc31zZXRUYWdzKHQpe3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi50fSx0aGlzLlIoKSx0aGlzfXNldFRhZyh0LGUpe3JldHVybiB0aGlzLnNldFRhZ3Moe1t0XTplfSl9c2V0QXR0cmlidXRlcyh0KXtyZXR1cm4gdGhpcy5tPXsuLi50aGlzLm0sLi4udH0sdGhpcy5SKCksdGhpc31zZXRBdHRyaWJ1dGUodCxlKXtyZXR1cm4gdGhpcy5zZXRBdHRyaWJ1dGVzKHtbdF06ZX0pfXJlbW92ZUF0dHJpYnV0ZSh0KXtyZXR1cm4gdCBpbiB0aGlzLm0mJihkZWxldGUgdGhpcy5tW3RdLHRoaXMuUigpKSx0aGlzfXNldEV4dHJhcyh0KXtyZXR1cm4gdGhpcy52PXsuLi50aGlzLnYsLi4udH0sdGhpcy5SKCksdGhpc31zZXRFeHRyYSh0LGUpe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudixbdF06ZX0sdGhpcy5SKCksdGhpc31zZXRGaW5nZXJwcmludCh0KXtyZXR1cm4gdGhpcy5QPXQsdGhpcy5SKCksdGhpc31zZXRMZXZlbCh0KXtyZXR1cm4gdGhpcy5DPXQsdGhpcy5SKCksdGhpc31zZXRUcmFuc2FjdGlvbk5hbWUodCl7cmV0dXJuIHRoaXMuTz10LHRoaXMuUigpLHRoaXN9c2V0Q29udGV4dCh0LGUpe3JldHVybiBudWxsPT09ZT9kZWxldGUgdGhpcy5TW3RdOnRoaXMuU1t0XT1lLHRoaXMuUigpLHRoaXN9c2V0U2Vzc2lvbih0KXtyZXR1cm4gdD90aGlzLmo9dDpkZWxldGUgdGhpcy5qLHRoaXMuUigpLHRoaXN9Z2V0U2Vzc2lvbigpe3JldHVybiB0aGlzLmp9dXBkYXRlKHQpe2lmKCF0KXJldHVybiB0aGlzO2NvbnN0IGU9ImZ1bmN0aW9uIj09dHlwZW9mIHQ/dCh0aGlzKTp0LG49ZSBpbnN0YW5jZW9mIEs/ZS5nZXRTY29wZURhdGEoKTpDKGUpP3Q6dm9pZCAwLHt0YWdzOnIsYXR0cmlidXRlczpvLGV4dHJhOnMsdXNlcjppLGNvbnRleHRzOmMsbGV2ZWw6dSxmaW5nZXJwcmludDphPVtdLHByb3BhZ2F0aW9uQ29udGV4dDpmLGNvbnZlcnNhdGlvbklkOmh9PW58fHt9O3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi5yfSx0aGlzLm09ey4uLnRoaXMubSwuLi5vfSx0aGlzLnY9ey4uLnRoaXMudiwuLi5zfSx0aGlzLlM9ey4uLnRoaXMuUywuLi5jfSxpJiZPYmplY3Qua2V5cyhpKS5sZW5ndGgmJih0aGlzLnA9aSksdSYmKHRoaXMuQz11KSxhLmxlbmd0aCYmKHRoaXMuUD1hKSxmJiYodGhpcy5OPWYpLGgmJih0aGlzLlQ9aCksdGhpc31hZGRCcmVhZGNydW1iKHQsZSl7Y29uc3Qgbj0ibnVtYmVyIj09dHlwZW9mIGU/ZToxMDA7aWYobjw9MClyZXR1cm4gdGhpcztjb25zdCByPXt0aW1lc3RhbXA6RigpLC4uLnQsbWVzc2FnZTp0Lm1lc3NhZ2U/eih0Lm1lc3NhZ2UsMjA0OCk6dC5tZXNzYWdlfTtyZXR1cm4gdGhpcy51LnB1c2gociksdGhpcy51Lmxlbmd0aD5uJiYodGhpcy51PXRoaXMudS5zbGljZSgtbikpLHRoaXMuUigpLHRoaXN9Z2V0TGFzdEJyZWFkY3J1bWIoKXtyZXR1cm4gdGhpcy51W3RoaXMudS5sZW5ndGgtMV19Y2xlYXJCcmVhZGNydW1icygpe3JldHVybiB0aGlzLnU9W10sdGhpcy5SKCksdGhpc31hZGRBdHRhY2htZW50KHQpe3JldHVybiB0aGlzLmgucHVzaCh0KSx0aGlzfWNsZWFyQXR0YWNobWVudHMoKXtyZXR1cm4gdGhpcy5oPVtdLHRoaXN9Z2V0U2NvcGVEYXRhKCl7cmV0dXJue2JyZWFkY3J1bWJzOnRoaXMudSxhdHRhY2htZW50czp0aGlzLmgsY29udGV4dHM6dGhpcy5TLHRhZ3M6dGhpcy5sLGF0dHJpYnV0ZXM6dGhpcy5tLGV4dHJhOnRoaXMudix1c2VyOnRoaXMucCxsZXZlbDp0aGlzLkMsZmluZ2VycHJpbnQ6dGhpcy5QfHxbXSxldmVudFByb2Nlc3NvcnM6dGhpcy5pLHByb3BhZ2F0aW9uQ29udGV4dDp0aGlzLk4sc2RrUHJvY2Vzc2luZ01ldGFkYXRhOnRoaXMuXyx0cmFuc2FjdGlvbk5hbWU6dGhpcy5PLGNvbnZlcnNhdGlvbklkOnRoaXMuVH19c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLl89cSh0aGlzLl8sdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5OPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5OfWNhcHR1cmVFeGNlcHRpb24odCxlKXtjb25zdCBuPWU/LmV2ZW50X2lkfHxXKCk7aWYoIXRoaXMuaylyZXR1cm4gaCYmXy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLG47Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5rLmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5lLGV2ZW50X2lkOm59LHRoaXMpLG59Y2FwdHVyZU1lc3NhZ2UodCxlLG4pe2NvbnN0IHI9bj8uZXZlbnRfaWR8fFcoKTtpZighdGhpcy5rKXJldHVybiBoJiZfLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1uPy5zeW50aGV0aWNFeGNlcHRpb24/P25ldyBFcnJvcih0KTtyZXR1cm4gdGhpcy5rLmNhcHR1cmVNZXNzYWdlKHQsZSx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246bywuLi5uLGV2ZW50X2lkOnJ9LHRoaXMpLHJ9Y2FwdHVyZUV2ZW50KHQsZSl7Y29uc3Qgbj10LmV2ZW50X2lkfHxlPy5ldmVudF9pZHx8VygpO3JldHVybiB0aGlzLms/KHRoaXMuay5jYXB0dXJlRXZlbnQodCx7Li4uZSxldmVudF9pZDpufSx0aGlzKSxuKTooaCYmXy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhIiksbil9Uigpe3RoaXMudHx8KHRoaXMudD0hMCx0aGlzLm8uZm9yRWFjaCh0PT57dCh0aGlzKX0pLHRoaXMudD0hMSl9fWNvbnN0IFY9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyIsUT10PT50IGluc3RhbmNlb2YgUHJvbWlzZSYmIXRbWF0sWD1TeW1ib2woImNoYWluZWQgUHJvbWlzZUxpa2UiKSx0dD0odCxlKT0+e2lmKCFlKXJldHVybiB0O2xldCBuPSExO2Zvcihjb25zdCByIGluIHQpe2lmKHIgaW4gZSljb250aW51ZTtuPSEwO2NvbnN0IG89dFtyXTsiZnVuY3Rpb24iPT10eXBlb2Ygbz9PYmplY3QuZGVmaW5lUHJvcGVydHkoZSxyLHt2YWx1ZTooLi4uZSk9Pm8uYXBwbHkodCxlKSxlbnVtZXJhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMCx3cml0YWJsZTohMH0pOmVbcl09b31yZXR1cm4gbiYmT2JqZWN0LmFzc2lnbihlLHtbWF06ITB9KSxlfTtjbGFzcyBldHtjb25zdHJ1Y3Rvcih0LGUpe2xldCBuLHI7bj10fHxuZXcgSyxyPWV8fG5ldyBLLHRoaXMuST1be3Njb3BlOm59XSx0aGlzLkQ9cn13aXRoU2NvcGUodCl7Y29uc3QgZT10aGlzLlUoKTtsZXQgbjt0cnl7bj10KGUpfWNhdGNoKHQpe3Rocm93IHRoaXMuQigpLHR9cmV0dXJuIGoobik/KCh0LGUsbik9Pntjb25zdCByPXQudGhlbih0PT4oZSh0KSx0KSx0PT57dGhyb3cgbih0KSx0fSk7cmV0dXJuIFEocikmJlEodCk/cjp0dCh0LHIpfSkobiwoKT0+dGhpcy5CKCksKCk9PnRoaXMuQigpKToodGhpcy5CKCksbil9Z2V0Q2xpZW50KCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnR9Z2V0U2NvcGUoKXtyZXR1cm4gdGhpcy5nZXRTdGFja1RvcCgpLnNjb3BlfWdldElzb2xhdGlvblNjb3BlKCl7cmV0dXJuIHRoaXMuRH1nZXRTdGFja1RvcCgpe3JldHVybiB0aGlzLklbdGhpcy5JLmxlbmd0aC0xXX1VKCl7Y29uc3QgdD10aGlzLmdldFNjb3BlKCkuY2xvbmUoKTtyZXR1cm4gdGhpcy5JLnB1c2goe2NsaWVudDp0aGlzLmdldENsaWVudCgpLHNjb3BlOnR9KSx0fUIoKXtyZXR1cm4hKHRoaXMuSS5sZW5ndGg8PTEpJiYhIXRoaXMuSS5wb3AoKX19ZnVuY3Rpb24gbnQoKXtjb25zdCB0PWcoZCgpKTtyZXR1cm4gdC5zdGFjaz10LnN0YWNrfHxuZXcgZXQobSgiZGVmYXVsdEN1cnJlbnRTY29wZSIsKCk9Pm5ldyBLKSxtKCJkZWZhdWx0SXNvbGF0aW9uU2NvcGUiLCgpPT5uZXcgSykpfWZ1bmN0aW9uIHJ0KHQpe3JldHVybiBudCgpLndpdGhTY29wZSh0KX1mdW5jdGlvbiBvdCh0LGUpe2NvbnN0IG49bnQoKTtyZXR1cm4gbi53aXRoU2NvcGUoKCk9PihuLmdldFN0YWNrVG9wKCkuc2NvcGU9dCxlKHQpKSl9ZnVuY3Rpb24gc3QodCl7cmV0dXJuIG50KCkud2l0aFNjb3BlKCgpPT50KG50KCkuZ2V0SXNvbGF0aW9uU2NvcGUoKSkpfWZ1bmN0aW9uIGl0KHQpe3JldHVybiBydChlPT57ZS5zZXRTREtQcm9jZXNzaW5nTWV0YWRhdGEoe1tWXTohMH0pO2NvbnN0IG49dCgpO3JldHVybiBlLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1ZdOnZvaWQgMH0pLG59KX1mdW5jdGlvbiBjdCh0KXtjb25zdCBlPWcodCk7cmV0dXJuIGUuYWNzP2UuYWNzOntzdXBwcmVzc1RyYWNpbmc6aXQsd2l0aElzb2xhdGlvblNjb3BlOnN0LHdpdGhTY29wZTpydCx3aXRoU2V0U2NvcGU6b3Qsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LGUpPT5zdChlKSxnZXRDdXJyZW50U2NvcGU6KCk9Pm50KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+bnQoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiB1dCh0LGU9ITEpe2NvbnN0e2hvc3Q6bixwYXRoOnIscGFzczpvLHBvcnQ6cyxwcm9qZWN0SWQ6aSxwcm90b2NvbDpjLHB1YmxpY0tleTp1fT10O3JldHVybmAke2N9Oi8vJHt1fSR7ZSYmbz9gOiR7b31gOiIifUAke259JHtzP2A6JHtzfWA6IiJ9LyR7cj9gJHtyfS9gOnJ9JHtpfWB9ZnVuY3Rpb24gYXQodCxlKXtjb25zdHtmaW5nZXJwcmludDpuLGJyZWFkY3J1bWJzOnIsc2RrUHJvY2Vzc2luZ01ldGFkYXRhOm99PWU7IWZ1bmN0aW9uKHQsZSl7Y29uc3R7ZXh0cmE6bix0YWdzOnIsdXNlcjpvLGNvbnRleHRzOnMsbGV2ZWw6aSx0cmFuc2FjdGlvbk5hbWU6Y309ZTtPYmplY3Qua2V5cyhuKS5sZW5ndGgmJih0LmV4dHJhPXsuLi5uLC4uLnQuZXh0cmF9KTtPYmplY3Qua2V5cyhyKS5sZW5ndGgmJih0LnRhZ3M9ey4uLnIsLi4udC50YWdzfSk7T2JqZWN0LmtleXMobykubGVuZ3RoJiYodC51c2VyPXsuLi5vLC4uLnQudXNlcn0pO09iamVjdC5rZXlzKHMpLmxlbmd0aCYmKHQuY29udGV4dHM9ey4uLnMsLi4udC5jb250ZXh0c30pO2kmJih0LmxldmVsPWkpO2MmJiJ0cmFuc2FjdGlvbiIhPT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPWMpfSh0LGUpLGZ1bmN0aW9uKHQsZSl7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sZSYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQoZSkpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxuKSxmdW5jdGlvbih0LGUpe2NvbnN0IG49Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLmVdO3QuYnJlYWRjcnVtYnM9bi5sZW5ndGg/bjp2b2lkIDB9KHQsciksZnVuY3Rpb24odCxlKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4uZX19KHQsbyl9ZnVuY3Rpb24gZnQodCxlPVtdKXtyZXR1cm5bdCxlXX1mdW5jdGlvbiBodCh0LGUpe2NvbnN0IG49dFsxXTtmb3IoY29uc3QgdCBvZiBuKXtpZihlKHQsdFswXS50eXBlKSlyZXR1cm4hMH1yZXR1cm4hMX1mdW5jdGlvbiBwdCh0KXtjb25zdCBlPWcocCk7cmV0dXJuIGUuZW5jb2RlUG9seWZpbGw/ZS5lbmNvZGVQb2x5ZmlsbCh0KToobmV3IFRleHRFbmNvZGVyKS5lbmNvZGUodCl9ZnVuY3Rpb24gbHQodCl7Y29uc3RbZSxuXT10O2xldCByPUpTT04uc3RyaW5naWZ5KGUpO2Z1bmN0aW9uIG8odCl7InN0cmluZyI9PXR5cGVvZiByP3I9InN0cmluZyI9PXR5cGVvZiB0P3IrdDpbcHQociksdF06ci5wdXNoKCJzdHJpbmciPT10eXBlb2YgdD9wdCh0KTp0KX1mb3IoY29uc3QgdCBvZiBuKXtjb25zdFtlLG5dPXQ7aWYobyhgXG4ke0pTT04uc3RyaW5naWZ5KGUpfVxuYCksInN0cmluZyI9PXR5cGVvZiBufHxuIGluc3RhbmNlb2YgVWludDhBcnJheSlvKG4pO2Vsc2V7bGV0IHQ7dHJ5e3Q9SlNPTi5zdHJpbmdpZnkobil9Y2F0Y2h7dD1KU09OLnN0cmluZ2lmeShCKG4pKX1vKHQpfX1yZXR1cm4ic3RyaW5nIj09dHlwZW9mIHI/cjpmdW5jdGlvbih0KXtjb25zdCBlPXQucmVkdWNlKCh0LGUpPT50K2UubGVuZ3RoLDApLG49bmV3IFVpbnQ4QXJyYXkoZSk7bGV0IHI9MDtmb3IoY29uc3QgZSBvZiB0KW4uc2V0KGUscikscis9ZS5sZW5ndGg7cmV0dXJuIG59KHIpfWNvbnN0IGR0PXtzZXNzaW9uczoic2Vzc2lvbiIsZXZlbnQ6ImVycm9yIixjbGllbnRfcmVwb3J0OiJpbnRlcm5hbCIsdXNlcl9yZXBvcnQ6ImRlZmF1bHQiLHByb2ZpbGVfY2h1bms6InByb2ZpbGUiLHJlcGxheV9ldmVudDoicmVwbGF5IixyZXBsYXlfcmVjb3JkaW5nOiJyZXBsYXkiLGNoZWNrX2luOiJtb25pdG9yIixyYXdfc2VjdXJpdHk6InNlY3VyaXR5Iixsb2c6ImxvZ19pdGVtIix0cmFjZV9tZXRyaWM6Im1ldHJpYyJ9O2Z1bmN0aW9uIGd0KHQpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gdCBpbiBkdH0odCk/ZHRbdF06dH1mdW5jdGlvbiBtdCh0KXtpZighdD8uc2RrKXJldHVybjtjb25zdHtuYW1lOmUsdmVyc2lvbjpufT10LnNkaztyZXR1cm57bmFtZTplLHZlcnNpb246bn19ZnVuY3Rpb24geXQodCl7Y29uc3QgZT1jdChkKCkpO3JldHVybiBlLnN1cHByZXNzVHJhY2luZz9lLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBlPWN0KGQoKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtuLHJdPXQ7cmV0dXJuIG4/ZS53aXRoU2V0U2NvcGUobixyKTplLndpdGhTY29wZShyKX1yZXR1cm4gZS53aXRoU2NvcGUodFswXSl9KGU9PihlLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1ZdOiEwfSksdCgpKSl9ZnVuY3Rpb24gYnQodCxlLG4scil7Y29uc3Qgbz1tdChuKSxzPXQudHlwZSYmInJlcGxheV9ldmVudCIhPT10LnR5cGU/dC50eXBlOiJldmVudCI7IWZ1bmN0aW9uKHQsZSl7aWYoIWUpcmV0dXJuIHQ7Y29uc3Qgbj10LnNka3x8e307dC5zZGs9ey4uLm4sbmFtZTpuLm5hbWV8fGUubmFtZSx2ZXJzaW9uOm4udmVyc2lvbnx8ZS52ZXJzaW9uLGludGVncmF0aW9uczpbLi4udC5zZGs/LmludGVncmF0aW9uc3x8W10sLi4uZS5pbnRlZ3JhdGlvbnN8fFtdXSxwYWNrYWdlczpbLi4udC5zZGs/LnBhY2thZ2VzfHxbXSwuLi5lLnBhY2thZ2VzfHxbXV0sc2V0dGluZ3M6dC5zZGs/LnNldHRpbmdzfHxlLnNldHRpbmdzP3suLi50LnNkaz8uc2V0dGluZ3MsLi4uZS5zZXR0aW5nc306dm9pZCAwfX0odCxuPy5zZGspO2NvbnN0IGk9ZnVuY3Rpb24odCxlLG4scil7Y29uc3Qgbz10LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT8uZHluYW1pY1NhbXBsaW5nQ29udGV4dDtyZXR1cm57ZXZlbnRfaWQ6dC5ldmVudF9pZCxzZW50X2F0Om5ldyBEYXRlKEkoKSkudG9JU09TdHJpbmcoKSwuLi5lJiZ7c2RrOmV9LC4uLiEhbiYmciYme2Rzbjp1dChyKX0sLi4ubyYme3RyYWNlOm99fX0odCxvLHIsZSk7ZGVsZXRlIHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhO3JldHVybiBmdChpLFtbe3R5cGU6c30sdF1dKX1jbGFzcyB2dHtjb25zdHJ1Y3Rvcih0KXt0aGlzLkw9MCx0aGlzLk09W10sdGhpcy5KKHQpfXRoZW4odCxlKXtyZXR1cm4gbmV3IHZ0KChuLHIpPT57dGhpcy5NLnB1c2goWyExLGU9PntpZih0KXRyeXtuKHQoZSkpfWNhdGNoKHQpe3IodCl9ZWxzZSBuKGUpfSx0PT57aWYoZSl0cnl7bihlKHQpKX1jYXRjaCh0KXtyKHQpfWVsc2Ugcih0KX1dKSx0aGlzLlcoKX0pfWNhdGNoKHQpe3JldHVybiB0aGlzLnRoZW4odD0+dCx0KX1maW5hbGx5KHQpe3JldHVybiBuZXcgdnQoKGUsbik9PntsZXQgcixvO3JldHVybiB0aGlzLnRoZW4oZT0+e289ITEscj1lLHQmJnQoKX0sZT0+e289ITAscj1lLHQmJnQoKX0pLnRoZW4oKCk9PntvP24ocik6ZShyKX0pfSl9Vygpe2lmKDA9PT10aGlzLkwpcmV0dXJuO2NvbnN0IHQ9dGhpcy5NLnNsaWNlKCk7dGhpcy5NPVtdLHQuZm9yRWFjaCh0PT57dFswXXx8KDE9PT10aGlzLkwmJnRbMV0odGhpcy5GKSwyPT09dGhpcy5MJiZ0WzJdKHRoaXMuRiksdFswXT0hMCl9KX1KKHQpe2NvbnN0IGU9KHQsZSk9PnswPT09dGhpcy5MJiYoaihlKT9lLnRoZW4obixyKToodGhpcy5MPXQsdGhpcy5GPWUsdGhpcy5XKCkpKX0sbj10PT57ZSgxLHQpfSxyPXQ9PntlKDIsdCl9O3RyeXt0KG4scil9Y2F0Y2godCl7cih0KX19fWNvbnN0IHd0PVN5bWJvbC5mb3IoIlNlbnRyeUJ1ZmZlckZ1bGxFcnJvciIpO2Z1bmN0aW9uIFN0KHQ9MTAwKXtjb25zdCBlPW5ldyBTZXQ7ZnVuY3Rpb24gbih0KXtlLmRlbGV0ZSh0KX1yZXR1cm57Z2V0ICQoKXtyZXR1cm4gQXJyYXkuZnJvbShlKX0sYWRkOmZ1bmN0aW9uKHIpe2lmKCEoZS5zaXplPHQpKXJldHVybiBvPXd0LG5ldyB2dCgodCxlKT0+e2Uobyl9KTt2YXIgbztjb25zdCBzPXIoKTtyZXR1cm4gZS5hZGQocykscy50aGVuKCgpPT5uKHMpLCgpPT5uKHMpKSxzfSxkcmFpbjpmdW5jdGlvbih0KXtpZighZS5zaXplKXJldHVybiBuPSEwLG5ldyB2dCh0PT57dChuKX0pO3ZhciBuO2NvbnN0IHI9UHJvbWlzZS5hbGxTZXR0bGVkKEFycmF5LmZyb20oZSkpLnRoZW4oKCk9PiEwKTtpZighdClyZXR1cm4gcjtjb25zdCBvPVtyLG5ldyBQcm9taXNlKGU9PntyZXR1cm4ib2JqZWN0Ij09dHlwZW9mKG49c2V0VGltZW91dCgoKT0+ZSghMSksdCkpJiYiZnVuY3Rpb24iPT10eXBlb2Ygbi51bnJlZiYmbi51bnJlZigpLG47dmFyIG59KV07cmV0dXJuIFByb21pc2UucmFjZShvKX19fWZ1bmN0aW9uIF90KHQse3N0YXR1c0NvZGU6ZSxoZWFkZXJzOm59LHI9SSgpKXtjb25zdCBvPXsuLi50fSxzPW4/LlsieC1zZW50cnktcmF0ZS1saW1pdHMiXSxpPW4/LlsicmV0cnktYWZ0ZXIiXTtpZihzKWZvcihjb25zdCB0IG9mIHMudHJpbSgpLnNwbGl0KCIsIikpe2NvbnN0W2UsbiwsLHNdPXQuc3BsaXQoIjoiLDUpLGk9cGFyc2VJbnQoZSwxMCksYz0xZTMqKGlzTmFOKGkpPzYwOmkpO2lmKG4pZm9yKGNvbnN0IHQgb2Ygbi5zcGxpdCgiOyIpKSJtZXRyaWNfYnVja2V0Ij09PXQmJnMmJiFzLnNwbGl0KCI7IikuaW5jbHVkZXMoImN1c3RvbSIpfHwob1t0XT1yK2MpO2Vsc2Ugby5hbGw9citjfWVsc2UgaT9vLmFsbD1yK2Z1bmN0aW9uKHQsZT1JKCkpe2NvbnN0IG49cGFyc2VJbnQoYCR7dH1gLDEwKTtpZighaXNOYU4obikpcmV0dXJuIDFlMypuO2NvbnN0IHI9RGF0ZS5wYXJzZShgJHt0fWApO3JldHVybiBpc05hTihyKT82ZTQ6ci1lfShpLHIpOjQyOT09PWUmJihvLmFsbD1yKzZlNCk7cmV0dXJuIG99ZnVuY3Rpb24gJHQodCxlLG49U3QodC5idWZmZXJTaXplfHw2NCkpe2xldCByPXt9O3JldHVybntzZW5kOmZ1bmN0aW9uKHQpe2NvbnN0IG89W107aWYoaHQodCwodCxlKT0+e2NvbnN0IG49Z3QoZSk7KGZ1bmN0aW9uKHQsZSxuPUkoKSl7cmV0dXJuIGZ1bmN0aW9uKHQsZSl7cmV0dXJuIHRbZV18fHQuYWxsfHwwfSh0LGUpPm59KShyLG4pfHxvLnB1c2godCl9KSwwPT09by5sZW5ndGgpcmV0dXJuIFByb21pc2UucmVzb2x2ZSh7fSk7Y29uc3Qgcz1mdCh0WzBdLG8pLGk9dD0+eyFmdW5jdGlvbih0LGUpe3JldHVybiBodCh0LCh0LG4pPT5lLmluY2x1ZGVzKG4pKX0ocyxbImNsaWVudF9yZXBvcnQiXSk/aHQocywodCxlKT0+e30pOmgmJl8ud2FybihgRHJvcHBpbmcgY2xpZW50IHJlcG9ydC4gV2lsbCBub3Qgc2VuZCBvdXRjb21lcyAocmVhc29uOiAke3R9KS5gKX07cmV0dXJuIG4uYWRkKCgpPT5lKHtib2R5Omx0KHMpfSkudGhlbih0PT40MTM9PT10LnN0YXR1c0NvZGU/KGgmJl8uZXJyb3IoIlNlbnRyeSByZXNwb25kZWQgd2l0aCBzdGF0dXMgY29kZSA0MTMuIEVudmVsb3BlIHdhcyBkaXNjYXJkZWQgZHVlIHRvIGV4Y2VlZGluZyBzaXplIGxpbWl0cy4iKSxpKCJzZW5kX2Vycm9yIiksdCk6KGgmJnZvaWQgMCE9PXQuc3RhdHVzQ29kZSYmKHQuc3RhdHVzQ29kZTwyMDB8fHQuc3RhdHVzQ29kZT49MzAwKSYmXy53YXJuKGBTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgJHt0LnN0YXR1c0NvZGV9IHRvIHNlbnQgZXZlbnQuYCkscj1fdChyLHQpLHQpLHQ9Pnt0aHJvdyBpKCJuZXR3b3JrX2Vycm9yIiksaCYmXy5lcnJvcigiRW5jb3VudGVyZWQgZXJyb3IgcnVubmluZyB0cmFuc3BvcnQgcmVxdWVzdDoiLHQpLHR9KSkudGhlbih0PT50LHQ9PntpZih0PT09d3QpcmV0dXJuIGgmJl8uZXJyb3IoIlNraXBwZWQgc2VuZGluZyBldmVudCBiZWNhdXNlIGJ1ZmZlciBpcyBmdWxsLiIpLGkoInF1ZXVlX292ZXJmbG93IiksUHJvbWlzZS5yZXNvbHZlKHt9KTt0aHJvdyB0fSl9LGZsdXNoOnQ9Pm4uZHJhaW4odCl9fWNvbnN0IEV0PS9eKFxTKzpcXHxcLz8pKFtcc1xTXSo/KSgoPzpcLnsxLDJ9fFteL1xcXSs/fCkoXC5bXi4vXFxdKnwpKSg/OlsvXFxdKikkLztmdW5jdGlvbiB4dCh0KXtjb25zdCBlPWZ1bmN0aW9uKHQpe2NvbnN0IGU9dC5sZW5ndGg+MTAyND9gPHRydW5jYXRlZD4ke3Quc2xpY2UoLTEwMjQpfWA6dCxuPUV0LmV4ZWMoZSk7cmV0dXJuIG4/bi5zbGljZSgxKTpbXX0odCksbj1lWzBdfHwiIjtsZXQgcj1lWzFdO3JldHVybiBufHxyPyhyJiYocj1yLnNsaWNlKDAsci5sZW5ndGgtMSkpLG4rcik6Ii4ifWZ1bmN0aW9uIE50KHQsZT0hMSl7cmV0dXJuIShlfHx0JiYhdC5zdGFydHNXaXRoKCIvIikmJiF0Lm1hdGNoKC9eW0EtWl06LykmJiF0LnN0YXJ0c1dpdGgoIi4iKSYmIXQubWF0Y2goL15bYS16QS1aXShbYS16QS1aMC05LlwtK10pKjpcL1wvLykpJiZ2b2lkIDAhPT10JiYhdC5pbmNsdWRlcygibm9kZV9tb2R1bGVzLyIpfXZhciBDdDtjb25zdCBqdD1TeW1ib2woIkFnZW50QmFzZUludGVybmFsU3RhdGUiKTtjbGFzcyBPdCBleHRlbmRzKEN0PXMuQWdlbnQsQ3Qpe2NvbnN0cnVjdG9yKHQpe3N1cGVyKHQpLHRoaXNbanRdPXt9fWlzU2VjdXJlRW5kcG9pbnQodCl7aWYodCl7aWYoImJvb2xlYW4iPT10eXBlb2YgdC5zZWN1cmVFbmRwb2ludClyZXR1cm4gdC5zZWN1cmVFbmRwb2ludDtpZigic3RyaW5nIj09dHlwZW9mIHQucHJvdG9jb2wpcmV0dXJuImh0dHBzOiI9PT10LnByb3RvY29sfWNvbnN0e3N0YWNrOmV9PW5ldyBFcnJvcjtyZXR1cm4ic3RyaW5nIj09dHlwZW9mIGUmJmUuc3BsaXQoIlxuIikuc29tZSh0PT4tMSE9PXQuaW5kZXhPZigiKGh0dHBzLmpzOiIpfHwtMSE9PXQuaW5kZXhPZigibm9kZTpodHRwczoiKSl9Y3JlYXRlU29ja2V0KHQsZSxuKXtjb25zdCByPXsuLi5lLHNlY3VyZUVuZHBvaW50OnRoaXMuaXNTZWN1cmVFbmRwb2ludChlKX07UHJvbWlzZS5yZXNvbHZlKCkudGhlbigoKT0+dGhpcy5jb25uZWN0KHQscikpLnRoZW4obz0+e2lmKG8gaW5zdGFuY2VvZiBzLkFnZW50KXJldHVybiBvLmFkZFJlcXVlc3QodCxyKTt0aGlzW2p0XS5jdXJyZW50U29ja2V0PW8sc3VwZXIuY3JlYXRlU29ja2V0KHQsZSxuKX0sbil9Y3JlYXRlQ29ubmVjdGlvbigpe2NvbnN0IHQ9dGhpc1tqdF0uY3VycmVudFNvY2tldDtpZih0aGlzW2p0XS5jdXJyZW50U29ja2V0PXZvaWQgMCwhdCl0aHJvdyBuZXcgRXJyb3IoIk5vIHNvY2tldCB3YXMgcmV0dXJuZWQgaW4gdGhlIGBjb25uZWN0KClgIGZ1bmN0aW9uIik7cmV0dXJuIHR9Z2V0IGRlZmF1bHRQb3J0KCl7cmV0dXJuIHRoaXNbanRdLmRlZmF1bHRQb3J0Pz8oImh0dHBzOiI9PT10aGlzLnByb3RvY29sPzQ0Mzo4MCl9c2V0IGRlZmF1bHRQb3J0KHQpe3RoaXNbanRdJiYodGhpc1tqdF0uZGVmYXVsdFBvcnQ9dCl9Z2V0IHByb3RvY29sKCl7cmV0dXJuIHRoaXNbanRdLnByb3RvY29sPz8odGhpcy5pc1NlY3VyZUVuZHBvaW50KCk/Imh0dHBzOiI6Imh0dHA6Iil9c2V0IHByb3RvY29sKHQpe3RoaXNbanRdJiYodGhpc1tqdF0ucHJvdG9jb2w9dCl9fWZ1bmN0aW9uIFB0KC4uLnQpe18ubG9nKCJbaHR0cHMtcHJveHktYWdlbnQ6cGFyc2UtcHJveHktcmVzcG9uc2VdIiwuLi50KX1mdW5jdGlvbiBrdCh0KXtyZXR1cm4gbmV3IFByb21pc2UoKGUsbik9PntsZXQgcj0wO2NvbnN0IG89W107ZnVuY3Rpb24gcygpe2NvbnN0IGM9dC5yZWFkKCk7Yz9mdW5jdGlvbihjKXtvLnB1c2goYykscis9Yy5sZW5ndGg7Y29uc3QgdT1CdWZmZXIuY29uY2F0KG8sciksYT11LmluZGV4T2YoIlxyXG5cclxuIik7aWYoLTE9PT1hKXJldHVybiBQdCgiaGF2ZSBub3QgcmVjZWl2ZWQgZW5kIG9mIEhUVFAgaGVhZGVycyB5ZXQuLi4iKSx2b2lkIHMoKTtjb25zdCBmPXUuc3ViYXJyYXkoMCxhKS50b1N0cmluZygiYXNjaWkiKS5zcGxpdCgiXHJcbiIpLGg9Zi5zaGlmdCgpO2lmKCFoKXJldHVybiB0LmRlc3Ryb3koKSxuKG5ldyBFcnJvcigiTm8gaGVhZGVyIHJlY2VpdmVkIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZSIpKTtjb25zdCBwPWguc3BsaXQoIiAiKSxsPSsocFsxXXx8MCksZD1wLnNsaWNlKDIpLmpvaW4oIiAiKSxnPXt9O2Zvcihjb25zdCBlIG9mIGYpe2lmKCFlKWNvbnRpbnVlO2NvbnN0IHI9ZS5pbmRleE9mKCI6Iik7aWYoLTE9PT1yKXJldHVybiB0LmRlc3Ryb3koKSxuKG5ldyBFcnJvcihgSW52YWxpZCBoZWFkZXIgZnJvbSBwcm94eSBDT05ORUNUIHJlc3BvbnNlOiAiJHtlfSJgKSk7Y29uc3Qgbz1lLnNsaWNlKDAscikudG9Mb3dlckNhc2UoKSxzPWUuc2xpY2UocisxKS50cmltU3RhcnQoKSxpPWdbb107InN0cmluZyI9PXR5cGVvZiBpP2dbb109W2ksc106QXJyYXkuaXNBcnJheShpKT9pLnB1c2gocyk6Z1tvXT1zfVB0KCJnb3QgcHJveHkgc2VydmVyIHJlc3BvbnNlOiAlbyAlbyIsaCxnKSxpKCksZSh7Y29ubmVjdDp7c3RhdHVzQ29kZTpsLHN0YXR1c1RleHQ6ZCxoZWFkZXJzOmd9LGJ1ZmZlcmVkOnV9KX0oYyk6dC5vbmNlKCJyZWFkYWJsZSIscyl9ZnVuY3Rpb24gaSgpe3QucmVtb3ZlTGlzdGVuZXIoImVuZCIsYyksdC5yZW1vdmVMaXN0ZW5lcigiZXJyb3IiLHUpLHQucmVtb3ZlTGlzdGVuZXIoInJlYWRhYmxlIixzKX1mdW5jdGlvbiBjKCl7aSgpLFB0KCJvbmVuZCIpLG4obmV3IEVycm9yKCJQcm94eSBjb25uZWN0aW9uIGVuZGVkIGJlZm9yZSByZWNlaXZpbmcgQ09OTkVDVCByZXNwb25zZSIpKX1mdW5jdGlvbiB1KHQpe2koKSxQdCgib25lcnJvciAlbyIsdCksbih0KX10Lm9uKCJlcnJvciIsdSksdC5vbigiZW5kIixjKSxzKCl9KX1mdW5jdGlvbiBBdCguLi50KXtfLmxvZygiW2h0dHBzLXByb3h5LWFnZW50XSIsLi4udCl9Y2xhc3MgVHQgZXh0ZW5kcyBPdHtjb25zdHJ1Y3Rvcih0LGUpe3N1cGVyKGUpLHRoaXMub3B0aW9ucz17fSx0aGlzLnByb3h5PSJzdHJpbmciPT10eXBlb2YgdD9uZXcgVVJMKHQpOnQsdGhpcy5wcm94eUhlYWRlcnM9ZT8uaGVhZGVycz8/e30sQXQoIkNyZWF0aW5nIG5ldyBIdHRwc1Byb3h5QWdlbnQgaW5zdGFuY2U6ICVvIix0aGlzLnByb3h5LmhyZWYpO2NvbnN0IG49KHRoaXMucHJveHkuaG9zdG5hbWV8fHRoaXMucHJveHkuaG9zdCkucmVwbGFjZSgvXlxbfFxdJC9nLCIiKSxyPXRoaXMucHJveHkucG9ydD9wYXJzZUludCh0aGlzLnByb3h5LnBvcnQsMTApOiJodHRwczoiPT09dGhpcy5wcm94eS5wcm90b2NvbD80NDM6ODA7dGhpcy5jb25uZWN0T3B0cz17QUxQTlByb3RvY29sczpbImh0dHAvMS4xIl0sLi4uZT9JdChlLCJoZWFkZXJzIik6bnVsbCxob3N0Om4scG9ydDpyfX1hc3luYyBjb25uZWN0KHQsZSl7Y29uc3R7cHJveHk6bn09dGhpcztpZighZS5ob3N0KXRocm93IG5ldyBUeXBlRXJyb3IoJ05vICJob3N0IiBwcm92aWRlZCcpO2xldCByO2lmKCJodHRwczoiPT09bi5wcm90b2NvbCl7QXQoIkNyZWF0aW5nIGB0bHMuU29ja2V0YDogJW8iLHRoaXMuY29ubmVjdE9wdHMpO2NvbnN0IHQ9dGhpcy5jb25uZWN0T3B0cy5zZXJ2ZXJuYW1lfHx0aGlzLmNvbm5lY3RPcHRzLmhvc3Q7cj1mLmNvbm5lY3Qoey4uLnRoaXMuY29ubmVjdE9wdHMsc2VydmVybmFtZTp0JiZhLmlzSVAodCk/dm9pZCAwOnR9KX1lbHNlIEF0KCJDcmVhdGluZyBgbmV0LlNvY2tldGA6ICVvIix0aGlzLmNvbm5lY3RPcHRzKSxyPWEuY29ubmVjdCh0aGlzLmNvbm5lY3RPcHRzKTtjb25zdCBvPSJmdW5jdGlvbiI9PXR5cGVvZiB0aGlzLnByb3h5SGVhZGVycz90aGlzLnByb3h5SGVhZGVycygpOnsuLi50aGlzLnByb3h5SGVhZGVyc30scz1hLmlzSVB2NihlLmhvc3QpP2BbJHtlLmhvc3R9XWA6ZS5ob3N0O2xldCBpPWBDT05ORUNUICR7c306JHtlLnBvcnR9IEhUVFAvMS4xXHJcbmA7aWYobi51c2VybmFtZXx8bi5wYXNzd29yZCl7Y29uc3QgdD1gJHtkZWNvZGVVUklDb21wb25lbnQobi51c2VybmFtZSl9OiR7ZGVjb2RlVVJJQ29tcG9uZW50KG4ucGFzc3dvcmQpfWA7b1siUHJveHktQXV0aG9yaXphdGlvbiJdPWBCYXNpYyAke0J1ZmZlci5mcm9tKHQpLnRvU3RyaW5nKCJiYXNlNjQiKX1gfW8uSG9zdD1gJHtzfToke2UucG9ydH1gLG9bIlByb3h5LUNvbm5lY3Rpb24iXXx8KG9bIlByb3h5LUNvbm5lY3Rpb24iXT10aGlzLmtlZXBBbGl2ZT8iS2VlcC1BbGl2ZSI6ImNsb3NlIik7Zm9yKGNvbnN0IHQgb2YgT2JqZWN0LmtleXMobykpaSs9YCR7dH06ICR7b1t0XX1cclxuYDtjb25zdCBjPWt0KHIpO3Iud3JpdGUoYCR7aX1cclxuYCk7Y29uc3R7Y29ubmVjdDp1LGJ1ZmZlcmVkOmh9PWF3YWl0IGM7aWYodC5lbWl0KCJwcm94eUNvbm5lY3QiLHUpLHRoaXMuZW1pdCgicHJveHlDb25uZWN0Iix1LHQpLDIwMD09PXUuc3RhdHVzQ29kZSl7aWYodC5vbmNlKCJzb2NrZXQiLFJ0KSxlLnNlY3VyZUVuZHBvaW50KXtBdCgiVXBncmFkaW5nIHNvY2tldCBjb25uZWN0aW9uIHRvIFRMUyIpO2NvbnN0IHQ9ZS5zZXJ2ZXJuYW1lfHxlLmhvc3Q7cmV0dXJuIGYuY29ubmVjdCh7Li4uSXQoZSwiaG9zdCIsInBhdGgiLCJwb3J0Iiksc29ja2V0OnIsc2VydmVybmFtZTphLmlzSVAodCk/dm9pZCAwOnR9KX1yZXR1cm4gcn1yLmRlc3Ryb3koKTtjb25zdCBwPW5ldyBhLlNvY2tldCh7d3JpdGFibGU6ITF9KTtyZXR1cm4gcC5yZWFkYWJsZT0hMCx0Lm9uY2UoInNvY2tldCIsdD0+e0F0KCJSZXBsYXlpbmcgcHJveHkgYnVmZmVyIGZvciBmYWlsZWQgcmVxdWVzdCIpLHQucHVzaChoKSx0LnB1c2gobnVsbCl9KSxwfX1mdW5jdGlvbiBSdCh0KXt0LnJlc3VtZSgpfWZ1bmN0aW9uIEl0KHQsLi4uZSl7Y29uc3Qgbj17fTtsZXQgcjtmb3IociBpbiB0KWUuaW5jbHVkZXMocil8fChuW3JdPXRbcl0pO3JldHVybiBufVR0LnByb3RvY29scz1bImh0dHAiLCJodHRwcyJdO2Z1bmN0aW9uIER0KHQpe3JldHVybiB0LnJlcGxhY2UoL15bQS1aXTovLCIiKS5yZXBsYWNlKC9cXC9nLCIvIil9Y29uc3QgVXQ9ZTtsZXQgQnQsTHQ9MCxNdD17fTtmdW5jdGlvbiB6dCh0KXtVdC5kZWJ1ZyYmY29uc29sZS5sb2coYFtBTlIgV29ya2VyXSAke3R9YCl9dmFyIEp0LFd0LEZ0O2NvbnN0IEd0PWZ1bmN0aW9uKHQpe2xldCBlO3RyeXtlPW5ldyBVUkwodC51cmwpfWNhdGNoe3JldHVybiBiKCgpPT57Y29uc29sZS53YXJuKCJbQHNlbnRyeS9ub2RlXTogSW52YWxpZCBkc24gb3IgdHVubmVsIG9wdGlvbiwgd2lsbCBub3Qgc2VuZCBhbnkgZXZlbnRzLiBUaGUgdHVubmVsIG9wdGlvbiBtdXN0IGJlIGEgZnVsbCBVUkwgd2hlbiB1c2VkLiIpfSksJHQodCwoKT0+UHJvbWlzZS5yZXNvbHZlKHt9KSl9Y29uc3Qgbj0iaHR0cHM6Ij09PWUucHJvdG9jb2wscj1mdW5jdGlvbih0LGUpe2NvbnN0e25vX3Byb3h5Om59PXByb2Nlc3MuZW52LHI9bj8uc3BsaXQoIiwiKS5zb21lKGU9PnQuaG9zdC5lbmRzV2l0aChlKXx8dC5ob3N0bmFtZS5lbmRzV2l0aChlKSk7cmV0dXJuIHI/dm9pZCAwOmV9KGUsdC5wcm94eXx8KG4/cHJvY2Vzcy5lbnYuaHR0cHNfcHJveHk6dm9pZCAwKXx8cHJvY2Vzcy5lbnYuaHR0cF9wcm94eSksbz1uP2k6cyxhPXQua2VlcEFsaXZlPz8hMCxmPXI/bmV3IFR0KHIpOm5ldyBvLkFnZW50KHtrZWVwQWxpdmU6YSxtYXhTb2NrZXRzOjMwLHRpbWVvdXQ6MmUzfSksaD1mdW5jdGlvbih0LGUsbil7Y29uc3R7aG9zdG5hbWU6cixwYXRobmFtZTpvLHBvcnQ6cyxwcm90b2NvbDppLHNlYXJjaDphfT1uZXcgVVJMKHQudXJsKTtyZXR1cm4gZnVuY3Rpb24oZil7Y29uc3QgaD1wPT5uZXcgUHJvbWlzZSgobCxkKT0+e3l0KCgpPT57bGV0IGc9ZnVuY3Rpb24odCl7cmV0dXJuIG5ldyBjKHtyZWFkKCl7dGhpcy5wdXNoKHQpLHRoaXMucHVzaChudWxsKX19KX0oZi5ib2R5KTtjb25zdCBtPXsuLi50LmhlYWRlcnN9O2YuYm9keS5sZW5ndGg+MzI3NjgmJihtWyJjb250ZW50LWVuY29kaW5nIl09Imd6aXAiLGc9Zy5waXBlKHUoKSkpO2NvbnN0IHk9ci5zdGFydHNXaXRoKCJbIiksYj1lLnJlcXVlc3Qoe21ldGhvZDoiUE9TVCIsYWdlbnQ6bixoZWFkZXJzOm0saG9zdG5hbWU6eT9yLnNsaWNlKDEsLTEpOnIscGF0aDpgJHtvfSR7YX1gLHBvcnQ6cyxwcm90b2NvbDppLGNhOnQuY2FDZXJ0c30sdD0+e3Qub24oImRhdGEiLCgpPT57fSksdC5vbigiZW5kIiwoKT0+e30pLHQuc2V0RW5jb2RpbmcoInV0ZjgiKTtjb25zdCBlPXQuaGVhZGVyc1sicmV0cnktYWZ0ZXIiXT8/bnVsbCxuPXQuaGVhZGVyc1sieC1zZW50cnktcmF0ZS1saW1pdHMiXT8/bnVsbDtsKHtzdGF0dXNDb2RlOnQuc3RhdHVzQ29kZSxoZWFkZXJzOnsicmV0cnktYWZ0ZXIiOmUsIngtc2VudHJ5LXJhdGUtbGltaXRzIjpBcnJheS5pc0FycmF5KG4pP25bMF18fG51bGw6bn19KX0pO2Iub24oImVycm9yIix0PT57cCYmYi5yZXVzZWRTb2NrZXQmJiJFQ09OTlJFU0VUIj09PXQuY29kZT9sKGgoITEpKTpkKHQpfSksZy5waXBlKGIpfSl9KTtyZXR1cm4gaCghMCl9fSh0LHQuaHR0cE1vZHVsZT8/byxmKTtyZXR1cm4gJHQodCxoKX0oe3VybDooSnQ9VXQuZHNuLFd0PVV0LnR1bm5lbCxGdD1VdC5zZGtNZXRhZGF0YS5zZGssV3R8fGAke2Z1bmN0aW9uKHQpe3JldHVybmAke2Z1bmN0aW9uKHQpe2NvbnN0IGU9dC5wcm90b2NvbD9gJHt0LnByb3RvY29sfTpgOiIiLG49dC5wb3J0P2A6JHt0LnBvcnR9YDoiIjtyZXR1cm5gJHtlfS8vJHt0Lmhvc3R9JHtufSR7dC5wYXRoP2AvJHt0LnBhdGh9YDoiIn0vYXBpL2B9KHQpfSR7dC5wcm9qZWN0SWR9L2VudmVsb3BlL2B9KEp0KX0/JHtmdW5jdGlvbih0LGUpe2NvbnN0IG49e3NlbnRyeV92ZXJzaW9uOiI3In07cmV0dXJuIHQucHVibGljS2V5JiYobi5zZW50cnlfa2V5PXQucHVibGljS2V5KSxlJiYobi5zZW50cnlfY2xpZW50PWAke2UubmFtZX0vJHtlLnZlcnNpb259YCksbmV3IFVSTFNlYXJjaFBhcmFtcyhuKS50b1N0cmluZygpfShKdCxGdCl9YCl9KTthc3luYyBmdW5jdGlvbiBZdCgpe2lmKEJ0KXt6dCgiU2VuZGluZyBhYm5vcm1hbCBzZXNzaW9uIiksWihCdCx7c3RhdHVzOiJhYm5vcm1hbCIsYWJub3JtYWxfbWVjaGFuaXNtOiJhbnJfZm9yZWdyb3VuZCIscmVsZWFzZTpVdC5yZWxlYXNlLGVudmlyb25tZW50OlV0LmVudmlyb25tZW50fSk7Y29uc3QgdD1mdW5jdGlvbih0LGUsbixyKXtjb25zdCBvPW10KG4pO3JldHVybiBmdCh7c2VudF9hdDpuZXcgRGF0ZShJKCkpLnRvSVNPU3RyaW5nKCksLi4ubyYme3NkazpvfSwuLi4hIXImJmUmJntkc246dXQoZSl9fSxbImFnZ3JlZ2F0ZXMiaW4gdD9be3R5cGU6InNlc3Npb25zIn0sdF06W3t0eXBlOiJzZXNzaW9uIn0sdC50b0pTT04oKV1dKX0oQnQsVXQuZHNuLFV0LnNka01ldGFkYXRhLFV0LnR1bm5lbCk7enQoSlNPTi5zdHJpbmdpZnkodCkpLGF3YWl0IEd0LnNlbmQodCk7dHJ5e24/LnBvc3RNZXNzYWdlKCJzZXNzaW9uLWVuZGVkIil9Y2F0Y2h7fX19ZnVuY3Rpb24gSHQodCl7aWYoIXQpcmV0dXJuO2NvbnN0IGU9ZnVuY3Rpb24odCl7aWYoIXQubGVuZ3RoKXJldHVybltdO2NvbnN0IGU9QXJyYXkuZnJvbSh0KTtyZXR1cm4vc2VudHJ5V3JhcHBlZC8udGVzdChFKGUpLmZ1bmN0aW9ufHwiIikmJmUucG9wKCksZS5yZXZlcnNlKCksJC50ZXN0KEUoZSkuZnVuY3Rpb258fCIiKSYmKGUucG9wKCksJC50ZXN0KEUoZSkuZnVuY3Rpb258fCIiKSYmZS5wb3AoKSksZS5zbGljZSgwLDUwKS5tYXAodD0+KHsuLi50LGZpbGVuYW1lOnQuZmlsZW5hbWV8fEUoZSkuZmlsZW5hbWUsZnVuY3Rpb246dC5mdW5jdGlvbnx8Ij8ifSkpfSh0KTtpZihVdC5hcHBSb290UGF0aClmb3IoY29uc3QgdCBvZiBlKXQuZmlsZW5hbWUmJih0LmZpbGVuYW1lPU0odC5maWxlbmFtZSxVdC5hcHBSb290UGF0aCkpO3JldHVybiBlfWFzeW5jIGZ1bmN0aW9uIFp0KHQsZSl7aWYoTHQ+PVV0Lm1heEFuckV2ZW50cylyZXR1cm47THQrPTEsYXdhaXQgWXQoKSx6dCgiU2VuZGluZyBldmVudCIpO2NvbnN0IG49e2V2ZW50X2lkOlcoKSxjb250ZXh0czpVdC5jb250ZXh0cyxyZWxlYXNlOlV0LnJlbGVhc2UsZW52aXJvbm1lbnQ6VXQuZW52aXJvbm1lbnQsZGlzdDpVdC5kaXN0LHBsYXRmb3JtOiJub2RlIixsZXZlbDoiZXJyb3IiLGV4Y2VwdGlvbjp7dmFsdWVzOlt7dHlwZToiQXBwbGljYXRpb25Ob3RSZXNwb25kaW5nIix2YWx1ZTpgQXBwbGljYXRpb24gTm90IFJlc3BvbmRpbmcgZm9yIGF0IGxlYXN0ICR7VXQuYW5yVGhyZXNob2xkfSBtc2Asc3RhY2t0cmFjZTp7ZnJhbWVzOkh0KHQpfSxtZWNoYW5pc206e3R5cGU6IkFOUiJ9fV19LHRhZ3M6VXQuc3RhdGljVGFnc307ZSYmZnVuY3Rpb24odCxlKXtpZihhdCh0LGUpLCF0LmNvbnRleHRzPy50cmFjZSl7Y29uc3R7dHJhY2VJZDpuLHBhcmVudFNwYW5JZDpyLHByb3BhZ2F0aW9uU3BhbklkOm99PWUucHJvcGFnYXRpb25Db250ZXh0O3QuY29udGV4dHM9e3RyYWNlOnt0cmFjZV9pZDpuLHNwYW5faWQ6b3x8VygpLnN1YnN0cmluZygxNikscGFyZW50X3NwYW5faWQ6cn0sLi4udC5jb250ZXh0c319fShuLGUpLGZ1bmN0aW9uKHQpe2lmKDA9PT1PYmplY3Qua2V5cyhNdCkubGVuZ3RoKXJldHVybjtjb25zdCBlPVV0LmFwcFJvb3RQYXRoP3t9Ok10O2lmKFV0LmFwcFJvb3RQYXRoKWZvcihjb25zdFt0LG5db2YgT2JqZWN0LmVudHJpZXMoTXQpKWVbTSh0LFV0LmFwcFJvb3RQYXRoKV09bjtjb25zdCBuPW5ldyBNYXA7Zm9yKGNvbnN0IHIgb2YgdC5leGNlcHRpb24/LnZhbHVlc3x8W10pZm9yKGNvbnN0IHQgb2Ygci5zdGFja3RyYWNlPy5mcmFtZXN8fFtdKXtjb25zdCByPXQuYWJzX3BhdGh8fHQuZmlsZW5hbWU7ciYmZVtyXSYmbi5zZXQocixlW3JdKX1pZihuLnNpemU+MCl7Y29uc3QgZT1bXTtmb3IoY29uc3RbdCxyXW9mIG4uZW50cmllcygpKWUucHVzaCh7dHlwZToic291cmNlbWFwIixjb2RlX2ZpbGU6dCxkZWJ1Z19pZDpyfSk7dC5kZWJ1Z19tZXRhPXtpbWFnZXM6ZX19fShuKTtjb25zdCByPWJ0KG4sVXQuZHNuLFV0LnNka01ldGFkYXRhLFV0LnR1bm5lbCk7enQoSlNPTi5zdHJpbmdpZnkocikpLGF3YWl0IEd0LnNlbmQociksYXdhaXQgR3QuZmx1c2goMmUzKSxMdD49VXQubWF4QW5yRXZlbnRzJiZzZXRUaW1lb3V0KCgpPT57cHJvY2Vzcy5leGl0KDApfSw1ZTMpfWxldCBxdDtpZih6dCgiU3RhcnRlZCIpLFV0LmNhcHR1cmVTdGFja1RyYWNlKXt6dCgiQ29ubmVjdGluZyB0byBkZWJ1Z2dlciIpO2NvbnN0IGU9bmV3IHQ7ZS5jb25uZWN0VG9NYWluVGhyZWFkKCksenQoIkNvbm5lY3RlZCB0byBkZWJ1Z2dlciIpO2NvbnN0IG49bmV3IE1hcDtlLm9uKCJEZWJ1Z2dlci5zY3JpcHRQYXJzZWQiLHQ9PntuLnNldCh0LnBhcmFtcy5zY3JpcHRJZCx0LnBhcmFtcy51cmwpfSksZS5vbigiRGVidWdnZXIucGF1c2VkIix0PT57aWYoIm90aGVyIj09PXQucGFyYW1zLnJlYXNvbil0cnl7enQoIkRlYnVnZ2VyIHBhdXNlZCIpO2NvbnN0IHM9Wy4uLnQucGFyYW1zLmNhbGxGcmFtZXNdLGk9VXQuYXBwUm9vdFBhdGg/ZnVuY3Rpb24odD0ocHJvY2Vzcy5hcmd2WzFdP3h0KHByb2Nlc3MuYXJndlsxXSk6cHJvY2Vzcy5jd2QoKSksZT0iXFwiPT09byl7Y29uc3Qgbj1lP0R0KHQpOnQ7cmV0dXJuIHQ9PntpZighdClyZXR1cm47Y29uc3Qgbz1lP0R0KHQpOnQ7bGV0e2RpcjpzLGJhc2U6aSxleHQ6Y309ci5wYXJzZShvKTsiLmpzIiE9PWMmJiIubWpzIiE9PWMmJiIuY2pzIiE9PWN8fChpPWkuc2xpY2UoMCwtMSpjLmxlbmd0aCkpO2NvbnN0IHU9ZGVjb2RlVVJJQ29tcG9uZW50KGkpO3N8fChzPSIuIik7Y29uc3QgYT1zLmxhc3RJbmRleE9mKCIvbm9kZV9tb2R1bGVzIik7aWYoYT4tMSlyZXR1cm5gJHtzLnNsaWNlKGErMTQpLnJlcGxhY2UoL1wvL2csIi4iKX06JHt1fWA7aWYocy5zdGFydHNXaXRoKG4pKXtjb25zdCB0PXMuc2xpY2Uobi5sZW5ndGgrMSkucmVwbGFjZSgvXC8vZywiLiIpO3JldHVybiB0P2Ake3R9OiR7dX1gOnV9cmV0dXJuIHV9fShVdC5hcHBSb290UGF0aCk6KCk9Pnt9LGM9cy5tYXAodD0+ZnVuY3Rpb24odCxlLG4pe2NvbnN0IHI9ZT9lLnJlcGxhY2UoL15maWxlOlwvXC8vLCIiKTp2b2lkIDAsbz10LmxvY2F0aW9uLmNvbHVtbk51bWJlcj90LmxvY2F0aW9uLmNvbHVtbk51bWJlcisxOnZvaWQgMCxzPXQubG9jYXRpb24ubGluZU51bWJlcj90LmxvY2F0aW9uLmxpbmVOdW1iZXIrMTp2b2lkIDA7cmV0dXJue2ZpbGVuYW1lOnIsbW9kdWxlOm4ociksZnVuY3Rpb246dC5mdW5jdGlvbk5hbWV8fCI/Iixjb2xubzpvLGxpbmVubzpzLGluX2FwcDpyP050KHIpOnZvaWQgMH19KHQsbi5nZXQodC5sb2NhdGlvbi5zY3JpcHRJZCksaSkpLHU9c2V0VGltZW91dCgoKT0+e1p0KGMpLnRoZW4obnVsbCwoKT0+e3p0KCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQuIil9KX0sNWUzKTtlLnBvc3QoIlJ1bnRpbWUuZXZhbHVhdGUiLHtleHByZXNzaW9uOiJnbG9iYWwuX19TRU5UUllfR0VUX1NDT1BFU19fKCk7IixzaWxlbnQ6ITAscmV0dXJuQnlWYWx1ZTohMH0sKHQsbik9Pnt0JiZ6dChgRXJyb3IgZXhlY3V0aW5nIHNjcmlwdDogJyR7dC5tZXNzYWdlfSdgKSxjbGVhclRpbWVvdXQodSk7Y29uc3Qgcj1uPy5yZXN1bHQ/bi5yZXN1bHQudmFsdWU6dm9pZCAwO2UucG9zdCgiRGVidWdnZXIucmVzdW1lIiksZS5wb3N0KCJEZWJ1Z2dlci5kaXNhYmxlIiksWnQoYyxyKS50aGVuKG51bGwsKCk9Pnt6dCgiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSl9KX1jYXRjaCh0KXt0aHJvdyBlLnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpLGUucG9zdCgiRGVidWdnZXIuZGlzYWJsZSIpLHR9fSkscXQ9KCk9Pnt0cnl7ZS5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiLCgpPT57ZS5wb3N0KCJEZWJ1Z2dlci5wYXVzZSIpfSl9Y2F0Y2h7fX19Y29uc3R7cG9sbDpLdH09ZnVuY3Rpb24odCxlLG4scil7Y29uc3Qgbz10KCk7bGV0IHM9ITEsaT0hMDtyZXR1cm4gc2V0SW50ZXJ2YWwoKCk9Pntjb25zdCB0PW8uZ2V0VGltZU1zKCk7ITE9PT1zJiZ0PmUrbiYmKHM9ITAsaSYmcigpKSx0PGUrbiYmKHM9ITEpfSwyMCkse3BvbGw6KCk9PntvLnJlc2V0KCl9LGVuYWJsZWQ6dD0+e2k9dH19fShmdW5jdGlvbigpe2xldCB0PXByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO3JldHVybntnZXRUaW1lTXM6KCk9Pk51bWJlcigocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCktdCkvMTAwMDAwMG4pLHJlc2V0OigpPT57dD1wcm9jZXNzLmhydGltZS5iaWdpbnQoKX19fSxVdC5wb2xsSW50ZXJ2YWwsVXQuYW5yVGhyZXNob2xkLGZ1bmN0aW9uKCl7enQoIldhdGNoZG9nIHRpbWVvdXQiKSxxdD8oenQoIlBhdXNpbmcgZGVidWdnZXIgdG8gY2FwdHVyZSBzdGFjayB0cmFjZSIpLHF0KCkpOih6dCgiQ2FwdHVyaW5nIGV2ZW50IHdpdGhvdXQgYSBzdGFjayB0cmFjZSIpLFp0KCkudGhlbihudWxsLCgpPT57enQoIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZCBvbiB3YXRjaGRvZyB0aW1lb3V0LiIpfSkpfSk7bj8ub24oIm1lc3NhZ2UiLHQ9Pnt0LnNlc3Npb24mJihCdD1IKHQuc2Vzc2lvbikpLHQuZGVidWdJbWFnZXMmJihNdD10LmRlYnVnSW1hZ2VzKSxLdCgpfSksbj8ucG9zdE1lc3NhZ2UoIndvcmtlci1yZWFkeSIpOw==";
|
|
9
|
+
const base64WorkerScript = "LyohIEBzZW50cnkvbm9kZSAxMS4yLjAgKGUxYTQzMTYpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyB0fWZyb20ibm9kZTppbnNwZWN0b3IiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIGUscGFyZW50UG9ydCBhcyBufWZyb20ibm9kZTp3b3JrZXJfdGhyZWFkcyI7aW1wb3J0e3Bvc2l4IGFzIHIsc2VwIGFzIG99ZnJvbSJub2RlOnBhdGgiO2ltcG9ydCphcyBzIGZyb20ibm9kZTpodHRwIjtpbXBvcnQqYXMgaSBmcm9tIm5vZGU6aHR0cHMiO2ltcG9ydHtSZWFkYWJsZSBhcyBjfWZyb20ibm9kZTpzdHJlYW0iO2ltcG9ydHtjcmVhdGVHemlwIGFzIHV9ZnJvbSJub2RlOnpsaWIiO2ltcG9ydCphcyBhIGZyb20ibm9kZTpuZXQiO2ltcG9ydCphcyBmIGZyb20ibm9kZTp0bHMiO2NvbnN0IGg9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLHA9Z2xvYmFsVGhpcyxsPSIxMS4yLjAiO2Z1bmN0aW9uIGQoKXtyZXR1cm4gZyhwKSxwfWZ1bmN0aW9uIGcodCl7Y29uc3QgZT10Ll9fU0VOVFJZX189dC5fX1NFTlRSWV9ffHx7fTtyZXR1cm4gZS52ZXJzaW9uPWUudmVyc2lvbnx8bCxlW2xdPWVbbF18fHt9fWZ1bmN0aW9uIG0odCxlLG49cCl7Y29uc3Qgcj1uLl9fU0VOVFJZX189bi5fX1NFTlRSWV9ffHx7fSxvPXJbbF09cltsXXx8e307cmV0dXJuIG9bdF18fChvW3RdPWUoKSl9Y29uc3QgeT17fTtmdW5jdGlvbiBiKHQpe2lmKCEoImNvbnNvbGUiaW4gcCkpcmV0dXJuIHQoKTtjb25zdCBlPXAuY29uc29sZSxuPXt9LHI9T2JqZWN0LmtleXMoeSk7ci5mb3JFYWNoKHQ9Pntjb25zdCByPXlbdF07blt0XT1lW3RdLGVbdF09cn0pO3RyeXtyZXR1cm4gdCgpfWZpbmFsbHl7ci5mb3JFYWNoKHQ9PntlW3RdPW5bdF19KX19ZnVuY3Rpb24gdigpe3JldHVybiBTKCkuZW5hYmxlZH1mdW5jdGlvbiB3KHQsLi4uZSl7aCYmdigpJiZiKCgpPT57cC5jb25zb2xlW3RdKGBTZW50cnkgTG9nZ2VyIFske3R9XTpgLC4uLmUpfSl9ZnVuY3Rpb24gUygpe3JldHVybiBoP20oImxvZ2dlclNldHRpbmdzIiwoKT0+KHtlbmFibGVkOiExfSkpOntlbmFibGVkOiExfX1jb25zdCBfPXtlbmFibGU6ZnVuY3Rpb24oKXtTKCkuZW5hYmxlZD0hMH0sZGlzYWJsZTpmdW5jdGlvbigpe1MoKS5lbmFibGVkPSExfSxpc0VuYWJsZWQ6dixsb2c6ZnVuY3Rpb24oLi4udCl7dygibG9nIiwuLi50KX0sd2FybjpmdW5jdGlvbiguLi50KXt3KCJ3YXJuIiwuLi50KX0sZXJyb3I6ZnVuY3Rpb24oLi4udCl7dygiZXJyb3IiLC4uLnQpfX0sJD0vY2FwdHVyZU1lc3NhZ2V8Y2FwdHVyZUV4Y2VwdGlvbi87ZnVuY3Rpb24gRSh0KXtyZXR1cm4gdFt0Lmxlbmd0aC0xXXx8e319Y29uc3QgeD0iPGFub255bW91cz4iO2NvbnN0IE49T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZztmdW5jdGlvbiBDKHQpe3JldHVybiBmdW5jdGlvbih0LGUpe3JldHVybiBOLmNhbGwodCk9PT1gW29iamVjdCAke2V9XWB9KHQsIk9iamVjdCIpfWZ1bmN0aW9uIGoodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gTyh0LGUpe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIGV9Y2F0Y2h7cmV0dXJuITF9fWZ1bmN0aW9uIFAodCl7aWYoZnVuY3Rpb24odCl7c3dpdGNoKE4uY2FsbCh0KSl7Y2FzZSJbb2JqZWN0IEVycm9yXSI6Y2FzZSJbb2JqZWN0IEV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBET01FeGNlcHRpb25dIjpjYXNlIltvYmplY3QgV2ViQXNzZW1ibHkuRXhjZXB0aW9uXSI6cmV0dXJuITA7ZGVmYXVsdDpyZXR1cm4gTyh0LEVycm9yKX19KHQpKXJldHVybnttZXNzYWdlOnQubWVzc2FnZSxuYW1lOnQubmFtZSxzdGFjazp0LnN0YWNrLC4uLmsodCl9O2lmKGU9dCwidW5kZWZpbmVkIiE9dHlwZW9mIEV2ZW50JiZPKGUsRXZlbnQpKXtjb25zdHt0eXBlOmUsdGFyZ2V0Om4sY3VycmVudFRhcmdldDpyLGRldGFpbDpvfT10O3JldHVybnt0eXBlOmUsdGFyZ2V0Om4sY3VycmVudFRhcmdldDpyLC4uLm8/e2RldGFpbDpvfTp7fSwuLi5rKHQpfX12YXIgZTtyZXR1cm4gdH1mdW5jdGlvbiBrKHQpe3JldHVybiJvYmplY3QiPT10eXBlb2YoZT10KSYmbnVsbCE9PWU/T2JqZWN0LmZyb21FbnRyaWVzKE9iamVjdC5lbnRyaWVzKHQpKTp7fTt2YXIgZX1sZXQgQTtmdW5jdGlvbiBUKHQpe2lmKHZvaWQgMCE9PUEpcmV0dXJuIEE/QSh0KTp0KCk7Y29uc3QgZT1TeW1ib2wuZm9yKCJfX1NFTlRSWV9TQUZFX1JBTkRPTV9JRF9XUkFQUEVSX18iKSxuPXA7cmV0dXJuIGUgaW4gbiYmImZ1bmN0aW9uIj09dHlwZW9mIG5bZV0/KEE9bltlXSxBKHQpKTooQT1udWxsLHQoKSl9ZnVuY3Rpb24gUigpe3JldHVybiBUKCgpPT5NYXRoLnJhbmRvbSgpKX1mdW5jdGlvbiBJKCl7cmV0dXJuIFQoKCk9PkRhdGUubm93KCkpfWNvbnN0IEQ9U3ltYm9sLmZvcigic2VudHJ5LnNraXBOb3JtYWxpemF0aW9uIiksVT1TeW1ib2wuZm9yKCJzZW50cnkub3ZlcnJpZGVOb3JtYWxpemF0aW9uRGVwdGgiKTtmdW5jdGlvbiBCKHQsZT0xMDAsbj0xLzApe3RyeXtyZXR1cm4gTCgiIix0LGUsbil9Y2F0Y2godCl7cmV0dXJue0VSUk9SOmAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHt0fSlgfX19ZnVuY3Rpb24gTCh0LGUsbj0xLzAscj0xLzAsbz1mdW5jdGlvbigpe2NvbnN0IHQ9bmV3IFdlYWtTZXQ7ZnVuY3Rpb24gZShlKXtyZXR1cm4hIXQuaGFzKGUpfHwodC5hZGQoZSksITEpfWZ1bmN0aW9uIG4oZSl7dC5kZWxldGUoZSl9cmV0dXJuW2Usbl19KCkpe2NvbnN0W3MsaV09bztpZihudWxsPT1lfHxbImJvb2xlYW4iLCJzdHJpbmciXS5pbmNsdWRlcyh0eXBlb2YgZSl8fCJudW1iZXIiPT10eXBlb2YgZSYmTnVtYmVyLmlzRmluaXRlKGUpKXJldHVybiBlO2NvbnN0IGM9ZnVuY3Rpb24odCxlKXt0cnl7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiBnbG9iYWwmJmU9PT1nbG9iYWwpcmV0dXJuIltHbG9iYWxdIjtpZigibnVtYmVyIj09dHlwZW9mIGUmJiFOdW1iZXIuaXNGaW5pdGUoZSkpcmV0dXJuYFske2V9XWA7aWYoImZ1bmN0aW9uIj09dHlwZW9mIGUpcmV0dXJuYFtGdW5jdGlvbjogJHtmdW5jdGlvbih0KXt0cnl7cmV0dXJuIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0JiZ0Lm5hbWV8fHh9Y2F0Y2h7cmV0dXJuIHh9fShlKX1dYDtpZigic3ltYm9sIj09dHlwZW9mIGUpcmV0dXJuYFske1N0cmluZyhlKX1dYDtpZigiYmlnaW50Ij09dHlwZW9mIGUpcmV0dXJuYFtCaWdJbnQ6ICR7U3RyaW5nKGUpfV1gO2NvbnN0IHQ9ZnVuY3Rpb24odCl7Y29uc3QgZT1PYmplY3QuZ2V0UHJvdG90eXBlT2YodCk7cmV0dXJuIGU/LmNvbnN0cnVjdG9yP2UuY29uc3RydWN0b3IubmFtZToibnVsbCBwcm90b3R5cGUifShlKTtyZXR1cm5gW29iamVjdCAke3R9XWB9Y2F0Y2godCl7cmV0dXJuYCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9fSgwLGUpO2lmKCFjLnN0YXJ0c1dpdGgoIltvYmplY3QgIikpcmV0dXJuIGM7aWYoZnVuY3Rpb24odCl7cmV0dXJuIEJvb2xlYW4odFtEXSl9KGUpKXJldHVybiBlO2NvbnN0IHU9ZnVuY3Rpb24odCl7Y29uc3QgZT10W1VdO3JldHVybiJudW1iZXIiPT10eXBlb2YgZT9lOnZvaWQgMH0oZSksYT12b2lkIDAhPT11P3U6bjtpZigwPT09YSlyZXR1cm4gYy5yZXBsYWNlKCJvYmplY3QgIiwiIik7aWYocyhlKSlyZXR1cm4iW0NpcmN1bGFyIH5dIjtjb25zdCBmPWU7aWYoZiYmImZ1bmN0aW9uIj09dHlwZW9mIGYudG9KU09OKXRyeXtyZXR1cm4gTCgiIixmLnRvSlNPTigpLGEtMSxyLG8pfWNhdGNoe31jb25zdCBoPUFycmF5LmlzQXJyYXkoZSk/W106e307bGV0IHA9MDtjb25zdCBsPVAoZSk7Zm9yKGNvbnN0IHQgaW4gbCl7aWYoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChsLHQpKWNvbnRpbnVlO2lmKHA+PXIpe2hbdF09IltNYXhQcm9wZXJ0aWVzIH5dIjticmVha31jb25zdCBlPWxbdF07aFt0XT1MKHQsZSxhLTEscixvKSxwKyt9cmV0dXJuIGkoZSksaH1mdW5jdGlvbiBNKHQsZSl7Y29uc3Qgbj1lLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC9bfFxce30oKVtcXV4kKyo/Ll0vZywiXFwkJiIpO2xldCByPXQ7dHJ5e3I9ZGVjb2RlVVJJKHQpfWNhdGNoe31yZXR1cm4gci5yZXBsYWNlKC9cXC9nLCIvIikucmVwbGFjZSgvd2VicGFjazpcLz8vZywiIikucmVwbGFjZShuZXcgUmVnRXhwKGAoZmlsZTovLyk/Lyoke259LypgLCJpZyIpLCJhcHA6Ly8vIil9ZnVuY3Rpb24geih0LGU9MCl7cmV0dXJuInN0cmluZyIhPXR5cGVvZiB0fHwwPT09ZXx8dC5sZW5ndGg8PWU/dDpgJHt0LnNsaWNlKDAsZSl9Li4uYH1sZXQgSjtmdW5jdGlvbiBXKHQ9ZnVuY3Rpb24oKXtjb25zdCB0PXA7cmV0dXJuIHQuY3J5cHRvfHx0Lm1zQ3J5cHRvfSgpKXt0cnl7aWYodD8ucmFuZG9tVVVJRClyZXR1cm4gVCgoKT0+dC5yYW5kb21VVUlEKCkpLnJlcGxhY2UoLy0vZywiIil9Y2F0Y2h7fXJldHVybiBKfHwoSj0iMTAwMDAwMDAxMDAwNDAwMDgwMDAxMDAwMDAwMDAwMDAiKSxKLnJlcGxhY2UoL1swMThdL2csdD0+KHReKDE2KlIoKSYxNSk+PnQvNCkudG9TdHJpbmcoMTYpKX1mdW5jdGlvbiBGKCl7cmV0dXJuIEkoKS8xZTN9bGV0IEc7ZnVuY3Rpb24gWSgpe3JldHVybihHPz8oRz1mdW5jdGlvbigpe2NvbnN0e3BlcmZvcm1hbmNlOnR9PXA7aWYoIXQ/Lm5vd3x8IXQudGltZU9yaWdpbilyZXR1cm4gRjtjb25zdCBlPXQudGltZU9yaWdpbjtyZXR1cm4oKT0+KGUrVCgoKT0+dC5ub3coKSkpLzFlM30oKSkpKCl9ZnVuY3Rpb24gSCh0KXtjb25zdCBlPVkoKSxuPXtzaWQ6VygpLGluaXQ6ITAsdGltZXN0YW1wOmUsc3RhcnRlZDplLGR1cmF0aW9uOjAsc3RhdHVzOiJvayIsZXJyb3JzOjAsaWdub3JlRHVyYXRpb246ITEsdG9KU09OOigpPT5mdW5jdGlvbih0KXtyZXR1cm57c2lkOmAke3Quc2lkfWAsaW5pdDp0LmluaXQsc3RhcnRlZDpuZXcgRGF0ZSgxZTMqdC5zdGFydGVkKS50b0lTT1N0cmluZygpLHRpbWVzdGFtcDpuZXcgRGF0ZSgxZTMqdC50aW1lc3RhbXApLnRvSVNPU3RyaW5nKCksc3RhdHVzOnQuc3RhdHVzLGVycm9yczp0LmVycm9ycyxkaWQ6Im51bWJlciI9PXR5cGVvZiB0LmRpZHx8InN0cmluZyI9PXR5cGVvZiB0LmRpZD9gJHt0LmRpZH1gOnZvaWQgMCxkdXJhdGlvbjp0LmR1cmF0aW9uLGFibm9ybWFsX21lY2hhbmlzbTp0LmFibm9ybWFsX21lY2hhbmlzbSxhdHRyczp7cmVsZWFzZTp0LnJlbGVhc2UsZW52aXJvbm1lbnQ6dC5lbnZpcm9ubWVudCxpcF9hZGRyZXNzOnQuaXBBZGRyZXNzLHVzZXJfYWdlbnQ6dC51c2VyQWdlbnR9fX0obil9O3JldHVybiB0JiZaKG4sdCksbn1mdW5jdGlvbiBaKHQsZT17fSl7aWYoZS51c2VyJiYoIXQuaXBBZGRyZXNzJiZlLnVzZXIuaXBfYWRkcmVzcyYmKHQuaXBBZGRyZXNzPWUudXNlci5pcF9hZGRyZXNzKSx0LmRpZHx8ZS5kaWR8fCh0LmRpZD1lLnVzZXIuaWR8fGUudXNlci5lbWFpbHx8ZS51c2VyLnVzZXJuYW1lKSksdC50aW1lc3RhbXA9ZS50aW1lc3RhbXB8fFkoKSxlLmFibm9ybWFsX21lY2hhbmlzbSYmKHQuYWJub3JtYWxfbWVjaGFuaXNtPWUuYWJub3JtYWxfbWVjaGFuaXNtKSxlLmlnbm9yZUR1cmF0aW9uJiYodC5pZ25vcmVEdXJhdGlvbj1lLmlnbm9yZUR1cmF0aW9uKSxlLnNpZCYmKHQuc2lkPTMyPT09ZS5zaWQubGVuZ3RoP2Uuc2lkOlcoKSksdm9pZCAwIT09ZS5pbml0JiYodC5pbml0PWUuaW5pdCksIXQuZGlkJiZlLmRpZCYmKHQuZGlkPWAke2UuZGlkfWApLCJudW1iZXIiPT10eXBlb2YgZS5zdGFydGVkJiYodC5zdGFydGVkPWUuc3RhcnRlZCksdC5pZ25vcmVEdXJhdGlvbil0LmR1cmF0aW9uPXZvaWQgMDtlbHNlIGlmKCJudW1iZXIiPT10eXBlb2YgZS5kdXJhdGlvbil0LmR1cmF0aW9uPWUuZHVyYXRpb247ZWxzZXtjb25zdCBlPXQudGltZXN0YW1wLXQuc3RhcnRlZDt0LmR1cmF0aW9uPWU+PTA/ZTowfWUucmVsZWFzZSYmKHQucmVsZWFzZT1lLnJlbGVhc2UpLGUuZW52aXJvbm1lbnQmJih0LmVudmlyb25tZW50PWUuZW52aXJvbm1lbnQpLCF0LmlwQWRkcmVzcyYmZS5pcEFkZHJlc3MmJih0LmlwQWRkcmVzcz1lLmlwQWRkcmVzcyksIXQudXNlckFnZW50JiZlLnVzZXJBZ2VudCYmKHQudXNlckFnZW50PWUudXNlckFnZW50KSwibnVtYmVyIj09dHlwZW9mIGUuZXJyb3JzJiYodC5lcnJvcnM9ZS5lcnJvcnMpLGUuc3RhdHVzJiYodC5zdGF0dXM9ZS5zdGF0dXMpfWZ1bmN0aW9uIHEodCxlLG49Mil7aWYoIWV8fCJvYmplY3QiIT10eXBlb2YgZXx8bjw9MClyZXR1cm4gZTtpZih0JiYwPT09T2JqZWN0LmtleXMoZSkubGVuZ3RoKXJldHVybiB0O2NvbnN0IHI9ey4uLnR9O2Zvcihjb25zdCB0IGluIGUpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGUsdCkmJihyW3RdPXEoclt0XSxlW3RdLG4tMSkpO3JldHVybiByfWNsYXNzIEt7Y29uc3RydWN0b3IoKXt0aGlzLnQ9ITEsdGhpcy5vPVtdLHRoaXMuaT1bXSx0aGlzLnU9W10sdGhpcy5oPVtdLHRoaXMucD17fSx0aGlzLmw9e30sdGhpcy5tPXt9LHRoaXMudj17fSx0aGlzLlM9e30sdGhpcy5fPXt9LGZ1bmN0aW9uKHQsZSxuKXt0cnl7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsZSx7dmFsdWU6bix3cml0YWJsZTohMCxjb25maWd1cmFibGU6ITB9KX1jYXRjaHtoJiZfLmxvZyhgRmFpbGVkIHRvIGFkZCBub24tZW51bWVyYWJsZSBwcm9wZXJ0eSAiJHtTdHJpbmcoZSl9IiB0byBvYmplY3RgLHQpfX0odGhpcywicmVmcyIse30pLHRoaXMuTj17dHJhY2VJZDpXKCksc2FtcGxlUmFuZDpSKCl9fWNsb25lKCl7Y29uc3QgdD1uZXcgSztyZXR1cm4gdC51PVsuLi50aGlzLnVdLHQubD17Li4udGhpcy5sfSx0Lm09ey4uLnRoaXMubX0sdC52PXsuLi50aGlzLnZ9LHQuUz17Li4udGhpcy5TfSx0aGlzLlMuZmxhZ3MmJih0LlMuZmxhZ3M9e3ZhbHVlczpbLi4udGhpcy5TLmZsYWdzLnZhbHVlc119KSx0LnA9dGhpcy5wLHQuQz10aGlzLkMsdC5qPXRoaXMuaix0Lk89dGhpcy5PLHQuUD10aGlzLlAsdC5pPVsuLi50aGlzLmldLHQuaD1bLi4udGhpcy5oXSx0Ll89ey4uLnRoaXMuX30sdC5OPXsuLi50aGlzLk59LHQuaz10aGlzLmssdC5BPXRoaXMuQSx0LlQ9dGhpcy5ULHQucmVmcz17Li4udGhpcy5yZWZzfSx0fXNldENsaWVudCh0KXt0aGlzLms9dH1zZXRMYXN0RXZlbnRJZCh0KXt0aGlzLkE9dH1nZXRDbGllbnQoKXtyZXR1cm4gdGhpcy5rfWxhc3RFdmVudElkKCl7cmV0dXJuIHRoaXMuQX1hZGRTY29wZUxpc3RlbmVyKHQpe3RoaXMuby5wdXNoKHQpfWFkZEV2ZW50UHJvY2Vzc29yKHQpe3JldHVybiB0aGlzLmkucHVzaCh0KSx0aGlzfXNldFVzZXIodCl7cmV0dXJuIHRoaXMucD10fHx7ZW1haWw6dm9pZCAwLGlkOnZvaWQgMCxpcF9hZGRyZXNzOnZvaWQgMCx1c2VybmFtZTp2b2lkIDB9LHRoaXMuaiYmWih0aGlzLmose3VzZXI6dH0pLHRoaXMuUigpLHRoaXN9Z2V0VXNlcigpe3JldHVybiB0aGlzLnB9c2V0Q29udmVyc2F0aW9uSWQodCl7cmV0dXJuIHRoaXMuVD10fHx2b2lkIDAsdGhpcy5SKCksdGhpc31zZXRUYWdzKHQpe3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi50fSx0aGlzLlIoKSx0aGlzfXNldFRhZyh0LGUpe3JldHVybiB0aGlzLnNldFRhZ3Moe1t0XTplfSl9c2V0QXR0cmlidXRlcyh0KXtyZXR1cm4gdGhpcy5tPXsuLi50aGlzLm0sLi4udH0sdGhpcy5SKCksdGhpc31zZXRBdHRyaWJ1dGUodCxlKXtyZXR1cm4gdGhpcy5zZXRBdHRyaWJ1dGVzKHtbdF06ZX0pfXJlbW92ZUF0dHJpYnV0ZSh0KXtyZXR1cm4gdCBpbiB0aGlzLm0mJihkZWxldGUgdGhpcy5tW3RdLHRoaXMuUigpKSx0aGlzfXNldEV4dHJhcyh0KXtyZXR1cm4gdGhpcy52PXsuLi50aGlzLnYsLi4udH0sdGhpcy5SKCksdGhpc31zZXRFeHRyYSh0LGUpe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudixbdF06ZX0sdGhpcy5SKCksdGhpc31zZXRGaW5nZXJwcmludCh0KXtyZXR1cm4gdGhpcy5QPXQsdGhpcy5SKCksdGhpc31zZXRMZXZlbCh0KXtyZXR1cm4gdGhpcy5DPXQsdGhpcy5SKCksdGhpc31zZXRUcmFuc2FjdGlvbk5hbWUodCl7cmV0dXJuIHRoaXMuTz10LHRoaXMuUigpLHRoaXN9c2V0Q29udGV4dCh0LGUpe3JldHVybiBudWxsPT09ZT9kZWxldGUgdGhpcy5TW3RdOnRoaXMuU1t0XT1lLHRoaXMuUigpLHRoaXN9c2V0U2Vzc2lvbih0KXtyZXR1cm4gdD90aGlzLmo9dDpkZWxldGUgdGhpcy5qLHRoaXMuUigpLHRoaXN9Z2V0U2Vzc2lvbigpe3JldHVybiB0aGlzLmp9dXBkYXRlKHQpe2lmKCF0KXJldHVybiB0aGlzO2NvbnN0IGU9ImZ1bmN0aW9uIj09dHlwZW9mIHQ/dCh0aGlzKTp0LG49ZSBpbnN0YW5jZW9mIEs/ZS5nZXRTY29wZURhdGEoKTpDKGUpP3Q6dm9pZCAwLHt0YWdzOnIsYXR0cmlidXRlczpvLGV4dHJhOnMsdXNlcjppLGNvbnRleHRzOmMsbGV2ZWw6dSxmaW5nZXJwcmludDphPVtdLHByb3BhZ2F0aW9uQ29udGV4dDpmLGNvbnZlcnNhdGlvbklkOmh9PW58fHt9O3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi5yfSx0aGlzLm09ey4uLnRoaXMubSwuLi5vfSx0aGlzLnY9ey4uLnRoaXMudiwuLi5zfSx0aGlzLlM9ey4uLnRoaXMuUywuLi5jfSxpJiZPYmplY3Qua2V5cyhpKS5sZW5ndGgmJih0aGlzLnA9aSksdSYmKHRoaXMuQz11KSxhLmxlbmd0aCYmKHRoaXMuUD1hKSxmJiYodGhpcy5OPWYpLGgmJih0aGlzLlQ9aCksdGhpc31hZGRCcmVhZGNydW1iKHQsZSl7Y29uc3Qgbj0ibnVtYmVyIj09dHlwZW9mIGU/ZToxMDA7aWYobjw9MClyZXR1cm4gdGhpcztjb25zdCByPXt0aW1lc3RhbXA6RigpLC4uLnQsbWVzc2FnZTp0Lm1lc3NhZ2U/eih0Lm1lc3NhZ2UsMjA0OCk6dC5tZXNzYWdlfTtyZXR1cm4gdGhpcy51LnB1c2gociksdGhpcy51Lmxlbmd0aD5uJiYodGhpcy51PXRoaXMudS5zbGljZSgtbikpLHRoaXMuUigpLHRoaXN9Z2V0TGFzdEJyZWFkY3J1bWIoKXtyZXR1cm4gdGhpcy51W3RoaXMudS5sZW5ndGgtMV19Y2xlYXJCcmVhZGNydW1icygpe3JldHVybiB0aGlzLnU9W10sdGhpcy5SKCksdGhpc31hZGRBdHRhY2htZW50KHQpe3JldHVybiB0aGlzLmgucHVzaCh0KSx0aGlzfWNsZWFyQXR0YWNobWVudHMoKXtyZXR1cm4gdGhpcy5oPVtdLHRoaXN9Z2V0U2NvcGVEYXRhKCl7cmV0dXJue2JyZWFkY3J1bWJzOnRoaXMudSxhdHRhY2htZW50czp0aGlzLmgsY29udGV4dHM6dGhpcy5TLHRhZ3M6dGhpcy5sLGF0dHJpYnV0ZXM6dGhpcy5tLGV4dHJhOnRoaXMudix1c2VyOnRoaXMucCxsZXZlbDp0aGlzLkMsZmluZ2VycHJpbnQ6dGhpcy5QfHxbXSxldmVudFByb2Nlc3NvcnM6dGhpcy5pLHByb3BhZ2F0aW9uQ29udGV4dDp0aGlzLk4sc2RrUHJvY2Vzc2luZ01ldGFkYXRhOnRoaXMuXyx0cmFuc2FjdGlvbk5hbWU6dGhpcy5PLGNvbnZlcnNhdGlvbklkOnRoaXMuVH19c2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKHQpe3JldHVybiB0aGlzLl89cSh0aGlzLl8sdCwyKSx0aGlzfXNldFByb3BhZ2F0aW9uQ29udGV4dCh0KXtyZXR1cm4gdGhpcy5OPXQsdGhpc31nZXRQcm9wYWdhdGlvbkNvbnRleHQoKXtyZXR1cm4gdGhpcy5OfWNhcHR1cmVFeGNlcHRpb24odCxlKXtjb25zdCBuPWU/LmV2ZW50X2lkfHxXKCk7aWYoIXRoaXMuaylyZXR1cm4gaCYmXy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXhjZXB0aW9uISIpLG47Y29uc3Qgcj1uZXcgRXJyb3IoIlNlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24iKTtyZXR1cm4gdGhpcy5rLmNhcHR1cmVFeGNlcHRpb24odCx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246ciwuLi5lLGV2ZW50X2lkOm59LHRoaXMpLG59Y2FwdHVyZU1lc3NhZ2UodCxlLG4pe2NvbnN0IHI9bj8uZXZlbnRfaWR8fFcoKTtpZighdGhpcy5rKXJldHVybiBoJiZfLndhcm4oIk5vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlISIpLHI7Y29uc3Qgbz1uPy5zeW50aGV0aWNFeGNlcHRpb24/P25ldyBFcnJvcih0KTtyZXR1cm4gdGhpcy5rLmNhcHR1cmVNZXNzYWdlKHQsZSx7b3JpZ2luYWxFeGNlcHRpb246dCxzeW50aGV0aWNFeGNlcHRpb246bywuLi5uLGV2ZW50X2lkOnJ9LHRoaXMpLHJ9Y2FwdHVyZUV2ZW50KHQsZSl7Y29uc3Qgbj10LmV2ZW50X2lkfHxlPy5ldmVudF9pZHx8VygpO3JldHVybiB0aGlzLms/KHRoaXMuay5jYXB0dXJlRXZlbnQodCx7Li4uZSxldmVudF9pZDpufSx0aGlzKSxuKTooaCYmXy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhIiksbil9Uigpe3RoaXMudHx8KHRoaXMudD0hMCx0aGlzLm8uZm9yRWFjaCh0PT57dCh0aGlzKX0pLHRoaXMudD0hMSl9fWNvbnN0IFY9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyIsUT10PT50IGluc3RhbmNlb2YgUHJvbWlzZSYmIXRbWF0sWD1TeW1ib2woImNoYWluZWQgUHJvbWlzZUxpa2UiKSx0dD0odCxlKT0+e2lmKCFlKXJldHVybiB0O2xldCBuPSExO2Zvcihjb25zdCByIGluIHQpe2lmKHIgaW4gZSljb250aW51ZTtuPSEwO2NvbnN0IG89dFtyXTsiZnVuY3Rpb24iPT10eXBlb2Ygbz9PYmplY3QuZGVmaW5lUHJvcGVydHkoZSxyLHt2YWx1ZTooLi4uZSk9Pm8uYXBwbHkodCxlKSxlbnVtZXJhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMCx3cml0YWJsZTohMH0pOmVbcl09b31yZXR1cm4gbiYmT2JqZWN0LmFzc2lnbihlLHtbWF06ITB9KSxlfTtjbGFzcyBldHtjb25zdHJ1Y3Rvcih0LGUpe2xldCBuLHI7bj10fHxuZXcgSyxyPWV8fG5ldyBLLHRoaXMuST1be3Njb3BlOm59XSx0aGlzLkQ9cn13aXRoU2NvcGUodCl7Y29uc3QgZT10aGlzLlUoKTtsZXQgbjt0cnl7bj10KGUpfWNhdGNoKHQpe3Rocm93IHRoaXMuQigpLHR9cmV0dXJuIGoobik/KCh0LGUsbik9Pntjb25zdCByPXQudGhlbih0PT4oZSh0KSx0KSx0PT57dGhyb3cgbih0KSx0fSk7cmV0dXJuIFEocikmJlEodCk/cjp0dCh0LHIpfSkobiwoKT0+dGhpcy5CKCksKCk9PnRoaXMuQigpKToodGhpcy5CKCksbil9Z2V0Q2xpZW50KCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnR9Z2V0U2NvcGUoKXtyZXR1cm4gdGhpcy5nZXRTdGFja1RvcCgpLnNjb3BlfWdldElzb2xhdGlvblNjb3BlKCl7cmV0dXJuIHRoaXMuRH1nZXRTdGFja1RvcCgpe3JldHVybiB0aGlzLklbdGhpcy5JLmxlbmd0aC0xXX1VKCl7Y29uc3QgdD10aGlzLmdldFNjb3BlKCkuY2xvbmUoKTtyZXR1cm4gdGhpcy5JLnB1c2goe2NsaWVudDp0aGlzLmdldENsaWVudCgpLHNjb3BlOnR9KSx0fUIoKXtyZXR1cm4hKHRoaXMuSS5sZW5ndGg8PTEpJiYhIXRoaXMuSS5wb3AoKX19ZnVuY3Rpb24gbnQoKXtjb25zdCB0PWcoZCgpKTtyZXR1cm4gdC5zdGFjaz10LnN0YWNrfHxuZXcgZXQobSgiZGVmYXVsdEN1cnJlbnRTY29wZSIsKCk9Pm5ldyBLKSxtKCJkZWZhdWx0SXNvbGF0aW9uU2NvcGUiLCgpPT5uZXcgSykpfWZ1bmN0aW9uIHJ0KHQpe3JldHVybiBudCgpLndpdGhTY29wZSh0KX1mdW5jdGlvbiBvdCh0LGUpe2NvbnN0IG49bnQoKTtyZXR1cm4gbi53aXRoU2NvcGUoKCk9PihuLmdldFN0YWNrVG9wKCkuc2NvcGU9dCxlKHQpKSl9ZnVuY3Rpb24gc3QodCl7cmV0dXJuIG50KCkud2l0aFNjb3BlKCgpPT50KG50KCkuZ2V0SXNvbGF0aW9uU2NvcGUoKSkpfWZ1bmN0aW9uIGl0KHQpe3JldHVybiBydChlPT57ZS5zZXRTREtQcm9jZXNzaW5nTWV0YWRhdGEoe1tWXTohMH0pO2NvbnN0IG49dCgpO3JldHVybiBlLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1ZdOnZvaWQgMH0pLG59KX1mdW5jdGlvbiBjdCh0KXtjb25zdCBlPWcodCk7cmV0dXJuIGUuYWNzP2UuYWNzOntzdXBwcmVzc1RyYWNpbmc6aXQsd2l0aElzb2xhdGlvblNjb3BlOnN0LHdpdGhTY29wZTpydCx3aXRoU2V0U2NvcGU6b3Qsd2l0aFNldElzb2xhdGlvblNjb3BlOih0LGUpPT5zdChlKSxnZXRDdXJyZW50U2NvcGU6KCk9Pm50KCkuZ2V0U2NvcGUoKSxnZXRJc29sYXRpb25TY29wZTooKT0+bnQoKS5nZXRJc29sYXRpb25TY29wZSgpfX1mdW5jdGlvbiB1dCh0LGU9ITEpe2NvbnN0e2hvc3Q6bixwYXRoOnIscGFzczpvLHBvcnQ6cyxwcm9qZWN0SWQ6aSxwcm90b2NvbDpjLHB1YmxpY0tleTp1fT10O3JldHVybmAke2N9Oi8vJHt1fSR7ZSYmbz9gOiR7b31gOiIifUAke259JHtzP2A6JHtzfWA6IiJ9LyR7cj9gJHtyfS9gOnJ9JHtpfWB9ZnVuY3Rpb24gYXQodCxlKXtjb25zdHtmaW5nZXJwcmludDpuLGJyZWFkY3J1bWJzOnIsc2RrUHJvY2Vzc2luZ01ldGFkYXRhOm99PWU7IWZ1bmN0aW9uKHQsZSl7Y29uc3R7ZXh0cmE6bix0YWdzOnIsdXNlcjpvLGNvbnRleHRzOnMsbGV2ZWw6aSx0cmFuc2FjdGlvbk5hbWU6Y309ZTtPYmplY3Qua2V5cyhuKS5sZW5ndGgmJih0LmV4dHJhPXsuLi5uLC4uLnQuZXh0cmF9KTtPYmplY3Qua2V5cyhyKS5sZW5ndGgmJih0LnRhZ3M9ey4uLnIsLi4udC50YWdzfSk7T2JqZWN0LmtleXMobykubGVuZ3RoJiYodC51c2VyPXsuLi5vLC4uLnQudXNlcn0pO09iamVjdC5rZXlzKHMpLmxlbmd0aCYmKHQuY29udGV4dHM9ey4uLnMsLi4udC5jb250ZXh0c30pO2kmJih0LmxldmVsPWkpO2MmJiJ0cmFuc2FjdGlvbiIhPT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPWMpfSh0LGUpLGZ1bmN0aW9uKHQsZSl7dC5maW5nZXJwcmludD10LmZpbmdlcnByaW50P0FycmF5LmlzQXJyYXkodC5maW5nZXJwcmludCk/dC5maW5nZXJwcmludDpbdC5maW5nZXJwcmludF06W10sZSYmKHQuZmluZ2VycHJpbnQ9dC5maW5nZXJwcmludC5jb25jYXQoZSkpO3QuZmluZ2VycHJpbnQubGVuZ3RofHxkZWxldGUgdC5maW5nZXJwcmludH0odCxuKSxmdW5jdGlvbih0LGUpe2NvbnN0IG49Wy4uLnQuYnJlYWRjcnVtYnN8fFtdLC4uLmVdO3QuYnJlYWRjcnVtYnM9bi5sZW5ndGg/bjp2b2lkIDB9KHQsciksZnVuY3Rpb24odCxlKXt0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT17Li4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEsLi4uZX19KHQsbyl9ZnVuY3Rpb24gZnQodCxlPVtdKXtyZXR1cm5bdCxlXX1mdW5jdGlvbiBodCh0LGUpe2NvbnN0IG49dFsxXTtmb3IoY29uc3QgdCBvZiBuKXtpZihlKHQsdFswXS50eXBlKSlyZXR1cm4hMH1yZXR1cm4hMX1mdW5jdGlvbiBwdCh0KXtjb25zdCBlPWcocCk7cmV0dXJuIGUuZW5jb2RlUG9seWZpbGw/ZS5lbmNvZGVQb2x5ZmlsbCh0KToobmV3IFRleHRFbmNvZGVyKS5lbmNvZGUodCl9ZnVuY3Rpb24gbHQodCl7Y29uc3RbZSxuXT10O2xldCByPUpTT04uc3RyaW5naWZ5KGUpO2Z1bmN0aW9uIG8odCl7InN0cmluZyI9PXR5cGVvZiByP3I9InN0cmluZyI9PXR5cGVvZiB0P3IrdDpbcHQociksdF06ci5wdXNoKCJzdHJpbmciPT10eXBlb2YgdD9wdCh0KTp0KX1mb3IoY29uc3QgdCBvZiBuKXtjb25zdFtlLG5dPXQ7aWYobyhgXG4ke0pTT04uc3RyaW5naWZ5KGUpfVxuYCksInN0cmluZyI9PXR5cGVvZiBufHxuIGluc3RhbmNlb2YgVWludDhBcnJheSlvKG4pO2Vsc2V7bGV0IHQ7dHJ5e3Q9SlNPTi5zdHJpbmdpZnkobil9Y2F0Y2h7dD1KU09OLnN0cmluZ2lmeShCKG4pKX1vKHQpfX1yZXR1cm4ic3RyaW5nIj09dHlwZW9mIHI/cjpmdW5jdGlvbih0KXtjb25zdCBlPXQucmVkdWNlKCh0LGUpPT50K2UubGVuZ3RoLDApLG49bmV3IFVpbnQ4QXJyYXkoZSk7bGV0IHI9MDtmb3IoY29uc3QgZSBvZiB0KW4uc2V0KGUscikscis9ZS5sZW5ndGg7cmV0dXJuIG59KHIpfWNvbnN0IGR0PXtzZXNzaW9uczoic2Vzc2lvbiIsZXZlbnQ6ImVycm9yIixjbGllbnRfcmVwb3J0OiJpbnRlcm5hbCIsdXNlcl9yZXBvcnQ6ImRlZmF1bHQiLHByb2ZpbGVfY2h1bms6InByb2ZpbGUiLHJlcGxheV9ldmVudDoicmVwbGF5IixyZXBsYXlfcmVjb3JkaW5nOiJyZXBsYXkiLGNoZWNrX2luOiJtb25pdG9yIixyYXdfc2VjdXJpdHk6InNlY3VyaXR5Iixsb2c6ImxvZ19pdGVtIix0cmFjZV9tZXRyaWM6Im1ldHJpYyJ9O2Z1bmN0aW9uIGd0KHQpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gdCBpbiBkdH0odCk/ZHRbdF06dH1mdW5jdGlvbiBtdCh0KXtpZighdD8uc2RrKXJldHVybjtjb25zdHtuYW1lOmUsdmVyc2lvbjpufT10LnNkaztyZXR1cm57bmFtZTplLHZlcnNpb246bn19ZnVuY3Rpb24geXQodCl7Y29uc3QgZT1jdChkKCkpO3JldHVybiBlLnN1cHByZXNzVHJhY2luZz9lLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBlPWN0KGQoKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtuLHJdPXQ7cmV0dXJuIG4/ZS53aXRoU2V0U2NvcGUobixyKTplLndpdGhTY29wZShyKX1yZXR1cm4gZS53aXRoU2NvcGUodFswXSl9KGU9PihlLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1ZdOiEwfSksdCgpKSl9ZnVuY3Rpb24gYnQodCxlLG4scil7Y29uc3Qgbz1tdChuKSxzPXQudHlwZSYmInJlcGxheV9ldmVudCIhPT10LnR5cGU/dC50eXBlOiJldmVudCI7IWZ1bmN0aW9uKHQsZSl7aWYoIWUpcmV0dXJuIHQ7Y29uc3Qgbj10LnNka3x8e307dC5zZGs9ey4uLm4sbmFtZTpuLm5hbWV8fGUubmFtZSx2ZXJzaW9uOm4udmVyc2lvbnx8ZS52ZXJzaW9uLGludGVncmF0aW9uczpbLi4udC5zZGs/LmludGVncmF0aW9uc3x8W10sLi4uZS5pbnRlZ3JhdGlvbnN8fFtdXSxwYWNrYWdlczpbLi4udC5zZGs/LnBhY2thZ2VzfHxbXSwuLi5lLnBhY2thZ2VzfHxbXV0sc2V0dGluZ3M6dC5zZGs/LnNldHRpbmdzfHxlLnNldHRpbmdzP3suLi50LnNkaz8uc2V0dGluZ3MsLi4uZS5zZXR0aW5nc306dm9pZCAwfX0odCxuPy5zZGspO2NvbnN0IGk9ZnVuY3Rpb24odCxlLG4scil7Y29uc3Qgbz10LnNka1Byb2Nlc3NpbmdNZXRhZGF0YT8uZHluYW1pY1NhbXBsaW5nQ29udGV4dDtyZXR1cm57ZXZlbnRfaWQ6dC5ldmVudF9pZCxzZW50X2F0Om5ldyBEYXRlKEkoKSkudG9JU09TdHJpbmcoKSwuLi5lJiZ7c2RrOmV9LC4uLiEhbiYmciYme2Rzbjp1dChyKX0sLi4ubyYme3RyYWNlOm99fX0odCxvLHIsZSk7ZGVsZXRlIHQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhO3JldHVybiBmdChpLFtbe3R5cGU6c30sdF1dKX1jbGFzcyB2dHtjb25zdHJ1Y3Rvcih0KXt0aGlzLkw9MCx0aGlzLk09W10sdGhpcy5KKHQpfXRoZW4odCxlKXtyZXR1cm4gbmV3IHZ0KChuLHIpPT57dGhpcy5NLnB1c2goWyExLGU9PntpZih0KXRyeXtuKHQoZSkpfWNhdGNoKHQpe3IodCl9ZWxzZSBuKGUpfSx0PT57aWYoZSl0cnl7bihlKHQpKX1jYXRjaCh0KXtyKHQpfWVsc2Ugcih0KX1dKSx0aGlzLlcoKX0pfWNhdGNoKHQpe3JldHVybiB0aGlzLnRoZW4odD0+dCx0KX1maW5hbGx5KHQpe3JldHVybiBuZXcgdnQoKGUsbik9PntsZXQgcixvO3JldHVybiB0aGlzLnRoZW4oZT0+e289ITEscj1lLHQmJnQoKX0sZT0+e289ITAscj1lLHQmJnQoKX0pLnRoZW4oKCk9PntvP24ocik6ZShyKX0pfSl9Vygpe2lmKDA9PT10aGlzLkwpcmV0dXJuO2NvbnN0IHQ9dGhpcy5NLnNsaWNlKCk7dGhpcy5NPVtdLHQuZm9yRWFjaCh0PT57dFswXXx8KDE9PT10aGlzLkwmJnRbMV0odGhpcy5GKSwyPT09dGhpcy5MJiZ0WzJdKHRoaXMuRiksdFswXT0hMCl9KX1KKHQpe2NvbnN0IGU9KHQsZSk9PnswPT09dGhpcy5MJiYoaihlKT9lLnRoZW4obixyKToodGhpcy5MPXQsdGhpcy5GPWUsdGhpcy5XKCkpKX0sbj10PT57ZSgxLHQpfSxyPXQ9PntlKDIsdCl9O3RyeXt0KG4scil9Y2F0Y2godCl7cih0KX19fWNvbnN0IHd0PVN5bWJvbC5mb3IoIlNlbnRyeUJ1ZmZlckZ1bGxFcnJvciIpO2Z1bmN0aW9uIFN0KHQ9MTAwKXtjb25zdCBlPW5ldyBTZXQ7ZnVuY3Rpb24gbih0KXtlLmRlbGV0ZSh0KX1yZXR1cm57Z2V0ICQoKXtyZXR1cm4gQXJyYXkuZnJvbShlKX0sYWRkOmZ1bmN0aW9uKHIpe2lmKCEoZS5zaXplPHQpKXJldHVybiBvPXd0LG5ldyB2dCgodCxlKT0+e2Uobyl9KTt2YXIgbztjb25zdCBzPXIoKTtyZXR1cm4gZS5hZGQocykscy50aGVuKCgpPT5uKHMpLCgpPT5uKHMpKSxzfSxkcmFpbjpmdW5jdGlvbih0KXtpZighZS5zaXplKXJldHVybiBuPSEwLG5ldyB2dCh0PT57dChuKX0pO3ZhciBuO2NvbnN0IHI9UHJvbWlzZS5hbGxTZXR0bGVkKEFycmF5LmZyb20oZSkpLnRoZW4oKCk9PiEwKTtpZighdClyZXR1cm4gcjtjb25zdCBvPVtyLG5ldyBQcm9taXNlKGU9PntyZXR1cm4ib2JqZWN0Ij09dHlwZW9mKG49c2V0VGltZW91dCgoKT0+ZSghMSksdCkpJiYiZnVuY3Rpb24iPT10eXBlb2Ygbi51bnJlZiYmbi51bnJlZigpLG47dmFyIG59KV07cmV0dXJuIFByb21pc2UucmFjZShvKX19fWZ1bmN0aW9uIF90KHQse3N0YXR1c0NvZGU6ZSxoZWFkZXJzOm59LHI9SSgpKXtjb25zdCBvPXsuLi50fSxzPW4/LlsieC1zZW50cnktcmF0ZS1saW1pdHMiXSxpPW4/LlsicmV0cnktYWZ0ZXIiXTtpZihzKWZvcihjb25zdCB0IG9mIHMudHJpbSgpLnNwbGl0KCIsIikpe2NvbnN0W2UsbiwsLHNdPXQuc3BsaXQoIjoiLDUpLGk9cGFyc2VJbnQoZSwxMCksYz0xZTMqKGlzTmFOKGkpPzYwOmkpO2lmKG4pZm9yKGNvbnN0IHQgb2Ygbi5zcGxpdCgiOyIpKSJtZXRyaWNfYnVja2V0Ij09PXQmJnMmJiFzLnNwbGl0KCI7IikuaW5jbHVkZXMoImN1c3RvbSIpfHwob1t0XT1yK2MpO2Vsc2Ugby5hbGw9citjfWVsc2UgaT9vLmFsbD1yK2Z1bmN0aW9uKHQsZT1JKCkpe2NvbnN0IG49cGFyc2VJbnQoYCR7dH1gLDEwKTtpZighaXNOYU4obikpcmV0dXJuIDFlMypuO2NvbnN0IHI9RGF0ZS5wYXJzZShgJHt0fWApO3JldHVybiBpc05hTihyKT82ZTQ6ci1lfShpLHIpOjQyOT09PWUmJihvLmFsbD1yKzZlNCk7cmV0dXJuIG99ZnVuY3Rpb24gJHQodCxlLG49U3QodC5idWZmZXJTaXplfHw2NCkpe2xldCByPXt9O3JldHVybntzZW5kOmZ1bmN0aW9uKHQpe2NvbnN0IG89W107aWYoaHQodCwodCxlKT0+e2NvbnN0IG49Z3QoZSk7KGZ1bmN0aW9uKHQsZSxuPUkoKSl7cmV0dXJuIGZ1bmN0aW9uKHQsZSl7cmV0dXJuIHRbZV18fHQuYWxsfHwwfSh0LGUpPm59KShyLG4pfHxvLnB1c2godCl9KSwwPT09by5sZW5ndGgpcmV0dXJuIFByb21pc2UucmVzb2x2ZSh7fSk7Y29uc3Qgcz1mdCh0WzBdLG8pLGk9dD0+eyFmdW5jdGlvbih0LGUpe3JldHVybiBodCh0LCh0LG4pPT5lLmluY2x1ZGVzKG4pKX0ocyxbImNsaWVudF9yZXBvcnQiXSk/aHQocywodCxlKT0+e30pOmgmJl8ud2FybihgRHJvcHBpbmcgY2xpZW50IHJlcG9ydC4gV2lsbCBub3Qgc2VuZCBvdXRjb21lcyAocmVhc29uOiAke3R9KS5gKX07cmV0dXJuIG4uYWRkKCgpPT5lKHtib2R5Omx0KHMpfSkudGhlbih0PT40MTM9PT10LnN0YXR1c0NvZGU/KGgmJl8uZXJyb3IoIlNlbnRyeSByZXNwb25kZWQgd2l0aCBzdGF0dXMgY29kZSA0MTMuIEVudmVsb3BlIHdhcyBkaXNjYXJkZWQgZHVlIHRvIGV4Y2VlZGluZyBzaXplIGxpbWl0cy4iKSxpKCJzZW5kX2Vycm9yIiksdCk6KGgmJnZvaWQgMCE9PXQuc3RhdHVzQ29kZSYmKHQuc3RhdHVzQ29kZTwyMDB8fHQuc3RhdHVzQ29kZT49MzAwKSYmXy53YXJuKGBTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgJHt0LnN0YXR1c0NvZGV9IHRvIHNlbnQgZXZlbnQuYCkscj1fdChyLHQpLHQpLHQ9Pnt0aHJvdyBpKCJuZXR3b3JrX2Vycm9yIiksaCYmXy5lcnJvcigiRW5jb3VudGVyZWQgZXJyb3IgcnVubmluZyB0cmFuc3BvcnQgcmVxdWVzdDoiLHQpLHR9KSkudGhlbih0PT50LHQ9PntpZih0PT09d3QpcmV0dXJuIGgmJl8uZXJyb3IoIlNraXBwZWQgc2VuZGluZyBldmVudCBiZWNhdXNlIGJ1ZmZlciBpcyBmdWxsLiIpLGkoInF1ZXVlX292ZXJmbG93IiksUHJvbWlzZS5yZXNvbHZlKHt9KTt0aHJvdyB0fSl9LGZsdXNoOnQ9Pm4uZHJhaW4odCl9fWNvbnN0IEV0PS9eKFxTKzpcXHxcLz8pKFtcc1xTXSo/KSgoPzpcLnsxLDJ9fFteL1xcXSs/fCkoXC5bXi4vXFxdKnwpKSg/OlsvXFxdKikkLztmdW5jdGlvbiB4dCh0KXtjb25zdCBlPWZ1bmN0aW9uKHQpe2NvbnN0IGU9dC5sZW5ndGg+MTAyND9gPHRydW5jYXRlZD4ke3Quc2xpY2UoLTEwMjQpfWA6dCxuPUV0LmV4ZWMoZSk7cmV0dXJuIG4/bi5zbGljZSgxKTpbXX0odCksbj1lWzBdfHwiIjtsZXQgcj1lWzFdO3JldHVybiBufHxyPyhyJiYocj1yLnNsaWNlKDAsci5sZW5ndGgtMSkpLG4rcik6Ii4ifWZ1bmN0aW9uIE50KHQsZT0hMSl7cmV0dXJuIShlfHx0JiYhdC5zdGFydHNXaXRoKCIvIikmJiF0Lm1hdGNoKC9eW0EtWl06LykmJiF0LnN0YXJ0c1dpdGgoIi4iKSYmIXQubWF0Y2goL15bYS16QS1aXShbYS16QS1aMC05LlwtK10pKjpcL1wvLykpJiZ2b2lkIDAhPT10JiYhdC5pbmNsdWRlcygibm9kZV9tb2R1bGVzLyIpfXZhciBDdDtjb25zdCBqdD1TeW1ib2woIkFnZW50QmFzZUludGVybmFsU3RhdGUiKTtjbGFzcyBPdCBleHRlbmRzKEN0PXMuQWdlbnQsQ3Qpe2NvbnN0cnVjdG9yKHQpe3N1cGVyKHQpLHRoaXNbanRdPXt9fWlzU2VjdXJlRW5kcG9pbnQodCl7aWYodCl7aWYoImJvb2xlYW4iPT10eXBlb2YgdC5zZWN1cmVFbmRwb2ludClyZXR1cm4gdC5zZWN1cmVFbmRwb2ludDtpZigic3RyaW5nIj09dHlwZW9mIHQucHJvdG9jb2wpcmV0dXJuImh0dHBzOiI9PT10LnByb3RvY29sfWNvbnN0e3N0YWNrOmV9PW5ldyBFcnJvcjtyZXR1cm4ic3RyaW5nIj09dHlwZW9mIGUmJmUuc3BsaXQoIlxuIikuc29tZSh0PT4tMSE9PXQuaW5kZXhPZigiKGh0dHBzLmpzOiIpfHwtMSE9PXQuaW5kZXhPZigibm9kZTpodHRwczoiKSl9Y3JlYXRlU29ja2V0KHQsZSxuKXtjb25zdCByPXsuLi5lLHNlY3VyZUVuZHBvaW50OnRoaXMuaXNTZWN1cmVFbmRwb2ludChlKX07UHJvbWlzZS5yZXNvbHZlKCkudGhlbigoKT0+dGhpcy5jb25uZWN0KHQscikpLnRoZW4obz0+e2lmKG8gaW5zdGFuY2VvZiBzLkFnZW50KXJldHVybiBvLmFkZFJlcXVlc3QodCxyKTt0aGlzW2p0XS5jdXJyZW50U29ja2V0PW8sc3VwZXIuY3JlYXRlU29ja2V0KHQsZSxuKX0sbil9Y3JlYXRlQ29ubmVjdGlvbigpe2NvbnN0IHQ9dGhpc1tqdF0uY3VycmVudFNvY2tldDtpZih0aGlzW2p0XS5jdXJyZW50U29ja2V0PXZvaWQgMCwhdCl0aHJvdyBuZXcgRXJyb3IoIk5vIHNvY2tldCB3YXMgcmV0dXJuZWQgaW4gdGhlIGBjb25uZWN0KClgIGZ1bmN0aW9uIik7cmV0dXJuIHR9Z2V0IGRlZmF1bHRQb3J0KCl7cmV0dXJuIHRoaXNbanRdLmRlZmF1bHRQb3J0Pz8oImh0dHBzOiI9PT10aGlzLnByb3RvY29sPzQ0Mzo4MCl9c2V0IGRlZmF1bHRQb3J0KHQpe3RoaXNbanRdJiYodGhpc1tqdF0uZGVmYXVsdFBvcnQ9dCl9Z2V0IHByb3RvY29sKCl7cmV0dXJuIHRoaXNbanRdLnByb3RvY29sPz8odGhpcy5pc1NlY3VyZUVuZHBvaW50KCk/Imh0dHBzOiI6Imh0dHA6Iil9c2V0IHByb3RvY29sKHQpe3RoaXNbanRdJiYodGhpc1tqdF0ucHJvdG9jb2w9dCl9fWZ1bmN0aW9uIFB0KC4uLnQpe18ubG9nKCJbaHR0cHMtcHJveHktYWdlbnQ6cGFyc2UtcHJveHktcmVzcG9uc2VdIiwuLi50KX1mdW5jdGlvbiBrdCh0KXtyZXR1cm4gbmV3IFByb21pc2UoKGUsbik9PntsZXQgcj0wO2NvbnN0IG89W107ZnVuY3Rpb24gcygpe2NvbnN0IGM9dC5yZWFkKCk7Yz9mdW5jdGlvbihjKXtvLnB1c2goYykscis9Yy5sZW5ndGg7Y29uc3QgdT1CdWZmZXIuY29uY2F0KG8sciksYT11LmluZGV4T2YoIlxyXG5cclxuIik7aWYoLTE9PT1hKXJldHVybiBQdCgiaGF2ZSBub3QgcmVjZWl2ZWQgZW5kIG9mIEhUVFAgaGVhZGVycyB5ZXQuLi4iKSx2b2lkIHMoKTtjb25zdCBmPXUuc3ViYXJyYXkoMCxhKS50b1N0cmluZygiYXNjaWkiKS5zcGxpdCgiXHJcbiIpLGg9Zi5zaGlmdCgpO2lmKCFoKXJldHVybiB0LmRlc3Ryb3koKSxuKG5ldyBFcnJvcigiTm8gaGVhZGVyIHJlY2VpdmVkIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZSIpKTtjb25zdCBwPWguc3BsaXQoIiAiKSxsPSsocFsxXXx8MCksZD1wLnNsaWNlKDIpLmpvaW4oIiAiKSxnPXt9O2Zvcihjb25zdCBlIG9mIGYpe2lmKCFlKWNvbnRpbnVlO2NvbnN0IHI9ZS5pbmRleE9mKCI6Iik7aWYoLTE9PT1yKXJldHVybiB0LmRlc3Ryb3koKSxuKG5ldyBFcnJvcihgSW52YWxpZCBoZWFkZXIgZnJvbSBwcm94eSBDT05ORUNUIHJlc3BvbnNlOiAiJHtlfSJgKSk7Y29uc3Qgbz1lLnNsaWNlKDAscikudG9Mb3dlckNhc2UoKSxzPWUuc2xpY2UocisxKS50cmltU3RhcnQoKSxpPWdbb107InN0cmluZyI9PXR5cGVvZiBpP2dbb109W2ksc106QXJyYXkuaXNBcnJheShpKT9pLnB1c2gocyk6Z1tvXT1zfVB0KCJnb3QgcHJveHkgc2VydmVyIHJlc3BvbnNlOiAlbyAlbyIsaCxnKSxpKCksZSh7Y29ubmVjdDp7c3RhdHVzQ29kZTpsLHN0YXR1c1RleHQ6ZCxoZWFkZXJzOmd9LGJ1ZmZlcmVkOnV9KX0oYyk6dC5vbmNlKCJyZWFkYWJsZSIscyl9ZnVuY3Rpb24gaSgpe3QucmVtb3ZlTGlzdGVuZXIoImVuZCIsYyksdC5yZW1vdmVMaXN0ZW5lcigiZXJyb3IiLHUpLHQucmVtb3ZlTGlzdGVuZXIoInJlYWRhYmxlIixzKX1mdW5jdGlvbiBjKCl7aSgpLFB0KCJvbmVuZCIpLG4obmV3IEVycm9yKCJQcm94eSBjb25uZWN0aW9uIGVuZGVkIGJlZm9yZSByZWNlaXZpbmcgQ09OTkVDVCByZXNwb25zZSIpKX1mdW5jdGlvbiB1KHQpe2koKSxQdCgib25lcnJvciAlbyIsdCksbih0KX10Lm9uKCJlcnJvciIsdSksdC5vbigiZW5kIixjKSxzKCl9KX1mdW5jdGlvbiBBdCguLi50KXtfLmxvZygiW2h0dHBzLXByb3h5LWFnZW50XSIsLi4udCl9Y2xhc3MgVHQgZXh0ZW5kcyBPdHtjb25zdHJ1Y3Rvcih0LGUpe3N1cGVyKGUpLHRoaXMub3B0aW9ucz17fSx0aGlzLnByb3h5PSJzdHJpbmciPT10eXBlb2YgdD9uZXcgVVJMKHQpOnQsdGhpcy5wcm94eUhlYWRlcnM9ZT8uaGVhZGVycz8/e30sQXQoIkNyZWF0aW5nIG5ldyBIdHRwc1Byb3h5QWdlbnQgaW5zdGFuY2U6ICVvIix0aGlzLnByb3h5LmhyZWYpO2NvbnN0IG49KHRoaXMucHJveHkuaG9zdG5hbWV8fHRoaXMucHJveHkuaG9zdCkucmVwbGFjZSgvXlxbfFxdJC9nLCIiKSxyPXRoaXMucHJveHkucG9ydD9wYXJzZUludCh0aGlzLnByb3h5LnBvcnQsMTApOiJodHRwczoiPT09dGhpcy5wcm94eS5wcm90b2NvbD80NDM6ODA7dGhpcy5jb25uZWN0T3B0cz17QUxQTlByb3RvY29sczpbImh0dHAvMS4xIl0sLi4uZT9JdChlLCJoZWFkZXJzIik6bnVsbCxob3N0Om4scG9ydDpyfX1hc3luYyBjb25uZWN0KHQsZSl7Y29uc3R7cHJveHk6bn09dGhpcztpZighZS5ob3N0KXRocm93IG5ldyBUeXBlRXJyb3IoJ05vICJob3N0IiBwcm92aWRlZCcpO2xldCByO2lmKCJodHRwczoiPT09bi5wcm90b2NvbCl7QXQoIkNyZWF0aW5nIGB0bHMuU29ja2V0YDogJW8iLHRoaXMuY29ubmVjdE9wdHMpO2NvbnN0IHQ9dGhpcy5jb25uZWN0T3B0cy5zZXJ2ZXJuYW1lfHx0aGlzLmNvbm5lY3RPcHRzLmhvc3Q7cj1mLmNvbm5lY3Qoey4uLnRoaXMuY29ubmVjdE9wdHMsc2VydmVybmFtZTp0JiZhLmlzSVAodCk/dm9pZCAwOnR9KX1lbHNlIEF0KCJDcmVhdGluZyBgbmV0LlNvY2tldGA6ICVvIix0aGlzLmNvbm5lY3RPcHRzKSxyPWEuY29ubmVjdCh0aGlzLmNvbm5lY3RPcHRzKTtjb25zdCBvPSJmdW5jdGlvbiI9PXR5cGVvZiB0aGlzLnByb3h5SGVhZGVycz90aGlzLnByb3h5SGVhZGVycygpOnsuLi50aGlzLnByb3h5SGVhZGVyc30scz1hLmlzSVB2NihlLmhvc3QpP2BbJHtlLmhvc3R9XWA6ZS5ob3N0O2xldCBpPWBDT05ORUNUICR7c306JHtlLnBvcnR9IEhUVFAvMS4xXHJcbmA7aWYobi51c2VybmFtZXx8bi5wYXNzd29yZCl7Y29uc3QgdD1gJHtkZWNvZGVVUklDb21wb25lbnQobi51c2VybmFtZSl9OiR7ZGVjb2RlVVJJQ29tcG9uZW50KG4ucGFzc3dvcmQpfWA7b1siUHJveHktQXV0aG9yaXphdGlvbiJdPWBCYXNpYyAke0J1ZmZlci5mcm9tKHQpLnRvU3RyaW5nKCJiYXNlNjQiKX1gfW8uSG9zdD1gJHtzfToke2UucG9ydH1gLG9bIlByb3h5LUNvbm5lY3Rpb24iXXx8KG9bIlByb3h5LUNvbm5lY3Rpb24iXT10aGlzLmtlZXBBbGl2ZT8iS2VlcC1BbGl2ZSI6ImNsb3NlIik7Zm9yKGNvbnN0IHQgb2YgT2JqZWN0LmtleXMobykpaSs9YCR7dH06ICR7b1t0XX1cclxuYDtjb25zdCBjPWt0KHIpO3Iud3JpdGUoYCR7aX1cclxuYCk7Y29uc3R7Y29ubmVjdDp1LGJ1ZmZlcmVkOmh9PWF3YWl0IGM7aWYodC5lbWl0KCJwcm94eUNvbm5lY3QiLHUpLHRoaXMuZW1pdCgicHJveHlDb25uZWN0Iix1LHQpLDIwMD09PXUuc3RhdHVzQ29kZSl7aWYodC5vbmNlKCJzb2NrZXQiLFJ0KSxlLnNlY3VyZUVuZHBvaW50KXtBdCgiVXBncmFkaW5nIHNvY2tldCBjb25uZWN0aW9uIHRvIFRMUyIpO2NvbnN0IHQ9ZS5zZXJ2ZXJuYW1lfHxlLmhvc3Q7cmV0dXJuIGYuY29ubmVjdCh7Li4uSXQoZSwiaG9zdCIsInBhdGgiLCJwb3J0Iiksc29ja2V0OnIsc2VydmVybmFtZTphLmlzSVAodCk/dm9pZCAwOnR9KX1yZXR1cm4gcn1yLmRlc3Ryb3koKTtjb25zdCBwPW5ldyBhLlNvY2tldCh7d3JpdGFibGU6ITF9KTtyZXR1cm4gcC5yZWFkYWJsZT0hMCx0Lm9uY2UoInNvY2tldCIsdD0+e0F0KCJSZXBsYXlpbmcgcHJveHkgYnVmZmVyIGZvciBmYWlsZWQgcmVxdWVzdCIpLHQucHVzaChoKSx0LnB1c2gobnVsbCl9KSxwfX1mdW5jdGlvbiBSdCh0KXt0LnJlc3VtZSgpfWZ1bmN0aW9uIEl0KHQsLi4uZSl7Y29uc3Qgbj17fTtsZXQgcjtmb3IociBpbiB0KWUuaW5jbHVkZXMocil8fChuW3JdPXRbcl0pO3JldHVybiBufVR0LnByb3RvY29scz1bImh0dHAiLCJodHRwcyJdO2Z1bmN0aW9uIER0KHQpe3JldHVybiB0LnJlcGxhY2UoL15bQS1aXTovLCIiKS5yZXBsYWNlKC9cXC9nLCIvIil9Y29uc3QgVXQ9ZTtsZXQgQnQsTHQ9MCxNdD17fTtmdW5jdGlvbiB6dCh0KXtVdC5kZWJ1ZyYmY29uc29sZS5sb2coYFtBTlIgV29ya2VyXSAke3R9YCl9dmFyIEp0LFd0LEZ0O2NvbnN0IEd0PWZ1bmN0aW9uKHQpe2xldCBlO3RyeXtlPW5ldyBVUkwodC51cmwpfWNhdGNoe3JldHVybiBiKCgpPT57Y29uc29sZS53YXJuKCJbQHNlbnRyeS9ub2RlXTogSW52YWxpZCBkc24gb3IgdHVubmVsIG9wdGlvbiwgd2lsbCBub3Qgc2VuZCBhbnkgZXZlbnRzLiBUaGUgdHVubmVsIG9wdGlvbiBtdXN0IGJlIGEgZnVsbCBVUkwgd2hlbiB1c2VkLiIpfSksJHQodCwoKT0+UHJvbWlzZS5yZXNvbHZlKHt9KSl9Y29uc3Qgbj0iaHR0cHM6Ij09PWUucHJvdG9jb2wscj1mdW5jdGlvbih0LGUpe2NvbnN0e25vX3Byb3h5Om59PXByb2Nlc3MuZW52LHI9bj8uc3BsaXQoIiwiKS5zb21lKGU9PnQuaG9zdC5lbmRzV2l0aChlKXx8dC5ob3N0bmFtZS5lbmRzV2l0aChlKSk7cmV0dXJuIHI/dm9pZCAwOmV9KGUsdC5wcm94eXx8KG4/cHJvY2Vzcy5lbnYuaHR0cHNfcHJveHk6dm9pZCAwKXx8cHJvY2Vzcy5lbnYuaHR0cF9wcm94eSksbz1uP2k6cyxhPXQua2VlcEFsaXZlPz8hMCxmPXI/bmV3IFR0KHIpOm5ldyBvLkFnZW50KHtrZWVwQWxpdmU6YSxtYXhTb2NrZXRzOjMwLHRpbWVvdXQ6MmUzfSksaD1mdW5jdGlvbih0LGUsbil7Y29uc3R7aG9zdG5hbWU6cixwYXRobmFtZTpvLHBvcnQ6cyxwcm90b2NvbDppLHNlYXJjaDphfT1uZXcgVVJMKHQudXJsKTtyZXR1cm4gZnVuY3Rpb24oZil7Y29uc3QgaD1wPT5uZXcgUHJvbWlzZSgobCxkKT0+e3l0KCgpPT57bGV0IGc9ZnVuY3Rpb24odCl7cmV0dXJuIG5ldyBjKHtyZWFkKCl7dGhpcy5wdXNoKHQpLHRoaXMucHVzaChudWxsKX19KX0oZi5ib2R5KTtjb25zdCBtPXsuLi50LmhlYWRlcnN9O2YuYm9keS5sZW5ndGg+MzI3NjgmJihtWyJjb250ZW50LWVuY29kaW5nIl09Imd6aXAiLGc9Zy5waXBlKHUoKSkpO2NvbnN0IHk9ci5zdGFydHNXaXRoKCJbIiksYj1lLnJlcXVlc3Qoe21ldGhvZDoiUE9TVCIsYWdlbnQ6bixoZWFkZXJzOm0saG9zdG5hbWU6eT9yLnNsaWNlKDEsLTEpOnIscGF0aDpgJHtvfSR7YX1gLHBvcnQ6cyxwcm90b2NvbDppLGNhOnQuY2FDZXJ0c30sdD0+e3Qub24oImRhdGEiLCgpPT57fSksdC5vbigiZW5kIiwoKT0+e30pLHQuc2V0RW5jb2RpbmcoInV0ZjgiKTtjb25zdCBlPXQuaGVhZGVyc1sicmV0cnktYWZ0ZXIiXT8/bnVsbCxuPXQuaGVhZGVyc1sieC1zZW50cnktcmF0ZS1saW1pdHMiXT8/bnVsbDtsKHtzdGF0dXNDb2RlOnQuc3RhdHVzQ29kZSxoZWFkZXJzOnsicmV0cnktYWZ0ZXIiOmUsIngtc2VudHJ5LXJhdGUtbGltaXRzIjpBcnJheS5pc0FycmF5KG4pP25bMF18fG51bGw6bn19KX0pO2Iub24oImVycm9yIix0PT57cCYmYi5yZXVzZWRTb2NrZXQmJiJFQ09OTlJFU0VUIj09PXQuY29kZT9sKGgoITEpKTpkKHQpfSksZy5waXBlKGIpfSl9KTtyZXR1cm4gaCghMCl9fSh0LHQuaHR0cE1vZHVsZT8/byxmKTtyZXR1cm4gJHQodCxoKX0oe3VybDooSnQ9VXQuZHNuLFd0PVV0LnR1bm5lbCxGdD1VdC5zZGtNZXRhZGF0YS5zZGssV3R8fGAke2Z1bmN0aW9uKHQpe3JldHVybmAke2Z1bmN0aW9uKHQpe2NvbnN0IGU9dC5wcm90b2NvbD9gJHt0LnByb3RvY29sfTpgOiIiLG49dC5wb3J0P2A6JHt0LnBvcnR9YDoiIjtyZXR1cm5gJHtlfS8vJHt0Lmhvc3R9JHtufSR7dC5wYXRoP2AvJHt0LnBhdGh9YDoiIn0vYXBpL2B9KHQpfSR7dC5wcm9qZWN0SWR9L2VudmVsb3BlL2B9KEp0KX0/JHtmdW5jdGlvbih0LGUpe2NvbnN0IG49e3NlbnRyeV92ZXJzaW9uOiI3In07cmV0dXJuIHQucHVibGljS2V5JiYobi5zZW50cnlfa2V5PXQucHVibGljS2V5KSxlJiYobi5zZW50cnlfY2xpZW50PWAke2UubmFtZX0vJHtlLnZlcnNpb259YCksbmV3IFVSTFNlYXJjaFBhcmFtcyhuKS50b1N0cmluZygpfShKdCxGdCl9YCl9KTthc3luYyBmdW5jdGlvbiBZdCgpe2lmKEJ0KXt6dCgiU2VuZGluZyBhYm5vcm1hbCBzZXNzaW9uIiksWihCdCx7c3RhdHVzOiJhYm5vcm1hbCIsYWJub3JtYWxfbWVjaGFuaXNtOiJhbnJfZm9yZWdyb3VuZCIscmVsZWFzZTpVdC5yZWxlYXNlLGVudmlyb25tZW50OlV0LmVudmlyb25tZW50fSk7Y29uc3QgdD1mdW5jdGlvbih0LGUsbixyKXtjb25zdCBvPW10KG4pO3JldHVybiBmdCh7c2VudF9hdDpuZXcgRGF0ZShJKCkpLnRvSVNPU3RyaW5nKCksLi4ubyYme3NkazpvfSwuLi4hIXImJmUmJntkc246dXQoZSl9fSxbImFnZ3JlZ2F0ZXMiaW4gdD9be3R5cGU6InNlc3Npb25zIn0sdF06W3t0eXBlOiJzZXNzaW9uIn0sdC50b0pTT04oKV1dKX0oQnQsVXQuZHNuLFV0LnNka01ldGFkYXRhLFV0LnR1bm5lbCk7enQoSlNPTi5zdHJpbmdpZnkodCkpLGF3YWl0IEd0LnNlbmQodCk7dHJ5e24/LnBvc3RNZXNzYWdlKCJzZXNzaW9uLWVuZGVkIil9Y2F0Y2h7fX19ZnVuY3Rpb24gSHQodCl7aWYoIXQpcmV0dXJuO2NvbnN0IGU9ZnVuY3Rpb24odCl7aWYoIXQubGVuZ3RoKXJldHVybltdO2NvbnN0IGU9QXJyYXkuZnJvbSh0KTtyZXR1cm4vc2VudHJ5V3JhcHBlZC8udGVzdChFKGUpLmZ1bmN0aW9ufHwiIikmJmUucG9wKCksZS5yZXZlcnNlKCksJC50ZXN0KEUoZSkuZnVuY3Rpb258fCIiKSYmKGUucG9wKCksJC50ZXN0KEUoZSkuZnVuY3Rpb258fCIiKSYmZS5wb3AoKSksZS5zbGljZSgwLDUwKS5tYXAodD0+KHsuLi50LGZpbGVuYW1lOnQuZmlsZW5hbWV8fEUoZSkuZmlsZW5hbWUsZnVuY3Rpb246dC5mdW5jdGlvbnx8Ij8ifSkpfSh0KTtpZihVdC5hcHBSb290UGF0aClmb3IoY29uc3QgdCBvZiBlKXQuZmlsZW5hbWUmJih0LmZpbGVuYW1lPU0odC5maWxlbmFtZSxVdC5hcHBSb290UGF0aCkpO3JldHVybiBlfWFzeW5jIGZ1bmN0aW9uIFp0KHQsZSl7aWYoTHQ+PVV0Lm1heEFuckV2ZW50cylyZXR1cm47THQrPTEsYXdhaXQgWXQoKSx6dCgiU2VuZGluZyBldmVudCIpO2NvbnN0IG49e2V2ZW50X2lkOlcoKSxjb250ZXh0czpVdC5jb250ZXh0cyxyZWxlYXNlOlV0LnJlbGVhc2UsZW52aXJvbm1lbnQ6VXQuZW52aXJvbm1lbnQsZGlzdDpVdC5kaXN0LHBsYXRmb3JtOiJub2RlIixsZXZlbDoiZXJyb3IiLGV4Y2VwdGlvbjp7dmFsdWVzOlt7dHlwZToiQXBwbGljYXRpb25Ob3RSZXNwb25kaW5nIix2YWx1ZTpgQXBwbGljYXRpb24gTm90IFJlc3BvbmRpbmcgZm9yIGF0IGxlYXN0ICR7VXQuYW5yVGhyZXNob2xkfSBtc2Asc3RhY2t0cmFjZTp7ZnJhbWVzOkh0KHQpfSxtZWNoYW5pc206e3R5cGU6IkFOUiJ9fV19LHRhZ3M6VXQuc3RhdGljVGFnc307ZSYmZnVuY3Rpb24odCxlKXtpZihhdCh0LGUpLCF0LmNvbnRleHRzPy50cmFjZSl7Y29uc3R7dHJhY2VJZDpuLHBhcmVudFNwYW5JZDpyLHByb3BhZ2F0aW9uU3BhbklkOm99PWUucHJvcGFnYXRpb25Db250ZXh0O3QuY29udGV4dHM9e3RyYWNlOnt0cmFjZV9pZDpuLHNwYW5faWQ6b3x8VygpLnN1YnN0cmluZygxNikscGFyZW50X3NwYW5faWQ6cn0sLi4udC5jb250ZXh0c319fShuLGUpLGZ1bmN0aW9uKHQpe2lmKDA9PT1PYmplY3Qua2V5cyhNdCkubGVuZ3RoKXJldHVybjtjb25zdCBlPVV0LmFwcFJvb3RQYXRoP3t9Ok10O2lmKFV0LmFwcFJvb3RQYXRoKWZvcihjb25zdFt0LG5db2YgT2JqZWN0LmVudHJpZXMoTXQpKWVbTSh0LFV0LmFwcFJvb3RQYXRoKV09bjtjb25zdCBuPW5ldyBNYXA7Zm9yKGNvbnN0IHIgb2YgdC5leGNlcHRpb24/LnZhbHVlc3x8W10pZm9yKGNvbnN0IHQgb2Ygci5zdGFja3RyYWNlPy5mcmFtZXN8fFtdKXtjb25zdCByPXQuYWJzX3BhdGh8fHQuZmlsZW5hbWU7ciYmZVtyXSYmbi5zZXQocixlW3JdKX1pZihuLnNpemU+MCl7Y29uc3QgZT1bXTtmb3IoY29uc3RbdCxyXW9mIG4uZW50cmllcygpKWUucHVzaCh7dHlwZToic291cmNlbWFwIixjb2RlX2ZpbGU6dCxkZWJ1Z19pZDpyfSk7dC5kZWJ1Z19tZXRhPXtpbWFnZXM6ZX19fShuKTtjb25zdCByPWJ0KG4sVXQuZHNuLFV0LnNka01ldGFkYXRhLFV0LnR1bm5lbCk7enQoSlNPTi5zdHJpbmdpZnkocikpLGF3YWl0IEd0LnNlbmQociksYXdhaXQgR3QuZmx1c2goMmUzKSxMdD49VXQubWF4QW5yRXZlbnRzJiZzZXRUaW1lb3V0KCgpPT57cHJvY2Vzcy5leGl0KDApfSw1ZTMpfWxldCBxdDtpZih6dCgiU3RhcnRlZCIpLFV0LmNhcHR1cmVTdGFja1RyYWNlKXt6dCgiQ29ubmVjdGluZyB0byBkZWJ1Z2dlciIpO2NvbnN0IGU9bmV3IHQ7ZS5jb25uZWN0VG9NYWluVGhyZWFkKCksenQoIkNvbm5lY3RlZCB0byBkZWJ1Z2dlciIpO2NvbnN0IG49bmV3IE1hcDtlLm9uKCJEZWJ1Z2dlci5zY3JpcHRQYXJzZWQiLHQ9PntuLnNldCh0LnBhcmFtcy5zY3JpcHRJZCx0LnBhcmFtcy51cmwpfSksZS5vbigiRGVidWdnZXIucGF1c2VkIix0PT57aWYoIm90aGVyIj09PXQucGFyYW1zLnJlYXNvbil0cnl7enQoIkRlYnVnZ2VyIHBhdXNlZCIpO2NvbnN0IHM9Wy4uLnQucGFyYW1zLmNhbGxGcmFtZXNdLGk9VXQuYXBwUm9vdFBhdGg/ZnVuY3Rpb24odD0ocHJvY2Vzcy5hcmd2WzFdP3h0KHByb2Nlc3MuYXJndlsxXSk6cHJvY2Vzcy5jd2QoKSksZT0iXFwiPT09byl7Y29uc3Qgbj1lP0R0KHQpOnQ7cmV0dXJuIHQ9PntpZighdClyZXR1cm47Y29uc3Qgbz1lP0R0KHQpOnQ7bGV0e2RpcjpzLGJhc2U6aSxleHQ6Y309ci5wYXJzZShvKTsiLmpzIiE9PWMmJiIubWpzIiE9PWMmJiIuY2pzIiE9PWN8fChpPWkuc2xpY2UoMCwtMSpjLmxlbmd0aCkpO2NvbnN0IHU9ZGVjb2RlVVJJQ29tcG9uZW50KGkpO3N8fChzPSIuIik7Y29uc3QgYT1zLmxhc3RJbmRleE9mKCIvbm9kZV9tb2R1bGVzIik7aWYoYT4tMSlyZXR1cm5gJHtzLnNsaWNlKGErMTQpLnJlcGxhY2UoL1wvL2csIi4iKX06JHt1fWA7aWYocy5zdGFydHNXaXRoKG4pKXtjb25zdCB0PXMuc2xpY2Uobi5sZW5ndGgrMSkucmVwbGFjZSgvXC8vZywiLiIpO3JldHVybiB0P2Ake3R9OiR7dX1gOnV9cmV0dXJuIHV9fShVdC5hcHBSb290UGF0aCk6KCk9Pnt9LGM9cy5tYXAodD0+ZnVuY3Rpb24odCxlLG4pe2NvbnN0IHI9ZT9lLnJlcGxhY2UoL15maWxlOlwvXC8vLCIiKTp2b2lkIDAsbz10LmxvY2F0aW9uLmNvbHVtbk51bWJlcj90LmxvY2F0aW9uLmNvbHVtbk51bWJlcisxOnZvaWQgMCxzPXQubG9jYXRpb24ubGluZU51bWJlcj90LmxvY2F0aW9uLmxpbmVOdW1iZXIrMTp2b2lkIDA7cmV0dXJue2ZpbGVuYW1lOnIsbW9kdWxlOm4ociksZnVuY3Rpb246dC5mdW5jdGlvbk5hbWV8fCI/Iixjb2xubzpvLGxpbmVubzpzLGluX2FwcDpyP050KHIpOnZvaWQgMH19KHQsbi5nZXQodC5sb2NhdGlvbi5zY3JpcHRJZCksaSkpLHU9c2V0VGltZW91dCgoKT0+e1p0KGMpLnRoZW4obnVsbCwoKT0+e3p0KCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQuIil9KX0sNWUzKTtlLnBvc3QoIlJ1bnRpbWUuZXZhbHVhdGUiLHtleHByZXNzaW9uOiJnbG9iYWwuX19TRU5UUllfR0VUX1NDT1BFU19fKCk7IixzaWxlbnQ6ITAscmV0dXJuQnlWYWx1ZTohMH0sKHQsbik9Pnt0JiZ6dChgRXJyb3IgZXhlY3V0aW5nIHNjcmlwdDogJyR7dC5tZXNzYWdlfSdgKSxjbGVhclRpbWVvdXQodSk7Y29uc3Qgcj1uPy5yZXN1bHQ/bi5yZXN1bHQudmFsdWU6dm9pZCAwO2UucG9zdCgiRGVidWdnZXIucmVzdW1lIiksZS5wb3N0KCJEZWJ1Z2dlci5kaXNhYmxlIiksWnQoYyxyKS50aGVuKG51bGwsKCk9Pnt6dCgiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSl9KX1jYXRjaCh0KXt0aHJvdyBlLnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpLGUucG9zdCgiRGVidWdnZXIuZGlzYWJsZSIpLHR9fSkscXQ9KCk9Pnt0cnl7ZS5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiLCgpPT57ZS5wb3N0KCJEZWJ1Z2dlci5wYXVzZSIpfSl9Y2F0Y2h7fX19Y29uc3R7cG9sbDpLdH09ZnVuY3Rpb24odCxlLG4scil7Y29uc3Qgbz10KCk7bGV0IHM9ITEsaT0hMDtyZXR1cm4gc2V0SW50ZXJ2YWwoKCk9Pntjb25zdCB0PW8uZ2V0VGltZU1zKCk7ITE9PT1zJiZ0PmUrbiYmKHM9ITAsaSYmcigpKSx0PGUrbiYmKHM9ITEpfSwyMCkse3BvbGw6KCk9PntvLnJlc2V0KCl9LGVuYWJsZWQ6dD0+e2k9dH19fShmdW5jdGlvbigpe2xldCB0PXByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO3JldHVybntnZXRUaW1lTXM6KCk9Pk51bWJlcigocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCktdCkvMTAwMDAwMG4pLHJlc2V0OigpPT57dD1wcm9jZXNzLmhydGltZS5iaWdpbnQoKX19fSxVdC5wb2xsSW50ZXJ2YWwsVXQuYW5yVGhyZXNob2xkLGZ1bmN0aW9uKCl7enQoIldhdGNoZG9nIHRpbWVvdXQiKSxxdD8oenQoIlBhdXNpbmcgZGVidWdnZXIgdG8gY2FwdHVyZSBzdGFjayB0cmFjZSIpLHF0KCkpOih6dCgiQ2FwdHVyaW5nIGV2ZW50IHdpdGhvdXQgYSBzdGFjayB0cmFjZSIpLFp0KCkudGhlbihudWxsLCgpPT57enQoIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZCBvbiB3YXRjaGRvZyB0aW1lb3V0LiIpfSkpfSk7bj8ub24oIm1lc3NhZ2UiLHQ9Pnt0LnNlc3Npb24mJihCdD1IKHQuc2Vzc2lvbikpLHQuZGVidWdJbWFnZXMmJihNdD10LmRlYnVnSW1hZ2VzKSxLdCgpfSksbj8ucG9zdE1lc3NhZ2UoIndvcmtlci1yZWFkeSIpOw==";
|
|
10
10
|
const DEFAULT_INTERVAL = 50;
|
|
11
11
|
const DEFAULT_HANG_THRESHOLD = 5e3;
|
|
12
12
|
function log(message, ...args) {
|
|
@@ -26,6 +26,7 @@ async function getContexts(client) {
|
|
|
26
26
|
const eventHint = {};
|
|
27
27
|
for (const processor of client.getEventProcessors()) {
|
|
28
28
|
if (event === null) break;
|
|
29
|
+
if (processor.id === "Dedupe") continue;
|
|
29
30
|
event = await processor(event, eventHint);
|
|
30
31
|
}
|
|
31
32
|
return event?.contexts || {};
|
|
@@ -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 = {\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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"}
|
|
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 // Dedupe would drop this placeholder event when the worker restarts, and the contexts would be lost.\n if (processor.id === 'Dedupe') continue;\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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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const diagnosticsChannel = require('node:diagnostics_channel');
|
|
4
3
|
const core = require('@sentry/core');
|
|
4
|
+
const serverUtils = require('@sentry/server-utils');
|
|
5
5
|
|
|
6
6
|
const INTEGRATION_NAME = "ChildProcess";
|
|
7
7
|
const childProcessIntegration = core.defineIntegration((options = {}) => {
|
|
8
8
|
return {
|
|
9
9
|
name: INTEGRATION_NAME,
|
|
10
10
|
setup() {
|
|
11
|
-
|
|
11
|
+
serverUtils.subscribeDiagnosticsChannel("child_process", (event) => {
|
|
12
12
|
if (core.isObjectLike(event) && "process" in event) {
|
|
13
13
|
captureChildProcessEvents(event.process, options);
|
|
14
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"childProcess.js","sources":["../../../src/integrations/childProcess.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport
|
|
1
|
+
{"version":3,"file":"childProcess.js","sources":["../../../src/integrations/childProcess.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport { addBreadcrumb, defineIntegration, isObjectLike } from '@sentry/core';\nimport { subscribeDiagnosticsChannel } from '@sentry/server-utils';\n\ninterface Options {\n /**\n * Whether to include child process arguments in breadcrumbs data.\n *\n * @default false\n */\n includeChildProcessArgs?: boolean;\n}\n\nconst INTEGRATION_NAME = 'ChildProcess' as const;\n\n/**\n * Capture breadcrumbs and events for child processes.\n * For worker thread events, use `workerThreadsIntegration()` instead.\n */\nexport const childProcessIntegration = defineIntegration((options: Options = {}) => {\n return {\n name: INTEGRATION_NAME,\n setup() {\n subscribeDiagnosticsChannel('child_process', (event: unknown) => {\n if (isObjectLike(event) && 'process' in event) {\n captureChildProcessEvents(event.process as ChildProcess, options);\n }\n });\n },\n };\n});\n\nfunction captureChildProcessEvents(child: ChildProcess, options: Options): void {\n let hasExited = false;\n let data: Record<string, unknown> | undefined;\n\n child\n .on('spawn', () => {\n // This is Sentry getting macOS OS context\n if (child.spawnfile === '/usr/bin/sw_vers') {\n hasExited = true;\n return;\n }\n\n data = { spawnfile: child.spawnfile };\n if (options.includeChildProcessArgs) {\n data.spawnargs = child.spawnargs;\n }\n })\n .on('exit', code => {\n if (!hasExited) {\n hasExited = true;\n\n // Only log for non-zero exit codes\n if (code !== null && code !== 0) {\n addBreadcrumb({\n category: 'child_process',\n message: `Child process exited with code '${code}'`,\n level: code === 0 ? 'info' : 'warning',\n data,\n });\n }\n }\n })\n .on('error', error => {\n if (!hasExited) {\n hasExited = true;\n\n addBreadcrumb({\n category: 'child_process',\n message: `Child process errored with '${error.message}'`,\n level: 'error',\n data,\n });\n }\n });\n}\n"],"names":["defineIntegration","subscribeDiagnosticsChannel","isObjectLike","addBreadcrumb"],"mappings":";;;;;AAaA,MAAM,gBAAA,GAAmB,cAAA;AAMlB,MAAM,uBAAA,GAA0BA,sBAAA,CAAkB,CAAC,OAAA,GAAmB,EAAC,KAAM;AAClF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,KAAA,GAAQ;AACN,MAAAC,uCAAA,CAA4B,eAAA,EAAiB,CAAC,KAAA,KAAmB;AAC/D,QAAA,IAAIC,iBAAA,CAAa,KAAK,CAAA,IAAK,SAAA,IAAa,KAAA,EAAO;AAC7C,UAAA,yBAAA,CAA0B,KAAA,CAAM,SAAyB,OAAO,CAAA;AAAA,QAClE;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AACF,CAAC;AAED,SAAS,yBAAA,CAA0B,OAAqB,OAAA,EAAwB;AAC9E,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,IAAA;AAEJ,EAAA,KAAA,CACG,EAAA,CAAG,SAAS,MAAM;AAEjB,IAAA,IAAI,KAAA,CAAM,cAAc,kBAAA,EAAoB;AAC1C,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,GAAO,EAAE,SAAA,EAAW,KAAA,CAAM,SAAA,EAAU;AACpC,IAAA,IAAI,QAAQ,uBAAA,EAAyB;AACnC,MAAA,IAAA,CAAK,YAAY,KAAA,CAAM,SAAA;AAAA,IACzB;AAAA,EACF,CAAC,CAAA,CACA,EAAA,CAAG,MAAA,EAAQ,CAAA,IAAA,KAAQ;AAClB,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,SAAA,GAAY,IAAA;AAGZ,MAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,CAAA,EAAG;AAC/B,QAAAC,kBAAA,CAAc;AAAA,UACZ,QAAA,EAAU,eAAA;AAAA,UACV,OAAA,EAAS,mCAAmC,IAAI,CAAA,CAAA,CAAA;AAAA,UAChD,KAAA,EAAO,IAAA,KAAS,CAAA,GAAI,MAAA,GAAS,SAAA;AAAA,UAC7B;AAAA,SACD,CAAA;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC,CAAA,CACA,EAAA,CAAG,OAAA,EAAS,CAAA,KAAA,KAAS;AACpB,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,SAAA,GAAY,IAAA;AAEZ,MAAAA,kBAAA,CAAc;AAAA,QACZ,QAAA,EAAU,eAAA;AAAA,QACV,OAAA,EAAS,CAAA,4BAAA,EAA+B,KAAA,CAAM,OAAO,CAAA,CAAA,CAAA;AAAA,QACrD,KAAA,EAAO,OAAA;AAAA,QACP;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF,CAAC,CAAA;AACL;;;;"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const diagnosticsChannel = require('node:diagnostics_channel');
|
|
4
3
|
const api = require('@opentelemetry/api');
|
|
5
4
|
const core = require('@sentry/core');
|
|
6
5
|
const server = require('@sentry/core/server');
|
|
6
|
+
const serverUtils = require('@sentry/server-utils');
|
|
7
7
|
const nodeVersion = require('../../nodeVersion.js');
|
|
8
8
|
const node_events = require('node:events');
|
|
9
9
|
const http = require('node:http');
|
|
@@ -52,7 +52,7 @@ function instrumentHttpOutgoingRequests(instrumentationOptions = {}) {
|
|
|
52
52
|
}
|
|
53
53
|
function instrumentHttpOutgoingRequestsViaChannel(options) {
|
|
54
54
|
const { [server.HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = server.getHttpClientSubscriptions(options);
|
|
55
|
-
|
|
55
|
+
serverUtils.subscribeDiagnosticsChannel(server.HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);
|
|
56
56
|
}
|
|
57
57
|
function instrumentHttpOutgoingRequestsViaMonkeyPatching(options) {
|
|
58
58
|
server.patchHttpModuleClient(http, options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SentryHttpInstrumentation.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentation.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"SentryHttpInstrumentation.js","sources":["../../../../src/integrations/http/SentryHttpInstrumentation.ts"],"sourcesContent":["import { context, trace } from '@opentelemetry/api';\nimport type { ClientRequest, IncomingMessage } from 'node:http';\nimport type { Span } from '@sentry/core';\nimport { isTracingSuppressed } from '@sentry/core';\nimport type { HttpClientRequest, HttpIncomingMessage, HttpInstrumentationOptions } from '@sentry/core/server';\nimport {\n getHttpClientSubscriptions,\n getRequestOptions,\n HTTP_ON_CLIENT_REQUEST,\n patchHttpModuleClient,\n} from '@sentry/core/server';\nimport { subscribeDiagnosticsChannel } from '@sentry/server-utils';\nimport { NODE_VERSION } from '../../nodeVersion';\nimport { errorMonitor } from 'node:events';\nimport * as http from 'node:http';\n\nconst FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL =\n (NODE_VERSION.major === 22 && NODE_VERSION.minor >= 12) ||\n (NODE_VERSION.major === 23 && NODE_VERSION.minor >= 2) ||\n NODE_VERSION.major >= 24;\n\nexport interface OutgoingHttpRequestInstrumentationOptions {\n /**\n * Whether breadcrumbs should be recorded for outgoing requests.\n *\n * @default `true`\n */\n breadcrumbs?: boolean;\n\n /**\n * Whether to create spans for outgoing requests.\n *\n * @default `true`\n */\n spans?: boolean;\n\n /**\n * Whether to propagate Sentry trace headers in outgoing requests.\n *\n * @default `true`\n */\n tracePropagation?: boolean;\n\n /**\n * Do not instrument outgoing HTTP requests to URLs where the given callback returns `true`.\n * When it returns `true`, the request is skipped entirely: no breadcrumb and no span are created,\n * and no trace headers are propagated for that request.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.\n * @param request Contains the {@type RequestOptions} object used to make the outgoing request.\n */\n ignoreOutgoingRequests?: (url: string, request: http.RequestOptions) => boolean;\n\n /**\n * Hooks for outgoing request spans, only called when spans are created for outgoing requests\n * (i.e. when `spans` is enabled).\n */\n outgoingRequestHook?: (span: Span, request: ClientRequest) => void;\n outgoingResponseHook?: (span: Span, response: IncomingMessage) => void;\n outgoingRequestApplyCustomAttributes?: (\n span: Span,\n request: HttpClientRequest,\n response: HttpIncomingMessage,\n ) => void;\n}\n\n/**\n * This instruments the http modules for outgoing requests.\n * It uses the diagnostics channel if available, otherwise it falls back to monkey-patching.\n *\n * The instrumentation will start spans, create breadcrumbs, and propagate trace headers in outgoing requests (depending on the settings).\n */\nexport function instrumentHttpOutgoingRequests(\n instrumentationOptions: OutgoingHttpRequestInstrumentationOptions = {},\n): void {\n const { outgoingRequestApplyCustomAttributes: applyCustomAttributesOnSpan, ...options } = instrumentationOptions;\n const patchOptions = {\n applyCustomAttributesOnSpan,\n ...options,\n tracePropagation: options.tracePropagation ?? true,\n spans: options.spans ?? true,\n ignoreOutgoingRequests(url, request) {\n return isTracingSuppressed() || !!options.ignoreOutgoingRequests?.(url, getRequestOptions(request));\n },\n outgoingRequestHook(span, request) {\n options.outgoingRequestHook?.(span, request as ClientRequest);\n // We monkey-patch `req.once('response'), which is used to trigger\n // the callback of the request, so that it runs in the active context\n // eslint-disable-next-line @typescript-eslint/unbound-method, typescript/no-deprecated\n const originalOnce = request.once;\n\n const newOnce = new Proxy(originalOnce, {\n apply(target, thisArg, args: Parameters<typeof originalOnce>) {\n const [event] = args;\n if (event !== 'response') {\n return target.apply(thisArg, args);\n }\n\n const parentContext = context.active();\n const requestContext = trace.setSpan(parentContext, span);\n\n return context.with(requestContext, () => {\n return target.apply(thisArg, args);\n });\n },\n });\n\n // eslint-disable-next-line typescript/no-deprecated\n request.once = newOnce;\n },\n outgoingResponseHook(span, response) {\n options.outgoingResponseHook?.(span, response as IncomingMessage);\n },\n errorMonitor,\n // Pass these in to detect OTel double-wrapping if we're enabling spans\n http,\n } satisfies HttpInstrumentationOptions;\n\n if (FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) {\n instrumentHttpOutgoingRequestsViaChannel(patchOptions);\n } else {\n instrumentHttpOutgoingRequestsViaMonkeyPatching(patchOptions);\n }\n}\n\nfunction instrumentHttpOutgoingRequestsViaChannel(options: HttpInstrumentationOptions): void {\n const { [HTTP_ON_CLIENT_REQUEST]: onHttpClientRequestCreated } = getHttpClientSubscriptions(options);\n subscribeDiagnosticsChannel(HTTP_ON_CLIENT_REQUEST, onHttpClientRequestCreated);\n}\n\n/**\n * Instrument outgoing HTTP(S) requests by old-school monkey-patching the `http` module.\n * This is the fallback for runtimes that do not fully support the `node:http` client diagnostics channel\n * (Node < 22.12).\n */\nfunction instrumentHttpOutgoingRequestsViaMonkeyPatching(options: HttpInstrumentationOptions): void {\n // Patching http also patches https, as this uses the same underlying object\n patchHttpModuleClient(http, options);\n}\n"],"names":["NODE_VERSION","isTracingSuppressed","getRequestOptions","context","trace","errorMonitor","HTTP_ON_CLIENT_REQUEST","getHttpClientSubscriptions","subscribeDiagnosticsChannel","patchHttpModuleClient"],"mappings":";;;;;;;;;;AAgBA,MAAM,uCAAA,GACHA,wBAAA,CAAa,KAAA,KAAU,EAAA,IAAMA,yBAAa,KAAA,IAAS,EAAA,IACnDA,wBAAA,CAAa,KAAA,KAAU,EAAA,IAAMA,wBAAA,CAAa,KAAA,IAAS,CAAA,IACpDA,yBAAa,KAAA,IAAS,EAAA;AAqDjB,SAAS,8BAAA,CACd,sBAAA,GAAoE,EAAC,EAC/D;AACN,EAAA,MAAM,EAAE,oCAAA,EAAsC,2BAAA,EAA6B,GAAG,SAAQ,GAAI,sBAAA;AAC1F,EAAA,MAAM,YAAA,GAAe;AAAA,IACnB,2BAAA;AAAA,IACA,GAAG,OAAA;AAAA,IACH,gBAAA,EAAkB,QAAQ,gBAAA,IAAoB,IAAA;AAAA,IAC9C,KAAA,EAAO,QAAQ,KAAA,IAAS,IAAA;AAAA,IACxB,sBAAA,CAAuB,KAAK,OAAA,EAAS;AACnC,MAAA,OAAOC,wBAAA,MAAyB,CAAC,CAAC,QAAQ,sBAAA,GAAyB,GAAA,EAAKC,wBAAA,CAAkB,OAAO,CAAC,CAAA;AAAA,IACpG,CAAA;AAAA,IACA,mBAAA,CAAoB,MAAM,OAAA,EAAS;AACjC,MAAA,OAAA,CAAQ,mBAAA,GAAsB,MAAM,OAAwB,CAAA;AAI5D,MAAA,MAAM,eAAe,OAAA,CAAQ,IAAA;AAE7B,MAAA,MAAM,OAAA,GAAU,IAAI,KAAA,CAAM,YAAA,EAAc;AAAA,QACtC,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAuC;AAC5D,UAAA,MAAM,CAAC,KAAK,CAAA,GAAI,IAAA;AAChB,UAAA,IAAI,UAAU,UAAA,EAAY;AACxB,YAAA,OAAO,MAAA,CAAO,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,UACnC;AAEA,UAAA,MAAM,aAAA,GAAgBC,YAAQ,MAAA,EAAO;AACrC,UAAA,MAAM,cAAA,GAAiBC,SAAA,CAAM,OAAA,CAAQ,aAAA,EAAe,IAAI,CAAA;AAExD,UAAA,OAAOD,WAAA,CAAQ,IAAA,CAAK,cAAA,EAAgB,MAAM;AACxC,YAAA,OAAO,MAAA,CAAO,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,UACnC,CAAC,CAAA;AAAA,QACH;AAAA,OACD,CAAA;AAGD,MAAA,OAAA,CAAQ,IAAA,GAAO,OAAA;AAAA,IACjB,CAAA;AAAA,IACA,oBAAA,CAAqB,MAAM,QAAA,EAAU;AACnC,MAAA,OAAA,CAAQ,oBAAA,GAAuB,MAAM,QAA2B,CAAA;AAAA,IAClE,CAAA;AAAA,kBACAE,wBAAA;AAAA;AAAA,IAEA;AAAA,GACF;AAEA,EAAA,IAAI,uCAAA,EAAyC;AAC3C,IAAA,wCAAA,CAAyC,YAAY,CAAA;AAAA,EACvD,CAAA,MAAO;AACL,IAAA,+CAAA,CAAgD,YAAY,CAAA;AAAA,EAC9D;AACF;AAEA,SAAS,yCAAyC,OAAA,EAA2C;AAC3F,EAAA,MAAM,EAAE,CAACC,6BAAsB,GAAG,0BAAA,EAA2B,GAAIC,kCAA2B,OAAO,CAAA;AACnG,EAAAC,uCAAA,CAA4BF,+BAAwB,0BAA0B,CAAA;AAChF;AAOA,SAAS,gDAAgD,OAAA,EAA2C;AAElG,EAAAG,4BAAA,CAAsB,MAAM,OAAO,CAAA;AACrC;;;;"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const diagnosticsChannel = require('node:diagnostics_channel');
|
|
4
3
|
const api = require('@opentelemetry/api');
|
|
5
4
|
const core = require('@sentry/core');
|
|
6
5
|
const server = require('@sentry/core/server');
|
|
6
|
+
const serverUtils = require('@sentry/server-utils');
|
|
7
7
|
const debugBuild = require('../../debug-build.js');
|
|
8
8
|
|
|
9
9
|
const HTTP_SERVER_INSTRUMENTED_KEY = api.createContextKey("sentry_http_server_instrumented");
|
|
@@ -59,7 +59,7 @@ const _httpServerIntegration = ((options = {}) => {
|
|
|
59
59
|
name: INTEGRATION_NAME,
|
|
60
60
|
setupOnce() {
|
|
61
61
|
const { [server.HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = server.getHttpServerSubscriptions(_options);
|
|
62
|
-
|
|
62
|
+
serverUtils.subscribeDiagnosticsChannel(server.HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart);
|
|
63
63
|
},
|
|
64
64
|
afterAllSetup(client) {
|
|
65
65
|
if (debugBuild.DEBUG_BUILD && client.getIntegrationByName("Http")) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpServerIntegration.js","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"httpServerIntegration.js","sources":["../../../../src/integrations/http/httpServerIntegration.ts"],"sourcesContent":["import type { RequestOptions } from 'node:http';\nimport { context, createContextKey, propagation } from '@opentelemetry/api';\nimport type { Integration, IntegrationFn } from '@sentry/core';\nimport { addNonEnumerableProperty, debug, getClient } from '@sentry/core';\nimport type { HttpIncomingMessage, HttpServerResponse } from '@sentry/core/server';\nimport { getHttpServerSubscriptions, HTTP_ON_SERVER_REQUEST, recordRequestSession } from '@sentry/core/server';\nimport { subscribeDiagnosticsChannel } from '@sentry/server-utils';\nimport type { RequestEventData } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\n\n// Re-export so existing test imports continue to work; the implementation now lives in core.\nexport { recordRequestSession };\n\nconst HTTP_SERVER_INSTRUMENTED_KEY = createContextKey('sentry_http_server_instrumented');\nconst INTEGRATION_NAME = 'Http.Server' as const;\n\n// Inlining this type to not depend on newer TS types\ninterface WeakRefImpl<T> {\n deref(): T | undefined;\n}\n\ntype StartSpanCallback = (next: () => boolean) => boolean;\ntype RequestWithOptionalStartSpanCallback = HttpIncomingMessage & {\n _startSpanCallback?: WeakRefImpl<StartSpanCallback>;\n};\n\nexport interface HttpServerIntegrationOptions {\n /**\n * Whether the integration should create [Sessions](https://docs.sentry.io/product/releases/health/#sessions) for incoming requests to track the health and crash-free rate of your releases in Sentry.\n * Read more about Release Health: https://docs.sentry.io/product/releases/health/\n *\n * Defaults to `true`.\n */\n sessions?: boolean;\n\n /**\n * Number of milliseconds until sessions tracked with `sessions` will be flushed as a session aggregate.\n *\n * Defaults to `60000` (60s).\n */\n sessionFlushingDelayMS?: number;\n\n /**\n * Do not capture the request body for incoming HTTP requests to URLs where the given callback returns `true`.\n * This can be useful for long running requests where the body is not needed and we want to avoid capturing it.\n *\n * @param url Contains the entire URL, including query string (if any), protocol, host, etc. of the incoming request.\n * @param request Contains the {@type RequestOptions} object used to make the incoming request.\n */\n ignoreRequestBody?: (url: string, request: RequestOptions) => boolean;\n\n /**\n * Controls the maximum size of incoming HTTP request bodies attached to events.\n *\n * Available options:\n * - 'none': No request bodies will be attached\n * - 'small': Request bodies up to 1,000 bytes will be attached\n * - 'medium': Request bodies up to 10,000 bytes will be attached (default)\n * - 'always': Request bodies will always be attached\n *\n * Note that even with 'always' setting, bodies exceeding 1MB will never be attached\n * for performance and security reasons.\n *\n * @default 'medium'\n */\n maxRequestBodySize?: 'none' | 'small' | 'medium' | 'always';\n}\n\n/**\n * Add a callback to the request object that will be called when the request is started.\n * The callback will receive the next function to continue processing the request.\n */\nexport function addStartSpanCallback(request: RequestWithOptionalStartSpanCallback, callback: StartSpanCallback): void {\n addNonEnumerableProperty(request, '_startSpanCallback', new WeakRef(callback));\n}\n\nconst _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}) => {\n const _options = {\n sessions: options.sessions ?? true,\n sessionFlushingDelayMS: options.sessionFlushingDelayMS ?? 60_000,\n maxRequestBodySize: options.maxRequestBodySize,\n // Server spans are created by `httpServerSpansIntegration` via the\n // `httpServerRequest` client event + `_startSpanCallback`, not by the\n // core subscription helper. Explicitly opt out so the helper does not\n // double-create spans when the client has tracing enabled.\n spans: false as const,\n // Cast: core uses HttpIncomingMessage; node consumers pass\n // RequestOptions-typed callbacks.\n // The two are structurally compatible for the fields the callback reads\n // (url, method, headers).\n ignoreRequestBody: options.ignoreRequestBody as\n | ((url: string, request: HttpIncomingMessage) => boolean)\n | undefined,\n\n /**\n * Hook called by core's `instrumentServer` to wrap the upstream\n * `emit('request')` call.\n *\n * We use it to extract OTel context from request headers and re-enter\n * the OTel context before the framework sees the request, so subsequent\n * spans (eg from `httpServerSpansIntegration`) attach to the right trace.\n */\n wrapServerEmitRequest(\n request: HttpIncomingMessage,\n response: HttpServerResponse,\n normalizedRequest: RequestEventData,\n next: () => void,\n ): void {\n const client = getClient();\n if (!client) return next();\n\n // Guard against double-wrapping: if our wrapper somehow runs inside an\n // already-instrumented context, just continue without re-extracting\n // and re-emitting.\n if (context.active().getValue(HTTP_SERVER_INSTRUMENTED_KEY)) {\n return next();\n }\n\n const ctx = propagation\n .extract(context.active(), normalizedRequest.headers)\n .setValue(HTTP_SERVER_INSTRUMENTED_KEY, true);\n\n context.with(ctx, () => {\n // httpServerSpansIntegration listens for this and may attach\n // `_startSpanCallback` to the request to wrap span creation around\n // the emit.\n client.emit('httpServerRequest', request, response, normalizedRequest);\n\n const callback = (request as RequestWithOptionalStartSpanCallback)._startSpanCallback?.deref();\n if (callback) {\n callback(() => {\n next();\n return true;\n });\n } else {\n next();\n }\n });\n },\n };\n\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequestStart } = getHttpServerSubscriptions(_options);\n subscribeDiagnosticsChannel(HTTP_ON_SERVER_REQUEST, onHttpServerRequestStart);\n },\n afterAllSetup(client) {\n if (DEBUG_BUILD && client.getIntegrationByName('Http')) {\n debug.warn(\n 'It seems that you have manually added `httpServerIntegration` while `httpIntegration` is also present. Make sure to remove `httpServerIntegration` when adding `httpIntegration`.',\n );\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * This integration handles request isolation, trace continuation and other core Sentry functionality around incoming http requests\n * handled via the node `http` module.\n *\n * This version uses OpenTelemetry for context propagation and span management.\n *\n * @see {@link ../../light/integrations/httpServerIntegration.ts} for the lightweight version without OpenTelemetry\n */\nexport const httpServerIntegration = _httpServerIntegration as (\n options?: HttpServerIntegrationOptions,\n) => Integration & {\n name: 'Http.Server';\n setupOnce: () => void;\n};\n"],"names":["createContextKey","addNonEnumerableProperty","getClient","context","propagation","HTTP_ON_SERVER_REQUEST","getHttpServerSubscriptions","subscribeDiagnosticsChannel","DEBUG_BUILD","debug"],"mappings":";;;;;;;;AAaA,MAAM,4BAAA,GAA+BA,qBAAiB,iCAAiC,CAAA;AACvF,MAAM,gBAAA,GAAmB,aAAA;AA0DlB,SAAS,oBAAA,CAAqB,SAA+C,QAAA,EAAmC;AACrH,EAAAC,6BAAA,CAAyB,OAAA,EAAS,oBAAA,EAAsB,IAAI,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAC/E;AAEA,MAAM,sBAAA,IAA0B,CAAC,OAAA,GAAwC,EAAC,KAAM;AAC9E,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,QAAA,EAAU,QAAQ,QAAA,IAAY,IAAA;AAAA,IAC9B,sBAAA,EAAwB,QAAQ,sBAAA,IAA0B,GAAA;AAAA,IAC1D,oBAAoB,OAAA,CAAQ,kBAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAK5B,KAAA,EAAO,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKP,mBAAmB,OAAA,CAAQ,iBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAY3B,qBAAA,CACE,OAAA,EACA,QAAA,EACA,iBAAA,EACA,IAAA,EACM;AACN,MAAA,MAAM,SAASC,cAAA,EAAU;AACzB,MAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,IAAA,EAAK;AAKzB,MAAA,IAAIC,WAAA,CAAQ,MAAA,EAAO,CAAE,QAAA,CAAS,4BAA4B,CAAA,EAAG;AAC3D,QAAA,OAAO,IAAA,EAAK;AAAA,MACd;AAEA,MAAA,MAAM,GAAA,GAAMC,eAAA,CACT,OAAA,CAAQD,WAAA,CAAQ,MAAA,EAAO,EAAG,iBAAA,CAAkB,OAAO,CAAA,CACnD,QAAA,CAAS,4BAAA,EAA8B,IAAI,CAAA;AAE9C,MAAAA,WAAA,CAAQ,IAAA,CAAK,KAAK,MAAM;AAItB,QAAA,MAAA,CAAO,IAAA,CAAK,mBAAA,EAAqB,OAAA,EAAS,QAAA,EAAU,iBAAiB,CAAA;AAErE,QAAA,MAAM,QAAA,GAAY,OAAA,CAAiD,kBAAA,EAAoB,KAAA,EAAM;AAC7F,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,QAAA,CAAS,MAAM;AACb,YAAA,IAAA,EAAK;AACL,YAAA,OAAO,IAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH,CAAA,MAAO;AACL,UAAA,IAAA,EAAK;AAAA,QACP;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,MAAM,EAAE,CAACE,6BAAsB,GAAG,wBAAA,EAAyB,GAAIC,kCAA2B,QAAQ,CAAA;AAClG,MAAAC,uCAAA,CAA4BF,+BAAwB,wBAAwB,CAAA;AAAA,IAC9E,CAAA;AAAA,IACA,cAAc,MAAA,EAAQ;AACpB,MAAA,IAAIG,sBAAA,IAAe,MAAA,CAAO,oBAAA,CAAqB,MAAM,CAAA,EAAG;AACtD,QAAAC,UAAA,CAAM,IAAA;AAAA,UACJ;AAAA,SACF;AAAA,MACF;AAAA,IACF;AAAA,GACF;AACF,CAAA,CAAA;AAUO,MAAM,qBAAA,GAAwB;;;;;;"}
|