[
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "## Code of Conduct\n\n### Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of\nexperience, nationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\n### Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n- Other conduct which falls outside of\n  [NanoAPI's Open-Source Manifesto](https://github.com/Nano-API/oss-manifesto/blob/main/README.md)\n\n### Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, or to ban temporarily or permanently any\ncontributor for other behaviors that they deem inappropriate, threatening,\noffensive, or harmful.\n\n### Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n### Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at\n[info@nanoapi.io](mailto:info@nanoapi.io). All complaints will be reviewed and\ninvestigated and will result in a response that is deemed necessary and\nappropriate to the circumstances. The project team is obligated to maintain\nconfidentiality with regard to the reporter of an incident. Further details of\nspecific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n### Attribution\n\nThis Code of Conduct is adapted from the\n[ncc CoC](https://github.com/vercel/ncc/blob/main/CODE_OF_CONDUCT.md) which is\nitself adapted from the [Contributor Covenant][homepage], version 1.4, available\nat [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# How to Contribute to NanoAPI\n\n## Contributor License Agreement\n\n<!-- This section always comes first -->\n\n- By submitting code as an individual you agree to the\n  [individual contributor license agreement](/CLA/INDIVIDUAL_CONTRIBUTOR_LICENSE_AGREEMENT.md).\n- By submitting code as an entity you agree to the\n  [corporate contributor license agreement](/CLA/CORPORATE_CONTRIBUTOR_LICENSE_AGREEMENT.md).\n\n## Housekeeping\n\nFirst off, thank you for being here. You dropped this: 👑\n\nHere are some guidelines to help you get started contributing to NanoAPI.\n\n1. Follow our [Code of Conduct](/.github/CODE_OF_CONDUCT.md).\n2. Check for open issues before creating a new one.\n3. We require an open issue for all pull requests.\n4. Help others by reviewing their pull requests.\n5. All donations we receive go directly back to our contributors. We’re here to\n   support you when you successfully submit a PR to us. Your efforts help the\n   community grow, and we want to give back to those who help make that\n   possible!\n\n## How to File Issues\n\nMake use of the issue templates, and label your issues appropriately. If you’re\nunsure about which label to use, don’t worry! We will help you choose the right\none.\n\n## How to Submit a Pull Request\n\n1. Don't panic.\n2. Ensure an issue exists for the changes you want to make.\n3. Fork the repository.\n4. Create a new branch.\n5. Make your changes.\n6. Test your changes.\n7. Push your changes to your fork.\n   1. Make sure to rebase before pushing.\n8. Submit a pull request.\n9. Follow the template and fill in all the sections.\n10. Wait for feedback.\n11. Make changes if necessary.\n12. Celebrate your success after your PR gets merged. The Codex Astartes\n    supports this action.\n\n## Development Environment\n\nYou will need the following tools to develop NanoAPI:\n\n- [Node.js](https://nodejs.org/en/) version 22 or higher.\n\n### Environment Set Up\n\nWe use the fork-and-pull model for contributions. Here’s how you can set up your\ndevelopment environment:\n\n1. Fork the repository.\n2. Clone your fork locally:\n\n```bash\n$ git clone https://github.com/<your_username>/napi.git\n```\n\n3. Enter the folder:\n\n```bash\n$ cd napi\n```\n\n4. Add the original repository as a remote:\n\n```bash\n$ git remote add upstream https://github.com/nanoapi-io/napi.git\n```\n\n5. Install the dependencies:\n\n```bash\n$ npm install\n```\n\n> [!NOTE]\n> You may encounter issues on a second or third install of dependencies. If this\n> happens, install with `npm i --no-cache --force` to fix these issues.\n\n### Running the Project\n\nWhen running locally, the shared libraries and the UI must be built before the\nCLI can be run.\n\nTo build them:\n\n```bash\n$ npm run build\n```\n\nNext, we want to run the CLI and the UI with hot reload. You will need two\nterminal windows for this.\n\n1. In the first terminal, run the CLI. This command should be run in the `napi`\n   directory with a `workdir` pointing to the project you want to work on. For\n   example, if you want to work on Apache Airflow, run:\n\n```bash\n$ npm run dev:cli -- audit view -- --workdir=/path/to/airflow\n```\n\nRunning the `audit view` command from the CLI will spin up a web server on your\nlocalhost. You can access the UI by navigating to `http://localhost:3000`.\n\n> [!NOTE]\n> In case of port collisions, the UI will automatically switch to the next\n> available port.\n\n2. In the second terminal, run the UI. This command should be run in the `napi`\n   directory as well:\n\n```bash\n$ npm run dev:app\n```\n\nThis controls the hot reload functionality for the UI. You can now make changes\nto the UI and see them reflected in real-time.\n\n> [!IMPORTANT]\n> The react UI elements (sidebar, header, etc.) will automatically reload when\n> you make changes. However any Cytoscape elements will not. You will need to\n> refresh the page to see those changes.\n\n### Project Setup\n\nYou can use any project (in a supported language) to test the CLI. There are\nsome steps that must be taken to set up the project:\n\n1. Clone or CD to the repo you want to work on/test with. For this example we'll\n   use Apache Airflow.\n\n```bash\ngit clone https://github.com/apache/airflow.git\ncd airflow\n```\n\n2. From the `napi` repo initialize the project using the CLI, which will create\n   a `.napirc` file in the project root. This file contains the configuration\n   for the project and is required for the CLI to work.:\n\n```bash\ncd /path/to/napi # or just use a different terminal\nnpm start -- init -- --workdir=/path/to/airflow\n```\n\n> [!NOTE]\n> If you encounter any issues with the config file, you can\n> [check the reference for the file on our documentation](https://docs.nanoapi.io/default-guide/reference/napirc).\n\n### Testing\n\n```bash\n$ npm test\n```\n\n### Linting\n\n```bash\n$ npm run lint\n```\n\n### Release Process\n\nWe are currently formalizing the release process. For now, the NanoAPI team will\nhandle making regular releases.\n\nTo ensure releases run smoothly, put the content of your changes in our\n[CHANGELOG](/packages/cli/CHANGELOG.md) file.\n\n### Documentation\n\nWe are also building on the documentation process. For now, include any\ndocumentation changes in your PRs and we will add them into the main\ndocumentation.\n\nThe critical documentation to maintain is for any changes that impact the\nfollowing:\n\n- CLI commands\n- Configuration file\n- Local development setup\n- Release process\n- Testing\n- Linting\n\n### Discussions vs Issues\n\nWe use GitHub Discussions for general questions, ideas, and feedback. If you\nhave a question, please use the Discussions tab. If you have a bug report or\nfeature request, please use the Issues tab.\n\n---\n\nThat's it for this guide for now. So long, and thanks for all the fish! 🚀\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report for a bug, regression, or unexpected behavior\ntitle: \"[BUG] \"\nlabels: bug\nassignees: \"\"\n---\n\n<!--- Provide a general summary of the issue in the Title above -->\n\n## Description\n\n<!--- Provide a more detailed introduction to the issue itself, and why you consider it to be a bug -->\n\n## Expected Behavior\n\n<!--- Tell us what should happen -->\n\n## Actual Behavior\n\n<!--- Tell us what happens instead -->\n\n## Possible Fix\n\n<!--- Not obligatory, but suggest a fix or reason for the bug -->\n\n## Steps to Reproduce\n\n<!--- Provide a link to a live example, or an unambiguous set of steps to -->\n<!--- reproduce this bug. Include code to reproduce, if relevant -->\n\n1.\n2.\n3.\n4.\n\n## Context\n\n<!--- How has this bug affected you? What were you trying to accomplish? -->\n\n## Your Environment\n\n<!--- Include as many relevant details about the environment you experienced the bug in -->\n\n- Version used:\n- Environment name and version (e.g. Chrome 39, node.js 5.4):\n- Operating System and version (desktop or mobile):\n- Link to your project:\n\n### Config File\n\nPaste your `.napirc` below\n\n```json\n{\n  \"your\": \"config\"\n}\n```\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[FEATURE] \"\nlabels: enhancement\nassignees: \"\"\n---\n\n<!--- Provide a general summary of the issue in the Title above -->\n\n## Detailed Description\n\n<!--- Provide a detailed description of the change or addition you are proposing -->\n\n## Context\n\n<!--- Why is this change important to you? How would you use it? -->\n<!--- How can it benefit other users? -->\n\n## Possible Implementation\n\n<!--- Not obligatory, but suggest an idea for implementing addition or change -->\n\n## Your Environment\n\n<!--- Include as many relevant details about the environment you experienced the bug in -->\n\n- Version used:\n- Environment name and version (e.g. Chrome 39, node.js 5.4):\n- Operating System and version (desktop or mobile):\n- Link to your project:\n\n### Config File\n\nPaste your `.napirc` below\n\n```json\n{\n  \"your\": \"config\"\n}\n```\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## Type of change\n\n<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->\n\n- [ ] Bug fix (non-breaking change that fixes an issue)\n- [ ] New feature (non-breaking change that adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to\n      not work as expected)\n- [ ] Documentation update\n\n## Description\n\nPlease include a summary of the changes and the related issue. Explain the\nmotivation behind this change.\n\n## Related Issue\n\n<!--- This project only accepts pull requests related to open issues -->\n<!--- If suggesting a new feature or change, please discuss it in an issue first -->\n<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->\n<!--- Please link to the issue here: -->\n\nIssue Number: #<issue_number>\n\n## Motivation and Context\n\n<!--- Why is this change required? What problem does it solve? -->\n\n## How Has This Been Tested?\n\n<!--- Please describe in detail how you tested your changes. -->\n<!--- Include details of your testing environment, and the tests you ran to -->\n<!--- see how your change affects other areas of the code, etc. -->\n\n## Screenshots (if appropriate):\n\n## Checklist\n\n<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->\n<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->\n\n- [ ] I have read the [contributing guidelines](CONTRIBUTING.md)\n- [ ] My code follows the code style of this project.\n- [ ] I have added tests that prove my fix is effective or that my feature works\n- [ ] I have performed a self-review of my own code\n- [ ] I have commented my code, particularly in hard-to-understand areas\n- [ ] I have made corresponding changes to the documentation\n- [ ] My changes generate no new warnings\n- [ ] Any dependent changes have been merged and published\n"
  },
  {
    "path": ".github/SECURITY.md",
    "content": "# Security Policy\n\nPlease report any suspected security vulnerabilities privately to\n[security@nanoapi.io](mailto:security@nanoapi.io). Please do NOT create publicly\nviewable issues for suspected security vulnerabilities.\n\nWe will acknowledge receipt of your vulnerability report as soon as possible and\nstrive to send you regular updates about our progress. If you're curious about\nthe status of your disclosure please feel free to email us again. If you want to\nencrypt your disclosure email please email us to ask for our PGP key.\n\nPlease refrain from requesting compensation for reporting vulnerabilities. If\nyou want we will publicly acknowledge your responsible disclosure. We also try\nto make the issue public after the vulnerability is announced. Usually bug\nreports are made public after 72 hours, if possible.\n\nYou are not allowed to search for security vulnerabilities on any hosted service\nof NanoAPI without the consent of the party hosting it. NanoAPI is open source\nsoftware and can be installed for testing and security issues on your own\ninfrastructure.\n"
  },
  {
    "path": ".github/workflows/lint_test_compile.yml",
    "content": "name: Lint, tests and build\n\non:\n  push:\n    branches-ignore: [\"main\"]\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Set up Deno\n        uses: denoland/setup-deno@v2\n        with:\n          deno-version: \"2.4.0\"\n\n      - name: Install dependencies\n        run: |\n          deno install --allow-scripts --reload\n\n      - name: Deno lint\n        run: deno lint\n\n      - name: Deno fmt (check)\n        run: deno fmt --check\n\n  tests:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Set up Deno\n        uses: denoland/setup-deno@v2\n        with:\n          deno-version: \"2.4.0\"\n\n      - name: Install dependencies\n        run: |\n          deno install --allow-scripts --reload\n\n      - name: Tests\n        run: deno test -A\n\n  compile:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Set up Deno\n        uses: denoland/setup-deno@v2\n        with:\n          deno-version: \"2.4.0\"\n\n      - name: Install dependencies\n        run: |\n          deno install --allow-scripts --reload\n\n      - name: Compile\n        run: deno task compile:all\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release Packages\n\npermissions:\n  contents: write\n  packages: write\n\non: [workflow_dispatch]\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Set up Deno\n        uses: denoland/setup-deno@v2\n        with:\n          deno-version: \"2.4.0\"\n\n      - name: Get new version and bump deno.json\n        id: get_version\n        run: |\n          VERSION=$(deno run -A scripts/get_version.ts)\n          echo \"release_version=$VERSION\" >> $GITHUB_OUTPUT\n\n      - name: Install dependencies\n        run: |\n          deno install --reload --allow-scripts\n\n      - name: Compile\n        run: |\n          deno task compile:all\n\n      - name: Create release\n        uses: softprops/action-gh-release@v2\n        with:\n          tag_name: v${{ steps.get_version.outputs.release_version }}\n          files: |\n            dist/*\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ndist\n.env\nnapi_dist\n__pycache__\n.ruff_cache\n.OPENAIKEY\nexamples/csharp/EndpointExample/obj\nexamples/csharp/EndpointExample/bin\nsrc/languagePlugins/csharp/testFiles/csharpFiles/**/obj\nnapi.sln\nexamples/csharp/EndpointExample/bin\nauditResponse.json\n.extracted/\nnapi-output/\ncoverage/\n.vite/\n.mvn/\ntarget/\n.settings/\n.classpath\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.formatOnSave\": true,\n  \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n}\n"
  },
  {
    "path": "CLA/CORPORATE_CONTRIBUTOR_LICENSE_AGREEMENT.md",
    "content": "# Corporate contributor license agreement\n\nYou accept and agree to the following terms and conditions for Your present and\nfuture Contributions submitted to Nano API B.V. Except for the license granted\nherein to Nano API B.V. and recipients of software distributed by Nano API B.V.,\nYou reserve all right, title, and interest in and to Your Contributions.\n\n1. Definitions.\n\n   \"You\" (or \"Your\") shall mean the copyright owner or legal entity authorized\n   by the copyright owner that is making this Agreement with Nano API B.V. For\n   legal entities, the entity making a Contribution and all other entities that\n   control, are controlled by, or are under common control with that entity are\n   considered to be a single Contributor. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the direction or\n   management of such entity, whether by contract or otherwise, or (ii)\n   ownership of fifty percent (50%) or more of the outstanding shares, or (iii)\n   beneficial ownership of such entity.\n\n   \"Contribution\" shall mean the code, documentation or other original works of\n   authorship, including any modifications or additions to an existing work,\n   that is submitted by You to Nano API B.V. for inclusion in, or documentation\n   of, any of the products owned or managed by Nano API B.V. (the \"Work\"). For\n   the purposes of this definition, \"submitted\" means any form of electronic,\n   verbal, or written communication sent to Nano API B.V. or its\n   representatives, including but not limited to communication on electronic\n   mailing lists, source code control systems, and issue tracking systems that\n   are managed by, or on behalf of, Nano API B.V. for the purpose of discussing\n   and improving the Work, but excluding communication that is conspicuously\n   marked or otherwise designated in writing by You as \"Not a Contribution.\"\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this Agreement, You hereby grant to Nano\nAPI B.V. and to recipients of software distributed by Nano API B.V. a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license\nto reproduce, prepare derivative works of, publicly display, publicly perform,\nsublicense, and distribute Your Contributions and such derivative works.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this Agreement, You hereby grant to Nano\nAPI B.V. and to recipients of software distributed by Nano API B.V. a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated\nin this section) patent license to make, have made, use, offer to sell, sell,\nimport, and otherwise transfer the Work, where such license applies only to\nthose patent claims licensable by You that are necessarily infringed by Your\nContribution(s) alone or by combination of Your Contribution(s) with the Work to\nwhich such Contribution(s) was submitted. If any entity institutes patent\nlitigation against You or any other entity (including a cross-claim or\ncounterclaim in a lawsuit) alleging that your Contribution, or the Work to which\nyou have contributed, constitutes direct or contributory patent infringement,\nthen any patent licenses granted to that entity under this Agreement for that\nContribution or Work shall terminate as of the date such litigation is filed.\n\n1. You represent that You are legally entitled to grant the above license. You\n   represent further that each of Your employees is authorized to submit\n   Contributions on Your behalf, but excluding employees that are designated in\n   writing by You as \"Not authorized to submit Contributions on behalf of [name\n   of Your corporation here].\" Such designations of exclusion for unauthorized\n   employees are to be submitted via email to\n   [legal@nanoapi.io](mailto:legal@nanoapi.io).\n\n2. You represent that each of Your Contributions is Your original creation (see\n   section 7 for submissions on behalf of others).\n\n3. You are not expected to provide support for Your Contributions, except to the\n   extent You desire to provide support. You may provide support for free, for a\n   fee, or not at all. Unless required by applicable law or agreed to in\n   writing, You provide Your Contributions on an \"AS IS\" BASIS, WITHOUT\n   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including,\n   without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,\n   MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.\n\n4. Should You wish to submit work that is not Your original creation, You may\n   submit it to Nano API B.V. separately from any Contribution, identifying the\n   complete details of its source and of any license or other restriction\n   (including, but not limited to, related patents, trademarks, and license\n   agreements) of which you are personally aware, and conspicuously marking the\n   work as \"Submitted on behalf of a third-party: [named here]\".\n\n5. It is Your responsibility to notify Nano API B.V. when any change is required\n   to the list of designated employees excluded from submitting Contributions on\n   Your behalf per Section 4. Such notification should be sent via email to\n   [legal@nanoapi.io](mailto:legal@nanoapi.io).\n\nThis text is licensed under the\n[Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/)\nand the original source is the Google Open Source Programs Office.\n"
  },
  {
    "path": "CLA/INDIVIDUAL_CONTRIBUTOR_LICENSE_AGREEMENT.md",
    "content": "# Individual contributor license agreement\n\nYou accept and agree to the following terms and conditions for Your present and\nfuture Contributions submitted to Nano API B.V. Except for the license granted\nherein to Nano API B.V. and recipients of software distributed by Nano API B.V.,\nYou reserve all right, title, and interest in and to Your Contributions.\n\n1. Definitions.\n\n   \"You\" (or \"Your\") shall mean the copyright owner or legal entity authorized\n   by the copyright owner that is making this Agreement with Nano API B.V. For\n   legal entities, the entity making a Contribution and all other entities that\n   control, are controlled by, or are under common control with that entity are\n   considered to be a single Contributor. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the direction or\n   management of such entity, whether by contract or otherwise, or (ii)\n   ownership of fifty percent (50%) or more of the outstanding shares, or (iii)\n   beneficial ownership of such entity.\n\n   \"Contribution\" shall mean any original work of authorship, including any\n   modifications or additions to an existing work, that is intentionally\n   submitted by You to Nano API B.V. for inclusion in, or documentation of, any\n   of the products owned or managed by Nano API B.V. (the \"Work\"). For the\n   purposes of this definition, \"submitted\" means any form of electronic,\n   verbal, or written communication sent to Nano API B.V. or its\n   representatives, including but not limited to communication on electronic\n   mailing lists, source code control systems, and issue tracking systems that\n   are managed by, or on behalf of, Nano API B.V. for the purpose of discussing\n   and improving the Work, but excluding communication that is conspicuously\n   marked or otherwise designated in writing by You as \"Not a Contribution.\"\n\n2. Grant of Copyright License. Subject to the terms and conditions of this\n   Agreement, You hereby grant to Nano API B.V. and to recipients of software\n   distributed by Nano API B.V. a perpetual, worldwide, non-exclusive,\n   no-charge, royalty-free, irrevocable copyright license to reproduce, prepare\n   derivative works of, publicly display, publicly perform, sublicense, and\n   distribute Your Contributions and such derivative works.\n\n3. Grant of Patent License. Subject to the terms and conditions of this\n   Agreement, You hereby grant to Nano API B.V. and to recipients of software\n   distributed by Nano API B.V. a perpetual, worldwide, non-exclusive,\n   no-charge, royalty-free, irrevocable (except as stated in this section)\n   patent license to make, have made, use, offer to sell, sell, import, and\n   otherwise transfer the Work, where such license applies only to those patent\n   claims licensable by You that are necessarily infringed by Your\n   Contribution(s) alone or by combination of Your Contribution(s) with the Work\n   to which such Contribution(s) was submitted. If any entity institutes patent\n   litigation against You or any other entity (including a cross-claim or\n   counterclaim in a lawsuit) alleging that your Contribution, or the Work to\n   which you have contributed, constitutes direct or contributory patent\n   infringement, then any patent licenses granted to that entity under this\n   Agreement for that Contribution or Work shall terminate as of the date such\n   litigation is filed.\n\n4. You represent that you are legally entitled to grant the above license. If\n   your employer(s) has rights to intellectual property that you create that\n   includes your Contributions, you represent that you have received permission\n   to make Contributions on behalf of that employer, that your employer has\n   waived such rights for your Contributions to Nano API B.V., or that your\n   employer has executed a separate Corporate CLA with Nano API B.V.\n\n5. You represent that each of Your Contributions is Your original creation (see\n   section 7 for submissions on behalf of others). You represent that Your\n   Contribution submissions include complete details of any third-party license\n   or other restriction (including, but not limited to, related patents and\n   trademarks) of which you are personally aware and which are associated with\n   any part of Your Contributions.\n\n6. You are not expected to provide support for Your Contributions, except to the\n   extent You desire to provide support. You may provide support for free, for a\n   fee, or not at all. Unless required by applicable law or agreed to in\n   writing, You provide Your Contributions on an \"AS IS\" BASIS, WITHOUT\n   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including,\n   without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT,\n   MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.\n\n7. Should You wish to submit work that is not Your original creation, You may\n   submit it to Nano API B.V. separately from any Contribution, identifying the\n   complete details of its source and of any license or other restriction\n   (including, but not limited to, related patents, trademarks, and license\n   agreements) of which you are personally aware, and conspicuously marking the\n   work as \"Submitted on behalf of a third-party: [insert_name_here]\".\n\n8. You agree to notify Nano API B.V. of any facts or circumstances of which you\n   become aware that would make these representations inaccurate in any respect.\n\nThis text is licensed under the\n[Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/)\nand the original source is the Google Open Source Programs Office.\n"
  },
  {
    "path": "LICENSE.md",
    "content": "# License\n\nPortions of this software are licensed as follows:\n\n- Content of branches other than the main branch (i.e. \"master\") are not\n  licensed.\n- Source code files that contain \".ee.\" in their filename are NOT licensed under\n  the Sustainable Use License. To use source code files that contain \".ee.\" in\n  their filename you must hold a valid NanoAPI Enterprise License specifically\n  allowing you access to such source code files and as defined in\n  \"LICENSE_EE.md\".\n- All third party components incorporated into the NanoAPI Software are licensed\n  under the original license provided by the owner of the applicable component.\n- Content outside of the above mentioned files or restrictions is available\n  under the \"Sustainable Use License\" as defined below.\n\n## Sustainable Use License\n\nVersion 1.0\n\n### Acceptance\n\nBy using the software, you agree to all of the terms and conditions below.\n\n### Copyright License\n\nThe licensor grants you a non-exclusive, royalty-free, worldwide,\nnon-sublicensable, non-transferable license to use, copy, distribute, make\navailable, and prepare derivative works of the software, in each case subject to\nthe limitations below.\n\n### Limitations\n\nYou may use or modify the software only for your own internal business purposes\nor for non-commercial or personal use. You may distribute the software or\nprovide it to others only if you do so free of charge for non-commercial\npurposes. You may not alter, remove, or obscure any licensing, copyright, or\nother notices of the licensor in the software. Any use of the licensor’s\ntrademarks is subject to applicable law.\n\n### Patents\n\nThe licensor grants you a license, under any patent claims the licensor can\nlicense, or becomes able to license, to make, have made, use, sell, offer for\nsale, import and have imported the software, in each case subject to the\nlimitations and conditions in this license. This license does not cover any\npatent claims that you cause to be infringed by modifications or additions to\nthe software. If you or your company make any written claim that the software\ninfringes or contributes to infringement of any patent, your patent license for\nthe software granted under these terms ends immediately. If your company makes\nsuch a claim, your patent license ends immediately for work on behalf of your\ncompany.\n\n### Notices\n\nYou must ensure that anyone who gets a copy of any part of the software from you\nalso gets a copy of these terms. If you modify the software, you must include in\nany modified copies of the software a prominent notice stating that you have\nmodified the software.\n\n### No Other Rights\n\nThese terms do not imply any licenses other than those expressly granted in\nthese terms.\n\n### Termination\n\nIf you use the software in violation of these terms, such use is not licensed,\nand your license will automatically terminate. If the licensor provides you with\na notice of your violation, and you cease all violation of this license no later\nthan 30 days after you receive that notice, your license will be reinstated\nretroactively. However, if you violate these terms after such reinstatement, any\nadditional violation of these terms will cause your license to terminate\nautomatically and permanently.\n\n### No Liability\n\nAs far as the law allows, the software comes as is, without any warranty or\ncondition, and the licensor will not be liable to you for any damages arising\nout of these terms or the use or nature of the software, under any kind of legal\nclaim.\n\n### Definitions\n\nThe “licensor” is the entity offering these terms.\n\nThe “software” is the software the licensor makes available under these terms,\nincluding any portion of it.\n\n“You” refers to the individual or entity agreeing to these terms.\n\n“Your company” is any legal entity, sole proprietorship, or other kind of\norganization that you work for, plus all organizations that have control over,\nare under the control of, or are under common control with that organization.\nControl means ownership of substantially all the assets of an entity, or the\npower to direct its management and policies by vote, contract, or otherwise.\nControl can be direct or indirect.\n\n“Your license” is the license granted to you for the software under these terms.\n\n“Use” means anything you do with the software requiring your license.\n\n“Trademark” means trademarks, service marks, and similar rights.\n"
  },
  {
    "path": "LICENSE_EE.md",
    "content": "# The NanoAPI Enterprise License (the “Enterprise License”)\n\nCopyright (c) 2024-present Nano API B.V.\n\nWith regard to the NanoAPI Software:\n\nThis software and associated documentation files (the \"Software\") may only be\nused in production, if you (and any entity that you represent) hold a valid\nNanoAPI Enterprise license corresponding to your usage. Subject to the foregoing\nsentence, you are free to modify this Software and publish patches to the\nSoftware. You agree that NanoAPI and/or its licensors (as applicable) retain all\nright, title and interest in and to all such modifications and/or patches, and\nall such modifications and/or patches may only be used, copied, modified,\ndisplayed, distributed, or otherwise exploited with a valid NanoAPI Enterprise\nlicense for the corresponding usage. Notwithstanding the foregoing, you may copy\nand modify the Software for development and testing purposes, without requiring\na subscription. You agree that NanoAPI and/or its licensors (as applicable)\nretain all right, title and interest in and to all such modifications. You are\nnot granted any other rights beyond what is expressly stated herein. Subject to\nthe foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,\nand/or sell the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nFor all third party components incorporated into the NanoAPI Software, those\ncomponents are licensed under the original license provided by the owner of the\napplicable component.\n"
  },
  {
    "path": "README.md",
    "content": "![NanoAPI Banner](/media/github-banner.png)\n\n# napi - Better Software Architecture for the AI Age\n\n`napi` is a fully offline CLI that analyzes your codebase's architecture --\ndependencies, complexity, and structure -- then lets you visualize and refactor\nit, all without sending your code anywhere.\n\nIt generates dependency manifests from your source code, stores them locally,\nand serves an interactive graph visualizer directly from the CLI.\n\n![NanoAPI UI Overview](/media/hero-app.png)\n\n## Features\n\n- **🔍 Dependency Analysis**: Map every file, symbol, and dependency in your\n  codebase automatically.\n- **🚨 Audit**: Detect files and symbols that exceed complexity, size, or\n  coupling thresholds.\n- **📊 Interactive Visualizer**: Explore your architecture through Cytoscape.js\n  graphs served locally in your browser.\n- **📝 Symbol Extraction**: Extract specific functions, classes, or symbols into\n  standalone files for refactoring.\n- **🏷️ AI Labeling** (optional): Use OpenAI, Google, or Anthropic models to\n  auto-label dependencies.\n- **⚙️ CI/CD Ready**: Integrates into any pipeline -- generate manifests on\n  every push and track architecture over time.\n- **🔒 Fully Offline**: No accounts, no servers, no data leaves your machine.\n\n## Supported Languages\n\n| Language | Status         |\n| -------- | -------------- |\n| Python   | ✅ Supported   |\n| C#       | ✅ Supported   |\n| C        | ✅ Supported   |\n| Java     | ✅ Supported   |\n| C++      | 🚧 In Progress |\n| PHP      | 🚧 In Progress |\n| JS/TS    | 🚧 In Progress |\n\n## Installation\n\n### Unix (macOS, Linux)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/nanoapi-io/napi/refs/heads/main/install_scripts/install.sh | bash\n```\n\nOr download a binary directly from\n[GitHub Releases](https://github.com/nanoapi-io/napi/releases/latest).\n\n### Windows\n\nUse [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) to run napi.\nNative Windows support is in progress.\n\n## Quick Start\n\n```bash\n# 1. Initialize your project (creates .napirc)\nnapi init\n\n# 2. Generate a dependency manifest\nnapi generate\n\n# 3. Open the visualizer in your browser\nnapi view\n```\n\nThat's it. Your manifest is saved locally in `.napi/manifests/` and the\nvisualizer opens at `http://localhost:3000`.\n\n## CLI Commands\n\n### `napi init`\n\nInteractive setup that creates a `.napirc` configuration file in your project\nroot.\n\nPrompts you for:\n\n- **Language** -- Python, C#, C, or Java\n- **Include/exclude patterns** -- which files to analyze\n- **Output directory** -- where extracted symbols are written\n- **AI labeling** (optional) -- provider and concurrency settings\n\n```bash\nnapi init\n```\n\n### `napi generate`\n\nAnalyzes your codebase and generates a dependency manifest. The manifest\ncaptures every file, symbol, dependency, and metric (lines, complexity,\ncoupling).\n\nManifests are saved as JSON files in `.napi/manifests/` with the naming pattern\n`{timestamp}-{commitSha}.json`.\n\n```bash\n# Interactive (prompts for branch/commit if not in git)\nnapi generate\n\n# Non-interactive (for CI)\nnapi generate --branch main --commit-sha abc1234 --commit-sha-date 2026-01-01T00:00:00Z\n```\n\nOptions:\n\n- `--branch` -- Git branch name (auto-detected if omitted)\n- `--commit-sha` -- Git commit hash (auto-detected if omitted)\n- `--commit-sha-date` -- Commit date in ISO 8601 format (auto-detected if\n  omitted)\n- `--labelingApiKey` -- API key for AI labeling (overrides global config)\n\n### `napi view`\n\nStarts a local web server and opens an interactive dependency visualizer in your\nbrowser.\n\n```bash\nnapi view\nnapi view --port 8080\n```\n\nThe viewer provides:\n\n- **Manifest list** -- browse all locally stored manifests by branch, commit,\n  and date\n- **Project graph** -- file-level dependency map with Cytoscape.js\n- **File graph** -- symbol-level view within a file (functions, classes,\n  variables)\n- **Symbol graph** -- transitive dependency chain for a specific symbol\n- **File explorer sidebar** -- navigate your codebase structure\n- **Audit alerts** -- visual indicators for files/symbols exceeding thresholds\n\n### `napi extract`\n\nExtracts specific symbols from your codebase into separate files using a local\nmanifest.\n\n```bash\n# Extract a function from a specific file\nnapi extract --symbol \"src/auth/login.py|authenticate\"\n\n# Extract multiple symbols\nnapi extract --symbol \"src/models.py|User\" --symbol \"src/models.py|Session\"\n\n# Use a specific manifest (defaults to latest)\nnapi extract --symbol \"src/main.py|run\" --manifestId 1712500000000-a1b2c3d\n```\n\nOutput is written to `{outDir}/extracted-{timestamp}/`.\n\n### `napi set apiKey`\n\nConfigure API keys for AI-powered dependency labeling. Keys are stored in the\nglobal config (not in your project).\n\n```bash\nnapi set apiKey\n```\n\nPrompts for:\n\n- **Provider** -- Google, OpenAI, or Anthropic\n- **API key** -- your provider API key\n\n## Local Manifest Storage\n\nAll manifests are stored in `.napi/manifests/` relative to your project root.\nEach manifest is a self-contained JSON file:\n\n```json\n{\n  \"id\": \"1712500000000-a1b2c3d\",\n  \"branch\": \"main\",\n  \"commitSha\": \"a1b2c3d4e5f6...\",\n  \"commitShaDate\": \"2026-04-07T10:00:00Z\",\n  \"createdAt\": \"2026-04-07T10:01:00Z\",\n  \"manifest\": {}\n}\n```\n\nAdd `.napi/` to your `.gitignore` or commit it to track architecture history in\nversion control -- your choice.\n\n## CI/CD Integration\n\nGenerate manifests automatically on every push:\n\n```yaml\n# .github/workflows/napi.yml\nname: Generate Manifest\non: [push]\njobs:\n  manifest:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Install napi\n        run: curl -fsSL https://raw.githubusercontent.com/nanoapi-io/napi/refs/heads/main/install_scripts/install.sh | bash\n\n      - name: Generate manifest\n        run: napi generate --branch ${{ github.ref_name }} --commit-sha ${{ github.sha }} --commit-sha-date \"$(git log -1 --format=%cI)\"\n```\n\n## Configuration Reference\n\n### `.napirc`\n\nProject-level configuration created by `napi init`:\n\n```json\n{\n  \"language\": \"python\",\n  \"python\": { \"version\": \"3.10\" },\n  \"project\": {\n    \"include\": [\"src/**/*.py\"],\n    \"exclude\": [\".git/**\", \"**/__pycache__/**\", \"napi_out/**\"]\n  },\n  \"outDir\": \"napi_out\",\n  \"labeling\": {\n    \"modelProvider\": \"openai\",\n    \"maxConcurrency\": 5\n  }\n}\n```\n\n### Global Config\n\nStored in your OS config directory (`~/.config/napi/config.json` on Linux,\n`~/Library/Application Support/napi/config.json` on macOS). Managed via\n`napi set apiKey`.\n\n```json\n{\n  \"labeling\": {\n    \"apiKeys\": {\n      \"openai\": \"sk-...\",\n      \"google\": \"AIza...\",\n      \"anthropic\": \"sk-ant-...\"\n    }\n  }\n}\n```\n\n## Development\n\nRequires [Deno](https://deno.land/) v2.4+.\n\n```bash\n# Install dependencies\ndeno install --allow-scripts\n\n# Run CLI in dev mode\ndeno task dev\n\n# Run viewer dev server (hot-reload)\ndeno task dev:viewer\n\n# Build viewer for production\ndeno task build:viewer\n\n# Compile binary (includes viewer)\ndeno task compile\n\n# Run tests\ndeno task test\n\n# Lint\ndeno lint\n\n# Format\ndeno fmt\n```\n\n## Contributing\n\nWe welcome contributions from the community. Please read our\n[contributing guide](https://github.com/nanoapi-io/napi/blob/main/.github/CONTRIBUTING.md)\nfor details on how to get involved.\n\n## License\n\n`napi` is licensed under the\n[Sustainable Use License](https://github.com/nanoapi-io/napi/blob/main/LICENSE.md).\n\n## Further Reading\n\n- [Automating the Strangler Pattern with Microlithic Development](https://medium.com/@joel_40950/automating-the-strangler-pattern-with-microlithic-development-241e4e0dd79b)\n- [Rise of the \"Microlith\": Rethinking Microservices for Modern Developers](https://dev.to/nanojoel/open-sourcing-nanoapi-rethinking-microservices-for-modern-developers-14m2)\n\n## Donations\n\nNanoAPI is a fair-source project. Because of this, we feel it would be unethical\nto keep any donations to ourselves. Instead, here is how we will handle\ndonations:\n\n- Donations go into a pool\n- Money from the pool will be distributed to contributors\n- At the end of the year, any remaining money will be donated to a charity of\n  the community's choice\n\nWe will post regular updates on how much money is in the pool and how it is\nbeing distributed.\n"
  },
  {
    "path": "SUPPORT.md",
    "content": "# Support using napi\n\nWelcome to NanoAPI! We use Github for tracking bugs and feature requests. There\nare helpful volunteers who may be able to help you.\n\nIf it happens that you know the solution to an existing bug, please first open\nthe issue in order to keep track of it. Afterwards open the relevant pull/merge\nrequest that potentially fixes it.\n\nPlease remember this is a community project and you are not entitled to free\nsupport. Be kind to anyone helping out.\n\nFor commercial support reach out to [info@nanoapi.io](mailto:info@nanoapi.io).\n\n## Documentation\n\n- [User Documentation](https://nanoapi.io/docs)\n"
  },
  {
    "path": "USERS.md",
    "content": "- [Ainur](https://ainurhq.cloud/)\n"
  },
  {
    "path": "WEEKLY_UPDATE_LOG.md",
    "content": "# Weekly Update Log\n\nThis log tracks significant changes, improvements, and new features implemented\nin the NAPI project on a weekly basis. It serves as a quick reference for team\nmembers and users to stay informed about the project's progress and recent\ndevelopments.\n\n## April 27th to May 16th, 2024\n\n### Deno Migration & Performance Optimization\n\n- Migrate from Node to Deno for improved performance and ease of development\n- Replaced Node.js dependencies with native Deno modules, reducing bundle size\n  and performance\n- Migrated from Express to Oak framework (JSR:@oak/oak) for improved Deno\n  compatibility\n- Eliminated external dependencies (uuid, express, http-proxy-middleware,\n  octokit) for built-in Deno modules\n- Switched to JSR registry for standard libraries (@std/path@^1.0.9) to leverage\n  Deno's ecosystem\n\n### Build System & Deployment\n\n- Change release from npm registry to Deno executables published on GitHub\n- Created convenience installation scripts\n- Streamlined GitHub Actions release workflow to reduce complexity and build\n  times\n- Enhanced installation instructions in README to facilitate installation\n\n### API & Development Experience\n\n- Refactored version checking to use native fetch API with timeout control (5s)\n  to prevent blocking the use of the tool when being rate limited by GitHub API\n- Updated version checking to check against our GitHub release instead of npm\n  registry\n- Implemented platform-specific browser launching with Deno.Command for better\n  cross-OS compatibility\n- Enhanced port detection using Deno's native networking APIs\n\n### Frontend\n\n- Improved API integration between frontend and backend components\n- Optimized frontend routing to work seamlessly with Oak middleware\n- Move from bare Radix UI component to more comprehensive Shadcn/UI component\n- Simplify some UX flow\n- Improve highlighting logic from the file explorer to the graph\n\n## April 21st to 27th, 2024\n\n### Feature Improvements\n\n- Improved Python symbol extraction with better handling of partial imports\n- Enhanced visual representation of nodes for large codebases\n- Updated highlighting mechanism for better code navigation\n- Implemented extraction mode with API integration and symbol editing\n  capabilities\n- Fixed Python error AST node cleanup for more reliable extraction\n- Added C# metrics feature for enhanced code analysis capabilities\n\n### Build System and Package Management Improvements\n\n- Switched from using published `@nanoapi.io/shared` package to bundling it\n  directly with the CLI\n- Added `tsup` for improved bundling configuration\n- Updated package versions and dependencies across the workspace\n- Made the root package private and updated workspace configurations\n\n### CLI Enhancements\n\n- Enhanced version checking with detailed update instructions\n- Fixed path resolution for static file serving\n- Improved build process with better bundling configuration\n- Added proper shebang handling for the CLI executable\n\n### Build System Updates\n\n- Removed separate build step for shared package\n- Updated build scripts to use tsup for better bundling\n- Fixed path resolution in development and production environments\n- Improved static file serving configuration\n\n### Version Management\n\n- Updated CLI version to 1.0.3\n- Set shared package version to 0.0.0 since it's now bundled\n- Added proper version checking middleware with detailed update instructions\n"
  },
  {
    "path": "deno.json",
    "content": "{\n  \"version\": \"1.0.15\",\n  \"name\": \"@napi/cli\",\n  \"exports\": \"./src/index.ts\",\n  \"nodeModulesDir\": \"auto\",\n  \"lock\": false,\n  \"imports\": {\n    \"@deno/vite-plugin\": \"npm:@deno/vite-plugin@^1.0.4\",\n    \"@inquirer/prompts\": \"npm:@inquirer/prompts@^7.5.3\",\n    \"@langchain/anthropic\": \"npm:@langchain/anthropic@^0.3.23\",\n    \"@langchain/core\": \"npm:@langchain/core@^0.3.61\",\n    \"@langchain/google-genai\": \"npm:@langchain/google-genai@^0.2.14\",\n    \"@langchain/google-vertexai\": \"npm:@langchain/google-vertexai@^0.2.14\",\n    \"@langchain/langgraph\": \"npm:@langchain/langgraph@^0.3.5\",\n    \"@langchain/openai\": \"npm:@langchain/openai@^0.5.15\",\n    \"@oak/oak\": \"jsr:@oak/oak@^17.1.4\",\n    \"@radix-ui/react-dialog\": \"npm:@radix-ui/react-dialog@^1.1.14\",\n    \"@radix-ui/react-dropdown-menu\": \"npm:@radix-ui/react-dropdown-menu@^2.1.15\",\n    \"@radix-ui/react-label\": \"npm:@radix-ui/react-label@^2.1.7\",\n    \"@radix-ui/react-scroll-area\": \"npm:@radix-ui/react-scroll-area@^1.2.9\",\n    \"@radix-ui/react-separator\": \"npm:@radix-ui/react-separator@^1.1.7\",\n    \"@radix-ui/react-slider\": \"npm:@radix-ui/react-slider@^1.3.5\",\n    \"@radix-ui/react-slot\": \"npm:@radix-ui/react-slot@^1.2.3\",\n    \"@radix-ui/react-tooltip\": \"npm:@radix-ui/react-tooltip@^1.2.7\",\n    \"@std/expect\": \"jsr:@std/expect@^1.0.16\",\n    \"@std/path\": \"jsr:@std/path@^1.0.9\",\n    \"@std/testing\": \"jsr:@std/testing@^1.0.14\",\n    \"@tailwindcss/vite\": \"npm:@tailwindcss/vite@^4.1.8\",\n    \"@types/cytoscape-fcose\": \"npm:@types/cytoscape-fcose@^2.2.4\",\n    \"@types/react\": \"npm:@types/react@^19.1.6\",\n    \"@types/react-dom\": \"npm:@types/react-dom@^19.1.3\",\n    \"@vitejs/plugin-react\": \"npm:@vitejs/plugin-react@^4.4.1\",\n    \"class-variance-authority\": \"npm:class-variance-authority@^0.7.1\",\n    \"clsx\": \"npm:clsx@^2.1.1\",\n    \"cytoscape\": \"npm:cytoscape@^3.32.0\",\n    \"cytoscape-fcose\": \"npm:cytoscape-fcose@^2.2.0\",\n    \"glob\": \"npm:glob@^11.0.2\",\n    \"lucide-react\": \"npm:lucide-react@^0.513.0\",\n    \"react\": \"npm:react@^19.1.0\",\n    \"react-dom\": \"npm:react-dom@^19.1.0\",\n    \"react-router-dom\": \"npm:react-router-dom@^7.5.3\",\n    \"tailwind-merge\": \"npm:tailwind-merge@^3.3.0\",\n    \"tailwindcss\": \"npm:tailwindcss@^4.1.8\",\n    \"tree-sitter\": \"npm:tree-sitter@^0.22.4\",\n    \"tree-sitter-c\": \"npm:tree-sitter-c@0.23.6\",\n    \"tree-sitter-c-sharp\": \"npm:tree-sitter-c-sharp@^0.23.1\",\n    \"tree-sitter-python\": \"npm:tree-sitter-python@^0.23.6\",\n    \"tree-sitter-java\": \"npm:tree-sitter-java@^0.23.5\",\n    \"tw-animate-css\": \"npm:tw-animate-css@^1.3.4\",\n    \"vite\": \"npm:vite@^6.3.5\",\n    \"yargs\": \"https://deno.land/x/yargs@v18.0.0-deno/deno.ts\",\n    \"yargs-types\": \"https://deno.land/x/yargs@v18.0.0-deno/deno-types.ts\",\n    \"zod\": \"npm:zod@^3.24.4\"\n  },\n  \"exclude\": [\"viewer/\"],\n  \"tasks\": {\n    \"dev\": \"deno run -A src/index.ts\",\n    \"dev:viewer\": \"cd viewer && deno run -A npm:vite\",\n    \"build:viewer\": \"cd viewer && deno run -A npm:vite build\",\n    \"compile\": \"deno task build:viewer && deno compile -A --include=viewer/dist --output=dist/napi src/index.ts\",\n    \"compile-linux\": \"deno compile -A --include=viewer/dist --output=dist/napi.linux --target=x86_64-unknown-linux-gnu src/index.ts\",\n    \"compile-macos\": \"deno compile -A --include=viewer/dist --output=dist/napi.macos --target=x86_64-apple-darwin src/index.ts\",\n    \"compile-windows\": \"deno compile -A --include=viewer/dist --output=dist/napi.exe --target=x86_64-pc-windows-msvc src/index.ts\",\n    \"compile:all\": \"deno task build:viewer && deno task 'compile-*'\",\n    \"test\": \"deno test -A\"\n  },\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"react\",\n    \"jsxImportSourceTypes\": \"@types/react\"\n  }\n}\n"
  },
  {
    "path": "examples/README.md",
    "content": "## Quickstart\n\nEach example is already setup and initialize to use napi.\n\n### To view the project on the UI\n\n```\nnapi split configure\n```\n\n### Or to split the APIs from the command line directly\n\n```\nnapi split\n```\n"
  },
  {
    "path": "examples/c/network/.napirc",
    "content": "{\n  \"language\": \"c\",\n  \"project\": {\n    \"include\": [\n      \"**/*.c\",\n      \"**/*.h\"\n    ],\n    \"exclude\": [\n      \".git/**\",\n      \"**/dist/**\",\n      \"**/build/**\",\n      \"**/bin/**\",\n      \"**/obj/**\",\n      \"**/napi_out/**\"\n    ]\n  },\n  \"outDir\": \"napi_out\",\n  \"audit\": {\n    \"file\": { \"maxCodeChar\": 5000, \"maxCodeLine\": 300, \"maxDependency\": 20, \"maxCyclomaticComplexity\": 50 },\n    \"symbol\": { \"maxCodeChar\": 1000, \"maxCodeLine\": 80, \"maxDependency\": 10, \"maxCyclomaticComplexity\": 15 }\n  }\n}\n"
  },
  {
    "path": "examples/c/network/makefile",
    "content": "# --- Macros ---\n\n# compilation\nexec = gcc -Wall -Wextra -pedantic -O3 -g3 -fopenmp -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls\ncomp = gcc -c -Wall -Wextra -pedantic -O3 -g3 -fopenmp -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls\ntoexe = -o\ntoobj = -o\n\n# sources\nSRCSC= $(wildcard src/*/*.c)\nSRCSH= $(wildcard src/*/*.h)\n\nTSTSC= $(wildcard tst/*.c)\n\n# objects\nOBJS= $(wildcard obj/*.o)\nOBJBM= $(wildcard src/*/*.o)\n\n\n# --- Functions ---\n\nall: test main\n\nmain: create_exe_main to_obj_src\n\ntest: bin_mkdir create_exe_new to_obj\n\nclean: clean_o clean_bin\n\nbin_mkdir:\n\tmkdir -p bin;\n\nobj_mkdir:\n\tmkdir -p obj;\n\ntst_mov:\n\tmv $(foreach   exe,  $(TSTSC:tst/%.c=%)  ,   $(exe) )   bin\n\n\ncreate_exe_new: create_obj \n\t$(foreach test_obj,$(TSTSC:%.c=%.o), $(exec) $(test_obj) $(SRCSC:%.c=%.o) $(toexe) $(test_obj:%.o=%);)\n\ncreate_exe_main: create_obj_main\n\t$(exec) obj/main.o $(SRCSC:%.c=%.o) $(toexe)  app\n\nto_obj: obj_mkdir to_obj_src to_obj_tst\n\n\n\nto_obj_src: \n\tmv $(foreach obj,$(SRCSC:%.c=%.o), $(obj) ) obj\n\nto_obj_tst: \n\tmv $(foreach obj,$(TSTSC:%.c=%.o), $(obj) ) obj\n\n\ncreate_obj: create_obj_src create_obj_tst\n\ncreate_obj_src: \n\t$(foreach cfl,  $(SRCSC),  $(comp) $(cfl)  $(toobj) $(cfl:%.c=%).o;)\n\ncreate_obj_tst:\n\t$(foreach cfl,  $(TSTSC),  $(comp) $(cfl)  $(toobj) $(cfl:%.c=%).o;)\n\ncreate_obj_main: obj_mkdir create_obj_src\n\t$(comp) src/main.c $(toobj) obj/main.o"
  },
  {
    "path": "examples/c/network/src/cartographie/cartographie.c",
    "content": "#include \"cartographie.h\"\n\nvoid scanHorizontal(const char* network) {\n    struct sockaddr_in addr;\n    int sockfd, ttl = 64, timeout = 1000;\n    char ip[INET_ADDRSTRLEN];\n\n    // Création du socket\n    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);\n    if (sockfd < 0) {\n        perror(\"socket\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Définition de l'option TTL\n    if (setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {\n        perror(\"setsockopt\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Définition de l'option de timeout\n    struct timeval tv;\n    tv.tv_sec = timeout / 1000;\n    tv.tv_usec = (timeout % 1000) * 1000;\n    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {\n        perror(\"setsockopt\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Scan du réseau\n    for (int i = 1; i <= 255; i++) {\n        memset(&addr, 0, sizeof(addr));\n        addr.sin_family = AF_INET;\n\n        // Adresse --> octets\n        int octet1, octet2, octet3, octet4;\n        sscanf(network, \"%d.%d.%d.%d\", &octet1, &octet2, &octet3, &octet4);\n\n        // Dernier octet --> i\n        octet4 = i;\n\n        // Réassemblement de l'adresse\n        char host[INET_ADDRSTRLEN];\n        sprintf(host, \"%d.%d.%d.%d\", octet1, octet2, octet3, octet4);\n\n        // Conversion adresse en binaire\n        inet_pton(AF_INET, host, &(addr.sin_addr));\n\n        // Conversion adresse en texte\n        inet_ntop(AF_INET, &(addr.sin_addr), ip, INET_ADDRSTRLEN);\n\n        // Envoi du paquet ICMP\n        struct icmphdr icmp;\n        memset(&icmp, 0, sizeof(icmp));\n        icmp.type = ICMP_ECHO;\n        icmp.code = 0;\n        icmp.checksum = htons(~(ICMP_ECHO << 8));\n\n        if (sendto(sockfd, &icmp, sizeof(icmp), 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {\n            perror(\"sendto\");\n            continue;\n        }\n\n        // Attente de la réponse\n        fd_set read_set;\n        FD_ZERO(&read_set);\n        FD_SET(sockfd, &read_set);\n        struct timeval timeout;\n        timeout.tv_sec = 1;\n        timeout.tv_usec = 0;\n        int select_result = select(sockfd + 1, &read_set, NULL, NULL, &timeout);\n        if (select_result < 0) {\n            perror(\"select\");\n            continue;\n        } else if (select_result == 0) {\n            printf(\"Aucune réponse de %s\\n\", ip);\n            continue;\n        }\n\n        // Réception de la réponse\n        char buffer[IP_MAXPACKET];\n        struct sockaddr_in sender;\n        socklen_t sender_len = sizeof(sender);\n        ssize_t packet_len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&sender, &sender_len);\n        if (packet_len < 0) {\n            perror(\"recvfrom\");\n            continue;\n        }\n\n        // Affichage de l'adresse\n        printf(\"L'hôte %s est en ligne\\n\", ip);\n    }\n\n    // Fermeture socket\n    close(sockfd);\n}\n"
  },
  {
    "path": "examples/c/network/src/cartographie/cartographie.h",
    "content": "#ifndef CARTOGRAPHIE_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <netinet/in.h>\n#include <netinet/ip.h>\n#include <netinet/ip_icmp.h>\n#include \"tnmap.h\"\n\nvoid scanHorizontal(const char* network);\n\n#define CARTOGRAPHIE_H\n#endif\n"
  },
  {
    "path": "examples/c/network/src/cartographie/tnmap.c",
    "content": "#include \"tnmap.h\"\n\n// Vérifie si un hôte est actif\n// Retourne un socket si l'hôte est actif, 1 sinon\nint tnmap(const char* ip_addr) {\n    int sock;\n    struct sockaddr_in server;\n    int port;\n    int result[MAX_PORTS] = {0};\n\n    // Création du socket\n    sock = socket(AF_INET, SOCK_STREAM, 0);\n    if (sock == -1) {\n        perror(\"Échec de la création du socket\");\n        return -1;\n    }\n\n    server.sin_addr.s_addr = inet_addr(ip_addr);\n    server.sin_family = AF_INET;\n\n    // Scan des ports\n    for (port = 1; port <= MAX_PORTS; port++) {\n        server.sin_port = htons(port);\n\n        // Connexion à l'hôte distant\n        if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {\n            result[port - 1] = 0; // Port fermé\n        } else {\n            result[port - 1] = 1; // Port ouvert\n            close(sock);\n            sock = socket(AF_INET, SOCK_STREAM, 0); // Socket pour le prochain port\n            if (sock == -1) {\n                perror(\"Échec de la création du socket\");\n                return -1;\n            }\n        }\n    }\n\n    #pragma omp parallel for\n    // Affichage des résultats\n    for (port = 1; port <= MAX_PORTS; port++) {\n        if (result[port - 1] == 1) {\n            printf(\"Le port %d de %s est ouvert\\n\", port, ip_addr);\n        }\n    }\n\n    return 1;\n}\n\nvoid scanVertical(const char* ip_addr) {\n    for (int i = 0; i < 255; i++) {\n        tnmap(ip_addr);\n    }\n}\n"
  },
  {
    "path": "examples/c/network/src/cartographie/tnmap.h",
    "content": "#ifndef TNMAP_H\n#define TNMAP_H\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#define MAX_PORTS 65535\n#endif\n\nint tnmap(const char* ip_addr);\nvoid scanVertical(const char* ip_addr);\n"
  },
  {
    "path": "examples/c/network/src/main.c",
    "content": "#include \"cartographie/cartographie.h\"\n#include <stdio.h>\n#include <string.h>\n\nint main(int argc, char *argv[]) {\n    if (argc < 3) {\n        fprintf(stderr, \"Utilisation : %s <mode> <ip>\\n\", argv[0]);\n        fprintf(stderr, \"Modes :\\n\");\n        fprintf(stderr, \"  -h: Scan horizontal\\n\");\n        fprintf(stderr, \"  -v: Scan vertical\\n\");\n        fprintf(stderr, \"Option:\\n\");\n        fprintf(stderr, \"  <ip>: IP à scanner verticalemnet or horizontalement (adresse de réseau)\\n\");\n        return 1;\n    }\n    if (strcmp(argv[1], \"-h\") == 0) {\n        scanHorizontal(argv[2]);\n    } else if (strcmp(argv[1], \"-v\") == 0) {\n        scanVertical(argv[2]);\n    } else {\n        fprintf(stderr, \"Mode invalide\\n\");\n        return 1;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "examples/c/network/src/tcp_cs/clientTCP.c",
    "content": "#include \"clientTCP.h\"\n\nint startClient(int argc, char *argv[]) {\n    int serverSocket;\n    /*\n    * Ouvrir socket (socket STREAM)\n    */\n    if ((serverSocket = socket(PF_INET, SOCK_STREAM, 0)) <0) {\n        perror (\"erreur socket\");\n        exit (1);\n    }\n\n    struct sockaddr_in serv_addr;\n    /*\n    * Lier l'adresse locale à la socket\n    */;\n    memset(&serv_addr, 0, sizeof(serv_addr) );\n    serv_addr.sin_family = AF_INET ;\n    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);\n    serv_addr.sin_port = htons(CLIENT_PORT);\n    if (bind(serverSocket,(struct sockaddr *)&serv_addr, sizeof(serv_addr) ) <0) {\n        perror (\"servecho: erreur bind\\n\");\n        exit (1);\n    }\n\n    /*\n    * Remplir la structure serv_addr avec\n    l'adresse du serveur\n    */\n    bzero( (char *) &serv_addr, sizeof(serv_addr) );\n    serv_addr.sin_family = AF_INET;\n\n    if (argc != 3) {\n        fprintf(stderr, \"usage : %s <adresse IP> <port>\\n\", argv[0]);\n        exit(1);\n    }\n    serv_addr.sin_port = htons((uint16_t) atoi(argv[2]));\n    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);\n    if (connect(serverSocket, (struct sockaddr *) &serv_addr, sizeof(serv_addr) ) < 0){\n        perror (\"cliecho : erreur connect\");\n        return 1;\n    }\n    char buffer[280];\n    int n;\n    while (1) {\n        printf(\"cliecho : message à envoyer : \");\n        memset(buffer, 0, sizeof(buffer));\n        scanf(\"%s\", buffer);\n        n = write(serverSocket, buffer, strlen(buffer));\n        if (n < 0) {\n            perror(\"cliecho : erreur write\\n\");\n            close(serverSocket);\n            return 1;\n        }\n        memset(buffer, 0, sizeof(buffer));\n        n = read(serverSocket, buffer, 280);\n        if (n < 0) {\n            perror(\"cliecho : erreur read\\n\");\n            close(serverSocket);\n            return 1;\n        }\n        printf(\"cliecho : message reçu : %s\\n\", buffer);\n    }\n}\n"
  },
  {
    "path": "examples/c/network/src/tcp_cs/clientTCP.h",
    "content": "#ifndef CLIENTTCP_H\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <stdio.h>\n#include <netinet/in.h>\n#include <string.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n#define CLIENT_PORT 7776\n\nint startClient(int argc, char *argv[]);\n\n#define CLIENTTCP_H\n#endif\n"
  },
  {
    "path": "examples/c/network/src/tcp_cs/serveurTCP.c",
    "content": "#include \"serveurTCP.h\"\r\n\r\nint startServer(void) {\r\n    int serverSocket;\r\n    struct sockaddr_in serv_addr;\r\n    /*\r\n    * Ouvrir socket (socket STREAM)\r\n    */\r\n    if ((serverSocket = socket(PF_INET, SOCK_STREAM, 0))<0) {\r\n        perror(\"erreur socket\");\r\n        return 1;\r\n    }\r\n\r\n    memset(&serv_addr, 0, sizeof(serv_addr) );\r\n    serv_addr.sin_family = AF_INET ;\r\n\r\n    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    serv_addr.sin_port = htons(SERV_PORT);\r\n    if (bind(serverSocket,(struct sockaddr *)&serv_addr, sizeof(serv_addr) ) <0) {\r\n        perror (\"servecho: erreur bind\\n\");\r\n        return 1;\r\n    }\r\n\r\n    if (listen(serverSocket, SOMAXCONN) <0) {\r\n        perror (\"servecho: erreur listen\\n\");\r\n        return 1;\r\n    }\r\n\r\n    int dialogSocket;\r\n    int clilen;\r\n    struct sockaddr_in cli_addr;\r\n    clilen = sizeof(cli_addr);\r\n    dialogSocket = accept(serverSocket, (struct sockaddr *)&cli_addr, (socklen_t *)&clilen);\r\n    if (dialogSocket < 0) {\r\n        perror(\"servecho : erreur accep\\n\");\r\n        close(serverSocket);\r\n        return 1;\r\n    }\r\n    while (1) {\r\n        char buffer[280];\r\n        int n;\r\n        n = read(dialogSocket, buffer, 280);\r\n        if (n < 0) {\r\n            perror(\"servecho : erreur read\\n\");\r\n            close(dialogSocket);\r\n            return 1;\r\n        }\r\n        printf(\"servecho : message reçu : %s\\n\", buffer);\r\n        n = write(dialogSocket, buffer, strlen(buffer));\r\n        if (n < 0) {\r\n            perror(\"servecho : erreur write\\n\");\r\n            close(dialogSocket);\r\n            return 1;\r\n        }\r\n    }\r\n    close(dialogSocket);\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "examples/c/network/src/tcp_cs/serveurTCP.h",
    "content": "#ifndef SERVEURTCP_H\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <stdio.h>\n#include <netinet/in.h>\n#include <string.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n#define SERV_PORT 7777\n\nint startServer(void);\n\n#define SERVEURTCP_H\n#endif\n"
  },
  {
    "path": "examples/csharp/EndpointExample/.napirc",
    "content": "{\n  \"language\": \"c-sharp\",\n  \"project\": {\n    \"include\": [\"**/*\"],\n    \"exclude\": [\"bin/**\", \"obj/**\", \"napi-output/**\"]\n  },\n  \"outDir\": \"napi-output\",\n  \"audit\": {\n    \"file\": { \"maxCodeChar\": 5000, \"maxCodeLine\": 300, \"maxDependency\": 20, \"maxCyclomaticComplexity\": 50 },\n    \"symbol\": { \"maxCodeChar\": 1000, \"maxCodeLine\": 80, \"maxDependency\": 10, \"maxCyclomaticComplexity\": 15 }\n  }\n}\n"
  },
  {
    "path": "examples/csharp/EndpointExample/EndpointExample.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"FastEndpoints\" Version=\"5.35.0\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "examples/csharp/EndpointExample/Program.cs",
    "content": "using FastEndpoints;\n\nvar bld = WebApplication.CreateBuilder();\nbld.Services.AddFastEndpoints();\n\nvar app = bld.Build();\napp.UseFastEndpoints();\napp.Run();\n"
  },
  {
    "path": "examples/csharp/EndpointExample/Properties/launchSettings.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/launchsettings.json\",\n  \"profiles\": {\n    \"http\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"applicationUrl\": \"http://localhost:5288\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"https\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"applicationUrl\": \"https://localhost:7299;http://localhost:5288\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/csharp/EndpointExample/appsettings.Development.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "examples/csharp/EndpointExample/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "examples/csharp/EndpointExample/src/MyEndpoint.cs",
    "content": "public class MyEndpoint : Endpoint<MyRequest, MyResponse>\n{\n    public override void Configure()\n    {\n        Post(\"/api/user/create\");\n        AllowAnonymous();\n    }\n\n    public override async Task HandleAsync(MyRequest req, CancellationToken ct)\n    {\n        await SendAsync(new()\n        {\n            FullName = req.FirstName + \" \" + req.LastName,\n            IsOver18 = req.Age > 18\n        });\n    }\n}\n"
  },
  {
    "path": "examples/csharp/EndpointExample/src/MyRequest.cs",
    "content": "public class MyRequest\n{\n    public string FirstName { get; set; }\n    public string LastName { get; set; }\n    public int Age { get; set; }\n}\n"
  },
  {
    "path": "examples/csharp/EndpointExample/src/MyResponse.cs",
    "content": "public class MyResponse\n{\n    public string FullName { get; set; }\n    public bool IsOver18 { get; set; }\n}\n"
  },
  {
    "path": "examples/java/websocket/README.md",
    "content": "# NAPI Java example\n\nThis example uses Spring's websocket sample artifact from the Maven repository.\nTo experiment with it, simply run :\n\n```Bash\nnapi init\n```\n\nin this folder and follow the instructions.\n"
  },
  {
    "path": "examples/java/websocket/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project\n  xmlns=\"http://maven.apache.org/POM/4.0.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\"\n>\n  <modelVersion>4.0.0</modelVersion>\n  <artifactId>websocket</artifactId>\n  <parent>\n    <!-- Your own application should inherit from spring-boot-starter-parent -->\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-parent</artifactId>\n    <version>1.0.2.RELEASE</version>\n  </parent>\n  <groupId>napi</groupId>\n  <name>Spring Boot WebSocket Sample</name>\n  <description>Spring Boot WebSocket Sample</description>\n  <version>1.0-SNAPSHOT</version>\n  <url>http://projects.spring.io/spring-boot/</url>\n  <organization>\n    <name>Pivotal Software, Inc.</name>\n    <url>http://www.spring.io</url>\n  </organization>\n  <properties>\n    <main.basedir>${basedir}/../..</main.basedir>\n    <java.version>1.7</java.version>\n  </properties>\n  <dependencies>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-websocket</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-actuator</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-test</artifactId>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-maven-plugin</artifactId>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/client/GreetingService.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.client;\n\npublic interface GreetingService {\n\n\tString getGreeting();\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/client/SimpleClientWebSocketHandler.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.client;\n\nimport java.util.concurrent.CountDownLatch;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\npublic class SimpleClientWebSocketHandler extends TextWebSocketHandler {\n\n\tprotected Log logger = LogFactory.getLog(SimpleClientWebSocketHandler.class);\n\n\tprivate final GreetingService greetingService;\n\n\tprivate final CountDownLatch latch;\n\n\t@Autowired\n\tpublic SimpleClientWebSocketHandler(GreetingService greetingService,\n\t\t\tCountDownLatch latch) {\n\t\tthis.greetingService = greetingService;\n\t\tthis.latch = latch;\n\t}\n\n\t@Override\n\tpublic void afterConnectionEstablished(WebSocketSession session) throws Exception {\n\t\tTextMessage message = new TextMessage(this.greetingService.getGreeting());\n\t\tsession.sendMessage(message);\n\t}\n\n\t@Override\n\tpublic void handleTextMessage(WebSocketSession session, TextMessage message)\n\t\t\tthrows Exception {\n\t\tthis.logger.info(\"Received: \" + message + \" (\" + this.latch.getCount() + \")\");\n\t\tsession.close();\n\t\tthis.latch.countDown();\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/client/SimpleGreetingService.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.client;\n\npublic class SimpleGreetingService implements GreetingService {\n\n\t@Override\n\tpublic String getGreeting() {\n\t\treturn \"Hello world!\";\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/config/SampleWebSocketsApplication.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.config;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.boot.context.web.SpringBootServletInitializer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.config.annotation.EnableWebSocket;\nimport org.springframework.web.socket.config.annotation.WebSocketConfigurer;\nimport org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;\nimport org.springframework.web.socket.handler.PerConnectionWebSocketHandler;\n\nimport samples.websocket.client.GreetingService;\nimport samples.websocket.client.SimpleGreetingService;\nimport samples.websocket.echo.DefaultEchoService;\nimport samples.websocket.echo.EchoService;\nimport samples.websocket.echo.EchoWebSocketHandler;\nimport samples.websocket.snake.SnakeWebSocketHandler;\n\n@Configuration\n@EnableAutoConfiguration\n@EnableWebSocket\npublic class SampleWebSocketsApplication extends SpringBootServletInitializer implements\n\t\tWebSocketConfigurer {\n\n\t@Override\n\tpublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {\n\t\tregistry.addHandler(echoWebSocketHandler(), \"/echo\").withSockJS();\n\t\tregistry.addHandler(snakeWebSocketHandler(), \"/snake\").withSockJS();\n\t}\n\n\t@Override\n\tprotected SpringApplicationBuilder configure(SpringApplicationBuilder application) {\n\t\treturn application.sources(SampleWebSocketsApplication.class);\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(SampleWebSocketsApplication.class, args);\n\t}\n\n\t@Bean\n\tpublic EchoService echoService() {\n\t\treturn new DefaultEchoService(\"Did you say \\\"%s\\\"?\");\n\t}\n\n\t@Bean\n\tpublic GreetingService greetingService() {\n\t\treturn new SimpleGreetingService();\n\t}\n\n\t@Bean\n\tpublic WebSocketHandler echoWebSocketHandler() {\n\t\treturn new EchoWebSocketHandler(echoService());\n\t}\n\n\t@Bean\n\tpublic WebSocketHandler snakeWebSocketHandler() {\n\t\treturn new PerConnectionWebSocketHandler(SnakeWebSocketHandler.class);\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/echo/DefaultEchoService.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.echo;\n\npublic class DefaultEchoService implements EchoService {\n\n\tprivate final String echoFormat;\n\n\tpublic DefaultEchoService(String echoFormat) {\n\t\tthis.echoFormat = (echoFormat != null) ? echoFormat : \"%s\";\n\t}\n\n\t@Override\n\tpublic String getMessage(String message) {\n\t\treturn String.format(this.echoFormat, message);\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/echo/EchoService.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.echo;\n\npublic interface EchoService {\n\n\tString getMessage(String message);\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/echo/EchoWebSocketHandler.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.echo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.socket.CloseStatus;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\n/**\n * Echo messages by implementing a Spring {@link WebSocketHandler} abstraction.\n */\npublic class EchoWebSocketHandler extends TextWebSocketHandler {\n\n\tprivate static Logger logger = LoggerFactory.getLogger(EchoWebSocketHandler.class);\n\n\tprivate final EchoService echoService;\n\n\t@Autowired\n\tpublic EchoWebSocketHandler(EchoService echoService) {\n\t\tthis.echoService = echoService;\n\t}\n\n\t@Override\n\tpublic void afterConnectionEstablished(WebSocketSession session) {\n\t\tlogger.debug(\"Opened new session in instance \" + this);\n\t}\n\n\t@Override\n\tpublic void handleTextMessage(WebSocketSession session, TextMessage message)\n\t\t\tthrows Exception {\n\t\tString echoMessage = this.echoService.getMessage(message.getPayload());\n\t\tlogger.debug(echoMessage);\n\t\tsession.sendMessage(new TextMessage(echoMessage));\n\t}\n\n\t@Override\n\tpublic void handleTransportError(WebSocketSession session, Throwable exception)\n\t\t\tthrows Exception {\n\t\tsession.close(CloseStatus.SERVER_ERROR);\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/snake/Direction.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.snake;\n\npublic enum Direction {\n\tNONE, NORTH, SOUTH, EAST, WEST\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/snake/Location.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.snake;\n\npublic class Location {\n\n\tpublic int x;\n\tpublic int y;\n\tpublic static final int GRID_SIZE = 10;\n\tpublic static final int PLAYFIELD_HEIGHT = 480;\n\tpublic static final int PLAYFIELD_WIDTH = 640;\n\n\tpublic Location(int x, int y) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t}\n\n\tpublic Location getAdjacentLocation(Direction direction) {\n\t\tswitch (direction) {\n\t\tcase NORTH:\n\t\t\treturn new Location(this.x, this.y - Location.GRID_SIZE);\n\t\tcase SOUTH:\n\t\t\treturn new Location(this.x, this.y + Location.GRID_SIZE);\n\t\tcase EAST:\n\t\t\treturn new Location(this.x + Location.GRID_SIZE, this.y);\n\t\tcase WEST:\n\t\t\treturn new Location(this.x - Location.GRID_SIZE, this.y);\n\t\tcase NONE:\n\t\t\t// fall through\n\t\tdefault:\n\t\t\treturn this;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tLocation location = (Location) o;\n\n\t\tif (this.x != location.x) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.y != location.y) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.x;\n\t\tresult = 31 * result + this.y;\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/snake/Snake.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.snake;\n\nimport java.util.ArrayDeque;\nimport java.util.Collection;\nimport java.util.Deque;\n\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\n\npublic class Snake {\n\n\tprivate static final int DEFAULT_LENGTH = 5;\n\n\tprivate final int id;\n\tprivate final WebSocketSession session;\n\n\tprivate Direction direction;\n\tprivate int length = DEFAULT_LENGTH;\n\tprivate Location head;\n\tprivate final Deque<Location> tail = new ArrayDeque<Location>();\n\tprivate final String hexColor;\n\n\tpublic Snake(int id, WebSocketSession session) {\n\t\tthis.id = id;\n\t\tthis.session = session;\n\t\tthis.hexColor = SnakeUtils.getRandomHexColor();\n\t\tresetState();\n\t}\n\n\tprivate void resetState() {\n\t\tthis.direction = Direction.NONE;\n\t\tthis.head = SnakeUtils.getRandomLocation();\n\t\tthis.tail.clear();\n\t\tthis.length = DEFAULT_LENGTH;\n\t}\n\n\tprivate synchronized void kill() throws Exception {\n\t\tresetState();\n\t\tsendMessage(\"{'type': 'dead'}\");\n\t}\n\n\tprivate synchronized void reward() throws Exception {\n\t\tthis.length++;\n\t\tsendMessage(\"{'type': 'kill'}\");\n\t}\n\n\tprotected void sendMessage(String msg) throws Exception {\n\t\tthis.session.sendMessage(new TextMessage(msg));\n\t}\n\n\tpublic synchronized void update(Collection<Snake> snakes) throws Exception {\n\t\tLocation nextLocation = this.head.getAdjacentLocation(this.direction);\n\t\tif (nextLocation.x >= SnakeUtils.PLAYFIELD_WIDTH) {\n\t\t\tnextLocation.x = 0;\n\t\t}\n\t\tif (nextLocation.y >= SnakeUtils.PLAYFIELD_HEIGHT) {\n\t\t\tnextLocation.y = 0;\n\t\t}\n\t\tif (nextLocation.x < 0) {\n\t\t\tnextLocation.x = SnakeUtils.PLAYFIELD_WIDTH;\n\t\t}\n\t\tif (nextLocation.y < 0) {\n\t\t\tnextLocation.y = SnakeUtils.PLAYFIELD_HEIGHT;\n\t\t}\n\t\tif (this.direction != Direction.NONE) {\n\t\t\tthis.tail.addFirst(this.head);\n\t\t\tif (this.tail.size() > this.length) {\n\t\t\t\tthis.tail.removeLast();\n\t\t\t}\n\t\t\tthis.head = nextLocation;\n\t\t}\n\n\t\thandleCollisions(snakes);\n\t}\n\n\tprivate void handleCollisions(Collection<Snake> snakes) throws Exception {\n\t\tfor (Snake snake : snakes) {\n\t\t\tboolean headCollision = this.id != snake.id\n\t\t\t\t\t&& snake.getHead().equals(this.head);\n\t\t\tboolean tailCollision = snake.getTail().contains(this.head);\n\t\t\tif (headCollision || tailCollision) {\n\t\t\t\tkill();\n\t\t\t\tif (this.id != snake.id) {\n\t\t\t\t\tsnake.reward();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic synchronized Location getHead() {\n\t\treturn this.head;\n\t}\n\n\tpublic synchronized Collection<Location> getTail() {\n\t\treturn this.tail;\n\t}\n\n\tpublic synchronized void setDirection(Direction direction) {\n\t\tthis.direction = direction;\n\t}\n\n\tpublic synchronized String getLocationsJson() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(String.format(\"{x: %d, y: %d}\", Integer.valueOf(this.head.x),\n\t\t\t\tInteger.valueOf(this.head.y)));\n\t\tfor (Location location : this.tail) {\n\t\t\tsb.append(',');\n\t\t\tsb.append(String.format(\"{x: %d, y: %d}\", Integer.valueOf(location.x),\n\t\t\t\t\tInteger.valueOf(location.y)));\n\t\t}\n\t\treturn String.format(\"{'id':%d,'body':[%s]}\", Integer.valueOf(this.id),\n\t\t\t\tsb.toString());\n\t}\n\n\tpublic int getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic String getHexColor() {\n\t\treturn this.hexColor;\n\t}\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/snake/SnakeTimer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.snake;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\nimport org.apache.juli.logging.Log;\nimport org.apache.juli.logging.LogFactory;\n\n/**\n * Sets up the timer for the multi-player snake game WebSocket example.\n */\npublic class SnakeTimer {\n\n\tprivate static final Log log = LogFactory.getLog(SnakeTimer.class);\n\n\tprivate static Timer gameTimer = null;\n\n\tprivate static final long TICK_DELAY = 100;\n\n\tprivate static final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<Integer, Snake>();\n\n\tpublic static synchronized void addSnake(Snake snake) {\n\t\tif (snakes.size() == 0) {\n\t\t\tstartTimer();\n\t\t}\n\t\tsnakes.put(Integer.valueOf(snake.getId()), snake);\n\t}\n\n\tpublic static Collection<Snake> getSnakes() {\n\t\treturn Collections.unmodifiableCollection(snakes.values());\n\t}\n\n\tpublic static synchronized void removeSnake(Snake snake) {\n\t\tsnakes.remove(Integer.valueOf(snake.getId()));\n\t\tif (snakes.size() == 0) {\n\t\t\tstopTimer();\n\t\t}\n\t}\n\n\tpublic static void tick() throws Exception {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (Iterator<Snake> iterator = SnakeTimer.getSnakes().iterator(); iterator\n\t\t\t\t.hasNext();) {\n\t\t\tSnake snake = iterator.next();\n\t\t\tsnake.update(SnakeTimer.getSnakes());\n\t\t\tsb.append(snake.getLocationsJson());\n\t\t\tif (iterator.hasNext()) {\n\t\t\t\tsb.append(',');\n\t\t\t}\n\t\t}\n\t\tbroadcast(String.format(\"{'type': 'update', 'data' : [%s]}\", sb.toString()));\n\t}\n\n\tpublic static void broadcast(String message) throws Exception {\n\t\tCollection<Snake> snakes = new CopyOnWriteArrayList<>(SnakeTimer.getSnakes());\n\t\tfor (Snake snake : snakes) {\n\t\t\ttry {\n\t\t\t\tsnake.sendMessage(message);\n\t\t\t}\n\t\t\tcatch (Throwable ex) {\n\t\t\t\t// if Snake#sendMessage fails the client is removed\n\t\t\t\tremoveSnake(snake);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void startTimer() {\n\t\tgameTimer = new Timer(SnakeTimer.class.getSimpleName() + \" Timer\");\n\t\tgameTimer.scheduleAtFixedRate(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\ttick();\n\t\t\t\t}\n\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\tlog.error(\"Caught to prevent timer from shutting down\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}, TICK_DELAY, TICK_DELAY);\n\t}\n\n\tpublic static void stopTimer() {\n\t\tif (gameTimer != null) {\n\t\t\tgameTimer.cancel();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/snake/SnakeUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.snake;\n\nimport java.awt.Color;\nimport java.util.Random;\n\npublic class SnakeUtils {\n\n\tpublic static final int PLAYFIELD_WIDTH = 640;\n\tpublic static final int PLAYFIELD_HEIGHT = 480;\n\tpublic static final int GRID_SIZE = 10;\n\n\tprivate static final Random random = new Random();\n\n\tpublic static String getRandomHexColor() {\n\t\tfloat hue = random.nextFloat();\n\t\t// sat between 0.1 and 0.3\n\t\tfloat saturation = (random.nextInt(2000) + 1000) / 10000f;\n\t\tfloat luminance = 0.9f;\n\t\tColor color = Color.getHSBColor(hue, saturation, luminance);\n\t\treturn '#' + Integer.toHexString((color.getRGB() & 0xffffff) | 0x1000000)\n\t\t\t\t.substring(1);\n\t}\n\n\tpublic static Location getRandomLocation() {\n\t\tint x = roundByGridSize(random.nextInt(PLAYFIELD_WIDTH));\n\t\tint y = roundByGridSize(random.nextInt(PLAYFIELD_HEIGHT));\n\t\treturn new Location(x, y);\n\t}\n\n\tprivate static int roundByGridSize(int value) {\n\t\tvalue = value + (GRID_SIZE / 2);\n\t\tvalue = value / GRID_SIZE;\n\t\tvalue = value * GRID_SIZE;\n\t\treturn value;\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/java/samples/websocket/snake/SnakeWebSocketHandler.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.snake;\n\nimport java.awt.Color;\nimport java.util.Iterator;\nimport java.util.Random;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.springframework.web.socket.CloseStatus;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\npublic class SnakeWebSocketHandler extends TextWebSocketHandler {\n\n\tpublic static final int PLAYFIELD_WIDTH = 640;\n\tpublic static final int PLAYFIELD_HEIGHT = 480;\n\tpublic static final int GRID_SIZE = 10;\n\n\tprivate static final AtomicInteger snakeIds = new AtomicInteger(0);\n\tprivate static final Random random = new Random();\n\n\tprivate final int id;\n\tprivate Snake snake;\n\n\tpublic static String getRandomHexColor() {\n\t\tfloat hue = random.nextFloat();\n\t\t// sat between 0.1 and 0.3\n\t\tfloat saturation = (random.nextInt(2000) + 1000) / 10000f;\n\t\tfloat luminance = 0.9f;\n\t\tColor color = Color.getHSBColor(hue, saturation, luminance);\n\t\treturn '#' + Integer.toHexString((color.getRGB() & 0xffffff) | 0x1000000)\n\t\t\t\t.substring(1);\n\t}\n\n\tpublic static Location getRandomLocation() {\n\t\tint x = roundByGridSize(random.nextInt(PLAYFIELD_WIDTH));\n\t\tint y = roundByGridSize(random.nextInt(PLAYFIELD_HEIGHT));\n\t\treturn new Location(x, y);\n\t}\n\n\tprivate static int roundByGridSize(int value) {\n\t\tvalue = value + (GRID_SIZE / 2);\n\t\tvalue = value / GRID_SIZE;\n\t\tvalue = value * GRID_SIZE;\n\t\treturn value;\n\t}\n\n\tpublic SnakeWebSocketHandler() {\n\t\tthis.id = snakeIds.getAndIncrement();\n\t}\n\n\t@Override\n\tpublic void afterConnectionEstablished(WebSocketSession session) throws Exception {\n\t\tthis.snake = new Snake(this.id, session);\n\t\tSnakeTimer.addSnake(this.snake);\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (Iterator<Snake> iterator = SnakeTimer.getSnakes().iterator(); iterator\n\t\t\t\t.hasNext();) {\n\t\t\tSnake snake = iterator.next();\n\t\t\tsb.append(String.format(\"{id: %d, color: '%s'}\",\n\t\t\t\t\tInteger.valueOf(snake.getId()), snake.getHexColor()));\n\t\t\tif (iterator.hasNext()) {\n\t\t\t\tsb.append(',');\n\t\t\t}\n\t\t}\n\t\tSnakeTimer\n\t\t\t\t.broadcast(String.format(\"{'type': 'join','data':[%s]}\", sb.toString()));\n\t}\n\n\t@Override\n\tprotected void handleTextMessage(WebSocketSession session, TextMessage message)\n\t\t\tthrows Exception {\n\t\tString payload = message.getPayload();\n\t\tif (\"west\".equals(payload)) {\n\t\t\tthis.snake.setDirection(Direction.WEST);\n\t\t}\n\t\telse if (\"north\".equals(payload)) {\n\t\t\tthis.snake.setDirection(Direction.NORTH);\n\t\t}\n\t\telse if (\"east\".equals(payload)) {\n\t\t\tthis.snake.setDirection(Direction.EAST);\n\t\t}\n\t\telse if (\"south\".equals(payload)) {\n\t\t\tthis.snake.setDirection(Direction.SOUTH);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void afterConnectionClosed(WebSocketSession session, CloseStatus status)\n\t\t\tthrows Exception {\n\t\tSnakeTimer.removeSnake(this.snake);\n\t\tSnakeTimer.broadcast(String.format(\"{'type': 'leave', 'id': %d}\",\n\t\t\t\tInteger.valueOf(this.id)));\n\t}\n}\n"
  },
  {
    "path": "examples/java/websocket/src/main/resources/static/echo.html",
    "content": "<!--\n  Licensed to the Apache Software Foundation (ASF) under one or more\n  contributor license agreements.  See the NOTICE file distributed with\n  this work for additional information regarding copyright ownership.\n  The ASF licenses this file to You under the Apache License, Version 2.0\n  (the \"License\"); you may not use this file except in compliance with\n  the License.  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Apache Tomcat WebSocket Examples: Echo</title>\n    <style type=\"text/css\">\n      #connect-container {\n        float: left;\n        width: 400px;\n      }\n\n      #connect-container div {\n        padding: 5px;\n      }\n\n      #console-container {\n        float: left;\n        margin-left: 15px;\n        width: 400px;\n      }\n\n      #console {\n        border: 1px solid #cccccc;\n        border-right-color: #999999;\n        border-bottom-color: #999999;\n        height: 170px;\n        overflow-y: scroll;\n        padding: 5px;\n        width: 100%;\n      }\n\n      #console p {\n        padding: 0;\n        margin: 0;\n      }\n    </style>\n    <script src=\"http://cdn.sockjs.org/sockjs-0.3.min.js\"></script>\n    <script type=\"text/javascript\">\n      var ws = null;\n\n      function setConnected(connected) {\n        document.getElementById(\"connect\").disabled = connected;\n        document.getElementById(\"disconnect\").disabled = !connected;\n        document.getElementById(\"echo\").disabled = !connected;\n      }\n\n      function connect() {\n        var target = document.getElementById(\"target\").value;\n        ws = new SockJS(target);\n        ws.onopen = function () {\n          setConnected(true);\n          log(\"Info: WebSocket connection opened.\");\n        };\n        ws.onmessage = function (event) {\n          log(\"Received: \" + event.data);\n        };\n        ws.onclose = function () {\n          setConnected(false);\n          log(\"Info: WebSocket connection closed.\");\n        };\n      }\n\n      function disconnect() {\n        if (ws != null) {\n          ws.close();\n          ws = null;\n        }\n        setConnected(false);\n      }\n\n      function echo() {\n        if (ws != null) {\n          var message = document.getElementById(\"message\").value;\n          log(\"Sent: \" + message);\n          ws.send(message);\n        } else {\n          alert(\n            \"WebSocket connection not established, please connect.\",\n          );\n        }\n      }\n\n      function log(message) {\n        var console = document.getElementById(\"console\");\n        var p = document.createElement(\"p\");\n        p.style.wordWrap = \"break-word\";\n        p.appendChild(document.createTextNode(message));\n        console.appendChild(p);\n        while (console.childNodes.length > 25) {\n          console.removeChild(console.firstChild);\n        }\n        console.scrollTop = console.scrollHeight;\n      }\n    </script>\n  </head>\n  <body>\n    <noscript><h2 style=\"color: #ff0000\">\n        Seems your browser doesn't support Javascript! Websockets rely on\n        Javascript being enabled. Please enable Javascript and reload this page!\n      </h2></noscript>\n    <div>\n      <div id=\"connect-container\">\n        <div>\n          <input\n            id=\"target\"\n            type=\"text\"\n            size=\"40\"\n            style=\"width: 350px\"\n            value=\"/echo\"\n          />\n        </div>\n        <div>\n          <button id=\"connect\" onclick=\"connect()\">Connect</button>\n          <button id=\"disconnect\" disabled=\"disabled\" onclick=\"disconnect()\">\n            Disconnect\n          </button>\n        </div>\n        <div>\n          <textarea id=\"message\" style=\"width: 350px\"\n          >Here is a message!</textarea>\n        </div>\n        <div>\n          <button id=\"echo\" onclick=\"echo()\" disabled=\"disabled\">\n            Echo message\n          </button>\n        </div>\n      </div>\n      <div id=\"console-container\">\n        <div id=\"console\"></div>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/java/websocket/src/main/resources/static/index.html",
    "content": "<!--\n  Licensed to the Apache Software Foundation (ASF) under one or more\n  contributor license agreements.  See the NOTICE file distributed with\n  this work for additional information regarding copyright ownership.\n  The ASF licenses this file to You under the Apache License, Version 2.0\n  (the \"License\"); you may not use this file except in compliance with\n  the License.  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Apache Tomcat WebSocket Examples: Index</title>\n  </head>\n  <body>\n    <noscript><h2 style=\"color: #ff0000\">\n        Seems your browser doesn't support Javascript! Websockets rely on\n        Javascript being enabled. Please enable Javascript and reload this page!\n      </h2></noscript>\n    <p>Please select the sample you would like to try.</p>\n    <ul>\n      <li><a href=\"./echo.html\">Echo</a></li>\n      <li><a href=\"./snake.html\">Snake</a></li>\n    </ul>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/java/websocket/src/main/resources/static/snake.html",
    "content": "<!--\n  Licensed to the Apache Software Foundation (ASF) under one or more\n  contributor license agreements.  See the NOTICE file distributed with\n  this work for additional information regarding copyright ownership.\n  The ASF licenses this file to You under the Apache License, Version 2.0\n  (the \"License\"); you may not use this file except in compliance with\n  the License.  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n        \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <title>Apache Tomcat WebSocket Examples: Multiplayer Snake</title>\n    <style type=\"text/css\">\n      #playground {\n        width: 640px;\n        height: 480px;\n        background-color: #000;\n      }\n\n      #console-container {\n        float: left;\n        margin-left: 15px;\n        width: 300px;\n      }\n\n      #console {\n        border: 1px solid #cccccc;\n        border-right-color: #999999;\n        border-bottom-color: #999999;\n        height: 480px;\n        overflow-y: scroll;\n        padding-left: 5px;\n        padding-right: 5px;\n        width: 100%;\n      }\n\n      #console p {\n        padding: 0;\n        margin: 0;\n      }\n    </style>\n    <script src=\"http://cdn.sockjs.org/sockjs-0.3.min.js\"></script>\n  </head>\n  <body>\n    <noscript><h2 style=\"color: #ff0000\">\n        Seems your browser doesn't support Javascript! Websockets rely on\n        Javascript being enabled. Please enable Javascript and reload this page!\n      </h2></noscript>\n\n    <div style=\"float: left\">\n      <canvas id=\"playground\" width=\"640\" height=\"480\"></canvas>\n    </div>\n    <div id=\"console-container\">\n      <div id=\"console\"></div>\n    </div>\n    <script type=\"text/javascript\">\n      var Game = {};\n\n      Game.fps = 30;\n      Game.socket = null;\n      Game.nextFrame = null;\n      Game.interval = null;\n      Game.direction = \"none\";\n      Game.gridSize = 10;\n\n      function Snake() {\n        this.snakeBody = [];\n        this.color = null;\n      }\n\n      Snake.prototype.draw = function (context) {\n        for (var id in this.snakeBody) {\n          context.fillStyle = this.color;\n          context.fillRect(\n            this.snakeBody[id].x,\n            this.snakeBody[id].y,\n            Game.gridSize,\n            Game.gridSize,\n          );\n        }\n      };\n\n      Game.initialize = function () {\n        this.entities = [];\n        canvas = document.getElementById(\"playground\");\n        if (!canvas.getContext) {\n          Console.log(\n            \"Error: 2d canvas not supported by this browser.\",\n          );\n          return;\n        }\n        this.context = canvas.getContext(\"2d\");\n        window.addEventListener(\"keydown\", function (e) {\n          var code = e.keyCode;\n          if (code > 36 && code < 41) {\n            switch (code) {\n              case 37:\n                if (Game.direction != \"east\") Game.setDirection(\"west\");\n                break;\n              case 38:\n                if (Game.direction != \"south\") {\n                  Game.setDirection(\"north\");\n                }\n                break;\n              case 39:\n                if (Game.direction != \"west\") Game.setDirection(\"east\");\n                break;\n              case 40:\n                if (Game.direction != \"north\") {\n                  Game.setDirection(\"south\");\n                }\n                break;\n            }\n          }\n        }, false);\n        Game.connect();\n      };\n\n      Game.setDirection = function (direction) {\n        Game.direction = direction;\n        Game.socket.send(direction);\n        Console.log(\"Sent: Direction \" + direction);\n      };\n\n      Game.startGameLoop = function () {\n        if (window.webkitRequestAnimationFrame) {\n          Game.nextFrame = function () {\n            webkitRequestAnimationFrame(Game.run);\n          };\n        } else if (window.mozRequestAnimationFrame) {\n          Game.nextFrame = function () {\n            mozRequestAnimationFrame(Game.run);\n          };\n        } else {\n          Game.interval = setInterval(Game.run, 1000 / Game.fps);\n        }\n        if (Game.nextFrame != null) {\n          Game.nextFrame();\n        }\n      };\n\n      Game.stopGameLoop = function () {\n        Game.nextFrame = null;\n        if (Game.interval != null) {\n          clearInterval(Game.interval);\n        }\n      };\n\n      Game.draw = function () {\n        this.context.clearRect(0, 0, 640, 480);\n        for (var id in this.entities) {\n          this.entities[id].draw(this.context);\n        }\n      };\n\n      Game.addSnake = function (id, color) {\n        Game.entities[id] = new Snake();\n        Game.entities[id].color = color;\n      };\n\n      Game.updateSnake = function (id, snakeBody) {\n        if (typeof Game.entities[id] != \"undefined\") {\n          Game.entities[id].snakeBody = snakeBody;\n        }\n      };\n\n      Game.removeSnake = function (id) {\n        Game.entities[id] = null;\n        // Force GC.\n        delete Game.entities[id];\n      };\n\n      Game.run = (function () {\n        var skipTicks = 1000 / Game.fps,\n          nextGameTick = (new Date()).getTime();\n\n        return function () {\n          while ((new Date()).getTime() > nextGameTick) {\n            nextGameTick += skipTicks;\n          }\n          Game.draw();\n          if (Game.nextFrame != null) {\n            Game.nextFrame();\n          }\n        };\n      })();\n\n      Game.connect = function () {\n        Game.socket = new SockJS(\"/snake\");\n\n        Game.socket.onopen = function () {\n          // Socket open.. start the game loop.\n          Console.log(\"Info: WebSocket connection opened.\");\n          Console.log(\"Info: Press an arrow key to begin.\");\n          Game.startGameLoop();\n          setInterval(function () {\n            // Prevent server read timeout.\n            Game.socket.send(\"ping\");\n          }, 5000);\n        };\n\n        Game.socket.onclose = function () {\n          Console.log(\"Info: WebSocket closed.\");\n          Game.stopGameLoop();\n        };\n\n        Game.socket.onmessage = function (message) {\n          // _Potential_ security hole, consider using json lib to parse data in production.\n          var packet = eval(\"(\" + message.data + \")\");\n          switch (packet.type) {\n            case \"update\":\n              for (var i = 0; i < packet.data.length; i++) {\n                Game.updateSnake(\n                  packet.data[i].id,\n                  packet.data[i].body,\n                );\n              }\n              break;\n            case \"join\":\n              for (var j = 0; j < packet.data.length; j++) {\n                Game.addSnake(packet.data[j].id, packet.data[j].color);\n              }\n              break;\n            case \"leave\":\n              Game.removeSnake(packet.id);\n              break;\n            case \"dead\":\n              Console.log(\"Info: Your snake is dead, bad luck!\");\n              Game.direction = \"none\";\n              break;\n            case \"kill\":\n              Console.log(\"Info: Head shot!\");\n              break;\n          }\n        };\n      };\n\n      var Console = {};\n\n      Console.log = function (message) {\n        var console = document.getElementById(\"console\");\n        var p = document.createElement(\"p\");\n        p.style.wordWrap = \"break-word\";\n        p.innerHTML = message;\n        console.appendChild(p);\n        while (console.childNodes.length > 25) {\n          console.removeChild(console.firstChild);\n        }\n        console.scrollTop = console.scrollHeight;\n      };\n\n      Game.initialize();\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/java/websocket/src/test/java/samples/websocket/echo/CustomContainerWebSocketsApplicationTests.java",
    "content": "/*\n * Copyright 2012-2014 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.echo;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;\nimport org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;\nimport org.springframework.boot.test.IntegrationTest;\nimport org.springframework.boot.test.SpringApplicationConfiguration;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.util.SocketUtils;\nimport org.springframework.web.socket.client.WebSocketConnectionManager;\nimport org.springframework.web.socket.client.standard.StandardWebSocketClient;\n\nimport samples.websocket.client.GreetingService;\nimport samples.websocket.client.SimpleClientWebSocketHandler;\nimport samples.websocket.client.SimpleGreetingService;\nimport samples.websocket.config.SampleWebSocketsApplication;\nimport samples.websocket.echo.CustomContainerWebSocketsApplicationTests.CustomContainerConfiguration;\n\nimport static org.junit.Assert.assertEquals;\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringApplicationConfiguration(classes = { SampleWebSocketsApplication.class,\n\t\tCustomContainerConfiguration.class })\n@WebAppConfiguration\n@IntegrationTest\n@DirtiesContext\npublic class CustomContainerWebSocketsApplicationTests {\n\n\tprivate static Log logger = LogFactory\n\t\t\t.getLog(CustomContainerWebSocketsApplicationTests.class);\n\n\tprivate static int PORT = SocketUtils.findAvailableTcpPort();\n\n\tprivate static final String WS_URI = \"ws://localhost:\" + PORT + \"/ws/echo/websocket\";\n\n\t@Configuration\n\tprotected static class CustomContainerConfiguration {\n\t\t@Bean\n\t\tpublic EmbeddedServletContainerFactory embeddedServletContainerFactory() {\n\t\t\treturn new TomcatEmbeddedServletContainerFactory(\"/ws\", PORT);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void runAndWait() throws Exception {\n\t\tConfigurableApplicationContext context = SpringApplication.run(\n\t\t\t\tClientConfiguration.class, \"--spring.main.web_environment=false\");\n\t\tlong count = context.getBean(ClientConfiguration.class).latch.getCount();\n\t\tcontext.close();\n\t\tassertEquals(0, count);\n\t}\n\n\t@Configuration\n\tstatic class ClientConfiguration implements CommandLineRunner {\n\n\t\tprivate final CountDownLatch latch = new CountDownLatch(1);\n\n\t\t@Override\n\t\tpublic void run(String... args) throws Exception {\n\t\t\tlogger.info(\"Waiting for response: latch=\" + this.latch.getCount());\n\t\t\tthis.latch.await(10, TimeUnit.SECONDS);\n\t\t\tlogger.info(\"Got response: latch=\" + this.latch.getCount());\n\t\t}\n\n\t\t@Bean\n\t\tpublic WebSocketConnectionManager wsConnectionManager() {\n\n\t\t\tWebSocketConnectionManager manager = new WebSocketConnectionManager(client(),\n\t\t\t\t\thandler(), WS_URI);\n\t\t\tmanager.setAutoStartup(true);\n\n\t\t\treturn manager;\n\t\t}\n\n\t\t@Bean\n\t\tpublic StandardWebSocketClient client() {\n\t\t\treturn new StandardWebSocketClient();\n\t\t}\n\n\t\t@Bean\n\t\tpublic SimpleClientWebSocketHandler handler() {\n\t\t\treturn new SimpleClientWebSocketHandler(greetingService(), this.latch);\n\t\t}\n\n\t\t@Bean\n\t\tpublic GreetingService greetingService() {\n\t\t\treturn new SimpleGreetingService();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/test/java/samples/websocket/echo/SampleWebSocketsApplicationTests.java",
    "content": "/*\n * Copyright 2012-2014 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.echo;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.test.IntegrationTest;\nimport org.springframework.boot.test.SpringApplicationConfiguration;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.web.socket.client.WebSocketConnectionManager;\nimport org.springframework.web.socket.client.standard.StandardWebSocketClient;\n\nimport samples.websocket.client.GreetingService;\nimport samples.websocket.client.SimpleClientWebSocketHandler;\nimport samples.websocket.client.SimpleGreetingService;\nimport samples.websocket.config.SampleWebSocketsApplication;\n\nimport static org.junit.Assert.assertEquals;\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringApplicationConfiguration(classes = SampleWebSocketsApplication.class)\n@WebAppConfiguration\n@IntegrationTest(\"server.port:0\")\n@DirtiesContext\npublic class SampleWebSocketsApplicationTests {\n\n\tprivate static Log logger = LogFactory.getLog(SampleWebSocketsApplicationTests.class);\n\n\tprivate static String WS_URI;\n\n\t@Value(\"${local.server.port}\")\n\tprivate int port;\n\n\t@Before\n\tpublic void init() {\n\t\tWS_URI = \"ws://localhost:\" + this.port + \"/echo/websocket\";\n\t}\n\n\t@Test\n\tpublic void runAndWait() throws Exception {\n\t\tConfigurableApplicationContext context = SpringApplication.run(\n\t\t\t\tClientConfiguration.class, \"--spring.main.web_environment=false\");\n\t\tlong count = context.getBean(ClientConfiguration.class).latch.getCount();\n\t\tcontext.close();\n\t\tassertEquals(0, count);\n\t}\n\n\t@Configuration\n\tstatic class ClientConfiguration implements CommandLineRunner {\n\n\t\tprivate final CountDownLatch latch = new CountDownLatch(1);\n\n\t\t@Override\n\t\tpublic void run(String... args) throws Exception {\n\t\t\tlogger.info(\"Waiting for response: latch=\" + this.latch.getCount());\n\t\t\tthis.latch.await(10, TimeUnit.SECONDS);\n\t\t\tlogger.info(\"Got response: latch=\" + this.latch.getCount());\n\t\t}\n\n\t\t@Bean\n\t\tpublic WebSocketConnectionManager wsConnectionManager() {\n\n\t\t\tWebSocketConnectionManager manager = new WebSocketConnectionManager(client(),\n\t\t\t\t\thandler(), WS_URI);\n\t\t\tmanager.setAutoStartup(true);\n\n\t\t\treturn manager;\n\t\t}\n\n\t\t@Bean\n\t\tpublic StandardWebSocketClient client() {\n\t\t\treturn new StandardWebSocketClient();\n\t\t}\n\n\t\t@Bean\n\t\tpublic SimpleClientWebSocketHandler handler() {\n\t\t\treturn new SimpleClientWebSocketHandler(greetingService(), this.latch);\n\t\t}\n\n\t\t@Bean\n\t\tpublic GreetingService greetingService() {\n\t\t\treturn new SimpleGreetingService();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "examples/java/websocket/src/test/java/samples/websocket/snake/SnakeTimerTests.java",
    "content": "/*\n * Copyright 2012-2013 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage samples.websocket.snake;\n\nimport java.io.IOException;\n\nimport org.junit.Test;\n\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.junit.Assert.assertThat;\nimport static org.mockito.Matchers.anyString;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\n\npublic class SnakeTimerTests {\n\n\t@Test\n\tpublic void removeDysfunctionalSnakes() throws Exception {\n\t\tSnake snake = mock(Snake.class);\n\t\tdoThrow(new IOException()).when(snake).sendMessage(anyString());\n\t\tSnakeTimer.addSnake(snake);\n\n\t\tSnakeTimer.broadcast(\"\");\n\t\tassertThat(SnakeTimer.getSnakes().size(), is(0));\n\t}\n}\n"
  },
  {
    "path": "examples/python/flask/.napi/manifests/1775598176791-d5c2217.json",
    "content": "{\n  \"id\": \"1775598176791-d5c2217\",\n  \"branch\": \"main\",\n  \"commitSha\": \"d5c22175a36c5de08482bfa3e1ea59758e888405\",\n  \"commitShaDate\": \"2025-07-07T11:42:05+02:00\",\n  \"createdAt\": \"2026-04-07T21:42:56.791Z\",\n  \"manifest\": {\n    \"api/data/elves.py\": {\n      \"id\": \"api/data/elves.py\",\n      \"filePath\": \"api/data/elves.py\",\n      \"metrics\": {\n        \"linesCount\": 11,\n        \"codeLineCount\": 11,\n        \"characterCount\": 297,\n        \"codeCharacterCount\": 239,\n        \"dependencyCount\": 0,\n        \"dependentCount\": 1,\n        \"cyclomaticComplexity\": 1\n      },\n      \"language\": \"python\",\n      \"dependencies\": {},\n      \"dependents\": {\n        \"api/services/elves.py\": {\n          \"id\": \"api/services/elves.py\",\n          \"symbols\": {\n            \"Elf\": \"Elf\",\n            \"elves\": \"elves\",\n            \"ElfService\": \"ElfService\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"Elf\": {\n          \"id\": \"Elf\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 0,\n                \"row\": 0,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 176,\n                \"row\": 6,\n                \"column\": 36\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 7,\n            \"codeLineCount\": 6,\n            \"characterCount\": 176,\n            \"codeCharacterCount\": 138,\n            \"dependencyCount\": 0,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {},\n          \"dependents\": {\n            \"api/services/elves.py\": {\n              \"id\": \"api/services/elves.py\",\n              \"symbols\": {\n                \"ElfService\": \"ElfService\"\n              }\n            }\n          }\n        },\n        \"elves\": {\n          \"id\": \"elves\",\n          \"type\": \"variable\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 179,\n                \"row\": 9,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 296,\n                \"row\": 13,\n                \"column\": 1\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 117,\n            \"codeCharacterCount\": 101,\n            \"dependencyCount\": 0,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {},\n          \"dependents\": {\n            \"api/services/elves.py\": {\n              \"id\": \"api/services/elves.py\",\n              \"symbols\": {\n                \"ElfService\": \"ElfService\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/data/hobbits.py\": {\n      \"id\": \"api/data/hobbits.py\",\n      \"filePath\": \"api/data/hobbits.py\",\n      \"metrics\": {\n        \"linesCount\": 11,\n        \"codeLineCount\": 11,\n        \"characterCount\": 323,\n        \"codeCharacterCount\": 265,\n        \"dependencyCount\": 0,\n        \"dependentCount\": 1,\n        \"cyclomaticComplexity\": 1\n      },\n      \"language\": \"python\",\n      \"dependencies\": {},\n      \"dependents\": {\n        \"api/services/hobbits.py\": {\n          \"id\": \"api/services/hobbits.py\",\n          \"symbols\": {\n            \"Hobbit\": \"Hobbit\",\n            \"hobbits\": \"hobbits\",\n            \"HobbitService\": \"HobbitService\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"Hobbit\": {\n          \"id\": \"Hobbit\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 0,\n                \"row\": 0,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 179,\n                \"row\": 6,\n                \"column\": 36\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 7,\n            \"codeLineCount\": 6,\n            \"characterCount\": 179,\n            \"codeCharacterCount\": 141,\n            \"dependencyCount\": 0,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {},\n          \"dependents\": {\n            \"api/services/hobbits.py\": {\n              \"id\": \"api/services/hobbits.py\",\n              \"symbols\": {\n                \"HobbitService\": \"HobbitService\"\n              }\n            }\n          }\n        },\n        \"hobbits\": {\n          \"id\": \"hobbits\",\n          \"type\": \"variable\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 182,\n                \"row\": 9,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 322,\n                \"row\": 13,\n                \"column\": 1\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 140,\n            \"codeCharacterCount\": 124,\n            \"dependencyCount\": 0,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {},\n          \"dependents\": {\n            \"api/services/hobbits.py\": {\n              \"id\": \"api/services/hobbits.py\",\n              \"symbols\": {\n                \"HobbitService\": \"HobbitService\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/services/elves.py\": {\n      \"id\": \"api/services/elves.py\",\n      \"filePath\": \"api/services/elves.py\",\n      \"metrics\": {\n        \"linesCount\": 21,\n        \"codeLineCount\": 21,\n        \"characterCount\": 602,\n        \"codeCharacterCount\": 419,\n        \"dependencyCount\": 1,\n        \"dependentCount\": 1,\n        \"cyclomaticComplexity\": 5\n      },\n      \"language\": \"python\",\n      \"dependencies\": {\n        \"api/data/elves.py\": {\n          \"id\": \"api/data/elves.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"Elf\": \"Elf\",\n            \"elves\": \"elves\"\n          }\n        }\n      },\n      \"dependents\": {\n        \"api/views/elves.py\": {\n          \"id\": \"api/views/elves.py\",\n          \"symbols\": {\n            \"ElfService\": \"ElfService\",\n            \"ElfListView\": \"ElfListView\",\n            \"ElfCreateView\": \"ElfCreateView\",\n            \"ElfDetailView\": \"ElfDetailView\",\n            \"ElfUpdateView\": \"ElfUpdateView\",\n            \"ElfDeleteView\": \"ElfDeleteView\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"ElfService\": {\n          \"id\": \"ElfService\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 40,\n                \"row\": 3,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 601,\n                \"row\": 26,\n                \"column\": 33\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 24,\n            \"codeLineCount\": 20,\n            \"characterCount\": 561,\n            \"codeCharacterCount\": 382,\n            \"dependencyCount\": 1,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 5\n          },\n          \"dependencies\": {\n            \"api/data/elves.py\": {\n              \"id\": \"api/data/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"Elf\": \"Elf\",\n                \"elves\": \"elves\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"symbols\": {\n                \"ElfListView\": \"ElfListView\",\n                \"ElfCreateView\": \"ElfCreateView\",\n                \"ElfDetailView\": \"ElfDetailView\",\n                \"ElfUpdateView\": \"ElfUpdateView\",\n                \"ElfDeleteView\": \"ElfDeleteView\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/services/hobbits.py\": {\n      \"id\": \"api/services/hobbits.py\",\n      \"filePath\": \"api/services/hobbits.py\",\n      \"metrics\": {\n        \"linesCount\": 21,\n        \"codeLineCount\": 21,\n        \"characterCount\": 698,\n        \"codeCharacterCount\": 515,\n        \"dependencyCount\": 1,\n        \"dependentCount\": 1,\n        \"cyclomaticComplexity\": 5\n      },\n      \"language\": \"python\",\n      \"dependencies\": {\n        \"api/data/hobbits.py\": {\n          \"id\": \"api/data/hobbits.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"Hobbit\": \"Hobbit\",\n            \"hobbits\": \"hobbits\"\n          }\n        }\n      },\n      \"dependents\": {\n        \"api/views/hobbits.py\": {\n          \"id\": \"api/views/hobbits.py\",\n          \"symbols\": {\n            \"HobbitService\": \"HobbitService\",\n            \"HobbitListView\": \"HobbitListView\",\n            \"HobbitCreateView\": \"HobbitCreateView\",\n            \"HobbitDetailView\": \"HobbitDetailView\",\n            \"HobbitUpdateView\": \"HobbitUpdateView\",\n            \"HobbitDeleteView\": \"HobbitDeleteView\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"HobbitService\": {\n          \"id\": \"HobbitService\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 47,\n                \"row\": 3,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 697,\n                \"row\": 26,\n                \"column\": 38\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 24,\n            \"codeLineCount\": 20,\n            \"characterCount\": 650,\n            \"codeCharacterCount\": 471,\n            \"dependencyCount\": 1,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 5\n          },\n          \"dependencies\": {\n            \"api/data/hobbits.py\": {\n              \"id\": \"api/data/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"Hobbit\": \"Hobbit\",\n                \"hobbits\": \"hobbits\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"symbols\": {\n                \"HobbitListView\": \"HobbitListView\",\n                \"HobbitCreateView\": \"HobbitCreateView\",\n                \"HobbitDetailView\": \"HobbitDetailView\",\n                \"HobbitUpdateView\": \"HobbitUpdateView\",\n                \"HobbitDeleteView\": \"HobbitDeleteView\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/views/elves.py\": {\n      \"id\": \"api/views/elves.py\",\n      \"filePath\": \"api/views/elves.py\",\n      \"metrics\": {\n        \"linesCount\": 39,\n        \"codeLineCount\": 39,\n        \"characterCount\": 1770,\n        \"codeCharacterCount\": 1279,\n        \"dependencyCount\": 3,\n        \"dependentCount\": 2,\n        \"cyclomaticComplexity\": 1\n      },\n      \"language\": \"python\",\n      \"dependencies\": {\n        \"api/services/elves.py\": {\n          \"id\": \"api/services/elves.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"ElfService\": \"ElfService\"\n          }\n        },\n        \"flask.views\": {\n          \"id\": \"flask.views\",\n          \"isExternal\": true,\n          \"symbols\": {\n            \"MethodView\": \"MethodView\"\n          }\n        },\n        \"flask\": {\n          \"id\": \"flask\",\n          \"isExternal\": true,\n          \"symbols\": {\n            \"request\": \"request\",\n            \"Blueprint\": \"Blueprint\"\n          }\n        }\n      },\n      \"dependents\": {\n        \"app.py\": {\n          \"id\": \"app.py\",\n          \"symbols\": {\n            \"elves_bp\": \"elves_bp\",\n            \"app\": \"app\"\n          }\n        },\n        \"api/views/elves.py\": {\n          \"id\": \"api/views/elves.py\",\n          \"symbols\": {\n            \"elves_bp\": \"elves_bp\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"ElfCreateView\": {\n          \"id\": \"ElfCreateView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 430,\n                \"row\": 17,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 604,\n                \"row\": 21,\n                \"column\": 22\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 174,\n            \"codeCharacterCount\": 142,\n            \"dependencyCount\": 3,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/elves.py\": {\n              \"id\": \"api/services/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"ElfService\": \"ElfService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"request\": \"request\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"symbols\": {\n                \"elves_bp\": \"elves_bp\"\n              }\n            }\n          }\n        },\n        \"ElfDeleteView\": {\n          \"id\": \"ElfDeleteView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 1450,\n                \"row\": 55,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1589,\n                \"row\": 58,\n                \"column\": 19\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 139,\n            \"codeCharacterCount\": 116,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/elves.py\": {\n              \"id\": \"api/services/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"ElfService\": \"ElfService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"symbols\": {\n                \"elves_bp\": \"elves_bp\"\n              }\n            }\n          }\n        },\n        \"ElfDetailView\": {\n          \"id\": \"ElfDetailView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 762,\n                \"row\": 30,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 900,\n                \"row\": 33,\n                \"column\": 18\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 138,\n            \"codeCharacterCount\": 115,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/elves.py\": {\n              \"id\": \"api/services/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"ElfService\": \"ElfService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"symbols\": {\n                \"elves_bp\": \"elves_bp\"\n              }\n            }\n          }\n        },\n        \"ElfListView\": {\n          \"id\": \"ElfListView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 157,\n                \"row\": 7,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 285,\n                \"row\": 10,\n                \"column\": 20\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 128,\n            \"codeCharacterCount\": 105,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/elves.py\": {\n              \"id\": \"api/services/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"ElfService\": \"ElfService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"symbols\": {\n                \"elves_bp\": \"elves_bp\"\n              }\n            }\n          }\n        },\n        \"ElfUpdateView\": {\n          \"id\": \"ElfUpdateView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 1076,\n                \"row\": 42,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1273,\n                \"row\": 46,\n                \"column\": 26\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 197,\n            \"codeCharacterCount\": 165,\n            \"dependencyCount\": 3,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/elves.py\": {\n              \"id\": \"api/services/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"ElfService\": \"ElfService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"request\": \"request\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"symbols\": {\n                \"elves_bp\": \"elves_bp\"\n              }\n            }\n          }\n        },\n        \"elves_bp\": {\n          \"id\": \"elves_bp\",\n          \"type\": \"variable\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 115,\n                \"row\": 4,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 154,\n                \"row\": 4,\n                \"column\": 39\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 341,\n                \"row\": 14,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 427,\n                \"row\": 14,\n                \"column\": 86\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 662,\n                \"row\": 25,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 759,\n                \"row\": 27,\n                \"column\": 1\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 965,\n                \"row\": 37,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1073,\n                \"row\": 39,\n                \"column\": 1\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 1339,\n                \"row\": 50,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1447,\n                \"row\": 52,\n                \"column\": 1\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 1658,\n                \"row\": 62,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1769,\n                \"row\": 64,\n                \"column\": 1\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 14,\n            \"codeLineCount\": 14,\n            \"characterCount\": 549,\n            \"codeCharacterCount\": 525,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"ElfListView\": \"ElfListView\",\n                \"ElfCreateView\": \"ElfCreateView\",\n                \"ElfDetailView\": \"ElfDetailView\",\n                \"ElfUpdateView\": \"ElfUpdateView\",\n                \"ElfDeleteView\": \"ElfDeleteView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"Blueprint\": \"Blueprint\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"app.py\": {\n              \"id\": \"app.py\",\n              \"symbols\": {\n                \"app\": \"app\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/views/hobbits.py\": {\n      \"id\": \"api/views/hobbits.py\",\n      \"filePath\": \"api/views/hobbits.py\",\n      \"metrics\": {\n        \"linesCount\": 47,\n        \"codeLineCount\": 47,\n        \"characterCount\": 1979,\n        \"codeCharacterCount\": 1418,\n        \"dependencyCount\": 3,\n        \"dependentCount\": 2,\n        \"cyclomaticComplexity\": 1\n      },\n      \"language\": \"python\",\n      \"dependencies\": {\n        \"api/services/hobbits.py\": {\n          \"id\": \"api/services/hobbits.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"HobbitService\": \"HobbitService\"\n          }\n        },\n        \"flask.views\": {\n          \"id\": \"flask.views\",\n          \"isExternal\": true,\n          \"symbols\": {\n            \"MethodView\": \"MethodView\"\n          }\n        },\n        \"flask\": {\n          \"id\": \"flask\",\n          \"isExternal\": true,\n          \"symbols\": {\n            \"request\": \"request\",\n            \"Blueprint\": \"Blueprint\"\n          }\n        }\n      },\n      \"dependents\": {\n        \"app.py\": {\n          \"id\": \"app.py\",\n          \"symbols\": {\n            \"hobbits_bp\": \"hobbits_bp\",\n            \"app\": \"app\"\n          }\n        },\n        \"api/views/hobbits.py\": {\n          \"id\": \"api/views/hobbits.py\",\n          \"symbols\": {\n            \"hobbits_bp\": \"hobbits_bp\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"HobbitCreateView\": {\n          \"id\": \"HobbitCreateView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 470,\n                \"row\": 19,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 659,\n                \"row\": 23,\n                \"column\": 25\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 189,\n            \"codeCharacterCount\": 157,\n            \"dependencyCount\": 3,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/hobbits.py\": {\n              \"id\": \"api/services/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"HobbitService\": \"HobbitService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"request\": \"request\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"symbols\": {\n                \"hobbits_bp\": \"hobbits_bp\"\n              }\n            }\n          }\n        },\n        \"HobbitDeleteView\": {\n          \"id\": \"HobbitDeleteView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 1616,\n                \"row\": 61,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1770,\n                \"row\": 64,\n                \"column\": 19\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 154,\n            \"codeCharacterCount\": 131,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/hobbits.py\": {\n              \"id\": \"api/services/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"HobbitService\": \"HobbitService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"symbols\": {\n                \"hobbits_bp\": \"hobbits_bp\"\n              }\n            }\n          }\n        },\n        \"HobbitDetailView\": {\n          \"id\": \"HobbitDetailView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 830,\n                \"row\": 32,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 989,\n                \"row\": 35,\n                \"column\": 21\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 159,\n            \"codeCharacterCount\": 136,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/hobbits.py\": {\n              \"id\": \"api/services/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"HobbitService\": \"HobbitService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"symbols\": {\n                \"hobbits_bp\": \"hobbits_bp\"\n              }\n            }\n          }\n        },\n        \"HobbitListView\": {\n          \"id\": \"HobbitListView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 166,\n                \"row\": 7,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 306,\n                \"row\": 10,\n                \"column\": 22\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 140,\n            \"codeCharacterCount\": 117,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/hobbits.py\": {\n              \"id\": \"api/services/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"HobbitService\": \"HobbitService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"symbols\": {\n                \"hobbits_bp\": \"hobbits_bp\"\n              }\n            }\n          }\n        },\n        \"hobbits_bp\": {\n          \"id\": \"hobbits_bp\",\n          \"type\": \"variable\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 120,\n                \"row\": 4,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 163,\n                \"row\": 4,\n                \"column\": 43\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 367,\n                \"row\": 14,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 467,\n                \"row\": 16,\n                \"column\": 1\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 722,\n                \"row\": 27,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 827,\n                \"row\": 29,\n                \"column\": 1\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 1062,\n                \"row\": 39,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1190,\n                \"row\": 43,\n                \"column\": 1\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 1485,\n                \"row\": 54,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1613,\n                \"row\": 58,\n                \"column\": 1\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 1847,\n                \"row\": 68,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1978,\n                \"row\": 72,\n                \"column\": 1\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 22,\n            \"codeLineCount\": 22,\n            \"characterCount\": 635,\n            \"codeCharacterCount\": 575,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"HobbitListView\": \"HobbitListView\",\n                \"HobbitCreateView\": \"HobbitCreateView\",\n                \"HobbitDetailView\": \"HobbitDetailView\",\n                \"HobbitUpdateView\": \"HobbitUpdateView\",\n                \"HobbitDeleteView\": \"HobbitDeleteView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"Blueprint\": \"Blueprint\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"app.py\": {\n              \"id\": \"app.py\",\n              \"symbols\": {\n                \"app\": \"app\"\n              }\n            }\n          }\n        },\n        \"HobbitUpdateView\": {\n          \"id\": \"HobbitUpdateView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 1193,\n                \"row\": 46,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 1411,\n                \"row\": 50,\n                \"column\": 29\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 218,\n            \"codeCharacterCount\": 186,\n            \"dependencyCount\": 3,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/services/hobbits.py\": {\n              \"id\": \"api/services/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"HobbitService\": \"HobbitService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"request\": \"request\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"symbols\": {\n                \"hobbits_bp\": \"hobbits_bp\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/wizards/data.py\": {\n      \"id\": \"api/wizards/data.py\",\n      \"filePath\": \"api/wizards/data.py\",\n      \"metrics\": {\n        \"linesCount\": 11,\n        \"codeLineCount\": 11,\n        \"characterCount\": 316,\n        \"codeCharacterCount\": 258,\n        \"dependencyCount\": 0,\n        \"dependentCount\": 1,\n        \"cyclomaticComplexity\": 1\n      },\n      \"language\": \"python\",\n      \"dependencies\": {},\n      \"dependents\": {\n        \"api/wizards/services.py\": {\n          \"id\": \"api/wizards/services.py\",\n          \"symbols\": {\n            \"Wizard\": \"Wizard\",\n            \"wizards\": \"wizards\",\n            \"WizardService\": \"WizardService\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"Wizard\": {\n          \"id\": \"Wizard\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 0,\n                \"row\": 0,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 179,\n                \"row\": 6,\n                \"column\": 36\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 7,\n            \"codeLineCount\": 6,\n            \"characterCount\": 179,\n            \"codeCharacterCount\": 141,\n            \"dependencyCount\": 0,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {},\n          \"dependents\": {\n            \"api/wizards/services.py\": {\n              \"id\": \"api/wizards/services.py\",\n              \"symbols\": {\n                \"WizardService\": \"WizardService\"\n              }\n            }\n          }\n        },\n        \"wizards\": {\n          \"id\": \"wizards\",\n          \"type\": \"variable\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 182,\n                \"row\": 9,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 315,\n                \"row\": 13,\n                \"column\": 1\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 133,\n            \"codeCharacterCount\": 117,\n            \"dependencyCount\": 0,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {},\n          \"dependents\": {\n            \"api/wizards/services.py\": {\n              \"id\": \"api/wizards/services.py\",\n              \"symbols\": {\n                \"WizardService\": \"WizardService\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/wizards/services.py\": {\n      \"id\": \"api/wizards/services.py\",\n      \"filePath\": \"api/wizards/services.py\",\n      \"metrics\": {\n        \"linesCount\": 22,\n        \"codeLineCount\": 22,\n        \"characterCount\": 742,\n        \"codeCharacterCount\": 559,\n        \"dependencyCount\": 1,\n        \"dependentCount\": 1,\n        \"cyclomaticComplexity\": 5\n      },\n      \"language\": \"python\",\n      \"dependencies\": {\n        \"api/wizards/data.py\": {\n          \"id\": \"api/wizards/data.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"Wizard\": \"Wizard\",\n            \"wizards\": \"wizards\"\n          }\n        }\n      },\n      \"dependents\": {\n        \"api/wizards/views.py\": {\n          \"id\": \"api/wizards/views.py\",\n          \"symbols\": {\n            \"WizardService\": \"WizardService\",\n            \"WizardListView\": \"WizardListView\",\n            \"WizardCreateView\": \"WizardCreateView\",\n            \"WizardDetailView\": \"WizardDetailView\",\n            \"WizardUpdateView\": \"WizardUpdateView\",\n            \"WizardDeleteView\": \"WizardDeleteView\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"WizardService\": {\n          \"id\": \"WizardService\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 91,\n                \"row\": 3,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 741,\n                \"row\": 26,\n                \"column\": 38\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 24,\n            \"codeLineCount\": 20,\n            \"characterCount\": 650,\n            \"codeCharacterCount\": 471,\n            \"dependencyCount\": 1,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 5\n          },\n          \"dependencies\": {\n            \"api/wizards/data.py\": {\n              \"id\": \"api/wizards/data.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"Wizard\": \"Wizard\",\n                \"wizards\": \"wizards\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"symbols\": {\n                \"WizardListView\": \"WizardListView\",\n                \"WizardCreateView\": \"WizardCreateView\",\n                \"WizardDetailView\": \"WizardDetailView\",\n                \"WizardUpdateView\": \"WizardUpdateView\",\n                \"WizardDeleteView\": \"WizardDeleteView\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"api/wizards/views.py\": {\n      \"id\": \"api/wizards/views.py\",\n      \"filePath\": \"api/wizards/views.py\",\n      \"metrics\": {\n        \"linesCount\": 49,\n        \"codeLineCount\": 49,\n        \"characterCount\": 2120,\n        \"codeCharacterCount\": 1446,\n        \"dependencyCount\": 3,\n        \"dependentCount\": 2,\n        \"cyclomaticComplexity\": 1\n      },\n      \"language\": \"python\",\n      \"dependencies\": {\n        \"api/wizards/services.py\": {\n          \"id\": \"api/wizards/services.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"WizardService\": \"WizardService\"\n          }\n        },\n        \"flask.views\": {\n          \"id\": \"flask.views\",\n          \"isExternal\": true,\n          \"symbols\": {\n            \"MethodView\": \"MethodView\"\n          }\n        },\n        \"flask\": {\n          \"id\": \"flask\",\n          \"isExternal\": true,\n          \"symbols\": {\n            \"request\": \"request\",\n            \"Blueprint\": \"Blueprint\"\n          }\n        }\n      },\n      \"dependents\": {\n        \"app.py\": {\n          \"id\": \"app.py\",\n          \"symbols\": {\n            \"get_wizzards_bp\": \"get_wizzards_bp\",\n            \"app\": \"app\"\n          }\n        },\n        \"api/wizards/views.py\": {\n          \"id\": \"api/wizards/views.py\",\n          \"symbols\": {\n            \"get_wizzards_bp\": \"get_wizzards_bp\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"get_wizzards_bp\": {\n          \"id\": \"get_wizzards_bp\",\n          \"type\": \"function\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 984,\n                \"row\": 36,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 2120,\n                \"row\": 70,\n                \"column\": 21\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 35,\n            \"codeLineCount\": 24,\n            \"characterCount\": 1136,\n            \"codeCharacterCount\": 614,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"WizardListView\": \"WizardListView\",\n                \"WizardCreateView\": \"WizardCreateView\",\n                \"WizardDetailView\": \"WizardDetailView\",\n                \"WizardUpdateView\": \"WizardUpdateView\",\n                \"WizardDeleteView\": \"WizardDeleteView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"Blueprint\": \"Blueprint\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"app.py\": {\n              \"id\": \"app.py\",\n              \"symbols\": {\n                \"app\": \"app\"\n              }\n            }\n          }\n        },\n        \"WizardCreateView\": {\n          \"id\": \"WizardCreateView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 252,\n                \"row\": 10,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 441,\n                \"row\": 14,\n                \"column\": 25\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 189,\n            \"codeCharacterCount\": 157,\n            \"dependencyCount\": 3,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/wizards/services.py\": {\n              \"id\": \"api/wizards/services.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"WizardService\": \"WizardService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"request\": \"request\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"symbols\": {\n                \"get_wizzards_bp\": \"get_wizzards_bp\"\n              }\n            }\n          }\n        },\n        \"WizardDeleteView\": {\n          \"id\": \"WizardDeleteView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 827,\n                \"row\": 30,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 981,\n                \"row\": 33,\n                \"column\": 19\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 154,\n            \"codeCharacterCount\": 131,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/wizards/services.py\": {\n              \"id\": \"api/wizards/services.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"WizardService\": \"WizardService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"symbols\": {\n                \"get_wizzards_bp\": \"get_wizzards_bp\"\n              }\n            }\n          }\n        },\n        \"WizardDetailView\": {\n          \"id\": \"WizardDetailView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 444,\n                \"row\": 17,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 603,\n                \"row\": 20,\n                \"column\": 21\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 159,\n            \"codeCharacterCount\": 136,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/wizards/services.py\": {\n              \"id\": \"api/wizards/services.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"WizardService\": \"WizardService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"symbols\": {\n                \"get_wizzards_bp\": \"get_wizzards_bp\"\n              }\n            }\n          }\n        },\n        \"WizardListView\": {\n          \"id\": \"WizardListView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 109,\n                \"row\": 4,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 249,\n                \"row\": 7,\n                \"column\": 22\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 140,\n            \"codeCharacterCount\": 117,\n            \"dependencyCount\": 2,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/wizards/services.py\": {\n              \"id\": \"api/wizards/services.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"WizardService\": \"WizardService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"symbols\": {\n                \"get_wizzards_bp\": \"get_wizzards_bp\"\n              }\n            }\n          }\n        },\n        \"WizardUpdateView\": {\n          \"id\": \"WizardUpdateView\",\n          \"type\": \"class\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 606,\n                \"row\": 23,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 824,\n                \"row\": 27,\n                \"column\": 29\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 5,\n            \"codeLineCount\": 5,\n            \"characterCount\": 218,\n            \"codeCharacterCount\": 186,\n            \"dependencyCount\": 3,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/wizards/services.py\": {\n              \"id\": \"api/wizards/services.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"WizardService\": \"WizardService\"\n              }\n            },\n            \"flask.views\": {\n              \"id\": \"flask.views\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"MethodView\": \"MethodView\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"request\": \"request\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"symbols\": {\n                \"get_wizzards_bp\": \"get_wizzards_bp\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"app.py\": {\n      \"id\": \"app.py\",\n      \"filePath\": \"app.py\",\n      \"metrics\": {\n        \"linesCount\": 17,\n        \"codeLineCount\": 17,\n        \"characterCount\": 698,\n        \"codeCharacterCount\": 538,\n        \"dependencyCount\": 4,\n        \"dependentCount\": 1,\n        \"cyclomaticComplexity\": 1\n      },\n      \"language\": \"python\",\n      \"dependencies\": {\n        \"api/wizards/views.py\": {\n          \"id\": \"api/wizards/views.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"get_wizzards_bp\": \"get_wizzards_bp\"\n          }\n        },\n        \"api/views/elves.py\": {\n          \"id\": \"api/views/elves.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"elves_bp\": \"elves_bp\"\n          }\n        },\n        \"api/views/hobbits.py\": {\n          \"id\": \"api/views/hobbits.py\",\n          \"isExternal\": false,\n          \"symbols\": {\n            \"hobbits_bp\": \"hobbits_bp\"\n          }\n        },\n        \"flask\": {\n          \"id\": \"flask\",\n          \"isExternal\": true,\n          \"symbols\": {\n            \"Flask\": \"Flask\"\n          }\n        }\n      },\n      \"dependents\": {\n        \"app.py\": {\n          \"id\": \"app.py\",\n          \"symbols\": {\n            \"hello_world\": \"hello_world\",\n            \"liveness\": \"liveness\",\n            \"readiness\": \"readiness\"\n          }\n        }\n      },\n      \"symbols\": {\n        \"app\": {\n          \"id\": \"app\",\n          \"type\": \"variable\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 150,\n                \"row\": 5,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 171,\n                \"row\": 5,\n                \"column\": 21\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 451,\n                \"row\": 23,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 519,\n                \"row\": 23,\n                \"column\": 68\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 548,\n                \"row\": 26,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 605,\n                \"row\": 26,\n                \"column\": 57\n              }\n            },\n            {\n              \"start\": {\n                \"index\": 636,\n                \"row\": 29,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 697,\n                \"row\": 29,\n                \"column\": 61\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 4,\n            \"codeLineCount\": 4,\n            \"characterCount\": 207,\n            \"codeCharacterCount\": 207,\n            \"dependencyCount\": 4,\n            \"dependentCount\": 1,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"api/wizards/views.py\": {\n              \"id\": \"api/wizards/views.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"get_wizzards_bp\": \"get_wizzards_bp\"\n              }\n            },\n            \"api/views/elves.py\": {\n              \"id\": \"api/views/elves.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"elves_bp\": \"elves_bp\"\n              }\n            },\n            \"api/views/hobbits.py\": {\n              \"id\": \"api/views/hobbits.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"hobbits_bp\": \"hobbits_bp\"\n              }\n            },\n            \"flask\": {\n              \"id\": \"flask\",\n              \"isExternal\": true,\n              \"symbols\": {\n                \"Flask\": \"Flask\"\n              }\n            }\n          },\n          \"dependents\": {\n            \"app.py\": {\n              \"id\": \"app.py\",\n              \"symbols\": {\n                \"hello_world\": \"hello_world\",\n                \"liveness\": \"liveness\",\n                \"readiness\": \"readiness\"\n              }\n            }\n          }\n        },\n        \"hello_world\": {\n          \"id\": \"hello_world\",\n          \"type\": \"function\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 174,\n                \"row\": 8,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 246,\n                \"row\": 10,\n                \"column\": 39\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 3,\n            \"codeLineCount\": 3,\n            \"characterCount\": 72,\n            \"codeCharacterCount\": 66,\n            \"dependencyCount\": 1,\n            \"dependentCount\": 0,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"app.py\": {\n              \"id\": \"app.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"app\": \"app\"\n              }\n            }\n          },\n          \"dependents\": {}\n        },\n        \"liveness\": {\n          \"id\": \"liveness\",\n          \"type\": \"function\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 249,\n                \"row\": 13,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 314,\n                \"row\": 15,\n                \"column\": 27\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 3,\n            \"codeLineCount\": 3,\n            \"characterCount\": 65,\n            \"codeCharacterCount\": 59,\n            \"dependencyCount\": 1,\n            \"dependentCount\": 0,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"app.py\": {\n              \"id\": \"app.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"app\": \"app\"\n              }\n            }\n          },\n          \"dependents\": {}\n        },\n        \"readiness\": {\n          \"id\": \"readiness\",\n          \"type\": \"function\",\n          \"positions\": [\n            {\n              \"start\": {\n                \"index\": 353,\n                \"row\": 18,\n                \"column\": 0\n              },\n              \"end\": {\n                \"index\": 420,\n                \"row\": 20,\n                \"column\": 27\n              }\n            }\n          ],\n          \"description\": \"\",\n          \"metrics\": {\n            \"linesCount\": 3,\n            \"codeLineCount\": 3,\n            \"characterCount\": 67,\n            \"codeCharacterCount\": 61,\n            \"dependencyCount\": 1,\n            \"dependentCount\": 0,\n            \"cyclomaticComplexity\": 1\n          },\n          \"dependencies\": {\n            \"app.py\": {\n              \"id\": \"app.py\",\n              \"isExternal\": false,\n              \"symbols\": {\n                \"app\": \"app\"\n              }\n            }\n          },\n          \"dependents\": {}\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/python/flask/.napirc",
    "content": "{\n  \"language\": \"python\",\n  \"python\": {\n    \"version\": \"3.12\"\n  },\n  \"project\": {\n    \"include\": [\"app.py\", \"api/**\"],\n    \"exclude\": []\n  },\n  \"outDir\": \"napi-output\",\n  \"audit\": {\n    \"file\": { \"maxCodeChar\": 5000, \"maxCodeLine\": 300, \"maxDependency\": 20, \"maxCyclomaticComplexity\": 50 },\n    \"symbol\": { \"maxCodeChar\": 1000, \"maxCodeLine\": 80, \"maxDependency\": 10, \"maxCyclomaticComplexity\": 15 }\n  }\n}\n"
  },
  {
    "path": "examples/python/flask/api/data/elves.py",
    "content": "class Elf:\n    def __init__(self, data):\n        self.id = data.get(\"id\")\n        self.name = data.get(\"name\")\n\n    def update(self, data):\n        self.name = data.get(\"name\")\n\n\nelves = [\n    {\"id\": 1, \"name\": \"Legolas\"},\n    {\"id\": 2, \"name\": \"Thranduil\"},\n    {\"id\": 3, \"name\": \"Galadriel\"},\n]\n"
  },
  {
    "path": "examples/python/flask/api/data/hobbits.py",
    "content": "class Hobbit:\n    def __init__(self, data):\n        self.id = data.get(\"id\")\n        self.name = data.get(\"name\")\n\n    def update(self, data):\n        self.name = data.get(\"name\")\n\n\nhobbits = [\n    {\"id\": 1, \"name\": \"Frodo Baggins\"},\n    {\"id\": 2, \"name\": \"Samwise Gamgee\"},\n    {\"id\": 3, \"name\": \"Meriadoc Brandybuck\"},\n]\n"
  },
  {
    "path": "examples/python/flask/api/services/elves.py",
    "content": "from api.data.elves import Elf, elves\n\n\nclass ElfService:\n    def get_elves(self):\n        return elves\n\n    def get_elf(self, elf_id):\n        for elf in elves:\n            if elf.id == elf_id:\n                return elf\n        return None\n\n    def create_elf(self, data):\n        new_elf = Elf(data)\n        elves.append(new_elf)\n        return new_elf\n\n    def update_elf(self, elf_id, data):\n        elf: Elf = elves[elf_id]\n        elf.update(data)\n        return elf\n\n    def delete_elf(self, elf_id):\n        for elf in elves:\n            if elf.id == elf_id:\n                elves.remove(elf)\n"
  },
  {
    "path": "examples/python/flask/api/services/hobbits.py",
    "content": "from api.data.hobbits import Hobbit, hobbits\n\n\nclass HobbitService:\n    def get_hobbits(self):\n        return hobbits\n\n    def get_hobbit(self, hobbit_id):\n        for hobbit in hobbits:\n            if hobbit.id == hobbit_id:\n                return hobbit\n        return None\n\n    def create_hobbit(self, data):\n        new_hobbit = Hobbit(data)\n        hobbits.append(new_hobbit)\n        return new_hobbit\n\n    def update_hobbit(self, hobbit_id, data):\n        hobbit: Hobbit = hobbits[hobbit_id]\n        hobbit.update(data)\n        return hobbit\n\n    def delete_hobbit(self, hobbit_id):\n        for hobbit in hobbits:\n            if hobbit.id == hobbit_id:\n                hobbits.remove(hobbit)\n"
  },
  {
    "path": "examples/python/flask/api/views/elves.py",
    "content": "from flask.views import MethodView\nfrom flask import request, Blueprint\nfrom api.services.elves import ElfService\n\nelves_bp = Blueprint(\"elves\", __name__)\n\n\nclass ElfListView(MethodView):\n    def get(self, *args, **kwargs):\n        elves = ElfService().get_elves()\n        return elves\n\n\n# @nanoapi method:GET path:/api/elves group:elf_read\nelves_bp.add_url_rule(\"/\", view_func=ElfListView.as_view(\"elf_list\"), methods=[\"GET\"])\n\n\nclass ElfCreateView(MethodView):\n    def post(self, *args, **kwargs):\n        data = request.get_json()\n        new_elf = ElfService().create_elf(data)\n        return new_elf\n\n\n# @nanoapi method:POST path:/api/elves group:elf_write\nelves_bp.add_url_rule(\n    \"/\", view_func=ElfCreateView.as_view(\"elf_create\"), methods=[\"POST\"]\n)\n\n\nclass ElfDetailView(MethodView):\n    def get(self, elf_id, *args, **kwargs):\n        elf = ElfService().get_elf(elf_id)\n        return elf\n\n\n# @nanoapi method:GET path:/api/elves/<elf_id> group:elf_read\nelves_bp.add_url_rule(\n    \"/<int:elf_id>\", view_func=ElfDetailView.as_view(\"elf_detail\"), methods=[\"GET\"]\n)\n\n\nclass ElfUpdateView(MethodView):\n    def put(self, elf_id, *args, **kwargs):\n        data = request.get_json()\n        updated_elf = ElfService().update_elf(elf_id, data)\n        return updated_elf\n\n\n# @nanoapi method:PUT path:/api/elves/<elf_id> group:elf_write\nelves_bp.add_url_rule(\n    \"/<int:elf_id>\", view_func=ElfUpdateView.as_view(\"elf_update\"), methods=[\"PUT\"]\n)\n\n\nclass ElfDeleteView(MethodView):\n    def delete(self, elf_id, *args, **kwargs):\n        ElfService().delete_elf(elf_id)\n        return None\n\n\n# @nanoapi method:DELETE path:/api/elves/<elf_id> group:elf_write\nelves_bp.add_url_rule(\n    \"/<int:elf_id>\", view_func=ElfDeleteView.as_view(\"elf_delete\"), methods=[\"DELETE\"]\n)\n"
  },
  {
    "path": "examples/python/flask/api/views/hobbits.py",
    "content": "from flask.views import MethodView\nfrom flask import request, Blueprint\nfrom api.services.hobbits import HobbitService\n\nhobbits_bp = Blueprint(\"hobbits\", __name__)\n\n\nclass HobbitListView(MethodView):\n    def get(self, *args, **kwargs):\n        hobbits = HobbitService().get_hobbits()\n        return hobbits\n\n\n# @nanoapi method:GET path:/api/hobbits group:hobbit_read\nhobbits_bp.add_url_rule(\n    \"/\", view_func=HobbitListView.as_view(\"hobbit_list\"), methods=[\"GET\"]\n)\n\n\nclass HobbitCreateView(MethodView):\n    def post(self, *args, **kwargs):\n        data = request.get_json()\n        new_hobbit = HobbitService().create_hobbit(data)\n        return new_hobbit\n\n\n# @nanoapi method:POST path:/api/hobbits group:hobbit_write\nhobbits_bp.add_url_rule(\n    \"/\", view_func=HobbitCreateView.as_view(\"hobbit_create\"), methods=[\"POST\"]\n)\n\n\nclass HobbitDetailView(MethodView):\n    def get(self, hobbit_id, *args, **kwargs):\n        hobbit = HobbitService().get_hobbit(hobbit_id)\n        return hobbit\n\n\n# @nanoapi method:GET path:/api/hobbits/<hobbit_id> group:hobbit_read\nhobbits_bp.add_url_rule(\n    \"/<int:hobbit_id>\",\n    view_func=HobbitDetailView.as_view(\"hobbit_detail\"),\n    methods=[\"GET\"],\n)\n\n\nclass HobbitUpdateView(MethodView):\n    def put(self, hobbit_id, *args, **kwargs):\n        data = request.get_json()\n        updated_hobbit = HobbitService().update_hobbit(hobbit_id, data)\n        return updated_hobbit\n\n\n# @nanoapi method:PUT path:/api/hobbits/<hobbit_id> group:hobbit_write\nhobbits_bp.add_url_rule(\n    \"/<int:hobbit_id>\",\n    view_func=HobbitUpdateView.as_view(\"hobbit_update\"),\n    methods=[\"PUT\"],\n)\n\n\nclass HobbitDeleteView(MethodView):\n    def delete(self, hobbit_id, *args, **kwargs):\n        HobbitService().delete_hobbit(hobbit_id)\n        return None\n\n\n# @nanoapi method:DELETE path:/api/hobbits/<hobbit_id> group:hobbit_write\nhobbits_bp.add_url_rule(\n    \"/<int:hobbit_id>\",\n    view_func=HobbitDeleteView.as_view(\"hobbit_delete\"),\n    methods=[\"DELETE\"],\n)\n"
  },
  {
    "path": "examples/python/flask/api/wizards/data.py",
    "content": "class Wizard:\n    def __init__(self, data):\n        self.id = data.get(\"id\")\n        self.name = data.get(\"name\")\n\n    def update(self, data):\n        self.name = data.get(\"name\")\n\n\nwizards = [\n    {\"id\": 1, \"name\": \"Harry Potter\"},\n    {\"id\": 2, \"name\": \"Hermione Granger\"},\n    {\"id\": 3, \"name\": \"Ron Weasley\"},\n]\n"
  },
  {
    "path": "examples/python/flask/api/wizards/services.py",
    "content": "from api.data.hobbits import Hobbit, hobbits\nfrom api.wizards.data import Wizard, wizards\n\nclass WizardService:\n    def get_wizards(self):\n        return wizards\n\n    def get_wizard(self, wizard_id):\n        for wizard in wizards:\n            if wizard.id == wizard_id:\n                return wizard\n        return None\n\n    def create_wizard(self, data):\n        new_wizard = Wizard(data)\n        wizards.append(new_wizard)\n        return new_wizard\n\n    def update_wizard(self, wizard_id, data):\n        wizard: Wizard = wizards[wizard_id]\n        wizard.update(data)\n        return wizard\n\n    def delete_wizard(self, wizard_id):\n        for wizard in wizards:\n            if wizard.id == wizard_id:\n                wizards.remove(wizard)\n"
  },
  {
    "path": "examples/python/flask/api/wizards/views.py",
    "content": "from flask.views import MethodView\nfrom flask import request, Blueprint\nfrom .services import WizardService\n\nclass WizardListView(MethodView):\n    def get(self, *args, **kwargs):\n        wizards = WizardService().get_wizards()\n        return wizards\n\n\nclass WizardCreateView(MethodView):\n    def post(self, *args, **kwargs):\n        data = request.get_json()\n        new_wizard = WizardService().create_wizard(data)\n        return new_wizard\n\n\nclass WizardDetailView(MethodView):\n    def get(self, wizard_id, *args, **kwargs):\n        wizard = WizardService().get_wizard(wizard_id)\n        return wizard\n\n\nclass WizardUpdateView(MethodView):\n    def put(self, wizard_id, *args, **kwargs):\n        data = request.get_json()\n        updated_wizard = WizardService().update_wizard(wizard_id, data)\n        return updated_wizard\n\n\nclass WizardDeleteView(MethodView):\n    def delete(self, wizard_id, *args, **kwargs):\n        WizardService().delete_wizard(wizard_id)\n        return None\n\n\ndef get_wizzards_bp():\n    wizards_bp = Blueprint(\"wizards\", __name__)\n\n    # @nanoapi method:GET path:/api/wizards group:wizard_read\n    wizards_bp.add_url_rule(\n        \"/\", view_func=WizardListView.as_view(\"wizard_list\"), methods=[\"GET\"]\n    )\n\n    # @nanoapi method:POST path:/api/wizards group:wizard_write\n    wizards_bp.add_url_rule(\n        \"/\", view_func=WizardCreateView.as_view(\"wizard_create\"), methods=[\"POST\"]\n    )\n\n    # @nanoapi method:GET path:/api/wizards/<wizard_id> group:wizard_read\n    wizards_bp.add_url_rule(\n        \"/<int:wizard_id>\",\n        view_func=WizardDetailView.as_view(\"wizard_detail\"),\n        methods=[\"GET\"],\n    )\n\n    # @nanoapi method:PUT path:/api/wizards/<wizard_id> group:wizard_write\n    wizards_bp.add_url_rule(\n        \"/<int:wizard_id>\",\n        view_func=WizardUpdateView.as_view(\"wizard_update\"),\n        methods=[\"PUT\"],\n    )\n\n    # @nanoapi method:DELETE path:/api/wizards/<wizard_id> group:wizard_write\n    wizards_bp.add_url_rule(\n        \"/<int:wizard_id>\",\n        view_func=WizardDeleteView.as_view(\"wizard_delete\"),\n        methods=[\"DELETE\"],\n    )\n    \n    return wizards_bp"
  },
  {
    "path": "examples/python/flask/app.py",
    "content": "from flask import Flask\nfrom .api.wizards.views import get_wizzards_bp\nfrom api.views.elves import elves_bp\nfrom api.views.hobbits import hobbits_bp\n\napp = Flask(__name__)\n\n\n@app.get(\"/\")\ndef hello_world():\n    return {\"message\": \"Hello, World!\"}\n\n\n@app.get(\"/liveness\")\ndef liveness():\n    return {\"status\": \"ok\"}\n\n# @nanoapi method:GET path:readiness\n@app.get(\"/readiness\")\ndef readiness():\n    return {\"status\": \"ok\"}\n\n# @nanoapi path:/api/wizards\napp.register_blueprint(get_wizzards_bp(), url_prefix=\"/api/wizards\")\n\n# @nanoapi path:/api/elves\napp.register_blueprint(elves_bp, url_prefix=\"/api/elves\")\n\n# @nanoapi path:/api/hobbits\napp.register_blueprint(hobbits_bp, url_prefix=\"/api/hobbits\")\n"
  },
  {
    "path": "examples/python/flask/pyproject.toml",
    "content": "[tool.poetry]\nname = \"flask-example\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"florianbgt <florian.bigot321@gmail.com>\"]\nreadme = \"README.md\"\n\n[tool.poetry.dependencies]\npython = \"^3.12\"\nflask = \"^3.1.0\"\n\n\n[tool.poetry.group.dev.dependencies]\nruff = \"^0.7.4\"\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "install_scripts/install.ps1",
    "content": "# PowerShell script to install NanoAPI CLI on Windows\n\n$ErrorActionPreference = \"Stop\"\n\n# Define platform-specific filename\n$FILENAME = \"napi.exe\"\n$RELEASE_URL = \"https://github.com/nanoapi-io/napi/releases/latest/download/\"\n$DOWNLOAD_URL = \"$RELEASE_URL$FILENAME\"\n\n# Define install path (add this path to your system PATH if needed)\n$INSTALL_DIR = \"$env:ProgramFiles\\NanoAPI\"\n$INSTALL_PATH = Join-Path $INSTALL_DIR \"napi.exe\"\n$BACKUP_PATH = \"$INSTALL_PATH.bak\"\n\n# Create install directory if it doesn't exist\nif (-Not (Test-Path $INSTALL_DIR)) {\n    New-Item -ItemType Directory -Path $INSTALL_DIR | Out-Null\n}\n\n# Backup existing binary\nif (Test-Path $INSTALL_PATH) {\n    Write-Output \"Existing version found, creating a backup...\"\n    Move-Item -Force -Path $INSTALL_PATH -Destination $BACKUP_PATH\n} else {\n    Write-Output \"No existing version found, proceeding with installation.\"\n}\n\n# Download the binary\n$TEMP_PATH = Join-Path $env:TEMP $FILENAME\nWrite-Output \"Downloading the latest version of $FILENAME...\"\nInvoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $TEMP_PATH\n\n# Install the binary\nWrite-Output \"Installing new version...\"\ntry {\n    Move-Item -Force -Path $TEMP_PATH -Destination $INSTALL_PATH\n    Write-Output \"Installation/Update successful! You can now use your CLI by typing 'napi' if it's in your PATH.\"\n} catch {\n    Write-Error \"Update failed! Restoring the old version...\"\n    if (Test-Path $BACKUP_PATH) {\n        Move-Item -Force -Path $BACKUP_PATH -Destination $INSTALL_PATH\n        Write-Output \"Restored the old version. Please try again later.\"\n    } else {\n        Write-Error \"Backup not found. Manual intervention required.\"\n    }\n}\n"
  },
  {
    "path": "install_scripts/install.sh",
    "content": "#!/bin/bash\n\n# Determine platform and release URL for GitHub\nOS=$(uname -s)\nRELEASE_URL=\"https://github.com/nanoapi-io/napi/releases/latest/download/\"\n\n# Define the appropriate binary based on the OS\nif [ \"$OS\" == \"Darwin\" ]; then\n    FILENAME=\"napi.macos\"\nelif [ \"$OS\" == \"Linux\" ]; then\n    FILENAME=\"napi.linux\"\nelse\n    echo \"Unsupported OS: $OS\"\n    exit 1\nfi\n\n# Set the installation path\nINSTALL_PATH=\"/usr/local/bin/napi\"\nBACKUP_PATH=\"$INSTALL_PATH.bak\"\n\n# Check if the executable already exists\nif [ -f \"$INSTALL_PATH\" ]; then\n    echo \"Existing version found, creating a backup...\"\n    sudo mv \"$INSTALL_PATH\" \"$BACKUP_PATH\"\nelse\n    echo \"No existing version found, proceeding with installation.\"\nfi\n\n# Download the binary\necho \"Downloading the latest version of $FILENAME...\"\ncurl -L \"$RELEASE_URL$FILENAME\" -o \"$FILENAME\"\n\n# Make it executable\nchmod +x \"$FILENAME\"\n\n# Attempt to install the new version\necho \"Installing new version...\"\nsudo mv \"$FILENAME\" \"$INSTALL_PATH\"\n\n# Check if the installation was successful\nif [ $? -eq 0 ]; then\n    echo \"Installation/Update successful! You can now use your CLI by typing 'napi' in the terminal.\"\nelse\n    # If the installation failed, restore the backup\n    echo \"Update failed! Restoring the old version...\"\n    sudo mv \"$BACKUP_PATH\" \"$INSTALL_PATH\"\n    echo \"Restored the old version. Please try again later.\"\nfi\n"
  },
  {
    "path": "media/README.md",
    "content": "## Media\n\nThe content in this folder is meant to be used when creating content about\nNanoAPI. It includes images and other media that can be used to promote the\nproject.\n\n### Fonts\n\nThe main font for the logo is Plus Jakarta Sans. It can be found on\n[Google Fonts](https://fonts.google.com/specimen/Plus+Jakarta+Sans).\n\n### Colors\n\nThe main colors for the project are:\n\n- Background: #0B0A32\n- Secondary Background: #15143D\n- Surface: #3A397C\n- Secondary Surface: #25235C\n- Border: #3A397C\n- Text: #FFFFFF\n- Gray: #B4B4C9\n- Primary: #5848E8\n- Secondary: #D62B80\n\n### Mission\n\nNanoAPI is a tool that helps developers refactor their codebase into smaller,\nmore manageable pieces at build time. It is designed to help developers quickly\nrefactor large/monolith codebases into smaller, more manageable pieces at build\ntime. It increases robustness by reducing downtime through isolating. It helps\ndevelopers better understand what their codebase is doing today. It creates a\nnew development paradigm (develop monolith, deploy microservice) for projects\nmoving fast. We aim to drag developers kicking and screaming into more energy\nefficient software.\n\n### Past PR\n\n- [Sustainable Software](https://www.linkedin.com/pulse/nanoapi-founders-sustainable-software-why-our-planet-m63be/)\n\n### Contact\n\n- [info@nanoapi.io](mailto:info@nanoapi.io)\n"
  },
  {
    "path": "scripts/get_version.ts",
    "content": "import denoJson from \"../deno.json\" with { type: \"json\" };\n\nconst encoder = new TextEncoder();\nconst version = encoder.encode(denoJson.version);\nDeno.stdout.writeSync(version);\nDeno.exit(0);\n"
  },
  {
    "path": "src/cli/handlers/extract/index.ts",
    "content": "import type { Arguments } from \"yargs-types\";\nimport type { localConfigSchema } from \"../../middlewares/napiConfig.ts\";\n\nimport type { z } from \"zod\";\nimport { extractSymbols } from \"../../../symbolExtractor/index.ts\";\nimport {\n  getExtensionsForLanguage,\n  getFilesFromDirectory,\n  writeFilesToDirectory,\n} from \"../../../helpers/fileSystem/index.ts\";\nimport { join } from \"@std/path\";\nimport { napiConfigMiddleware } from \"../../middlewares/napiConfig.ts\";\nimport type { DependencyManifest } from \"../../../manifest/dependencyManifest/types.ts\";\nimport type { globalConfigSchema } from \"../../middlewares/globalConfig.ts\";\n\nconst NAPI_DIR = \".napi\";\nconst MANIFESTS_DIR = \"manifests\";\n\ninterface ManifestEnvelope {\n  id: string;\n  branch: string;\n  commitSha: string;\n  commitShaDate: string;\n  createdAt: string;\n  manifest: DependencyManifest;\n}\n\nfunction getLatestManifestId(workdir: string): string | null {\n  const manifestsDir = join(workdir, NAPI_DIR, MANIFESTS_DIR);\n  try {\n    const entries: { name: string }[] = [];\n    for (const entry of Deno.readDirSync(manifestsDir)) {\n      if (entry.isFile && entry.name.endsWith(\".json\")) {\n        entries.push(entry);\n      }\n    }\n    if (entries.length === 0) return null;\n    entries.sort((a, b) => b.name.localeCompare(a.name));\n    return entries[0].name.replace(\".json\", \"\");\n  } catch {\n    return null;\n  }\n}\n\nfunction loadManifest(workdir: string, manifestId: string): ManifestEnvelope {\n  const manifestPath = join(\n    workdir,\n    NAPI_DIR,\n    MANIFESTS_DIR,\n    `${manifestId}.json`,\n  );\n  const content = Deno.readTextFileSync(manifestPath);\n  return JSON.parse(content) as ManifestEnvelope;\n}\n\nfunction builderFunction(\n  yargs: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  },\n) {\n  return yargs\n    .middleware(napiConfigMiddleware)\n    .option(\"symbol\", {\n      type: \"array\" as const,\n      description:\n        \"Symbols to extract (format: file|symbol where file is absolute path from .napirc and symbol is the symbol name)\",\n      string: true,\n      requiresArg: true,\n      demandOption: true,\n    })\n    .option(\"manifestId\", {\n      type: \"string\",\n      description:\n        \"The manifest ID to use for the extraction (defaults to latest)\",\n      requiresArg: true,\n    })\n    .check(\n      (\n        argv: Arguments & {\n          globalConfig: z.infer<typeof globalConfigSchema>;\n        } & {\n          symbol: string[];\n        },\n      ) => {\n        const symbols = argv.symbol;\n\n        if (!symbols || symbols.length === 0) {\n          throw new Error(\"At least one symbol must be specified\");\n        }\n\n        for (const symbolSpec of symbols) {\n          const splitSymbol = symbolSpec.split(\"|\");\n          if (splitSymbol.length !== 2) {\n            throw new Error(\n              `Invalid symbol format: \"${symbolSpec}\". Expected format: file|symbol (e.g., \"src/main.py|myFunction\")`,\n            );\n          }\n        }\n\n        return true;\n      },\n    );\n}\n\nfunction handler(\n  argv: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  } & {\n    symbol: string[];\n    manifestId?: string;\n  },\n) {\n  const napiConfig = argv.napiConfig as z.infer<typeof localConfigSchema>;\n  const start = Date.now();\n\n  console.info(\"🎯 Starting symbol extraction...\");\n\n  try {\n    let manifestId = argv.manifestId;\n\n    if (!manifestId) {\n      manifestId = getLatestManifestId(argv.workdir) ?? undefined;\n      if (!manifestId) {\n        console.error(\"❌ No manifests found in .napi/manifests/\");\n        console.error(\"   Run 'napi generate' first to create a manifest.\");\n        Deno.exit(1);\n      }\n      console.info(`📄 Using latest manifest: ${manifestId}`);\n    } else {\n      console.info(`📄 Using manifest: ${manifestId}`);\n    }\n\n    let envelope: ManifestEnvelope;\n    try {\n      envelope = loadManifest(argv.workdir, manifestId);\n    } catch (error) {\n      console.error(`❌ Failed to load manifest: ${manifestId}`);\n      if (error instanceof Deno.errors.NotFound) {\n        console.error(\n          `   File not found: .napi/manifests/${manifestId}.json`,\n        );\n      } else {\n        console.error(\n          `   Error: ${error instanceof Error ? error.message : String(error)}`,\n        );\n      }\n      Deno.exit(1);\n    }\n\n    const dependencyManifest = envelope.manifest;\n\n    console.info(\"🔍 Validating symbol specifications...\");\n    const symbolsToExtract = new Map<\n      string,\n      { filePath: string; symbols: Set<string> }\n    >();\n\n    for (const symbolSpec of argv.symbol) {\n      const [filePath, symbolName] = symbolSpec.split(\"|\", 2);\n\n      if (!dependencyManifest[filePath]) {\n        console.warn(`⚠️  File not found in manifest: ${filePath}`);\n        console.warn(\n          \"   Make sure the file is included in your project configuration\",\n        );\n      }\n\n      const existingEntry = symbolsToExtract.get(filePath) || {\n        filePath,\n        symbols: new Set<string>(),\n      };\n\n      existingEntry.symbols.add(symbolName);\n      symbolsToExtract.set(filePath, existingEntry);\n    }\n\n    console.info(\n      `📊 Extracting ${argv.symbol.length} symbols from ${symbolsToExtract.size} files:`,\n    );\n    for (const [filePath, { symbols }] of symbolsToExtract) {\n      console.info(`   • ${filePath}: ${Array.from(symbols).join(\", \")}`);\n    }\n\n    console.info(\"🔧 Scanning project files...\");\n    const fileExtensions = getExtensionsForLanguage(napiConfig.language);\n\n    const files = getFilesFromDirectory(argv.workdir, {\n      includes: napiConfig.project.include,\n      excludes: napiConfig.project.exclude,\n      extensions: fileExtensions,\n      logMessages: false,\n    });\n\n    console.info(`📁 Found ${files.size} files to process`);\n    console.info(\"⚙️  Extracting symbols...\");\n\n    const extractedFiles = extractSymbols(\n      files,\n      dependencyManifest,\n      symbolsToExtract,\n      napiConfig,\n    );\n\n    const unixTimestamp = Date.now();\n    const outputDir = join(\n      argv.workdir,\n      napiConfig.outDir,\n      `extracted-${unixTimestamp}`,\n    );\n\n    console.info(`💾 Writing extracted symbols to: ${outputDir}`);\n    writeFilesToDirectory(extractedFiles, outputDir);\n\n    const duration = Date.now() - start;\n    console.info(\n      `✅ Symbol extraction completed successfully in ${duration}ms`,\n    );\n    console.info(`📄 Extracted files:`);\n    console.info(`   • ${extractedFiles.size} files generated`);\n    console.info(`   • Output directory: ${outputDir}`);\n  } catch (error: unknown) {\n    const errorMessage = error instanceof Error ? error.message : String(error);\n\n    console.error(\"❌ Symbol extraction failed\");\n    console.error(`   Error: ${errorMessage}`);\n    console.error(\"\");\n    console.error(\"💡 Common solutions:\");\n    console.error(\n      \"   • Check your symbol specifications (format: file|symbol)\",\n    );\n    console.error(\n      \"   • Ensure the manifest is up to date: napi generate\",\n    );\n    console.error(\n      \"   • Verify the specified files contain the requested symbols\",\n    );\n\n    Deno.exit(1);\n  }\n}\n\nexport default {\n  command: \"extract\",\n  describe: \"extract symbols from your program\",\n  builder: builderFunction,\n  handler,\n};\n"
  },
  {
    "path": "src/cli/handlers/generate/index.ts",
    "content": "import type { Arguments } from \"yargs-types\";\nimport {\n  type localConfigSchema,\n  napiConfigMiddleware,\n} from \"../../middlewares/napiConfig.ts\";\nimport {\n  getExtensionsForLanguage,\n  getFilesFromDirectory,\n} from \"../../../helpers/fileSystem/index.ts\";\nimport {\n  generateDependencyManifest,\n} from \"../../../manifest/dependencyManifest/index.ts\";\nimport type { z } from \"zod\";\nimport type { globalConfigSchema } from \"../../middlewares/globalConfig.ts\";\nimport { join } from \"@std/path\";\n\nconst NAPI_DIR = \".napi\";\nconst MANIFESTS_DIR = \"manifests\";\n\nfunction builder(\n  yargs: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  },\n) {\n  return yargs\n    .middleware(napiConfigMiddleware)\n    .option(\"branch\", {\n      type: \"string\",\n      description: \"The branch to use for the manifest\",\n    }).option(\"commit-sha\", {\n      type: \"string\",\n      description: \"The commit SHA to use for the manifest\",\n    }).option(\"commit-sha-date\", {\n      type: \"string\",\n      description: \"The commit SHA date to use for the manifest\",\n      coerce: (value: string) => {\n        const date = new Date(value);\n        if (isNaN(date.getTime())) {\n          throw new Error(\n            \"Invalid date format for commit-sha-date. Please use ISO 8601 format (e.g. 2024-01-01T00:00:00Z)\",\n          );\n        }\n        return value;\n      },\n    }).option(\"labelingApiKey\", {\n      type: \"string\",\n      description: \"The API key to use for the labeling\",\n    });\n}\n\nasync function getGitBranch(workDir: string): Promise<string> {\n  try {\n    const command = new Deno.Command(\"git\", {\n      args: [\"branch\", \"--show-current\"],\n      cwd: workDir,\n      stdout: \"piped\",\n      stderr: \"piped\",\n    });\n\n    const { code, stdout } = await command.output();\n\n    if (code === 0) {\n      const branch = new TextDecoder().decode(stdout).trim();\n      return branch || \"main\";\n    }\n\n    return \"main\";\n  } catch {\n    return \"main\";\n  }\n}\n\nasync function getGitCommitSha(workDir: string): Promise<string> {\n  try {\n    const command = new Deno.Command(\"git\", {\n      args: [\"rev-parse\", \"HEAD\"],\n      cwd: workDir,\n      stdout: \"piped\",\n      stderr: \"piped\",\n    });\n\n    const { code, stdout } = await command.output();\n\n    if (code === 0) {\n      return new TextDecoder().decode(stdout).trim();\n    }\n\n    return \"\";\n  } catch {\n    return \"\";\n  }\n}\n\nasync function getGitCommitDate(workDir: string): Promise<string> {\n  try {\n    const command = new Deno.Command(\"git\", {\n      args: [\"log\", \"-1\", \"--format=%cI\"],\n      cwd: workDir,\n      stdout: \"piped\",\n      stderr: \"piped\",\n    });\n\n    const { code, stdout } = await command.output();\n\n    if (code === 0) {\n      return new TextDecoder().decode(stdout).trim();\n    }\n\n    return new Date().toISOString();\n  } catch {\n    return new Date().toISOString();\n  }\n}\n\nfunction ensureManifestsDir(workdir: string): string {\n  const manifestsDir = join(workdir, NAPI_DIR, MANIFESTS_DIR);\n  try {\n    Deno.mkdirSync(manifestsDir, { recursive: true });\n  } catch (error) {\n    if (!(error instanceof Deno.errors.AlreadyExists)) {\n      throw error;\n    }\n  }\n  return manifestsDir;\n}\n\nasync function handler(\n  argv: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  } & {\n    branch?: string;\n    commitSha?: string;\n    commitShaDate?: string;\n    labelingApiKey?: string;\n  },\n) {\n  const napiConfig = argv.napiConfig as z.infer<typeof localConfigSchema>;\n  const globalConfig = argv.globalConfig as z.infer<typeof globalConfigSchema>;\n\n  let branch: string;\n  let commitSha: string;\n  let commitShaDate: string;\n\n  if (argv.branch) {\n    branch = argv.branch;\n    console.info(`🌿 Using provided branch: ${branch}`);\n  } else {\n    console.info(\"🔍 Detecting Git branch...\");\n    const detectedBranch = await getGitBranch(argv.workdir);\n    const userBranch = prompt(\n      `Enter branch name or leave empty:`,\n      detectedBranch,\n    );\n    branch = userBranch || detectedBranch;\n    console.info(`🌿 Branch: ${branch}`);\n  }\n\n  if (argv.commitSha) {\n    commitSha = argv.commitSha;\n    console.info(`📝 Using provided commit: ${commitSha.substring(0, 8)}...`);\n  } else {\n    console.info(\"🔍 Detecting Git commit SHA...\");\n    const detectedCommitSha = await getGitCommitSha(argv.workdir);\n    const userCommitSha = prompt(\n      `Enter commit SHA or leave empty:`,\n      detectedCommitSha,\n    );\n    commitSha = userCommitSha || detectedCommitSha;\n    if (commitSha) {\n      console.info(`📝 Commit SHA: ${commitSha.substring(0, 8)}...`);\n    }\n  }\n\n  if (argv.commitShaDate) {\n    commitShaDate = argv.commitShaDate;\n    console.info(`📅 Using provided commit date: ${commitShaDate}`);\n  } else {\n    console.info(\"🔍 Detecting Git commit date...\");\n    const detectedCommitShaDate = await getGitCommitDate(argv.workdir);\n    const userCommitShaDate = prompt(\n      `Enter commit date or leave empty:`,\n      detectedCommitShaDate,\n    );\n    commitShaDate = userCommitShaDate || detectedCommitShaDate;\n    console.info(`📅 Commit date: ${commitShaDate}`);\n  }\n\n  const start = Date.now();\n\n  console.info(\"🔧 Generating dependency manifest...\");\n  try {\n    console.info(`📝 Language: ${napiConfig.language}`);\n    console.info(`📁 Working directory: ${argv.workdir}`);\n\n    const fileExtensions = getExtensionsForLanguage(napiConfig.language);\n    console.info(\n      `🔍 Looking for files with extensions: ${fileExtensions.join(\", \")}`,\n    );\n\n    const files = getFilesFromDirectory(argv.workdir, {\n      includes: napiConfig.project.include,\n      excludes: napiConfig.project.exclude,\n      extensions: fileExtensions,\n      logMessages: true,\n    });\n\n    if (files.size === 0) {\n      console.warn(\"⚠️  No files found matching your project configuration\");\n      console.warn(\"   Check your include/exclude patterns in .napirc\");\n      console.warn(\"\");\n      console.warn(\"💡 Current patterns:\");\n      console.warn(`   Include: ${napiConfig.project.include.join(\", \")}`);\n      if (napiConfig.project.exclude) {\n        console.warn(`   Exclude: ${napiConfig.project.exclude.join(\", \")}`);\n      }\n      Deno.exit(1);\n    }\n\n    console.info(`📊 Processing ${files.size} files...`);\n\n    const dependencyManifest = await generateDependencyManifest(\n      files,\n      napiConfig,\n      globalConfig,\n      argv.labelingApiKey,\n    );\n\n    const manifestsDir = ensureManifestsDir(argv.workdir);\n\n    const timestamp = Date.now();\n    const shortSha = commitSha ? commitSha.substring(0, 7) : \"unknown\";\n    const manifestId = `${timestamp}-${shortSha}`;\n    const manifestFileName = `${manifestId}.json`;\n\n    const manifestEnvelope = {\n      id: manifestId,\n      branch,\n      commitSha,\n      commitShaDate,\n      createdAt: new Date().toISOString(),\n      manifest: dependencyManifest,\n    };\n\n    const manifestPath = join(manifestsDir, manifestFileName);\n    Deno.writeTextFileSync(\n      manifestPath,\n      JSON.stringify(manifestEnvelope, null, 2),\n    );\n\n    const duration = Date.now() - start;\n    console.info(\n      `✅ Manifest saved successfully in ${duration}ms`,\n    );\n    console.info(`📄 Generated manifest contains:`);\n    console.info(`   • ${Object.keys(dependencyManifest).length} files`);\n    console.info(`   • Dependencies and relationships mapped`);\n    console.info(`\\n💾 Saved to: ${manifestPath}`);\n    console.info(`🔍 View it with: napi view`);\n  } catch (error: unknown) {\n    const errorMessage = error instanceof Error ? error.message : String(error);\n\n    console.error(\"❌ Failed to generate manifest\");\n    console.error(`   Error: ${errorMessage}`);\n    console.error(\"\");\n    console.error(\"💡 Common solutions:\");\n    console.error(\"   • Check that your project files are accessible\");\n    console.error(\"   • Verify your .napirc configuration\");\n\n    Deno.exit(1);\n  }\n}\n\nexport default {\n  command: \"generate\",\n  describe: \"generate a dependency manifest for your program\",\n  builder,\n  handler,\n};\n"
  },
  {
    "path": "src/cli/handlers/init/index.ts",
    "content": "import type { Arguments } from \"yargs-types\";\nimport {\n  createConfig,\n  getConfigFromWorkDir,\n} from \"../../middlewares/napiConfig.ts\";\nimport { join, normalize, relative, SEPARATOR } from \"@std/path\";\nimport type z from \"zod\";\nimport type { localConfigSchema } from \"../../middlewares/napiConfig.ts\";\nimport pythonStdlibList from \"../../../scripts/generate_python_stdlib_list/output.json\" with {\n  type: \"json\",\n};\nimport { confirm, input, search, select } from \"@inquirer/prompts\";\nimport { globSync } from \"glob\";\nimport {\n  cLanguage,\n  csharpLanguage,\n  javaLanguage,\n  pythonLanguage,\n} from \"../../../helpers/treeSitter/parsers.ts\";\nimport type { globalConfigSchema } from \"../../middlewares/globalConfig.ts\";\nimport {\n  ANTHROPIC_PROVIDER,\n  GOOGLE_PROVIDER,\n  OPENAI_PROVIDER,\n} from \"../../../manifest/dependencyManifest/labeling/model.ts\";\nimport { defaultAuditConfig } from \"../../../manifest/auditManifest/types.ts\";\n\nfunction builder(\n  yargs: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  },\n) {\n  return yargs;\n}\n\nasync function handler(\n  argv: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  },\n) {\n  try {\n    try {\n      if (getConfigFromWorkDir(argv.workdir)) {\n        const confirmOverwrite = await confirm({\n          message:\n            `⚠️ A .napirc file already exists in the selected directory. Do you want to overwrite it?`,\n          default: false,\n        });\n        if (!confirmOverwrite) {\n          console.info(\"✅ Keeping existing configuration\");\n          Deno.exit(0);\n        }\n        console.info(\"🔄 Proceeding with configuration overwrite\");\n      }\n    } catch {\n      console.info(\n        \"📝 No existing valid configuration found, creating new one\",\n      );\n    }\n\n    console.info(\"\\n🔧 Starting interactive configuration...\");\n\n    const napiConfig = await generateConfig(argv.workdir);\n\n    console.info(\"\\n📋 Generated configuration:\");\n    console.info(\"─\".repeat(50));\n    console.info(JSON.stringify(napiConfig, null, 2));\n    console.info(\"─\".repeat(50));\n\n    const confirmSave = await confirm({\n      message: \"Do you want to save this configuration?\",\n      default: true,\n    });\n\n    if (confirmSave) {\n      createConfig(napiConfig, argv.workdir);\n      console.info(\"\\n✅ Configuration saved successfully!\");\n      console.info(`📄 Created: ${argv.workdir}${SEPARATOR}.napirc`);\n      console.info(\"🎉 Your NanoAPI project is ready!\");\n      console.info(\"\\n💡 Next steps:\");\n      console.info(\"   1. Run: napi generate\");\n      console.info(\"   2. Run: napi view\");\n    } else {\n      console.info(\"❌ Configuration not saved\");\n      console.info(\"   Run 'napi init' again when you're ready to configure\");\n      Deno.exit(0);\n    }\n  } catch (error: unknown) {\n    const errorMessage = error instanceof Error ? error.message : String(error);\n\n    console.error(\"❌ Initialization failed\");\n    console.error(`   Error: ${errorMessage}`);\n    console.error(\"\\n💡 Common solutions:\");\n    console.error(\"  • Check that you have write permissions in the directory\");\n    console.error(\"  • Ensure the directory exists and is accessible\");\n    console.error(\"  • Try running the command again\");\n\n    Deno.exit(1);\n  }\n}\n\nexport default {\n  command: \"init\",\n  describe: \"Initialize a NanoAPI project with interactive configuration\",\n  builder,\n  handler,\n};\n\nfunction showMatchingFiles(\n  workDir: string,\n  pattern: string,\n  maxFilesToShow = 10,\n) {\n  try {\n    const files = globSync(pattern, {\n      cwd: workDir,\n      nodir: true,\n    });\n\n    if (files.length === 0) {\n      console.info(`No files match the pattern '${pattern}'`);\n      return;\n    }\n\n    const totalMatches = files.length;\n    const filesToShow = files.slice(0, maxFilesToShow);\n\n    console.info(`\\nPattern '${pattern}' matches ${totalMatches} file(s):`);\n    filesToShow.forEach((file: string) => console.info(`- ${file}`));\n\n    if (totalMatches > maxFilesToShow) {\n      console.info(`... and ${totalMatches - maxFilesToShow} more`);\n    }\n  } catch (error) {\n    console.info(`Error previewing files for pattern '${pattern}': ${error}`);\n  }\n}\n\nfunction showFinalFileSelection(\n  workDir: string,\n  includePatterns: string[],\n  excludePatterns: string[],\n  maxFilesToShow = 20,\n) {\n  try {\n    const files = globSync(includePatterns, {\n      cwd: workDir,\n      nodir: true,\n      ignore: excludePatterns,\n    });\n\n    console.info(\"\\n🔍 FINAL FILE SELECTION\");\n    console.info(\n      `After applying all patterns, ${files.length} files will be processed:`,\n    );\n\n    const filesToShow = files.slice(0, maxFilesToShow);\n    filesToShow.forEach((file: string) => console.info(`- ${file}`));\n\n    if (files.length > maxFilesToShow) {\n      console.info(`... and ${files.length - maxFilesToShow} more`);\n    }\n\n    if (files.length === 0) {\n      console.warn(\n        \"\\n⚠️ WARNING: No files match your include/exclude patterns. Please review your configuration.\",\n      );\n    }\n  } catch (error) {\n    console.info(`Error showing final file selection: ${error}`);\n  }\n}\n\nfunction getProjectStructureOverview(workDir: string): string[] {\n  const overview: string[] = [];\n\n  try {\n    const traverseDirectory = (dir: string, depth: number) => {\n      const entries = Deno.readDirSync(dir);\n\n      for (const entry of entries) {\n        const fullPath = join(dir, entry.name);\n        const relativePath = relative(workDir, fullPath);\n\n        try {\n          const stat = Deno.statSync(fullPath);\n          const isDirectory = stat.isDirectory;\n\n          const indentation = \"  \".repeat(depth);\n          const icon = isDirectory ? \"📂\" : \"📄\";\n          const line = `${indentation}${icon} ${relativePath}`;\n          overview.push(line);\n\n          if (isDirectory && depth < 2) {\n            traverseDirectory(fullPath, depth + 1);\n          }\n        } catch (error) {\n          console.info(`Could not access ${fullPath}: ${error}`);\n        }\n      }\n    };\n\n    traverseDirectory(workDir, 0);\n  } catch (error) {\n    console.info(`Error getting project structure: ${error}`);\n  }\n\n  return overview;\n}\n\nasync function collectIncludePatterns(\n  workDir: string,\n  language: string,\n): Promise<string[]> {\n  const projectStructure = getProjectStructureOverview(workDir);\n\n  console.info(\n    `\nInclude patterns define which files NanoAPI will process and analyze.\n\nExamples:\n- '**${SEPARATOR}*.py' for all Python files\n- 'src${SEPARATOR}**' for all files in src directory\n- '*.py' for all Python files in the root directory\n`,\n  );\n\n  const suggestedIncludes = suggestIncludePatterns(projectStructure, language);\n  console.info(\"\\nSuggested include patterns (based on project structure):\");\n  suggestedIncludes.forEach((pattern) => console.info(`- ${pattern}`));\n\n  console.info(\"\\nPreview of files that would be included:\");\n  for (const pattern of suggestedIncludes) {\n    showMatchingFiles(workDir, pattern);\n  }\n\n  const useSuggested = await confirm({\n    message: \"Do you want to use the suggested include patterns?\",\n    default: true,\n  });\n\n  if (useSuggested) {\n    console.info(\"Using suggested include patterns.\");\n    return suggestedIncludes;\n  }\n\n  console.info(\n    \"Please enter the glob patterns for files to include (one per line):\",\n  );\n\n  let includePatterns: string[] = [];\n  let continueAdding = true;\n  let validSelection = false;\n\n  while (!validSelection) {\n    includePatterns = [];\n    continueAdding = true;\n\n    while (continueAdding) {\n      const pattern = await input({\n        message:\n          `Enter glob pattern (e.g., '**${SEPARATOR}*.py', 'src${SEPARATOR}**')`,\n        validate: (value) => {\n          if (!value.trim()) return \"Pattern cannot be empty\";\n          try {\n            new RegExp(value.replace(/\\*\\*/g, \"*\").replace(/\\*/g, \".*\"));\n\n            console.info(`\\nPreviewing files matching '${value}':`);\n            const files = globSync(value, {\n              cwd: workDir,\n              nodir: true,\n            });\n\n            if (files.length === 0) {\n              return `No files match the pattern '${value}'. Please check and try again.`;\n            }\n\n            const totalMatches = files.length;\n            const filesToShow = files.slice(0, 5);\n\n            filesToShow.forEach((file: string) => console.info(`- ${file}`));\n\n            if (totalMatches > 5) {\n              console.info(`... and ${totalMatches - 5} more`);\n            }\n\n            return true;\n          } catch {\n            return \"Invalid pattern\";\n          }\n        },\n      });\n\n      includePatterns.push(pattern);\n\n      continueAdding = await confirm({\n        message: \"Do you want to add another include pattern?\",\n        default: false,\n      });\n    }\n\n    if (includePatterns.length === 0) {\n      console.info(\"No patterns provided, using default '**' (all files)\");\n      includePatterns = [\"**\"];\n    }\n\n    console.info(\"\\nSelected include patterns:\");\n    includePatterns.forEach((pattern) => console.info(`- ${pattern}`));\n\n    showFinalFileSelection(workDir, includePatterns, []);\n\n    validSelection = await confirm({\n      message: \"Are you satisfied with this file selection?\",\n      default: true,\n    });\n\n    if (!validSelection) {\n      console.info(\"\\nLet's try again with different include patterns.\");\n    }\n  }\n\n  return includePatterns;\n}\n\nasync function collectExcludePatterns(\n  workDir: string,\n  includePatterns: string[],\n  language: string,\n  outDir: string,\n): Promise<string[]> {\n  console.info(\"\\n❌ Specifying files to exclude from your project\");\n  console.info(\n    `\nExclude patterns define which files NanoAPI will ignore during processing.\n\nExamples:\n- 'node_modules${SEPARATOR}**' to exclude all node modules\n- '**${SEPARATOR}*.test.js' to exclude all JavaScript test files\n- '.git${SEPARATOR}**' to exclude git directory\n`,\n  );\n\n  const suggestedExcludes = suggestExcludePatterns(\n    includePatterns,\n    language,\n    outDir,\n  );\n  console.info(\"\\nSuggested exclude patterns (based on included files):\");\n  suggestedExcludes.forEach((pattern) => console.info(`- ${pattern}`));\n\n  console.info(\"\\nPreview of files that would be excluded:\");\n  for (const pattern of suggestedExcludes) {\n    showMatchingFiles(workDir, pattern);\n  }\n\n  const useSuggested = await confirm({\n    message: \"Do you want to use the suggested exclude patterns?\",\n    default: true,\n  });\n\n  if (useSuggested) {\n    console.info(\"Using suggested exclude patterns.\");\n    return suggestedExcludes;\n  }\n\n  console.info(\n    \"Please enter the glob patterns for files to exclude (one per line):\",\n  );\n\n  let excludePatterns: string[] = [];\n  let continueAdding = true;\n  let validSelection = false;\n\n  while (!validSelection) {\n    excludePatterns = [];\n    continueAdding = true;\n\n    while (continueAdding) {\n      const pattern = await input({\n        message:\n          `Enter glob pattern (e.g., 'node_modules${SEPARATOR}**', '**${SEPARATOR}*.test.js')`,\n        validate: (value) => {\n          if (!value.trim()) return \"Pattern cannot be empty\";\n          try {\n            new RegExp(value.replace(/\\*\\*/g, \"*\").replace(/\\*/g, \".*\"));\n\n            console.info(`\\nPreviewing files matching '${value}':`);\n            const files = globSync(value, {\n              cwd: workDir,\n              nodir: true,\n            });\n\n            if (files.length === 0) {\n              console.info(\n                `Note: No files currently match the pattern '${value}'`,\n              );\n              return true;\n            }\n\n            const totalMatches = files.length;\n            const filesToShow = files.slice(0, 5);\n\n            filesToShow.forEach((file: string) => console.info(`- ${file}`));\n\n            if (totalMatches > 5) {\n              console.info(`... and ${totalMatches - 5} more`);\n            }\n\n            return true;\n          } catch {\n            return \"Invalid pattern\";\n          }\n        },\n      });\n\n      excludePatterns.push(pattern);\n\n      continueAdding = await confirm({\n        message: \"Do you want to add another exclude pattern?\",\n        default: false,\n      });\n    }\n\n    console.info(\"\\nSelected exclude patterns:\");\n    excludePatterns.forEach((pattern) => console.info(`- ${pattern}`));\n\n    showFinalFileSelection(workDir, includePatterns, excludePatterns);\n\n    validSelection = await confirm({\n      message: \"Are you satisfied with this file selection?\",\n      default: true,\n    });\n\n    if (!validSelection) {\n      console.info(\"\\nLet's try again with different exclude patterns.\");\n    }\n  }\n\n  return excludePatterns;\n}\n\nfunction suggestIncludePatterns(\n  projectStructure: string[],\n  language: string,\n): string[] {\n  const suggestions: string[] = [];\n\n  if (language === pythonLanguage) {\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 src${SEPARATOR}`))\n    ) {\n      suggestions.push(`src${SEPARATOR}**${SEPARATOR}*.py`);\n    }\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 lib${SEPARATOR}`))\n    ) {\n      suggestions.push(`lib${SEPARATOR}**${SEPARATOR}*.py`);\n    }\n    if (suggestions.length === 0) {\n      if (\n        projectStructure.some((entry) => entry.includes(`📂 app${SEPARATOR}`))\n      ) {\n        suggestions.push(`app${SEPARATOR}**${SEPARATOR}*.py`);\n      }\n    }\n    if (suggestions.length === 0) {\n      suggestions.push(`**${SEPARATOR}*.py`);\n    }\n  } else if (language === csharpLanguage) {\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 src${SEPARATOR}`))\n    ) {\n      suggestions.push(`src${SEPARATOR}**${SEPARATOR}*.cs`);\n    }\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 lib${SEPARATOR}`))\n    ) {\n      suggestions.push(`lib${SEPARATOR}**${SEPARATOR}*.cs`);\n    }\n    if (suggestions.length === 0) {\n      if (\n        projectStructure.some((entry) =>\n          entry.includes(`📂 Controllers${SEPARATOR}`)\n        )\n      ) {\n        suggestions.push(`Controllers${SEPARATOR}**${SEPARATOR}*.cs`);\n      }\n      if (\n        projectStructure.some((entry) =>\n          entry.includes(`📂 Models${SEPARATOR}`)\n        )\n      ) {\n        suggestions.push(`Models${SEPARATOR}**${SEPARATOR}*.cs`);\n      }\n      if (\n        projectStructure.some((entry) =>\n          entry.includes(`📂 Services${SEPARATOR}`)\n        )\n      ) {\n        suggestions.push(`Services${SEPARATOR}**${SEPARATOR}*.cs`);\n      }\n    }\n    if (suggestions.length === 0) {\n      suggestions.push(`**${SEPARATOR}*.cs`);\n    }\n  } else if (language === cLanguage) {\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 src${SEPARATOR}`))\n    ) {\n      suggestions.push(`src${SEPARATOR}**${SEPARATOR}*.c`);\n      suggestions.push(`src${SEPARATOR}**${SEPARATOR}*.h`);\n    }\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 lib${SEPARATOR}`))\n    ) {\n      suggestions.push(`lib${SEPARATOR}**${SEPARATOR}*.c`);\n      suggestions.push(`lib${SEPARATOR}**${SEPARATOR}*.h`);\n    }\n    if (suggestions.length === 0) {\n      if (\n        projectStructure.some((entry) =>\n          entry.includes(`📂 include${SEPARATOR}`)\n        )\n      ) {\n        suggestions.push(`include${SEPARATOR}**${SEPARATOR}*.c`);\n        suggestions.push(`include${SEPARATOR}**${SEPARATOR}*.h`);\n      }\n    }\n    if (suggestions.length === 0) {\n      suggestions.push(`**${SEPARATOR}*.c`);\n      suggestions.push(`**${SEPARATOR}*.h`);\n    }\n  } else if (language === javaLanguage) {\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 src${SEPARATOR}`))\n    ) {\n      suggestions.push(`src${SEPARATOR}**${SEPARATOR}*.java`);\n    }\n    if (\n      projectStructure.some((entry) => entry.includes(`📂 lib${SEPARATOR}`))\n    ) {\n      suggestions.push(`lib${SEPARATOR}**${SEPARATOR}*.java`);\n    }\n    if (suggestions.length === 0) {\n      if (\n        projectStructure.some((entry) =>\n          entry.includes(`📂 buildSrc${SEPARATOR}`)\n        )\n      ) {\n        suggestions.push(`buildSrc${SEPARATOR}**${SEPARATOR}*.java`);\n      }\n      if (\n        projectStructure.some((entry) => entry.includes(`📂 app${SEPARATOR}`))\n      ) {\n        suggestions.push(`app${SEPARATOR}**${SEPARATOR}*.java`);\n      }\n      if (\n        projectStructure.some((entry) => entry.includes(`📂 core${SEPARATOR}`))\n      ) {\n        suggestions.push(`core${SEPARATOR}**${SEPARATOR}*.java`);\n      }\n      if (\n        projectStructure.some((entry) => entry.includes(`📂 util${SEPARATOR}`))\n      ) {\n        suggestions.push(`util${SEPARATOR}**${SEPARATOR}*.java`);\n      }\n      if (\n        projectStructure.some((entry) => entry.includes(`📂 libs${SEPARATOR}`))\n      ) {\n        suggestions.push(`libs${SEPARATOR}**${SEPARATOR}*.java`);\n      }\n    }\n    if (suggestions.length === 0) {\n      suggestions.push(`**${SEPARATOR}*.java`);\n    }\n  }\n\n  return suggestions;\n}\n\nfunction suggestExcludePatterns(\n  _includePatterns: string[],\n  language: string,\n  outDir: string,\n): string[] {\n  const suggestions: string[] = [];\n\n  suggestions.push(`${outDir}${SEPARATOR}**`);\n  suggestions.push(`.napi${SEPARATOR}**`);\n  suggestions.push(`.git${SEPARATOR}**`);\n  suggestions.push(`**${SEPARATOR}dist${SEPARATOR}**`);\n  suggestions.push(`**${SEPARATOR}build${SEPARATOR}**`);\n\n  if (language === pythonLanguage) {\n    suggestions.push(`**${SEPARATOR}__pycache__${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}*.pyc`);\n    suggestions.push(`**${SEPARATOR}.pytest_cache${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}venv${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.env${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}*.egg-info${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.tox${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.coverage`);\n    suggestions.push(`**${SEPARATOR}htmlcov${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.mypy_cache${SEPARATOR}**`);\n  } else if (language === csharpLanguage) {\n    suggestions.push(`**${SEPARATOR}bin${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}obj${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}packages${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.vs${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}TestResults${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}*.user`);\n    suggestions.push(`**${SEPARATOR}*.suo`);\n    suggestions.push(`**${SEPARATOR}.nuget${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}artifacts${SEPARATOR}**`);\n  } else if (language === javaLanguage) {\n    suggestions.push(`**${SEPARATOR}bin${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}obj${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.bin${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.obj${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}target${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.mvn${SEPARATOR}**`);\n    suggestions.push(`**${SEPARATOR}.svn${SEPARATOR}**`);\n  }\n\n  return suggestions;\n}\n\nexport async function generateConfig(\n  workDir: string,\n): Promise<z.infer<typeof localConfigSchema>> {\n  const language = await select({\n    message: \"Select the language of your project\",\n    choices: [\n      { name: \"Python\", value: pythonLanguage },\n      { name: \"C#\", value: csharpLanguage },\n      { name: \"C\", value: cLanguage },\n      { name: \"Java\", value: javaLanguage },\n    ],\n  });\n\n  let pythonConfig: z.infer<typeof localConfigSchema>[\"python\"] | undefined =\n    undefined;\n  if (language === pythonLanguage) {\n    const supportedVersions = Object.keys(pythonStdlibList);\n    const pythonVersion = await search<string>({\n      message: \"Enter or search for your Python version (e.g., 3.9)\",\n      source: (term) => {\n        if (!term) return supportedVersions;\n        return supportedVersions.filter((version) => version.includes(term));\n      },\n    });\n\n    if (pythonVersion) {\n      pythonConfig = {\n        version: pythonVersion,\n      };\n    }\n  }\n\n  let cConfig: z.infer<typeof localConfigSchema>[\"c\"] | undefined = undefined;\n  if (language === cLanguage) {\n    const hasIncludeDirs = await confirm({\n      message: \"Does your project have include directories for headers?\",\n    });\n    if (hasIncludeDirs) {\n      const includeDirsInput = await input({\n        message: \"Enter the include directories, separated by commas:\",\n        validate: (value) => {\n          if (!value.trim()) return \"Include directories cannot be empty\";\n          return true;\n        },\n      });\n\n      const includeDirs = includeDirsInput.split(\",\").map((dir) => dir.trim());\n\n      if (includeDirs.length > 0) {\n        cConfig = {\n          includedirs: includeDirs,\n        };\n      }\n    }\n  }\n\n  const outDir = await input({\n    message: \"Enter the output directory for NanoAPI artifacts\",\n    default: \"napi_out\",\n    validate: (value) => {\n      if (!value.trim()) return \"Output directory cannot be empty\";\n\n      try {\n        const normalizedPath = normalize(join(workDir, value));\n\n        if (!normalizedPath.startsWith(normalize(workDir))) {\n          return \"Output directory must be within the project directory\";\n        }\n\n        try {\n          const stat = Deno.statSync(normalizedPath);\n          if (stat && !stat.isDirectory) {\n            return \"A file with this name already exists. Please choose a different name\";\n          }\n        } catch (_error) {\n          // Path doesn't exist yet, which is fine\n        }\n\n        return true;\n      } catch (error) {\n        if (error instanceof Error) {\n          return `Invalid directory name: ${error.message}`;\n        }\n        return \"Invalid directory name\";\n      }\n    },\n  });\n\n  console.info(\"\\n🔍 ANALYZING PROJECT STRUCTURE...\");\n\n  const includePatterns = await collectIncludePatterns(workDir, language);\n\n  const excludePatterns = await collectExcludePatterns(\n    workDir,\n    includePatterns,\n    language,\n    outDir,\n  );\n\n  showFinalFileSelection(workDir, includePatterns, excludePatterns);\n\n  console.info(\"\\n🏷️  LABELING CONFIGURATION\");\n  console.info(\n    \"Labeling helps categorize and organize your code dependencies using AI models.\",\n  );\n\n  const enableLabeling = await confirm({\n    message: \"Would you like to enable AI-powered labeling?\",\n    default: false,\n  });\n\n  let labelingConfig:\n    | z.infer<typeof localConfigSchema>[\"labeling\"]\n    | undefined = undefined;\n\n  if (enableLabeling) {\n    console.info(\"\\n🤖 AI MODEL SELECTION\");\n    console.info(\n      \"Choose an AI provider for labeling your dependencies:\",\n    );\n\n    const modelProvider = await select({\n      message: \"Select AI model provider:\",\n      choices: [\n        { name: \"OpenAI (GPT-4o-mini)\", value: OPENAI_PROVIDER },\n        { name: \"Google (Gemini 2.5 Flash)\", value: GOOGLE_PROVIDER },\n        { name: \"Anthropic (Claude 3.5 Sonnet)\", value: ANTHROPIC_PROVIDER },\n      ],\n    }) as\n      | typeof OPENAI_PROVIDER\n      | typeof GOOGLE_PROVIDER\n      | typeof ANTHROPIC_PROVIDER;\n\n    const maxConcurrency = await input({\n      message: \"Enter maximum concurrent requests (leave empty for unlimited):\",\n      validate: (value) => {\n        if (!value.trim()) return true;\n        const num = parseInt(value);\n        if (isNaN(num) || num <= 0) {\n          return \"Please enter a positive number or leave empty for unlimited\";\n        }\n        return true;\n      },\n    });\n\n    labelingConfig = {\n      modelProvider,\n      maxConcurrency: maxConcurrency.trim()\n        ? parseInt(maxConcurrency)\n        : undefined,\n    };\n\n    console.info(\"✅ Labeling configuration added\");\n  }\n\n  console.info(\"\\n📊 AUDIT THRESHOLDS\");\n  console.info(\n    \"Audit thresholds flag files and symbols that exceed size or complexity limits.\",\n  );\n\n  const enableAudit = await confirm({\n    message: \"Would you like to configure custom audit thresholds?\",\n    default: false,\n  });\n\n  let auditConfig:\n    | z.infer<typeof localConfigSchema>[\"audit\"]\n    | undefined = undefined;\n\n  if (enableAudit) {\n    console.info(\n      \"\\n📄 File-level thresholds (defaults shown, press Enter to keep):\",\n    );\n\n    const fileMaxCodeLine = await input({\n      message:\n        `Max code lines per file [${defaultAuditConfig.file.maxCodeLine}]:`,\n    });\n    const fileMaxCodeChar = await input({\n      message:\n        `Max code characters per file [${defaultAuditConfig.file.maxCodeChar}]:`,\n    });\n    const fileMaxDependency = await input({\n      message:\n        `Max dependencies per file [${defaultAuditConfig.file.maxDependency}]:`,\n    });\n    const fileMaxDependent = await input({\n      message:\n        `Max dependents per file [${defaultAuditConfig.file.maxDependent}]:`,\n    });\n    const fileMaxCyclomaticComplexity = await input({\n      message:\n        `Max cyclomatic complexity per file [${defaultAuditConfig.file.maxCyclomaticComplexity}]:`,\n    });\n\n    console.info(\n      \"\\n🔤 Symbol-level thresholds (defaults shown, press Enter to keep):\",\n    );\n\n    const symbolMaxCodeLine = await input({\n      message:\n        `Max code lines per symbol [${defaultAuditConfig.symbol.maxCodeLine}]:`,\n    });\n    const symbolMaxCodeChar = await input({\n      message:\n        `Max code characters per symbol [${defaultAuditConfig.symbol.maxCodeChar}]:`,\n    });\n    const symbolMaxDependency = await input({\n      message:\n        `Max dependencies per symbol [${defaultAuditConfig.symbol.maxDependency}]:`,\n    });\n    const symbolMaxDependent = await input({\n      message:\n        `Max dependents per symbol [${defaultAuditConfig.symbol.maxDependent}]:`,\n    });\n    const symbolMaxCyclomaticComplexity = await input({\n      message:\n        `Max cyclomatic complexity per symbol [${defaultAuditConfig.symbol.maxCyclomaticComplexity}]:`,\n    });\n\n    const parseOptionalInt = (val: string): number | undefined => {\n      const trimmed = val.trim();\n      if (!trimmed) return undefined;\n      const num = parseInt(trimmed, 10);\n      return isNaN(num) ? undefined : num;\n    };\n\n    const fileOverrides = {\n      maxCodeLine: parseOptionalInt(fileMaxCodeLine),\n      maxCodeChar: parseOptionalInt(fileMaxCodeChar),\n      maxDependency: parseOptionalInt(fileMaxDependency),\n      maxDependent: parseOptionalInt(fileMaxDependent),\n      maxCyclomaticComplexity: parseOptionalInt(fileMaxCyclomaticComplexity),\n    };\n\n    const symbolOverrides = {\n      maxCodeLine: parseOptionalInt(symbolMaxCodeLine),\n      maxCodeChar: parseOptionalInt(symbolMaxCodeChar),\n      maxDependency: parseOptionalInt(symbolMaxDependency),\n      maxDependent: parseOptionalInt(symbolMaxDependent),\n      maxCyclomaticComplexity: parseOptionalInt(symbolMaxCyclomaticComplexity),\n    };\n\n    const cleanObj = (obj: Record<string, number | undefined>) => {\n      const result: Record<string, number> = {};\n      for (const [k, v] of Object.entries(obj)) {\n        if (v !== undefined) result[k] = v;\n      }\n      return Object.keys(result).length > 0 ? result : undefined;\n    };\n\n    const fileClean = cleanObj(fileOverrides);\n    const symbolClean = cleanObj(symbolOverrides);\n\n    if (fileClean || symbolClean) {\n      auditConfig = {};\n      if (fileClean) auditConfig.file = fileClean as typeof auditConfig.file;\n      if (symbolClean) {\n        auditConfig.symbol = symbolClean as typeof auditConfig.symbol;\n      }\n    }\n\n    console.info(\"✅ Audit threshold configuration added\");\n  }\n\n  const config: z.infer<typeof localConfigSchema> = {\n    language: language,\n    project: {\n      include: includePatterns,\n      exclude: excludePatterns.length > 0 ? excludePatterns : undefined,\n    },\n    outDir: outDir ? outDir : \"napi_out\",\n  };\n\n  if (pythonConfig) {\n    config.python = pythonConfig;\n  }\n\n  if (cConfig) {\n    config.c = cConfig;\n  }\n\n  if (labelingConfig) {\n    config.labeling = labelingConfig;\n  }\n\n  if (auditConfig) {\n    config.audit = auditConfig;\n  }\n\n  return config;\n}\n"
  },
  {
    "path": "src/cli/handlers/set/apiKey.ts",
    "content": "import type { Arguments } from \"yargs-types\";\nimport type { z } from \"zod\";\nimport {\n  type globalConfigSchema,\n  setConfig,\n} from \"../../middlewares/globalConfig.ts\";\nimport {\n  ANTHROPIC_PROVIDER,\n  GOOGLE_PROVIDER,\n  type ModelProvider,\n  OPENAI_PROVIDER,\n} from \"../../../manifest/dependencyManifest/labeling/model.ts\";\nimport { input, select } from \"@inquirer/prompts\";\n\nasync function handler(\n  argv: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  },\n) {\n  const globalConfig = argv.globalConfig as z.infer<typeof globalConfigSchema>;\n\n  const provider = await select({\n    message: \"Select a provider\",\n    choices: [\n      { name: \"Google\", value: GOOGLE_PROVIDER },\n      { name: \"OpenAI\", value: OPENAI_PROVIDER },\n      { name: \"Anthropic\", value: ANTHROPIC_PROVIDER },\n    ],\n  }) as ModelProvider;\n\n  const apiKey = await input({\n    message: \"Enter the API key\",\n    validate: (value) => {\n      if (value.length === 0) {\n        return \"API key cannot be empty\";\n      }\n      return true;\n    },\n  });\n\n  const labeling = globalConfig.labeling || { apiKeys: {} };\n  labeling.apiKeys[provider] = apiKey;\n  setConfig({ ...globalConfig, labeling });\n\n  console.info(\"API key set successfully\");\n}\n\nexport default {\n  command: \"apiKey\",\n  describe: \"set an API key for a model provider in your global config\",\n  builder: () => {},\n  handler,\n};\n"
  },
  {
    "path": "src/cli/handlers/set/index.ts",
    "content": "import apiKeyHandler from \"./apiKey.ts\";\nimport type { Arguments } from \"yargs-types\";\nimport type { globalConfigSchema } from \"../../middlewares/globalConfig.ts\";\nimport type { z } from \"zod\";\n\nfunction builder(\n  yargs: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  },\n) {\n  return yargs\n    .command(apiKeyHandler)\n    .demandCommand(1, \"You need to specify a valid command\");\n}\n\nexport default {\n  command: \"set\",\n  describe: \"set a value in the global config\",\n  builder,\n  handler: () => {},\n};\n"
  },
  {
    "path": "src/cli/handlers/view/index.ts",
    "content": "import type { Arguments } from \"yargs-types\";\nimport type { z } from \"zod\";\nimport type { globalConfigSchema } from \"../../middlewares/globalConfig.ts\";\nimport {\n  type localConfigSchema,\n  napiConfigMiddleware,\n} from \"../../middlewares/napiConfig.ts\";\nimport { dirname, fromFileUrl, join } from \"@std/path\";\nimport { generateAuditManifest } from \"../../../manifest/auditManifest/index.ts\";\nimport {\n  type AuditConfig,\n  defaultAuditConfig,\n} from \"../../../manifest/auditManifest/types.ts\";\nimport type { DependencyManifest } from \"../../../manifest/dependencyManifest/types.ts\";\n\nconst NAPI_DIR = \".napi\";\nconst MANIFESTS_DIR = \"manifests\";\n\ninterface ManifestEnvelope {\n  id: string;\n  branch: string;\n  commitSha: string;\n  commitShaDate: string;\n  createdAt: string;\n  manifest: DependencyManifest;\n}\n\ninterface ManifestListItem {\n  id: string;\n  branch: string;\n  commitSha: string;\n  commitShaDate: string;\n  createdAt: string;\n  fileCount: number;\n}\n\nfunction getManifestsDir(workdir: string): string {\n  return join(workdir, NAPI_DIR, MANIFESTS_DIR);\n}\n\nfunction listManifests(workdir: string): ManifestListItem[] {\n  const manifestsDir = getManifestsDir(workdir);\n  const items: ManifestListItem[] = [];\n\n  try {\n    for (const entry of Deno.readDirSync(manifestsDir)) {\n      if (!entry.isFile || !entry.name.endsWith(\".json\")) continue;\n\n      try {\n        const content = Deno.readTextFileSync(join(manifestsDir, entry.name));\n        const envelope = JSON.parse(content) as ManifestEnvelope;\n        items.push({\n          id: envelope.id,\n          branch: envelope.branch,\n          commitSha: envelope.commitSha,\n          commitShaDate: envelope.commitShaDate,\n          createdAt: envelope.createdAt,\n          fileCount: Object.keys(envelope.manifest).length,\n        });\n      } catch {\n        // Skip malformed manifest files\n      }\n    }\n  } catch {\n    // Directory doesn't exist yet\n  }\n\n  items.sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n  return items;\n}\n\nfunction loadManifest(\n  workdir: string,\n  manifestId: string,\n): ManifestEnvelope | null {\n  const manifestPath = join(\n    getManifestsDir(workdir),\n    `${manifestId}.json`,\n  );\n  try {\n    const content = Deno.readTextFileSync(manifestPath);\n    return JSON.parse(content) as ManifestEnvelope;\n  } catch {\n    return null;\n  }\n}\n\nfunction getViewerDistDir(): string {\n  const thisDir = dirname(fromFileUrl(import.meta.url));\n  return join(thisDir, \"..\", \"..\", \"..\", \"..\", \"viewer\", \"dist\");\n}\n\nfunction getContentType(path: string): string {\n  if (path.endsWith(\".html\")) return \"text/html; charset=utf-8\";\n  if (path.endsWith(\".js\")) return \"application/javascript; charset=utf-8\";\n  if (path.endsWith(\".css\")) return \"text/css; charset=utf-8\";\n  if (path.endsWith(\".json\")) return \"application/json; charset=utf-8\";\n  if (path.endsWith(\".svg\")) return \"image/svg+xml\";\n  if (path.endsWith(\".png\")) return \"image/png\";\n  if (path.endsWith(\".ico\")) return \"image/x-icon\";\n  if (path.endsWith(\".woff2\")) return \"font/woff2\";\n  if (path.endsWith(\".woff\")) return \"font/woff\";\n  return \"application/octet-stream\";\n}\n\nasync function tryServeStatic(\n  viewerDir: string,\n  pathname: string,\n): Promise<Response | null> {\n  let filePath = join(viewerDir, pathname);\n\n  try {\n    const stat = Deno.statSync(filePath);\n    if (stat.isDirectory) {\n      filePath = join(filePath, \"index.html\");\n    }\n  } catch {\n    // File doesn't exist\n  }\n\n  try {\n    const content = await Deno.readFile(filePath);\n    return new Response(content, {\n      headers: { \"content-type\": getContentType(filePath) },\n    });\n  } catch {\n    return null;\n  }\n}\n\nfunction jsonResponse(data: unknown, status = 200): Response {\n  return new Response(JSON.stringify(data), {\n    status,\n    headers: {\n      \"content-type\": \"application/json; charset=utf-8\",\n      \"access-control-allow-origin\": \"*\",\n    },\n  });\n}\n\nasync function openBrowser(url: string) {\n  try {\n    let cmd: string[];\n    if (Deno.build.os === \"darwin\") {\n      cmd = [\"open\", url];\n    } else if (Deno.build.os === \"windows\") {\n      cmd = [\"cmd\", \"/c\", \"start\", url];\n    } else {\n      cmd = [\"xdg-open\", url];\n    }\n    const command = new Deno.Command(cmd[0], {\n      args: cmd.slice(1),\n      stdout: \"null\",\n      stderr: \"null\",\n    });\n    const child = command.spawn();\n    await child.status;\n  } catch {\n    // Silently fail if browser can't be opened\n  }\n}\n\nfunction findAvailablePort(startPort: number): number {\n  for (let port = startPort; port < startPort + 100; port++) {\n    try {\n      const listener = Deno.listen({ port });\n      listener.close();\n      return port;\n    } catch {\n      continue;\n    }\n  }\n  throw new Error(\"No available port found\");\n}\n\nfunction mergeAuditConfig(\n  userAudit?: z.infer<typeof localConfigSchema>[\"audit\"],\n): AuditConfig {\n  return {\n    file: {\n      ...defaultAuditConfig.file,\n      ...userAudit?.file,\n    },\n    symbol: {\n      ...defaultAuditConfig.symbol,\n      ...userAudit?.symbol,\n    },\n  };\n}\n\nfunction builder(\n  yargs: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n  },\n) {\n  return yargs\n    .middleware(napiConfigMiddleware)\n    .option(\"port\", {\n      type: \"number\",\n      description: \"Port to serve the viewer on\",\n      default: 3000,\n    });\n}\n\nasync function handler(\n  argv: Arguments & {\n    globalConfig: z.infer<typeof globalConfigSchema>;\n    napiConfig: z.infer<typeof localConfigSchema>;\n  } & {\n    port: number;\n  },\n) {\n  const workdir = argv.workdir as string;\n  const viewerDir = getViewerDistDir();\n  const auditConfig = mergeAuditConfig(argv.napiConfig?.audit);\n\n  const port = findAvailablePort(argv.port);\n\n  const handler = async (request: Request): Promise<Response> => {\n    const url = new URL(request.url);\n    const pathname = url.pathname;\n\n    if (pathname === \"/api/manifests\" && request.method === \"GET\") {\n      const manifests = listManifests(workdir);\n      return jsonResponse(manifests);\n    }\n\n    const manifestDetailMatch = pathname.match(\n      /^\\/api\\/manifests\\/([^/]+)$/,\n    );\n    if (manifestDetailMatch && request.method === \"GET\") {\n      const manifestId = manifestDetailMatch[1];\n      const envelope = loadManifest(workdir, manifestId);\n      if (!envelope) {\n        return jsonResponse({ error: \"Manifest not found\" }, 404);\n      }\n      return jsonResponse(envelope);\n    }\n\n    const auditMatch = pathname.match(\n      /^\\/api\\/manifests\\/([^/]+)\\/audit$/,\n    );\n    if (auditMatch && request.method === \"GET\") {\n      const manifestId = auditMatch[1];\n      const envelope = loadManifest(workdir, manifestId);\n      if (!envelope) {\n        return jsonResponse({ error: \"Manifest not found\" }, 404);\n      }\n      const auditManifest = generateAuditManifest(\n        envelope.manifest,\n        auditConfig,\n      );\n      return jsonResponse(auditManifest);\n    }\n\n    // Serve static files from viewer dist\n    const staticResponse = await tryServeStatic(viewerDir, pathname);\n    if (staticResponse) return staticResponse;\n\n    // SPA fallback: serve index.html for any unmatched route\n    const indexResponse = await tryServeStatic(viewerDir, \"/index.html\");\n    if (indexResponse) return indexResponse;\n\n    return new Response(\"Not Found\", { status: 404 });\n  };\n\n  console.info(`🚀 Starting napi viewer...`);\n  console.info(\n    `📂 Serving manifests from: ${join(workdir, NAPI_DIR, MANIFESTS_DIR)}`,\n  );\n  console.info(`🌐 Open: http://localhost:${port}`);\n  console.info(`\\nPress Ctrl+C to stop.\\n`);\n\n  await openBrowser(`http://localhost:${port}`);\n\n  Deno.serve({ port }, handler);\n}\n\nexport default {\n  command: \"view\",\n  describe: \"open the dependency visualizer in your browser\",\n  builder,\n  handler,\n};\n"
  },
  {
    "path": "src/cli/index.ts",
    "content": "import yargs from \"yargs\";\nimport {\n  checkVersionMiddleware,\n  getCurrentVersion,\n} from \"./middlewares/checkVersion.ts\";\nimport initCommand from \"./handlers/init/index.ts\";\nimport setCommand from \"./handlers/set/index.ts\";\nimport generateCommand from \"./handlers/generate/index.ts\";\nimport viewCommand from \"./handlers/view/index.ts\";\nimport extractCommand from \"./handlers/extract/index.ts\";\nimport { globalConfigMiddleware } from \"./middlewares/globalConfig.ts\";\n\nexport const globalOptions = {\n  workdir: {\n    type: \"string\",\n    default: Deno.cwd(),\n    alias: \"wd\",\n    description: \"working directory\",\n  },\n};\n\nexport function initCli() {\n  yargs(Deno.args)\n    .scriptName(\"napi\")\n    .usage(\"Usage: $0 <command> [options]\")\n    .options(globalOptions)\n    .middleware(checkVersionMiddleware)\n    .middleware(globalConfigMiddleware)\n    .command(initCommand)\n    .command(setCommand)\n    .command(generateCommand)\n    .command(viewCommand)\n    .command(extractCommand)\n    .demandCommand(1, \"You need to specify a command\")\n    .strict()\n    .help()\n    .alias(\"help\", \"h\")\n    .version(getCurrentVersion())\n    .alias(\"version\", \"v\")\n    .epilogue(\"For more information, visit https://github.com/nanoapi-io/napi\")\n    .parse();\n}\n"
  },
  {
    "path": "src/cli/middlewares/checkVersion.ts",
    "content": "import type { Arguments } from \"yargs-types\";\nimport localPackageJson from \"../../../deno.json\" with { type: \"json\" };\n\nexport function getCurrentVersion() {\n  return localPackageJson.version;\n}\n\nexport async function checkVersionMiddleware(_args: Arguments) {\n  const currentVersion = getCurrentVersion();\n\n  try {\n    // Simple fetch with timeout to prevent blocking\n    const controller = new AbortController();\n    const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n    const response = await fetch(\n      \"https://api.github.com/repos/nanoapi-io/napi/releases/latest\",\n      {\n        signal: controller.signal,\n        headers: { \"Accept\": \"application/vnd.github.v3+json\" },\n      },\n    );\n    clearTimeout(timeoutId);\n\n    if (!response.ok) {\n      throw new Error(\n        `GitHub API returned ${response.status}: ${response.statusText}`,\n      );\n    }\n\n    const data = await response.json();\n    const latestVersion = data.tag_name.replace(/^v/, \"\");\n\n    if (currentVersion !== latestVersion) {\n      console.warn(\n        `\nYou are using version ${currentVersion}.\nThe latest version is ${latestVersion}.\nPlease update to the latest version to continue using napi.\n\nYou can update the version by running the following command:\n\n\\`\\`\\`bash\ncurl -fsSL https://raw.githubusercontent.com/nanoapi-io/napi/refs/heads/main/install_scripts/install.sh | bash\n\\`\\`\\`\n\nOr you can download and install them manually from here:\n${data.html_url}\n      `,\n      );\n      // Force the user to update to the latest version\n      Deno.exit(1);\n    }\n  } catch (err) {\n    console.warn(\n      `Skipping version check. Failed to check for updates: ${\n        err instanceof Error ? err.message : \"Unknown error\"\n      }`,\n    );\n    // Continue execution without blocking\n  }\n}\n"
  },
  {
    "path": "src/cli/middlewares/globalConfig.ts",
    "content": "import type { Arguments } from \"yargs-types\";\nimport { dirname, join } from \"@std/path\";\nimport z from \"zod\";\n\nexport const globalConfigSchema = z.object({\n  labeling: z.object({\n    apiKeys: z.object({\n      google: z.string().optional(),\n      openai: z.string().optional(),\n      anthropic: z.string().optional(),\n    }),\n  }).optional(),\n});\n\nconst defaultConfig: z.infer<typeof globalConfigSchema> = {};\n\nfunction getConfigPath() {\n  const appName = \"napi\";\n\n  if (Deno.build.os === \"windows\") {\n    // Windows: Use %APPDATA%\n    const homeDir = Deno.env.get(\"USERPROFILE\");\n    if (!homeDir) {\n      throw new Error(\"USERPROFILE environment variable not found\");\n    }\n    const appData = Deno.env.get(\"APPDATA\") ||\n      join(homeDir, \"AppData\", \"Roaming\");\n    return join(appData, appName, \"config.json\");\n  } else if (Deno.build.os === \"darwin\") {\n    // macOS: Use ~/Library/Application Support\n    const homeDir = Deno.env.get(\"HOME\");\n    if (!homeDir) {\n      throw new Error(\"HOME environment variable not found\");\n    }\n    return join(\n      homeDir,\n      \"Library\",\n      \"Application Support\",\n      appName,\n      \"config.json\",\n    );\n  } else {\n    // Linux and others: Use ~/.config\n    const homeDir = Deno.env.get(\"HOME\");\n    if (!homeDir) {\n      throw new Error(\"HOME environment variable not found\");\n    }\n    const configDir = Deno.env.get(\"XDG_CONFIG_HOME\") ||\n      join(homeDir, \".config\");\n    return join(configDir, appName, \"config.json\");\n  }\n}\n\nexport function globalConfigMiddleware(\n  args: Arguments & {\n    workdir: string;\n  },\n) {\n  const configPath = getConfigPath();\n\n  let config: z.infer<typeof globalConfigSchema> = defaultConfig;\n\n  try {\n    let exists = false;\n    try {\n      Deno.statSync(configPath);\n      exists = true;\n    } catch {\n      exists = false;\n    }\n\n    if (exists) {\n      const content = Deno.readTextFileSync(configPath);\n      const result = globalConfigSchema.safeParse(JSON.parse(content));\n      if (!result.success) {\n        // wrong config, generate a new one\n        setConfig(config);\n        args.globalConfig = config;\n        return;\n      }\n\n      // config is valid, use it\n      config = result.data;\n      args.globalConfig = config;\n      return;\n    } else {\n      // no config, generate a new one\n      setConfig(config);\n      args.globalConfig = config;\n      return;\n    }\n  } catch (_error) {\n    // failed to read or create config, generate a new one\n    config = defaultConfig;\n    setConfig(config);\n    args.globalConfig = config;\n    return;\n  }\n}\n\nexport function setConfig(\n  config: z.infer<typeof globalConfigSchema>,\n) {\n  const configPath = getConfigPath();\n  const dir = dirname(configPath);\n  let dirExists = false;\n  try {\n    Deno.statSync(dir);\n    dirExists = true;\n  } catch {\n    dirExists = false;\n  }\n  if (!dirExists) {\n    Deno.mkdirSync(dir, { recursive: true });\n  }\n  Deno.writeTextFileSync(configPath, JSON.stringify(config, null, 2));\n}\n"
  },
  {
    "path": "src/cli/middlewares/napiConfig.ts",
    "content": "import { join } from \"@std/path\";\nimport type { Arguments } from \"yargs-types\";\nimport z from \"zod\";\nimport pythonStdlibList from \"../../scripts/generate_python_stdlib_list/output.json\" with {\n  type: \"json\",\n};\nimport {\n  cLanguage,\n  csharpLanguage,\n  javaLanguage,\n  pythonLanguage,\n} from \"../../helpers/treeSitter/parsers.ts\";\nimport {\n  ANTHROPIC_PROVIDER,\n  GOOGLE_PROVIDER,\n  OPENAI_PROVIDER,\n} from \"../../manifest/dependencyManifest/labeling/model.ts\";\n\nconst pythonVersions = Object.keys(pythonStdlibList);\n\nexport const localConfigSchema = z.object({\n  language: z.enum([pythonLanguage, csharpLanguage, cLanguage, javaLanguage]),\n  [pythonLanguage]: z\n    .object({\n      version: z\n        .string()\n        .refine((val) => pythonVersions.includes(val), {\n          message: `Python version must be one of: ${\n            pythonVersions.join(\", \")\n          }`,\n        })\n        .optional(),\n    })\n    .optional(), // python specific config\n  [cLanguage]: z\n    .object({\n      includedirs: z.array(z.string()).optional(),\n    })\n    .optional(), // c specific config\n  project: z.object({\n    include: z.array(z.string()),\n    exclude: z.array(z.string()).optional(),\n  }),\n  outDir: z.string(),\n  labeling: z.object({\n    modelProvider: z.enum([\n      GOOGLE_PROVIDER,\n      OPENAI_PROVIDER,\n      ANTHROPIC_PROVIDER,\n    ]),\n    maxConcurrency: z.number().optional(),\n  }).optional(),\n  audit: z.object({\n    file: z.object({\n      maxCodeChar: z.number().optional(),\n      maxChar: z.number().optional(),\n      maxCodeLine: z.number().optional(),\n      maxLine: z.number().optional(),\n      maxDependency: z.number().optional(),\n      maxDependent: z.number().optional(),\n      maxCyclomaticComplexity: z.number().optional(),\n    }).optional(),\n    symbol: z.object({\n      maxCodeChar: z.number().optional(),\n      maxChar: z.number().optional(),\n      maxCodeLine: z.number().optional(),\n      maxLine: z.number().optional(),\n      maxDependency: z.number().optional(),\n      maxDependent: z.number().optional(),\n      maxCyclomaticComplexity: z.number().optional(),\n    }).optional(),\n  }).optional(),\n});\n\nconst napiConfigFileName = \".napirc\";\n\nexport function getConfigFromWorkDir(workdir: string) {\n  const napircPath = join(workdir, napiConfigFileName);\n\n  try {\n    Deno.statSync(napircPath);\n  } catch {\n    throw new Error(`${napiConfigFileName} not found in ${workdir}`);\n  }\n\n  const napircContent = Deno.readTextFileSync(napircPath);\n\n  const result = localConfigSchema.safeParse(JSON.parse(napircContent));\n\n  if (!result.success) {\n    throw new Error(\"Invalid NapiConfig: \" + result.error);\n  }\n\n  if (result.data) {\n    return result.data;\n  }\n}\n\nexport function createConfig(\n  napiConfig: z.infer<typeof localConfigSchema>,\n  workdir: string,\n) {\n  const napircPath = join(workdir, napiConfigFileName);\n  Deno.writeTextFileSync(napircPath, JSON.stringify(napiConfig, null, 2));\n}\n\nexport function napiConfigMiddleware(\n  args: Arguments & {\n    workdir: string;\n  },\n) {\n  try {\n    // First, check if the workdir itself exists\n    try {\n      const stat = Deno.statSync(args.workdir);\n      if (!stat.isDirectory) {\n        console.error(\"❌ Error: Specified path is not a directory\");\n        console.error(`   Path: ${args.workdir}`);\n        console.error(\"   Please specify a valid directory path.\");\n        Deno.exit(1);\n      }\n    } catch (error: unknown) {\n      if (error instanceof Deno.errors.NotFound) {\n        console.error(\"❌ Error: Directory not found\");\n        console.error(`   Path: ${args.workdir}`);\n        console.error(\n          \"   Please check that the directory exists and try again.\",\n        );\n      } else {\n        console.error(\"❌ Error: Cannot access directory\");\n        console.error(`   Path: ${args.workdir}`);\n        console.error(\n          `   Reason: ${\n            error instanceof Error ? error.message : String(error)\n          }`,\n        );\n      }\n      Deno.exit(1);\n    }\n\n    // Check if .napirc config file exists\n    let isConfigExist = false;\n    try {\n      const stat = Deno.statSync(join(args.workdir, napiConfigFileName));\n      isConfigExist = stat.isFile;\n    } catch {\n      isConfigExist = false;\n    }\n\n    if (!isConfigExist) {\n      console.error(\"❌ No .napirc configuration file found\");\n      console.error(`   Looking in: ${args.workdir}`);\n      console.error(\"   Expected file: .napirc\");\n      console.error(\"\");\n      console.error(\"💡 To get started:\");\n      console.error(\"   1. Navigate to your project directory\");\n      console.error(\"   2. Run: napi init\");\n      console.error(\"   3. Follow the interactive setup\");\n      Deno.exit(1);\n    }\n\n    // Then validate and load the config\n    try {\n      const napiConfig = getConfigFromWorkDir(args.workdir);\n      args.napiConfig = napiConfig;\n    } catch (error: unknown) {\n      console.error(\"❌ Invalid .napirc configuration file\");\n      console.error(`   File: ${join(args.workdir, \".napirc\")}`);\n      console.error(\n        `   Error: ${error instanceof Error ? error.message : String(error)}`,\n      );\n      console.error(\"\");\n      console.error(\"💡 To fix this:\");\n      console.error(\"   1. Check your .napirc file for syntax errors\");\n      console.error(\"   2. Or run 'napi init' to regenerate the configuration\");\n      Deno.exit(1);\n    }\n  } catch (error: unknown) {\n    // Catch any unexpected errors\n    console.error(\"❌ Unexpected error while loading configuration\");\n    console.error(\n      `   ${error instanceof Error ? error.message : String(error)}`,\n    );\n    console.error(\"\");\n    console.error(\"💡 If this persists, please report this issue at:\");\n    console.error(\"   https://github.com/nanoapi-io/napi/issues\");\n    Deno.exit(1);\n  }\n}\n"
  },
  {
    "path": "src/helpers/fileSystem/index.ts",
    "content": "import { globSync } from \"glob\";\nimport { dirname, join } from \"@std/path\";\nimport {\n  cLanguage,\n  csharpLanguage,\n  javaLanguage,\n  pythonLanguage,\n} from \"../treeSitter/parsers.ts\";\n\nexport function getExtensionsForLanguage(language: string) {\n  const supportedLanguages: Record<string, string[]> = {\n    [pythonLanguage]: [\"py\"],\n    [csharpLanguage]: [\"cs\", \"csproj\"],\n    [cLanguage]: [\"c\", \"h\"],\n    [javaLanguage]: [\"java\"],\n  };\n\n  const supportedLanguage = supportedLanguages[language];\n  if (!supportedLanguage) {\n    throw new Error(`Unsupported language: ${language}`);\n  }\n\n  return supportedLanguage;\n}\n\nexport function getFilesFromDirectory(\n  dir: string,\n  options?: {\n    includes?: string[];\n    excludes?: string[];\n    extensions?: string[];\n    logMessages?: boolean;\n  },\n) {\n  const defaultOptions = {\n    includes: [\"**\"],\n    excludes: [],\n    extensions: [],\n    logMessages: false,\n  };\n  const mergedOptions = { ...defaultOptions, ...options };\n\n  if (mergedOptions.logMessages) console.info(`Getting files from ${dir}...`);\n\n  const relativeFilePaths = globSync(mergedOptions.includes, {\n    cwd: dir,\n    nodir: true,\n    ignore: mergedOptions.excludes,\n  });\n\n  if (mergedOptions.logMessages) {\n    console.info(\n      `Found a total of ${relativeFilePaths.length} files in ${dir}`,\n    );\n  }\n\n  const files = new Map<string, { path: string; content: string }>();\n\n  relativeFilePaths.forEach((relativeFilePath) => {\n    let include = true;\n    if (mergedOptions.extensions.length > 0) {\n      const fileExtension = relativeFilePath.split(\".\").pop();\n      if (!fileExtension || !mergedOptions.extensions.includes(fileExtension)) {\n        include = false;\n      }\n    }\n\n    if (include) {\n      const fullPath = join(dir, relativeFilePath);\n      const fileContent = Deno.readTextFileSync(fullPath);\n\n      // ensure the file content is in UNIX format\n      const unixPath = relativeFilePath.replace(/\\\\/g, \"/\");\n\n      files.set(unixPath, {\n        path: unixPath,\n        content: fileContent,\n      });\n    } else {\n      if (mergedOptions.logMessages) {\n        console.info(`❌ Not including ${relativeFilePath} (not supported)`);\n      }\n    }\n  });\n\n  if (mergedOptions.logMessages) {\n    console.info(`Included a total of ${files.size} files from ${dir}`);\n  }\n\n  return files;\n}\n\nexport function writeFilesToDirectory(\n  files: Map<string, { path: string; content: string }>,\n  dir: string, // Always in UNIX format\n) {\n  // As dir is always in UNIX format, we need to map it to windows format when writing files\n  if (Deno.build.os === \"windows\") {\n    dir = dir.replace(/\\//g, \"\\\\\");\n  }\n\n  // empty the directory first\n  try {\n    Deno.removeSync(dir, { recursive: true });\n  } catch {\n    // directory doesn't exist\n  }\n  Deno.mkdirSync(dir, { recursive: true });\n\n  for (let { path, content } of files.values()) {\n    if (Deno.build.os === \"windows\") {\n      // Convert path to Windows format if necessary\n      path = path.replace(/\\//g, \"\\\\\");\n    }\n    const fullPath = join(dir, path);\n    Deno.mkdirSync(dirname(fullPath), { recursive: true });\n    Deno.writeTextFileSync(fullPath, content);\n  }\n}\n"
  },
  {
    "path": "src/helpers/sourceCode/index.test.ts",
    "content": "import { describe, it } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { removeIndexesFromSourceCode } from \"./index.ts\";\n\ndescribe(\"removeIndexesFromSourceCode\", () => {\n  it(\"should return the original source code if no indexes are provided\", () => {\n    const sourceCode = \"0123456789\";\n    const result = removeIndexesFromSourceCode(sourceCode, []);\n    expect(result).toBe(sourceCode);\n  });\n\n  it(\"should remove all the text if start and end indexes include all the text\", () => {\n    const sourceCode = \"0123456789\";\n    const result = removeIndexesFromSourceCode(sourceCode, [\n      { startIndex: 0, endIndex: 10 },\n    ]);\n    expect(result).toBe(\"\");\n  });\n\n  it(\"should remove a single range\", () => {\n    const sourceCode = \"0123456789\";\n    const result = removeIndexesFromSourceCode(sourceCode, [\n      { startIndex: 1, endIndex: 9 },\n    ]);\n    expect(result).toBe(\"09\");\n  });\n\n  it(\"should remove multiple non-overlapping indexes\", () => {\n    const sourceCode = \"0123456789\";\n    const result = removeIndexesFromSourceCode(sourceCode, [\n      { startIndex: 0, endIndex: 1 }, // '0'\n      { startIndex: 4, endIndex: 6 }, // '45'\n      { startIndex: 9, endIndex: 10 }, // '9'\n    ]);\n    expect(result).toBe(\"123678\");\n  });\n\n  it(\"should merge adjacent indexes\", () => {\n    const sourceCode = \"0123456789\";\n    const result = removeIndexesFromSourceCode(sourceCode, [\n      { startIndex: 0, endIndex: 2 }, // '01'\n      { startIndex: 2, endIndex: 3 }, // '2'\n    ]);\n    expect(result).toBe(\"3456789\");\n  });\n\n  it(\"should merge overlapping indexes\", () => {\n    const sourceCode = \"0123456789\";\n    const result = removeIndexesFromSourceCode(sourceCode, [\n      { startIndex: 0, endIndex: 3 }, // '012'\n      { startIndex: 2, endIndex: 5 }, // '234'\n    ]);\n    expect(result).toBe(\"56789\");\n  });\n\n  it(\"should handle indexes provided in random order\", () => {\n    const sourceCode = \"0123456789\";\n    const result = removeIndexesFromSourceCode(sourceCode, [\n      { startIndex: 8, endIndex: 9 }, // '89'\n      { startIndex: 2, endIndex: 4 }, // '3'\n      { startIndex: 6, endIndex: 8 }, // '67'\n    ]);\n    expect(result).toBe(\"01459\");\n  });\n\n  it(\"should handle empty source code\", () => {\n    const sourceCode = \"\";\n    const result = removeIndexesFromSourceCode(sourceCode, [\n      { startIndex: 0, endIndex: 0 },\n    ]);\n    expect(result).toBe(\"\");\n  });\n});\n"
  },
  {
    "path": "src/helpers/sourceCode/index.ts",
    "content": "export function removeIndexesFromSourceCode(\n  sourceCode: string,\n  indexesToRemove: { startIndex: number; endIndex: number }[],\n): string {\n  // Sort ranges in ascending order by startIndex.\n  indexesToRemove.sort((a, b) => a.startIndex - b.startIndex);\n\n  // Merge overlapping or contiguous ranges.\n  const mergedRanges: { startIndex: number; endIndex: number }[] = [];\n  for (const range of indexesToRemove) {\n    const last = mergedRanges[mergedRanges.length - 1];\n    if (last && range.startIndex <= last.endIndex) {\n      // Merge overlapping or contiguous ranges.\n      last.endIndex = Math.max(last.endIndex, range.endIndex);\n    } else {\n      mergedRanges.push({ ...range });\n    }\n  }\n\n  // Use native string manipulation instead of Buffer\n  let result = \"\";\n  let lastIndex = 0;\n\n  // Iterate over merged ranges in ascending order.\n  mergedRanges.forEach(({ startIndex, endIndex }) => {\n    // Append content from the end of the previous range to the start of the current range.\n    result += sourceCode.substring(lastIndex, startIndex);\n    lastIndex = endIndex;\n  });\n\n  // Append any remaining content after the last range.\n  result += sourceCode.substring(lastIndex);\n\n  return result;\n}\n"
  },
  {
    "path": "src/helpers/treeSitter/parsers.ts",
    "content": "import Parser, { type Language } from \"tree-sitter\";\nimport Python from \"tree-sitter-python\";\nimport CSharp from \"tree-sitter-c-sharp\";\nimport C from \"tree-sitter-c\";\nimport Java from \"tree-sitter-java\";\n\nconst pythonParser = new Parser();\npythonParser.setLanguage(Python as Language);\nconst pythonLanguage = Python.name as \"python\";\n\nconst csharpParser = new Parser();\ncsharpParser.setLanguage(CSharp as Language);\nconst csharpLanguage = CSharp.name as \"c-sharp\";\n\nconst cParser = new Parser();\ncParser.setLanguage(C as Language);\nconst cLanguage = C.name as \"c\";\n\nconst javaParser = new Parser();\njavaParser.setLanguage(Java as Language);\nconst javaLanguage = Java.name as \"java\";\n\nexport {\n  cLanguage,\n  cParser,\n  csharpLanguage,\n  csharpParser,\n  javaLanguage,\n  javaParser,\n  pythonLanguage,\n  pythonParser,\n};\n"
  },
  {
    "path": "src/index.test.ts",
    "content": "import { describe, it } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\n\ndescribe(\"Dummy Test Suite\", () => {\n  it(\"should pass this dummy test\", () => {\n    expect(true).toBe(true);\n  });\n});\n"
  },
  {
    "path": "src/index.ts",
    "content": "import { initCli } from \"./cli/index.ts\";\n\ninitCli();\n"
  },
  {
    "path": "src/languagePlugins/c/README.md",
    "content": "# NanoAPI C Plugin\n\nThis plugin manages parsing and mapping of dependencies in C projects.\n\n**Warning :** This plugin relies on tree-sitter, which has an unreliable parser\nfor C. Not every C project is entirely compatible. Warnings may be issued where\ntree-sitter finds errors.\n\n## Class diagram\n\n```mermaid\nclassDiagram\n    class CMetricsAnalyzer {\n        +analyzeNode(node: Parser.SyntaxNode): CComplexityMetrics\n    }\n\n    class CExtractor {\n        -manifest: DependencyManifest\n        -registry: Map<string, CFile>\n        -includeResolver: CIncludeResolver\n        +extractSymbols(symbolsMap: Map<string, SymbolsToExtract>): Map<string, File>\n    }\n\n    class CSymbolRegistry {\n        -headerResolver: CHeaderResolver\n        -files: Map<string, File>\n        +getRegistry(): Map<string, CFile>\n    }\n\n    class CHeaderResolver {\n        +resolveSymbols(file: File): ExportedSymbol[]\n    }\n\n    class CIncludeResolver {\n        -symbolRegistry: Map<string, CFile>\n        -files: Map<string, File>\n        +getInclusions(): Map<string, Inclusions>\n    }\n\n    class CInvocationResolver {\n        -includeResolver: CIncludeResolver\n        +getInvocationsForSymbol(symbol: Symbol): Invocations\n        +getInvocationsForFile(filepath: string): Invocations\n    }\n\n    class CDependencyFormatter {\n        -symbolRegistry: CSymbolRegistry\n        -includeResolver: CIncludeResolver\n        -invocationResolver: CInvocationResolver\n        +formatFile(filepath: string): CDepFile\n    }\n\n    class Symbol {\n        <<abstract>>\n        -name: string\n        -declaration: ExportedSymbol\n    }\n\n    class FunctionSignature {\n        -definition: FunctionDefinition\n        -isMacro: boolean\n    }\n\n    class FunctionDefinition {\n        -signature: FunctionSignature\n        -isMacro: boolean\n    }\n\n    class DataType {\n        -typedefs: Map<string, Typedef>\n    }\n\n    class Typedef {\n        -datatype: DataType\n    }\n\n    class Variable {\n        -isMacro: boolean\n    }\n\n    class CFile {\n        -file: File\n        -symbols: Map<string, Symbol>\n        -type: CFileType\n    }\n\n    class ExportedSymbol {\n        -name: string\n        -type: SymbolType\n        -specifiers: StorageClassSpecifier[]\n        -qualifiers: TypeQualifier[]\n        -node: Parser.SyntaxNode\n        -identifierNode: Parser.SyntaxNode\n        -filepath: string\n    }\n\n    class Inclusions {\n        -filepath: string\n        -symbols: Map<string, Symbol>\n        -internal: string[]\n        -standard: Map<string, Parser.SyntaxNode>\n    }\n\n    class Invocations {\n        -resolved: Map<string, Symbol>\n        -unresolved: Set<string>\n    }\n\n    class CDependency {\n        -id: string\n        -isExternal: boolean\n        -symbols: Record<string, string>\n    }\n\n    class CDepFile {\n        -id: string\n        -filePath: string\n        -rootNode: Parser.SyntaxNode\n        -lineCount: number\n        -characterCount: number\n        -dependencies: Record<string, CDependency>\n        -symbols: Record<string, CDepSymbol>\n    }\n\n    class CDepSymbol {\n        -id: string\n        -type: CDepSymbolType\n        -lineCount: number\n        -characterCount: number\n        -node: Parser.SyntaxNode\n        -dependents: Record<string, CDependent>\n        -dependencies: Record<string, CDependency>\n    }\n\n    class CComplexityMetrics {\n        -cyclomaticComplexity: number\n        -codeLinesCount: number\n        -linesCount: number\n        -codeCharacterCount: number\n        -characterCount: number\n    }\n\n    class CodeCounts {\n        -lines: number\n        -characters: number\n    }\n\n    class CommentSpan {\n        -start: Point\n        -end: Point\n    }\n\n    %% Relationships\n    Symbol <|-- FunctionSignature\n    Symbol <|-- FunctionDefinition\n    Symbol <|-- DataType\n    Symbol <|-- Typedef\n    Symbol <|-- Variable\n    CSymbolRegistry --> CFile\n    CSymbolRegistry --> CHeaderResolver\n    CIncludeResolver --> CSymbolRegistry\n    CInvocationResolver --> CIncludeResolver\n    CDependencyFormatter --> CSymbolRegistry\n    CDependencyFormatter --> CIncludeResolver\n    CDependencyFormatter --> CInvocationResolver\n    CExtractor --> CSymbolRegistry\n    CExtractor --> CIncludeResolver\n    CExtractor --> CFile\n    CFile --> Symbol\n    Typedef --> DataType\n    DataType --> Typedef\n    Invocations --> Symbol\n    Inclusions --> Symbol\n    CDepFile --> CDependency\n    CDepFile --> CDepSymbol\n    CDepSymbol --> CDependent\n    CDepSymbol --> CDependency\n    CMetricsAnalyzer --> CComplexityMetrics\n    CMetricsAnalyzer --> CodeCounts\n    CMetricsAnalyzer --> CommentSpan\n```\n"
  },
  {
    "path": "src/languagePlugins/c/dependencyFormatting/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { cFilesFolder, getCFilesMap } from \"../testFiles/index.ts\";\nimport { CDependencyFormatter } from \"./index.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CDependencyFormatter\", () => {\n  const cFilesMap = getCFilesMap();\n  const depFormatter = new CDependencyFormatter(cFilesMap);\n  const burgersh = join(cFilesFolder, \"burgers.h\");\n  const burgersc = join(cFilesFolder, \"burgers.c\");\n  const personnelh = join(cFilesFolder, \"personnel.h\");\n  const main = join(cFilesFolder, \"main.c\");\n\n  test(\"main.c\", () => {\n    const fmain = depFormatter.formatFile(main);\n    expect(fmain).toBeDefined();\n    expect(fmain.id).toBe(main);\n    expect(fmain.dependencies[burgersh]).toBeDefined();\n    expect(fmain.dependencies[burgersc]).not.toBeDefined();\n    expect(fmain.dependencies[personnelh]).toBeDefined();\n    expect(fmain.dependencies[\"<stdio.h>\"]).toBeDefined();\n    expect(fmain.dependencies[personnelh].isExternal).toBe(false);\n    expect(fmain.dependencies[burgersh].isExternal).toBe(false);\n    expect(fmain.dependencies[\"<stdio.h>\"].isExternal).toBe(true);\n    expect(fmain.dependencies[burgersh].symbols[\"Burger\"]).toBeDefined();\n    expect(fmain.dependencies[burgersh].symbols[\"create_burger\"]).toBeDefined();\n    expect(fmain.dependencies[personnelh].symbols[\"Employee\"]).toBeDefined();\n    expect(\n      fmain.dependencies[personnelh].symbols[\"create_employee\"],\n    ).toBeDefined();\n    expect(\n      fmain.dependencies[personnelh].symbols[\"print_employee_details\"],\n    ).toBeDefined();\n    expect(fmain.symbols[\"main\"]).toBeDefined();\n    expect(fmain.symbols[\"main\"].type).toBe(\"function\");\n    expect(fmain.symbols[\"main\"].lineCount > 1).toBe(true);\n    expect(fmain.symbols[\"main\"].characterCount > 1).toBe(true);\n    expect(fmain.symbols[\"main\"].dependents).toBeDefined();\n    expect(fmain.symbols[\"main\"].dependencies).toBeDefined();\n    expect(fmain.symbols[\"main\"].dependencies[burgersh]).toBeDefined();\n    expect(fmain.symbols[\"main\"].dependencies[burgersh].isExternal).toBe(false);\n    expect(fmain.symbols[\"main\"].dependencies[burgersh].symbols[\"Burger\"]).toBe(\n      \"Burger\",\n    );\n    expect(\n      fmain.symbols[\"main\"].dependencies[burgersh].symbols[\"create_burger\"],\n    ).toBe(\"create_burger\");\n    expect(fmain.symbols[\"main\"].dependencies[personnelh]).toBeDefined();\n    expect(fmain.symbols[\"main\"].dependencies[personnelh].isExternal).toBe(\n      false,\n    );\n    expect(\n      fmain.symbols[\"main\"].dependencies[personnelh].symbols[\"Employee\"],\n    ).toBe(\"Employee\");\n    expect(\n      fmain.symbols[\"main\"].dependencies[personnelh].symbols[\"create_employee\"],\n    ).toBe(\"create_employee\");\n    expect(\n      fmain.symbols[\"main\"].dependencies[personnelh].symbols[\n        \"print_employee_details\"\n      ],\n    ).toBe(\"print_employee_details\");\n    expect(fmain.symbols[\"main\"].dependencies[\"<stdio.h>\"]).not.toBeDefined();\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/dependencyFormatting/index.ts",
    "content": "import {\n  C_DEP_FUNCTION_TYPE,\n  type CDependency,\n  type CDepFile,\n  type CDepSymbol,\n  type CDepSymbolType,\n} from \"./types.ts\";\nimport { CSymbolRegistry } from \"../symbolRegistry/index.ts\";\nimport { CIncludeResolver } from \"../includeResolver/index.ts\";\nimport { CInvocationResolver } from \"../invocationResolver/index.ts\";\nimport {\n  type CFile,\n  EnumMember,\n  type Symbol,\n} from \"../symbolRegistry/types.ts\";\nimport type { Invocations } from \"../invocationResolver/types.ts\";\nimport type Parser from \"tree-sitter\";\nimport { C_VARIABLE_TYPE, type SymbolType } from \"../headerResolver/types.ts\";\n\nexport class CDependencyFormatter {\n  symbolRegistry: CSymbolRegistry;\n  includeResolver: CIncludeResolver;\n  invocationResolver: CInvocationResolver;\n  #registry: Map<string, CFile>;\n\n  constructor(\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n    includeDirs: string[] = [],\n  ) {\n    this.symbolRegistry = new CSymbolRegistry(files);\n    this.#registry = this.symbolRegistry.getRegistry();\n    this.includeResolver = new CIncludeResolver(\n      this.symbolRegistry,\n      includeDirs,\n    );\n    this.invocationResolver = new CInvocationResolver(this.includeResolver);\n  }\n\n  #formatSymbolType(st: SymbolType): CDepSymbolType {\n    if ([\"struct\", \"enum\", \"union\", \"typedef\", \"variable\"].includes(st)) {\n      return st as CDepSymbolType;\n    }\n    if (\n      [\"function_signature\", \"function_definition\", \"macro_function\"].includes(\n        st,\n      )\n    ) {\n      return C_DEP_FUNCTION_TYPE;\n    }\n    if (st === \"macro_constant\") {\n      return C_VARIABLE_TYPE as CDepSymbolType;\n    }\n    throw new Error(`Unknown symbol type: ${st}`);\n  }\n\n  /**\n   * Formats the dependencies of a file.\n   * @param fileDependencies - The dependencies of the file.\n   * @returns A formatted record of dependencies.\n   */\n  #formatDependencies(\n    fileDependencies: Invocations,\n  ): Record<string, CDependency> {\n    const dependencies: Record<string, CDependency> = {};\n    const resolved = fileDependencies.resolved;\n    for (const [symName, symbol] of resolved) {\n      const filepath = symbol.symbol.declaration.filepath;\n      const id = symName;\n      if (!dependencies[filepath]) {\n        dependencies[filepath] = {\n          id: filepath,\n          isExternal: false,\n          symbols: {},\n        };\n      }\n      dependencies[filepath].symbols[id] = id;\n    }\n    return dependencies;\n  }\n\n  #formatStandardIncludes(stdincludes: string[]): Record<string, CDependency> {\n    const dependencies: Record<string, CDependency> = {};\n    for (const id of stdincludes) {\n      if (!dependencies[id]) {\n        dependencies[id] = {\n          id: id,\n          isExternal: true,\n          symbols: {},\n        };\n      }\n    }\n    return dependencies;\n  }\n\n  /**\n   * Formats the symbols of a file.\n   * @param fileSymbols - The symbols of the file.\n   * @returns A formatted record of symbols.\n   */\n  #formatSymbols(fileSymbols: Map<string, Symbol>): Record<string, CDepSymbol> {\n    const symbols: Record<string, CDepSymbol> = {};\n    for (const [symName, symbol] of fileSymbols) {\n      const id = symName;\n      const dependencies = this.invocationResolver.getInvocationsForSymbol(\n        symbol,\n      );\n      if (!symbols[id] && !(symbol instanceof EnumMember)) {\n        symbols[id] = {\n          id: id,\n          type: this.#formatSymbolType(symbol.declaration.type),\n          lineCount: symbol.declaration.node.endPosition.row -\n            symbol.declaration.node.startPosition.row,\n          characterCount: symbol.declaration.node.endIndex -\n            symbol.declaration.node.startIndex,\n          node: symbol.declaration.node,\n          dependents: {},\n          dependencies: this.#formatDependencies(dependencies),\n        };\n      }\n    }\n    return symbols;\n  }\n\n  formatFile(filepath: string): CDepFile {\n    const file = this.#registry.get(filepath);\n    if (!file) {\n      throw new Error(`File not found: ${filepath}`);\n    }\n    const fileSymbols = file.symbols;\n    const fileDependencies = this.invocationResolver.getInvocationsForFile(\n      filepath,\n    );\n    const includes = this.includeResolver.getInclusions().get(filepath);\n    if (!includes) {\n      throw new Error(`File not found: ${filepath}`);\n    }\n    const stdincludes = Array.from(includes.standard.keys());\n    const invokedDependencies = this.#formatDependencies(fileDependencies);\n    const stdDependencies = this.#formatStandardIncludes(stdincludes);\n    const allDependencies = {\n      ...invokedDependencies,\n      ...stdDependencies,\n    };\n    const formattedFile: CDepFile = {\n      id: filepath,\n      filePath: file.file.path,\n      rootNode: file.file.rootNode,\n      lineCount: file.file.rootNode.endPosition.row,\n      characterCount: file.file.rootNode.endIndex,\n      dependencies: allDependencies,\n      symbols: this.#formatSymbols(fileSymbols),\n    };\n    return formattedFile;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/dependencyFormatting/types.ts",
    "content": "import type {\n  C_ENUM_TYPE,\n  C_STRUCT_TYPE,\n  C_TYPEDEF_TYPE,\n  C_UNION_TYPE,\n  C_VARIABLE_TYPE,\n} from \"../headerResolver/types.ts\";\nimport type Parser from \"tree-sitter\";\n\n/**\n * Represents a dependency in a C file\n */\nexport interface CDependency {\n  id: string;\n  isExternal: boolean;\n  symbols: Record<string, string>;\n}\n\n/**\n * Represents a dependent in a C file\n */\nexport interface CDependent {\n  id: string;\n  symbols: Record<string, string>;\n}\n\nexport const C_DEP_FUNCTION_TYPE = \"function\";\nexport type CDepSymbolType =\n  | typeof C_ENUM_TYPE\n  | typeof C_UNION_TYPE\n  | typeof C_STRUCT_TYPE\n  | typeof C_TYPEDEF_TYPE\n  | typeof C_VARIABLE_TYPE\n  | typeof C_DEP_FUNCTION_TYPE;\n\n/**\n * Represents a symbol in a C file\n */\nexport interface CDepSymbol {\n  id: string;\n  type: CDepSymbolType;\n  lineCount: number;\n  characterCount: number;\n  node: Parser.SyntaxNode;\n  dependents: Record<string, CDependent>;\n  dependencies: Record<string, CDependency>;\n}\n\n/**\n * Represents a C file with its dependencies and symbols.\n */\nexport interface CDepFile {\n  id: string;\n  filePath: string;\n  rootNode: Parser.SyntaxNode;\n  lineCount: number;\n  characterCount: number;\n  dependencies: Record<string, CDependency>;\n  symbols: Record<string, CDepSymbol>;\n}\n"
  },
  {
    "path": "src/languagePlugins/c/extractor/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport {\n  cFilesFolder,\n  dummyLocalConfig,\n  getCFilesContentMap,\n} from \"../testFiles/index.ts\";\nimport { CExtractor } from \"./index.ts\";\nimport { join } from \"@std/path\";\nimport { generateCDependencyManifest } from \"../../../manifest/dependencyManifest/c/index.ts\";\n\ndescribe(\"CExtractor\", () => {\n  const cContentMap = getCFilesContentMap();\n  const manifest = generateCDependencyManifest(cContentMap, dummyLocalConfig);\n  const extractor = new CExtractor(cContentMap, manifest, dummyLocalConfig);\n  const burgers = join(cFilesFolder, \"burgers.h\");\n  const burgersc = join(cFilesFolder, \"burgers.c\");\n  const main = join(cFilesFolder, \"main.c\");\n  const all = join(cFilesFolder, \"all.h\");\n  const errorsh = join(cFilesFolder, \"errors.h\");\n  test(\"extracts create_burger\", () => {\n    const symbolsToExtract = new Map<\n      string,\n      { filePath: string; symbols: Set<string> }\n    >();\n    symbolsToExtract.set(burgers, {\n      filePath: burgers,\n      symbols: new Set([\"create_burger\"]),\n    });\n    const extractedFiles = extractor.extractSymbols(symbolsToExtract);\n    expect(extractedFiles.size).toBe(2);\n    const newManifest = generateCDependencyManifest(\n      extractedFiles,\n      dummyLocalConfig,\n    );\n    expect(newManifest[burgers]).toBeDefined();\n    expect(newManifest[burgersc]).toBeDefined();\n    // Expected symbols to be kept\n    expect(newManifest[burgers].symbols[\"create_burger\"]).toBeDefined();\n    expect(newManifest[burgersc].symbols[\"create_burger\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"Condiment\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"Burger\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"Sauce\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"burger_count\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"BURGERS_H\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"ClassicSauces\"]).toBeDefined();\n    // Expected symbols to be removed\n    expect(newManifest[burgers].symbols[\"MAX_BURGERS\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"MAX\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"Fries\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"Drink_t\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"Drink\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"classicBurger\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"destroy_burger\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"get_burger_by_id\"]).not.toBeDefined();\n    expect(newManifest[burgers].symbols[\"get_cheapest_burger\"]).not\n      .toBeDefined();\n  });\n\n  test(\"extracts Drink_t\", () => {\n    const symbolsToExtract = new Map<\n      string,\n      { filePath: string; symbols: Set<string> }\n    >();\n    symbolsToExtract.set(burgers, {\n      filePath: burgers,\n      symbols: new Set([\"Drink_t\"]),\n    });\n    const extractedFiles = extractor.extractSymbols(symbolsToExtract);\n    expect(extractedFiles.size).toBe(1);\n    const newManifest = generateCDependencyManifest(\n      extractedFiles,\n      dummyLocalConfig,\n    );\n    expect(newManifest[burgers]).toBeDefined();\n    // Expected symbols to be kept\n    expect(newManifest[burgers].symbols[\"Drink_t\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"Drink\"]).toBeDefined();\n    expect(newManifest[burgers].symbols[\"BURGERS_H\"]).toBeDefined();\n    // Expected symbols to be removed\n    expect(Object.keys(newManifest[burgers].symbols).length).toBe(3);\n  });\n\n  test(\"keeps all.h\", () => {\n    const symbolsToExtract = new Map<\n      string,\n      { filePath: string; symbols: Set<string> }\n    >();\n    symbolsToExtract.set(main, {\n      filePath: main,\n      symbols: new Set([\"main\"]),\n    });\n    const extractedFiles = extractor.extractSymbols(symbolsToExtract);\n    expect(extractedFiles.size).toBe(6);\n    const newManifest = generateCDependencyManifest(\n      extractedFiles,\n      dummyLocalConfig,\n    );\n    expect(newManifest[all]).toBeDefined();\n  });\n\n  test(\"deletes impossible include\", () => {\n    const symbolsToExtract = new Map<\n      string,\n      { filePath: string; symbols: Set<string> }\n    >();\n    symbolsToExtract.set(errorsh, {\n      filePath: errorsh,\n      symbols: new Set([\"typedef\"]),\n    });\n    const extractedFiles = extractor.extractSymbols(symbolsToExtract);\n    expect(extractedFiles.size).toBe(1);\n    expect(extractedFiles.get(errorsh)).toBeDefined();\n    expect(\n      extractedFiles.get(errorsh)?.content.includes(\n        `#include \"thisfiledoesnotexist.h\"`,\n      ),\n    ).toBe(false);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/extractor/index.ts",
    "content": "import { CSymbolRegistry } from \"../symbolRegistry/index.ts\";\nimport { CIncludeResolver } from \"../includeResolver/index.ts\";\nimport type Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport type { CFile, Symbol } from \"../symbolRegistry/types.ts\";\nimport type { ExportedFile } from \"./types.ts\";\nimport { C_DECLARATION_QUERY } from \"../headerResolver/queries.ts\";\nimport { C_IFDEF_QUERY } from \"./queries.ts\";\nimport { CInvocationResolver } from \"../invocationResolver/index.ts\";\nimport type z from \"zod\";\nimport type { localConfigSchema } from \"../../../cli/middlewares/napiConfig.ts\";\nimport { join } from \"@std/path\";\nimport type { DependencyManifest } from \"../../../manifest/dependencyManifest/types.ts\";\n\nexport class CExtractor {\n  manifest: DependencyManifest;\n  registry: Map<string, CFile>;\n  includeResolver: CIncludeResolver;\n  invocationResolver: CInvocationResolver;\n\n  constructor(\n    files: Map<string, { path: string; content: string }>,\n    manifest: DependencyManifest,\n    napiConfig: z.infer<typeof localConfigSchema>,\n  ) {\n    this.manifest = manifest;\n    const parsedFiles = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    for (const [filePath, file] of files) {\n      parsedFiles.set(filePath, {\n        path: file.path,\n        rootNode: cParser.parse(file.content).rootNode,\n      });\n    }\n    const symbolRegistry = new CSymbolRegistry(parsedFiles);\n    this.registry = symbolRegistry.getRegistry();\n    const outDir = napiConfig.outDir;\n    const includeDirs = napiConfig[\"c\"]?.includedirs ?? [];\n    if (outDir) {\n      includeDirs.push(\n        ...includeDirs.map((i) => join(outDir, i).replace(/\\\\/g, \"/\")),\n      );\n    }\n    this.includeResolver = new CIncludeResolver(symbolRegistry, includeDirs);\n    this.invocationResolver = new CInvocationResolver(this.includeResolver);\n  }\n\n  /**\n   * Finds the first-level dependencies of a symbol in the manifest.\n   * @param symbol - The symbol to find dependencies for.\n   * @returns An array of symbols that are dependencies of the given symbol.\n   */\n  #findDependencies(symbol: Symbol): Symbol[] {\n    const dependencies: Symbol[] = [symbol];\n    const symbolDependencies = this.manifest[symbol.declaration.filepath]\n      ?.symbols[symbol.name].dependencies;\n    for (\n      const [filepath, dependencyinfo] of Object.entries(symbolDependencies)\n    ) {\n      const dependencyFile = this.registry.get(filepath);\n      if (dependencyFile) {\n        for (const symbolName of Object.keys(dependencyinfo.symbols)) {\n          const dependencySymbol = dependencyFile.symbols.get(symbolName);\n          if (dependencySymbol) {\n            dependencies.push(dependencySymbol);\n          }\n        }\n      }\n    }\n    return dependencies;\n  }\n\n  /**\n   * Finds all dependencies of a given symbol.\n   * @param symbol - The symbol for which to find dependencies.\n   * @returns An array of symbols.\n   */\n  #findAllDependencies(symbol: Symbol): Symbol[] {\n    const dependencies: Symbol[] = [];\n    const visited = new Set<Symbol>();\n    const stack = [symbol];\n\n    while (stack.length > 0) {\n      const currentSymbol = stack.pop()!;\n      if (visited.has(currentSymbol)) {\n        continue;\n      }\n      visited.add(currentSymbol);\n      dependencies.push(currentSymbol);\n      const currentDependencies = this.#findDependencies(currentSymbol);\n      stack.push(...currentDependencies);\n    }\n    return dependencies;\n  }\n\n  /**\n   * Build a map of files and their symbols to keep.\n   * @param symbolsToKeep - The symbols to keep.\n   * @returns A map of file paths to their corresponding ExportedFile objects.\n   */\n  #buildFileMap(symbolsToKeep: Symbol[]): Map<string, ExportedFile> {\n    const exportedFiles = new Map<string, ExportedFile>();\n    for (const symbol of symbolsToKeep) {\n      const filepath = symbol.declaration.filepath;\n      if (!exportedFiles.has(filepath)) {\n        const fileInRegistry = this.registry.get(filepath);\n        if (!fileInRegistry) {\n          throw new Error(`File not found: ${filepath}`);\n        }\n        const originalFile = fileInRegistry.file;\n        // Ifdefs are instances of things that aren't symbols yet invoke a symbol\n        const ifdefs = C_IFDEF_QUERY.captures(originalFile.rootNode).map((n) =>\n          n.node.text\n        );\n        const definesToKeep = ifdefs.map((i) => fileInRegistry.symbols.get(i)!)\n          .filter((i) => i);\n        const symbols = new Map<string, Symbol>();\n        for (const define of definesToKeep) {\n          symbols.set(define.name, define);\n        }\n        exportedFiles.set(filepath, {\n          symbols,\n          originalFile,\n          strippedFile: originalFile,\n        });\n      }\n      const exportedFile = exportedFiles.get(filepath)!;\n      const symbolName = symbol.name;\n      if (!exportedFile.symbols.has(symbolName)) {\n        exportedFile.symbols.set(symbolName, symbol);\n      }\n      // Keep the files that recursively lead to a symbol we need\n      const invocations = this.invocationResolver.getInvocationsForSymbol(\n        symbol,\n      );\n      const filestokeep = Array.from(\n        invocations.resolved.values().map((s) =>\n          this.includeResolver.findInclusionChain(\n            symbol.declaration.filepath,\n            s.symbol,\n          )\n        ).filter((c) => c !== undefined),\n      ).flatMap((c) => c.flatMap((f) => this.registry.get(f)!));\n      for (const f of filestokeep) {\n        if (!exportedFiles.has(f.file.path)) {\n          const ifdefs = C_IFDEF_QUERY.captures(f.file.rootNode).map((n) =>\n            n.node.text\n          );\n          const definesToKeep = ifdefs.map((i) => f.symbols.get(i)!)\n            .filter((i) => i);\n          const symbols = new Map<string, Symbol>();\n          for (const define of definesToKeep) {\n            symbols.set(define.name, define);\n          }\n          exportedFiles.set(f.file.path, {\n            symbols,\n            originalFile: f.file,\n            strippedFile: f.file,\n          });\n        }\n      }\n    }\n    return exportedFiles;\n  }\n\n  /**\n   * Edits the files to include only the symbols that are needed.\n   * @param files - The files to edit.\n   */\n  #stripFiles(files: Map<string, ExportedFile>) {\n    for (const [, file] of files) {\n      const rootNode = file.originalFile.rootNode;\n      const originalText = rootNode.text; // Original file content\n      const symbolsToKeep = new Set(\n        file.symbols.values().map((s) => s.declaration.node),\n      );\n      const symbolsToRemove = new Set<Parser.SyntaxNode>();\n      const matches = C_DECLARATION_QUERY.captures(rootNode);\n\n      for (const match of matches) {\n        const symbolNode = match.node;\n        if (!symbolsToKeep.has(symbolNode)) {\n          symbolsToRemove.add(symbolNode);\n        }\n      }\n\n      // Helper function to recursively filter nodes\n      const filterNodes = (node: Parser.SyntaxNode): string => {\n        if (symbolsToRemove.has(node)) {\n          return \"\"; // Skip this node\n        }\n\n        // If the node has children, process them recursively\n        if ([\"translation_unit\", \"preproc_ifdef\"].includes(node.type)) {\n          let result = \"\";\n          let lastEndIndex = node.startIndex;\n\n          for (const child of node.children) {\n            // Append the text between the last node and the current child\n            result += originalText.slice(lastEndIndex, child.startIndex);\n            result += filterNodes(child); // Process the child\n            lastEndIndex = child.endIndex;\n          }\n\n          // Append the text after the last child\n          result += originalText.slice(lastEndIndex, node.endIndex);\n          return result;\n        }\n\n        // If the node has no children, return its text\n        return originalText.slice(node.startIndex, node.endIndex);\n      };\n\n      // Rebuild the file content by filtering nodes\n      const newFileContent = filterNodes(rootNode);\n\n      // Compactify the file content\n      const compactedContent = this.#compactifyFile(newFileContent);\n\n      // Parse the new content and update the stripped file\n      const strippedFile = cParser.parse(compactedContent);\n      file.strippedFile = {\n        path: file.originalFile.path,\n        rootNode: strippedFile.rootNode,\n      };\n    }\n  }\n\n  #removeDeletedIncludes(\n    files: Map<string, ExportedFile>,\n  ) {\n    const newproject: Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    > = new Map();\n    for (const [key, value] of files) {\n      newproject.set(key, value.strippedFile);\n    }\n    const newregistry = new CSymbolRegistry(newproject);\n    const newincluderes = new CIncludeResolver(\n      newregistry,\n      this.includeResolver.includeDirs,\n    );\n    newincluderes.getInclusions();\n    for (const [key, value] of files) {\n      const unresolved = newincluderes.unresolvedDirectives.get(key);\n      if (unresolved) {\n        let filetext = value.strippedFile.rootNode.text;\n        for (const path of unresolved) {\n          filetext = filetext.replace(`#include \"${path}\"`, \"\");\n        }\n        filetext = this.#compactifyFile(filetext);\n        value.strippedFile.rootNode = cParser.parse(filetext).rootNode;\n      }\n    }\n  }\n\n  #compactifyFile(\n    filetext: string,\n  ): string {\n    // Remove empty lines and useless semicolons\n    filetext = filetext.replace(/^\\s*;\\s*$/gm, \"\"); // Remove empty lines with semicolons\n    filetext = filetext.replace(/^\\s*[\\r\\n]+/gm, \"\\n\"); // Remove empty lines\n    return filetext;\n  }\n\n  /**\n   * Finds the dependencies for a map of symbols.\n   * @param symbolsMap - A map of file paths to their corresponding symbols.\n   * @returns A set of symbols that are dependencies of the given symbols.\n   */\n  #findDependenciesForMap(\n    symbolsMap: Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >,\n  ): Symbol[] {\n    const symbolsToExtract: Symbol[] = [];\n    for (const [filePath, symbolInfo] of symbolsMap) {\n      const file = this.registry.get(filePath);\n      if (!file) {\n        throw new Error(`File not found: ${filePath}`);\n      }\n      const symbols = Array.from(symbolInfo.symbols);\n      for (const symbolName of symbols) {\n        const symbol = file.symbols.get(symbolName);\n        if (symbol) {\n          const dependencies = this.#findAllDependencies(symbol);\n          symbolsToExtract.push(...dependencies);\n        }\n      }\n    }\n    // Remove duplicates\n    return Array.from(new Set(symbolsToExtract));\n  }\n\n  extractSymbols(\n    symbolsMap: Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >,\n  ): Map<string, { path: string; content: string }> {\n    const symbolsToExtract = this.#findDependenciesForMap(symbolsMap);\n    const filesToExport = this.#buildFileMap(symbolsToExtract);\n    this.#stripFiles(filesToExport);\n    this.#removeDeletedIncludes(filesToExport);\n    const exportedFiles = new Map<string, { path: string; content: string }>();\n    for (const [filePath, file] of filesToExport) {\n      const content = file.strippedFile.rootNode.text;\n      exportedFiles.set(filePath, {\n        path: filePath,\n        content,\n      });\n    }\n    return exportedFiles;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/extractor/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\nexport const C_IFDEF_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `(preproc_ifdef\n  name: (identifier) @def)`,\n);\n"
  },
  {
    "path": "src/languagePlugins/c/extractor/types.ts",
    "content": "import type { Symbol } from \"../symbolRegistry/types.ts\";\nimport type Parser from \"tree-sitter\";\n\nexport interface ExportedFile {\n  symbols: Map<string, Symbol>;\n  originalFile: {\n    path: string;\n    rootNode: Parser.SyntaxNode;\n  };\n  strippedFile: {\n    path: string;\n    rootNode: Parser.SyntaxNode;\n  };\n}\n"
  },
  {
    "path": "src/languagePlugins/c/headerResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { cFilesFolder, getCFilesMap } from \"../testFiles/index.ts\";\nimport { CHeaderResolver } from \"./index.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CHeaderResolver\", () => {\n  const cFilesMap = getCFilesMap();\n  const resolver = new CHeaderResolver();\n  const burgers = join(cFilesFolder, \"burgers.h\");\n  const crashcases = join(cFilesFolder, \"crashcases.h\");\n  const errorsh = join(cFilesFolder, \"errors.h\");\n  const oldmanh = join(cFilesFolder, \"oldman.h\");\n  const file = cFilesMap.get(burgers);\n  if (!file) {\n    throw new Error(`File not found: ${burgers}`);\n  }\n  const ccfile = cFilesMap.get(crashcases);\n  if (!ccfile) {\n    throw new Error(`File not found: ${crashcases}`);\n  }\n  const errorsfile = cFilesMap.get(errorsh);\n  if (!errorsfile) {\n    throw new Error(`File not found: ${errorsh}`);\n  }\n  const exportedSymbols = resolver.resolveSymbols(file);\n\n  test(\"should resolve symbols in C header files\", () => {\n    expect(exportedSymbols).toHaveLength(16);\n  });\n\n  test(\"resolves structs\", () => {\n    const burger = exportedSymbols.find((symbol) => symbol.name === \"Burger\");\n    expect(burger).toBeDefined();\n    if (!burger) {\n      throw new Error(\"burger is undefined\");\n    }\n    expect(burger.type).toBe(\"struct\");\n    expect(burger.specifiers).toEqual([]);\n    expect(burger.qualifiers).toEqual([]);\n    expect(burger.node.type).toBe(\"struct_specifier\");\n    if (!burger.identifierNode) {\n      throw new Error(\"burger.identifierNode is undefined\");\n    }\n    expect(burger.identifierNode.type).toBe(\"type_identifier\");\n    expect(burger.filepath).toBe(burgers);\n  });\n\n  test(\"resolves unions\", () => {\n    const sauce = exportedSymbols.find((symbol) => symbol.name === \"Sauce\");\n    expect(sauce).toBeDefined();\n    if (!sauce) {\n      throw new Error(\"sauce is undefined\");\n    }\n    expect(sauce.type).toBe(\"union\");\n    expect(sauce.specifiers).toEqual([]);\n    expect(sauce.qualifiers).toEqual([]);\n    expect(sauce.node.type).toBe(\"union_specifier\");\n    if (!sauce.identifierNode) {\n      throw new Error(\"sauce.identifierNode is undefined\");\n    }\n    expect(sauce.identifierNode.type).toBe(\"type_identifier\");\n    expect(sauce.filepath).toBe(burgers);\n  });\n\n  test(\"resolves enums\", () => {\n    const condiment = exportedSymbols.find(\n      (symbol) => symbol.name === \"Condiment\",\n    );\n    expect(condiment).toBeDefined();\n    if (!condiment) {\n      throw new Error(\"condiment is undefined\");\n    }\n    expect(condiment.type).toBe(\"enum\");\n    expect(condiment.specifiers).toEqual([]);\n    expect(condiment.qualifiers).toEqual([]);\n    expect(condiment.node.type).toBe(\"enum_specifier\");\n    if (!condiment.identifierNode) {\n      throw new Error(\"condiment.identifierNode is undefined\");\n    }\n    expect(condiment.identifierNode.type).toBe(\"type_identifier\");\n    expect(condiment.filepath).toBe(burgers);\n\n    const classicsauces = exportedSymbols.find(\n      (symbol) => symbol.name === \"ClassicSauces\",\n    );\n    expect(classicsauces).toBeDefined();\n    if (!classicsauces) {\n      throw new Error(\"classicsauces is undefined\");\n    }\n    expect(classicsauces.type).toBe(\"enum\");\n\n    const drink_t = exportedSymbols.find((symbol) => symbol.name === \"Drink_t\");\n    expect(drink_t).toBeDefined();\n    if (!drink_t) {\n      throw new Error(\"drink_t is undefined\");\n    }\n    expect(drink_t.type).toBe(\"enum\");\n    expect(drink_t.specifiers).toEqual([]);\n    expect(drink_t.qualifiers).toEqual([]);\n    expect(drink_t.node.type).toBe(\"enum_specifier\");\n    if (!drink_t.identifierNode) {\n      throw new Error(\"drink_t.identifierNode is undefined\");\n    }\n    expect(drink_t.identifierNode.type).toBe(\"type_identifier\");\n    expect(drink_t.filepath).toBe(burgers);\n  });\n\n  test(\"resolves variables\", () => {\n    const classicburger = exportedSymbols.find(\n      (symbol) => symbol.name === \"classicBurger\",\n    );\n    expect(classicburger).toBeDefined();\n    if (!classicburger) {\n      throw new Error(\"classicburger is undefined\");\n    }\n    expect(classicburger.type).toBe(\"variable\");\n    expect(classicburger.specifiers).toEqual([]);\n    expect(classicburger.qualifiers).toEqual([\"const\"]);\n    expect(classicburger.node.type).toBe(\"declaration\");\n    if (!classicburger.identifierNode) {\n      throw new Error(\"classicburger.identifierNode is undefined\");\n    }\n    expect(classicburger.identifierNode.type).toBe(\"identifier\");\n    expect(classicburger.filepath).toBe(burgers);\n\n    const burger_count = exportedSymbols.find(\n      (symbol) => symbol.name === \"burger_count\",\n    );\n    expect(burger_count).toBeDefined();\n    if (!burger_count) {\n      throw new Error(\"burger_count is undefined\");\n    }\n    expect(burger_count.type).toBe(\"variable\");\n    expect(burger_count.specifiers).toEqual([\"static\"]);\n    expect(burger_count.qualifiers).toEqual([]);\n    expect(burger_count.node.type).toBe(\"declaration\");\n    if (!burger_count.identifierNode) {\n      throw new Error(\"burger_count.identifierNode is undefined\");\n    }\n    expect(burger_count.identifierNode.type).toBe(\"identifier\");\n    expect(burger_count.filepath).toBe(burgers);\n  });\n\n  test(\"resolves macro constants\", () => {\n    const burgers_h = exportedSymbols.find(\n      (symbol) => symbol.name === \"BURGERS_H\",\n    );\n    expect(burgers_h).toBeDefined();\n    if (!burgers_h) {\n      throw new Error(\"burgers_h is undefined\");\n    }\n    expect(burgers_h.type).toBe(\"macro_constant\");\n    expect(burgers_h.specifiers).toEqual([]);\n    expect(burgers_h.qualifiers).toEqual([]);\n    expect(burgers_h.node.type).toBe(\"preproc_def\");\n    if (!burgers_h.identifierNode) {\n      throw new Error(\"burgers_h.identifierNode is undefined\");\n    }\n    expect(burgers_h.identifierNode.type).toBe(\"identifier\");\n    expect(burgers_h.filepath).toBe(burgers);\n\n    const max_burgers = exportedSymbols.find(\n      (symbol) => symbol.name === \"MAX_BURGERS\",\n    );\n    expect(max_burgers).toBeDefined();\n    if (!max_burgers) {\n      throw new Error(\"max_burgers is undefined\");\n    }\n    expect(max_burgers.type).toBe(\"macro_constant\");\n    expect(max_burgers.specifiers).toEqual([]);\n    expect(max_burgers.qualifiers).toEqual([]);\n    expect(max_burgers.node.type).toBe(\"preproc_def\");\n    if (!max_burgers.identifierNode) {\n      throw new Error(\"max_burgers.identifierNode is undefined\");\n    }\n    expect(max_burgers.identifierNode.type).toBe(\"identifier\");\n    expect(max_burgers.filepath).toBe(burgers);\n  });\n\n  test(\"resolves function signatures\", () => {\n    const function_names = exportedSymbols\n      .filter((symbol) => symbol.type === \"function_signature\")\n      .map((symbol) => symbol.name);\n    expect(function_names).toHaveLength(4);\n    expect(function_names).toContain(\"get_burger_by_id\");\n    expect(function_names).toContain(\"get_cheapest_burger\");\n    expect(function_names).toContain(\"create_burger\");\n    expect(function_names).toContain(\"destroy_burger\");\n    expect(function_names).not.toContain(\"MAX\");\n  });\n\n  test(\"resolves macro functions\", () => {\n    const max_macro = exportedSymbols.find((symbol) => symbol.name === \"MAX\");\n    expect(max_macro).toBeDefined();\n    if (!max_macro) {\n      throw new Error(\"max_macro is undefined\");\n    }\n    expect(max_macro.type).toBe(\"macro_function\");\n    expect(max_macro.specifiers).toEqual([]);\n    expect(max_macro.qualifiers).toEqual([]);\n    expect(max_macro.node.type).toBe(\"preproc_function_def\");\n    if (!max_macro.identifierNode) {\n      throw new Error(\"max_macro.identifierNode is undefined\");\n    }\n    expect(max_macro.identifierNode.type).toBe(\"identifier\");\n    expect(max_macro.filepath).toBe(burgers);\n  });\n\n  test(\"resolves typedefs\", () => {\n    const fries = exportedSymbols.find((symbol) => symbol.name === \"Fries\");\n    expect(fries).toBeDefined();\n    if (!fries) {\n      throw new Error(\"fries is undefined\");\n    }\n    expect(fries.type).toBe(\"typedef\");\n    expect(fries.specifiers).toEqual([]);\n    expect(fries.qualifiers).toEqual([]);\n    expect(fries.node.type).toBe(\"type_definition\");\n    if (!fries.identifierNode) {\n      throw new Error(\"fries.identifierNode is undefined\");\n    }\n    expect(fries.identifierNode.type).toBe(\"type_identifier\");\n    expect(fries.filepath).toBe(burgers);\n\n    const drink = exportedSymbols.find((symbol) => symbol.name === \"Drink\");\n    expect(drink).toBeDefined();\n    if (!drink) {\n      throw new Error(\"drink is undefined\");\n    }\n    expect(drink.type).toBe(\"typedef\");\n    expect(drink.specifiers).toEqual([]);\n    expect(drink.qualifiers).toEqual([]);\n    expect(drink.node.type).toBe(\"type_definition\");\n    if (!drink.identifierNode) {\n      throw new Error(\"drink.identifierNode is undefined\");\n    }\n    expect(drink.identifierNode.type).toBe(\"type_identifier\");\n    expect(drink.filepath).toBe(burgers);\n  });\n\n  test(\"Crash Cases\", () => {\n    const ccexportedSymbols = resolver.resolveSymbols(ccfile);\n    expect(ccexportedSymbols).toBeDefined();\n\n    const sprite = ccexportedSymbols.find((s) => s.name === \"Sprite\");\n    expect(sprite).toBeDefined();\n    if (!sprite) {\n      throw new Error(\"sprite is undefined\");\n    }\n    expect(sprite.type).toBe(\"struct\");\n    expect(sprite.specifiers).toEqual([]);\n    expect(sprite.qualifiers).toEqual([]);\n    expect(sprite.node.type).toBe(\"struct_specifier\");\n    if (!sprite.identifierNode) {\n      throw new Error(\"sprite.identifierNode is undefined\");\n    }\n    expect(sprite.identifierNode.type).toBe(\"type_identifier\");\n    expect(sprite.filepath).toBe(crashcases);\n\n    const placeholderfunction = ccexportedSymbols.find(\n      (s) => s.name === \"PlaceholderFunction\",\n    );\n    expect(placeholderfunction).toBeDefined();\n    if (!placeholderfunction) {\n      throw new Error(\"placeholderfunction is undefined\");\n    }\n    expect(placeholderfunction.type).toBe(\"function_signature\");\n    expect(placeholderfunction.specifiers).toEqual([]);\n    expect(placeholderfunction.qualifiers).toEqual([]);\n    expect(placeholderfunction.node.type).toBe(\"declaration\");\n    if (!placeholderfunction.identifierNode) {\n      throw new Error(\"placeholderfunction.identifierNode is undefined\");\n    }\n    expect(placeholderfunction.identifierNode.type).toBe(\"identifier\");\n    expect(placeholderfunction.filepath).toBe(crashcases);\n\n    const gmtfwa = ccexportedSymbols.find(\n      (s) => s.name === \"gMovementTypeFuncs_WanderAround\",\n    );\n    expect(gmtfwa).toBeDefined();\n    if (!gmtfwa) {\n      throw new Error(\"gmtfwa is undefined\");\n    }\n    expect(gmtfwa.type).toBe(\"variable\");\n    expect(gmtfwa.specifiers).toEqual([]);\n    expect(gmtfwa.qualifiers).toEqual([]);\n    expect(gmtfwa.node.type).toBe(\"declaration\");\n    if (!gmtfwa.identifierNode) {\n      throw new Error(\"gmtfwa.identifierNode is undefined\");\n    }\n    expect(gmtfwa.identifierNode.type).toBe(\"identifier\");\n    expect(gmtfwa.filepath).toBe(crashcases);\n  });\n\n  test(\"resolves unnamed types\", () => {\n    const exportedErrorSymbols = resolver.resolveSymbols(errorsfile);\n    expect(exportedErrorSymbols).toBeDefined();\n    const symbolNames = exportedErrorSymbols.map((symbol) => symbol.name);\n    expect(symbolNames).toContain(\"#NAPI_UNNAMED_ENUM_0\");\n  });\n\n  test(\"resolves typedefs with same name as associated struct\", () => {\n    const oldmanSymbols = resolver.resolveSymbols(\n      cFilesMap.get(oldmanh)!,\n    );\n    expect(oldmanSymbols).toBeDefined();\n    const oldmen = oldmanSymbols.filter((s) => s.name === \"OldMan\");\n    expect(oldmen).toHaveLength(2);\n    const oldman = oldmen.find((s) => s.type === \"typedef\");\n    expect(oldman).toBeDefined();\n    if (!oldman) {\n      throw new Error(\"OldMan is undefined\");\n    }\n    expect(oldman.type).toBe(\"typedef\");\n    expect(oldman.specifiers).toEqual([]);\n    expect(oldman.qualifiers).toEqual([]);\n    expect(oldman.node.type).toBe(\"type_definition\");\n    if (!oldman.identifierNode) {\n      throw new Error(\"oldman.identifierNode is undefined\");\n    }\n    expect(oldman.identifierNode.type).toBe(\"type_identifier\");\n    expect(oldman.filepath).toBe(oldmanh);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/headerResolver/index.ts",
    "content": "import type {\n  ExportedSymbol,\n  StorageClassSpecifier,\n  SymbolType,\n  TypeQualifier,\n} from \"./types.ts\";\nimport { C_DECLARATION_QUERY } from \"./queries.ts\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport type Parser from \"tree-sitter\";\n\nexport class CHeaderResolver {\n  parser: Parser = cParser;\n  #unnamedSymbolCounter = 0;\n\n  /**\n   * Resolves the symbols in a C header file.\n   * @param file The file to resolve.\n   * @returns An array of exported symbols.\n   */\n  resolveSymbols(file: {\n    path: string;\n    rootNode: Parser.SyntaxNode;\n  }): ExportedSymbol[] {\n    const exportedSymbols: ExportedSymbol[] = [];\n    const query = C_DECLARATION_QUERY;\n    const captures = query.captures(file.rootNode);\n    for (const capture of captures) {\n      if (capture.name !== \"decl\" && capture.name !== \"function_definition\") {\n        let idNode: Parser.SyntaxNode | null;\n        if (capture.name !== \"typedef\") {\n          idNode = capture.node.childForFieldName(\"name\");\n        } else {\n          idNode = capture.node.childForFieldName(\n            \"declarator\",\n          );\n        }\n        let name: string;\n        if (!idNode) {\n          name = `#NAPI_UNNAMED_${capture.name.toUpperCase()}_${this\n            .#unnamedSymbolCounter++}`;\n        } else {\n          name = idNode.text;\n        }\n        exportedSymbols.push({\n          name: name,\n          type: capture.name as SymbolType,\n          node: capture.node,\n          identifierNode: idNode,\n          filepath: file.path,\n          specifiers: [],\n          qualifiers: [],\n        });\n      } else {\n        const specifiers = capture.node.children\n          .filter((child) => child.type === \"storage_class_specifier\")\n          .map((child) => child.text);\n        const qualifiers = capture.node.children\n          .filter((child) => child.type === \"type_qualifier\")\n          .map((child) => child.text);\n        let currentNode = capture.node;\n        // Traverse the tree to find the identifier node\n        // This is a workaround for the fact that the identifier node is not always the first child\n        // (e.g. in pointers or arrays)\n        while (\n          !currentNode.childForFieldName(\"declarator\") ||\n          currentNode.childForFieldName(\"declarator\")?.type !== \"identifier\"\n        ) {\n          if (!currentNode.childForFieldName(\"declarator\")) {\n            if (!currentNode.firstNamedChild) {\n              throw new Error(\n                `Could not find a named child for ${currentNode.text}`,\n              );\n            }\n            currentNode = currentNode.firstNamedChild;\n          } else {\n            if (!currentNode.childForFieldName(\"declarator\")) {\n              throw new Error(\n                `Could not find a declarator for ${currentNode.text}`,\n              );\n            }\n            currentNode = currentNode.childForFieldName(\n              \"declarator\",\n            ) as Parser.SyntaxNode;\n          }\n        }\n        const type = capture.name === \"function_definition\"\n          ? \"function_definition\"\n          : currentNode.type === \"function_declarator\"\n          ? \"function_signature\"\n          : \"variable\";\n        const idNode = currentNode.childForFieldName(\"declarator\");\n        if (!idNode) {\n          throw new Error(\n            `Couldn't find identifier node for symbol :\\n${capture.node.text}`,\n          );\n        }\n        exportedSymbols.push({\n          name: idNode.text,\n          type: type as SymbolType,\n          node: capture.node,\n          identifierNode: idNode,\n          filepath: file.path,\n          specifiers: specifiers as StorageClassSpecifier[],\n          qualifiers: qualifiers as TypeQualifier[],\n        });\n      }\n    }\n    return exportedSymbols;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/headerResolver/queries.ts",
    "content": "import { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport Parser from \"tree-sitter\";\n\n/** Query that catches every declaration including macros in a header file\n * Does not catch function definitions\n */\nexport const C_DECLARATION_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n  (translation_unit\n  [\n    (declaration) @decl\n    (struct_specifier\n    name: (_)) @struct\n    (enum_specifier) @enum\n    (union_specifier\n    name: (_)) @union\n    (function_definition) @function_definition\n    (type_definition\n    \ttype:[\n        \t(struct_specifier\n            name: (_)\n            body: (_)) @struct\n            (struct_specifier !name)\n            (struct_specifier !body)\n            (enum_specifier\n            name: (_)\n            body: (_)) @enum\n            (enum_specifier !name)\n            (enum_specifier !body)\n            (union_specifier\n            name: (_)\n            body: (_)) @union\n            (union_specifier !name)\n            (union_specifier !body)\n            (type_identifier)\n            (primitive_type)\n        ]\n    ) @typedef\n  ])\n  (preproc_ifdef\n  [\n    (declaration) @decl\n    (struct_specifier\n    name: (_)) @struct\n    (enum_specifier) @enum\n    (union_specifier\n    name: (_)) @union\n    (function_definition) @function_definition\n    (type_definition\n    \ttype:[\n        \t(struct_specifier\n            name: (_)\n            body: (_)) @struct\n            (struct_specifier !name)\n            (struct_specifier !body)\n            (enum_specifier\n            name: (_)\n            body: (_)) @enum\n            (enum_specifier !name)\n            (enum_specifier !body)\n            (union_specifier\n            name: (_)\n            body: (_)) @union\n            (union_specifier !name)\n            (union_specifier !body)\n            (type_identifier)\n            (primitive_type)\n        ]\n    ) @typedef\n  ])\n  (preproc_def) @macro_constant\n  (preproc_function_def) @macro_function\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/c/headerResolver/types.ts",
    "content": "import type Parser from \"tree-sitter\";\n\n// Constants representing different types of symbols in C\nexport const C_STRUCT_TYPE = \"struct\";\nexport const C_UNION_TYPE = \"union\";\nexport const C_ENUM_TYPE = \"enum\";\nexport const C_FUNCTION_DEFINITION_TYPE = \"function_definition\";\nexport const C_FUNCTION_SIGNATURE_TYPE = \"function_signature\";\nexport const C_MACRO_FUNCTION_TYPE = \"macro_function\";\nexport const C_MACRO_CONSTANT_TYPE = \"macro_constant\";\nexport const C_VARIABLE_TYPE = \"variable\";\nexport const C_TYPEDEF_TYPE = \"typedef\";\n\n// Constants representing different storage class specifiers in C\nexport const C_AUTO_SPECIFIER = \"auto\";\nexport const C_REGISTER_SPECIFIER = \"register\";\nexport const C_STATIC_SPECIFIER = \"static\";\nexport const C_EXTERN_SPECIFIER = \"extern\";\n\n// Constants representing different type qualifiers in C\nexport const C_CONST_QUALIFIER = \"const\";\nexport const C_VOLATILE_QUALIFIER = \"volatile\";\nexport const C_RESTRICT_QUALIFIER = \"restrict\";\nexport const C_ATOMIC_QUALIFIER = \"_Atomic\";\n\n/**  Type alias for the different symbol types */\nexport type SymbolType =\n  | typeof C_STRUCT_TYPE\n  | typeof C_UNION_TYPE\n  | typeof C_ENUM_TYPE\n  | typeof C_FUNCTION_DEFINITION_TYPE\n  | typeof C_FUNCTION_SIGNATURE_TYPE\n  | typeof C_MACRO_FUNCTION_TYPE\n  | typeof C_MACRO_CONSTANT_TYPE\n  | typeof C_VARIABLE_TYPE\n  | typeof C_TYPEDEF_TYPE;\n\n/** Type alias for the different storage class specifiers */\nexport type StorageClassSpecifier =\n  | typeof C_AUTO_SPECIFIER\n  | typeof C_REGISTER_SPECIFIER\n  | typeof C_STATIC_SPECIFIER\n  | typeof C_EXTERN_SPECIFIER;\n\n/** Type alias for the different type qualifiers */\nexport type TypeQualifier =\n  | typeof C_CONST_QUALIFIER\n  | typeof C_VOLATILE_QUALIFIER\n  | typeof C_RESTRICT_QUALIFIER;\n\n/** Interface representing an exported symbol */\nexport interface ExportedSymbol {\n  /** The name of the symbol */\n  name: string;\n  /** The type of the symbol (i.e. struct, union, enum, etc.) */\n  type: SymbolType;\n  /** The storage class specifiers of the symbol (i.e. static, extern) */\n  specifiers: StorageClassSpecifier[];\n  /** The type qualifiers of the symbol (i.e. const, volatile) */\n  qualifiers: TypeQualifier[];\n  /** The syntax node corresponding to the symbol */\n  node: Parser.SyntaxNode;\n  /** The syntax node corresponding to the identifier */\n  identifierNode: Parser.SyntaxNode | null;\n  /** The path of the symbol's file */\n  filepath: string;\n}\n"
  },
  {
    "path": "src/languagePlugins/c/includeResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { cFilesFolder, getCFilesMap } from \"../testFiles/index.ts\";\nimport { CSymbolRegistry } from \"../symbolRegistry/index.ts\";\nimport { CIncludeResolver } from \"./index.ts\";\nimport { join } from \"@std/path\";\nimport type { FunctionSignature } from \"../symbolRegistry/types.ts\";\n\ndescribe(\"CIncludeResolver\", () => {\n  const cFilesMap = getCFilesMap();\n  const registry = new CSymbolRegistry(cFilesMap);\n  const includeResolver = new CIncludeResolver(registry);\n  const burgersh = join(cFilesFolder, \"burgers.h\");\n  const burgersc = join(cFilesFolder, \"burgers.c\");\n  const allh = join(cFilesFolder, \"all.h\");\n  const main = join(cFilesFolder, \"main.c\");\n  const errorsh = join(cFilesFolder, \"errors.h\");\n  const inclusions = includeResolver.getInclusions();\n\n  test(\"resolves inclusions for burgers.h\", () => {\n    const bhinclusions = inclusions.get(burgersh);\n    if (!bhinclusions) {\n      throw new Error(`Inclusions not found for: ${burgersh}`);\n    }\n    expect(bhinclusions.filepath).toBe(burgersh);\n    expect(bhinclusions.symbols.size).toBe(0);\n    expect(bhinclusions.internal.children.size).toBe(0);\n    expect(bhinclusions.standard.size).toBe(1);\n    expect(Array.from(bhinclusions.standard.keys())[0]).toBe(\"<stdbool.h>\");\n  });\n\n  test(\"resolves inclusions for burgers.c\", () => {\n    const bcinclusions = inclusions.get(burgersc);\n    if (!bcinclusions) {\n      throw new Error(`Inclusions not found for: ${burgersc}`);\n    }\n    expect(bcinclusions.filepath).toBe(burgersc);\n    expect(bcinclusions.symbols.size).toBe(32);\n    expect(bcinclusions.internal.children.size).toBe(1);\n    expect(bcinclusions.internal.children.get(\"burgers.h\")).toBeDefined();\n    expect(bcinclusions.standard.size).toBe(4);\n    const stdincludes = Array.from(bcinclusions.standard.keys());\n    expect(stdincludes).toContain(\"<stdio.h>\");\n    expect(stdincludes).toContain(\"<stdlib.h>\");\n    expect(stdincludes).toContain(\"<string.h>\");\n  });\n\n  test(\"resolves inclusions for main.c\", () => {\n    const maininclusions = inclusions.get(main);\n    if (!maininclusions) {\n      throw new Error(`Inclusions not found for: ${main}`);\n    }\n    expect(maininclusions.filepath).toBe(main);\n    expect(maininclusions.symbols.size).toBe(32 + 17);\n    expect(maininclusions.symbols.get(\"create_burger\")?.includefile.file.path)\n      .toBe(allh);\n    expect(maininclusions.internal.children.size).toBe(1);\n    expect(maininclusions.internal.children.get(\"burgers.h\")).not.toBeDefined();\n    expect(maininclusions.internal.children.get(\"personnel.h\")).not\n      .toBeDefined();\n    expect(maininclusions.internal.children.get(\"all.h\")).toBeDefined();\n    const allinclusions = maininclusions.internal.children.get(\"all.h\")!;\n    expect(allinclusions.children.get(\"burgers.h\")).toBeDefined();\n    expect(allinclusions.children.get(\"personnel.h\")).toBeDefined();\n    expect(maininclusions.standard.size).toBe(2);\n    const stdincludes = Array.from(maininclusions.standard.keys());\n    expect(stdincludes).toContain(\"<stdio.h>\");\n  });\n\n  test(\"finds signatures for functions\", () => {\n    const burgers = registry.getRegistry().get(burgersh)?.symbols;\n    if (!burgers) {\n      throw new Error(`File not found: ${burgersh}`);\n    }\n    const create_burger = burgers.get(\"create_burger\") as FunctionSignature;\n    const destroy = burgers.get(\"destroy_burger\") as FunctionSignature;\n    const get = burgers.get(\"get_burger_by_id\") as FunctionSignature;\n    const cheapest = burgers.get(\"get_cheapest_burger\") as FunctionSignature;\n    expect(create_burger.definition).toBeDefined();\n    expect(destroy.definition).toBeDefined();\n    expect(get.definition).toBeDefined();\n    expect(cheapest.definition).toBeDefined();\n  });\n\n  test(\"correctly registers inexistant files\", () => {\n    const unresolvedIncludes = includeResolver.unresolvedDirectives.get(\n      errorsh,\n    );\n    expect(unresolvedIncludes).toBeDefined();\n    expect(unresolvedIncludes).toContainEqual(\"thisfiledoesnotexist.h\");\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/includeResolver/index.ts",
    "content": "import type { InclusionNode, Inclusions } from \"./types.ts\";\nimport { C_INCLUDE_QUERY, C_STANDARD_INCLUDE_QUERY } from \"./queries.ts\";\nimport type { CSymbolRegistry } from \"../symbolRegistry/index.ts\";\nimport type Parser from \"tree-sitter\";\nimport {\n  type CFile,\n  FunctionDefinition,\n  FunctionSignature,\n  type Symbol,\n} from \"../symbolRegistry/types.ts\";\nimport { dirname, join } from \"@std/path\";\n\nexport class CIncludeResolver {\n  symbolRegistry: Map<string, CFile>;\n  files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n  unresolvedDirectives: Map<string, Set<string>>;\n  includeDirs: string[] = [];\n  #inclusions?: Map<string, Inclusions>;\n  #inclusionCache: Map<CFile, Inclusions>;\n\n  constructor(symbolRegistry: CSymbolRegistry, includeDirs: string[] = []) {\n    this.symbolRegistry = symbolRegistry.getRegistry();\n    this.files = symbolRegistry.files;\n    this.#inclusionCache = new Map();\n    this.unresolvedDirectives = new Map();\n    this.includeDirs = includeDirs;\n  }\n\n  getFile(filepath: string, sourcepath: string): CFile | undefined {\n    const filepaths = Array.from(this.symbolRegistry.keys());\n    // 1. Check current file's directory\n    const sourceDir = dirname(sourcepath);\n    const pathfromrelative = join(sourceDir, filepath).replace(/\\\\/g, \"/\");\n    const corresponding1 = filepaths.find((f) => f === pathfromrelative);\n    if (corresponding1) {\n      return this.symbolRegistry.get(corresponding1);\n    }\n    // 2. Check include directories\n    for (const dir of this.includeDirs) {\n      const pathfrominclude = join(dir, filepath).replace(/\\\\/g, \"/\");\n      const corresponding = filepaths.find((f) => f === pathfrominclude);\n      if (corresponding) {\n        return this.symbolRegistry.get(corresponding);\n      }\n    }\n    // 3. Check from workspace root\n    const corresponding2 = filepaths.find((f) => f === filepath);\n    if (corresponding2) {\n      return this.symbolRegistry.get(corresponding2);\n    }\n    return undefined;\n  }\n\n  /**\n   * Looks for a chain of inclusions starting from the given file\n   * that leads to the given symbol.\n   * @param file The file to start the search from.\n   * @param symbol The symbol to search for.\n   * @returns An array of file paths representing the chain of inclusions.\n   */\n  findInclusionChain(\n    start: string,\n    symbol: Symbol,\n  ): string[] | undefined {\n    const inclusions = this.getInclusions().get(start);\n    if (!inclusions) {\n      return undefined;\n    }\n    const chain: string[] = [];\n    // Look inside the tree for a node that has a filepath that matches the symbol's file path\n    const findInclusion = (node: InclusionNode): boolean => {\n      if (node.filepath === symbol.declaration.filepath) {\n        chain.push(node.filepath);\n        return true;\n      }\n      for (const child of node.children.values()) {\n        if (findInclusion(child)) {\n          chain.push(node.filepath);\n          return true;\n        }\n      }\n      return false;\n    };\n    if (findInclusion(inclusions.internal)) {\n      chain.reverse(); // Reverse to get the chain from start to symbol\n      return chain;\n    }\n    return undefined;\n  }\n\n  /**\n   * Resolves the inclusions of a file.\n   * @param file The file to resolve inclusions for.\n   * @returns The inclusions of the file.\n   */\n  #resolveInclusions(\n    file: CFile,\n    visitedFiles = new Set<string>(),\n  ): Inclusions {\n    const inclusions: Inclusions = {\n      filepath: file.file.path,\n      symbols: new Map(),\n      internal: {\n        name: \".\",\n        children: new Map(),\n        filepath: file.file.path,\n      },\n      standard: new Map(),\n    };\n\n    // Add the current file to the visited set to prevent infinite recursion\n    visitedFiles.add(file.file.path);\n\n    // Check for file in cache\n    if (this.#inclusionCache.has(file)) {\n      return this.#inclusionCache.get(file)!;\n    }\n\n    const includeNodes = C_INCLUDE_QUERY.captures(file.file.rootNode);\n    const standardIncludeNodes = C_STANDARD_INCLUDE_QUERY.captures(\n      file.file.rootNode,\n    );\n\n    for (const node of includeNodes) {\n      const path = node.node.text;\n      const includedfile = this.getFile(path, file.file.path);\n      if (!includedfile) {\n        if (!this.unresolvedDirectives.has(file.file.path)) {\n          this.unresolvedDirectives.set(file.file.path, new Set());\n        }\n        this.unresolvedDirectives.get(file.file.path)?.add(path);\n      } else if (!visitedFiles.has(includedfile.file.path)) {\n        // Add the included file's symbols to the current file's symbols\n        for (const [name, symbol] of includedfile.symbols) {\n          inclusions.symbols.set(name, {\n            symbol: symbol,\n            includefile: includedfile,\n          });\n        }\n        // Recursively resolve inclusions for the included file\n        const nestedInclusions = this.#resolveInclusions(\n          includedfile,\n          visitedFiles,\n        );\n        for (const [name, symbol] of nestedInclusions.symbols) {\n          inclusions.symbols.set(name, {\n            symbol: symbol.symbol,\n            includefile: includedfile,\n          });\n        }\n        inclusions.internal.children.set(path, {\n          name: path,\n          filepath: includedfile.file.path,\n          children: nestedInclusions.internal.children,\n          parent: inclusions.internal,\n        });\n        for (const node of nestedInclusions.standard) {\n          inclusions.standard.set(node[0], node[1]);\n        }\n        // Associate function definitions to their signatures\n        const funcdefs = Array.from(\n          file.symbols.entries().filter(([, s]) =>\n            s instanceof FunctionDefinition\n          ).map(([, s]) => s as FunctionDefinition),\n        );\n        for (const funcdef of funcdefs) {\n          if (inclusions.symbols.has(funcdef.name)) {\n            const symbol = inclusions.symbols.get(funcdef.name);\n            if (symbol && symbol.symbol instanceof FunctionSignature) {\n              funcdef.signature = symbol.symbol;\n              symbol.symbol.definition = funcdef;\n            }\n          }\n        }\n      }\n    }\n\n    for (const node of standardIncludeNodes) {\n      const child = node.node.childForFieldName(\"path\");\n      if (child) {\n        inclusions.standard.set(child.text, node.node);\n      }\n    }\n\n    // Save inclusions in cache\n    this.#inclusionCache.set(file, inclusions);\n    return inclusions;\n  }\n\n  /**\n   * Retrieves the inclusions of all files and caches the results.\n   * @returns A map of file paths to their inclusions.\n   */\n  getInclusions() {\n    if (!this.#inclusions) {\n      this.#inclusions = new Map();\n      for (const file of this.symbolRegistry.values()) {\n        const inclusions = this.#resolveInclusions(file);\n        this.#inclusions.set(file.file.path, inclusions);\n      }\n    }\n    return this.#inclusions;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/includeResolver/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\nexport const C_INCLUDE_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n  (preproc_include\n  path: (string_literal\n  (string_content) @include))\n  `,\n);\n\nexport const C_STANDARD_INCLUDE_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n  (preproc_include\n  path: (system_lib_string)) @include\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/c/includeResolver/types.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport type { CFile, Symbol } from \"../symbolRegistry/types.ts\";\n\n/** Interface representing the #include statements of a file */\nexport interface Inclusions {\n  /** The path of the file */\n  filepath: string;\n  /** The imported symbols from internal imports */\n  symbols: Map<string, IncludedSymbol>;\n  /** The tree of recursive inclusions of the internal imports */\n  internal: InclusionNode;\n  /** The list of include directives of the standard imports */\n  standard: Map<string, Parser.SyntaxNode>;\n}\n\n/** Interface representing a symbol imported through an include directive */\nexport interface IncludedSymbol {\n  /** The corresponding symbol */\n  symbol: Symbol;\n  /** The file that allows the usage of this symbol */\n  includefile: CFile;\n  // Due to recursive inclusions, the file may not be the one that exports\n  // said symbol. Check \"all.h\" in the test files.\n}\n\n/** Interface representing a node in the internal inclusion tree */\nexport interface InclusionNode {\n  /** The path to the inclusion relative to its parent, or \".\" if root */\n  name: string;\n  /** The complete file path */\n  filepath: string;\n  /** The inclusions it contains, relative to itself */\n  children: Map<string, InclusionNode>;\n  /** The parent of the node */\n  parent?: InclusionNode;\n}\n"
  },
  {
    "path": "src/languagePlugins/c/invocationResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { cFilesFolder, getCFilesMap } from \"../testFiles/index.ts\";\nimport { CSymbolRegistry } from \"../symbolRegistry/index.ts\";\nimport { CIncludeResolver } from \"../includeResolver/index.ts\";\nimport { CInvocationResolver } from \"./index.ts\";\nimport type { Symbol } from \"../symbolRegistry/types.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CInvocationResolver\", () => {\n  const cFilesMap = getCFilesMap();\n  const symbolRegistry = new CSymbolRegistry(cFilesMap);\n  const includeResolver = new CIncludeResolver(symbolRegistry);\n  const invocationResolver = new CInvocationResolver(includeResolver);\n  const burgersh = join(cFilesFolder, \"burgers.h\");\n  const burgersc = join(cFilesFolder, \"burgers.c\");\n  const personnelc = join(cFilesFolder, \"personnel.c\");\n  const crashcasesh = join(cFilesFolder, \"crashcases.h\");\n  const errorsh = join(cFilesFolder, \"errors.h\");\n  const main = join(cFilesFolder, \"main.c\");\n  const registry = symbolRegistry.getRegistry();\n\n  test(\"resolves invocations for burgers.h\", () => {\n    const symbols = registry.get(burgersh)?.symbols;\n    if (!symbols) {\n      throw new Error(`Symbol not found for: ${burgersh}`);\n    }\n    const sauce = symbols.get(\"Sauce\") as Symbol;\n    const sauceinvocations = invocationResolver.getInvocationsForSymbol(sauce);\n    expect(sauceinvocations.resolved.size).toBe(1);\n    expect(sauceinvocations.resolved.get(\"ClassicSauces\")).toBeDefined();\n\n    const fries = symbols.get(\"Fries\") as Symbol;\n    const friesinvocations = invocationResolver.getInvocationsForSymbol(fries);\n    expect(friesinvocations.resolved.size).toBe(1);\n    expect(friesinvocations.resolved.get(\"Sauce\")).toBeDefined();\n\n    const burger = symbols.get(\"Burger\") as Symbol;\n    const burgerinvocations = invocationResolver.getInvocationsForSymbol(\n      burger,\n    );\n    expect(burgerinvocations.resolved.size).toBe(2);\n    expect(burgerinvocations.resolved.get(\"Condiment\")).toBeDefined();\n    expect(burgerinvocations.resolved.get(\"Sauce\")).toBeDefined();\n  });\n\n  test(\"resolves invocations for burgers.c\", () => {\n    const symbols = registry.get(burgersc)?.symbols;\n    if (!symbols) {\n      throw new Error(`Symbol not found for: ${burgersc}`);\n    }\n    const create_burger = symbols.get(\"create_burger\") as Symbol;\n    const create_burger_invocations = invocationResolver\n      .getInvocationsForSymbol(create_burger);\n    const create_resolved = Array.from(\n      create_burger_invocations.resolved.keys(),\n    );\n    expect(create_resolved.length).toBe(5);\n    expect(create_resolved).toContain(\"create_burger\");\n    expect(create_resolved).toContain(\"Burger\");\n    expect(create_resolved).toContain(\"Condiment\");\n    expect(create_resolved).toContain(\"Sauce\");\n    expect(create_resolved).toContain(\"burger_count\");\n\n    const destroy_burger = symbols.get(\"destroy_burger\") as Symbol;\n    const destroy_burger_invocations = invocationResolver\n      .getInvocationsForSymbol(destroy_burger);\n    const destroy_resolved = Array.from(\n      destroy_burger_invocations.resolved.keys(),\n    );\n    expect(destroy_resolved.length).toBe(2);\n    expect(destroy_resolved).toContain(\"destroy_burger\");\n    expect(destroy_resolved).toContain(\"Burger\");\n\n    const symbolsc = registry.get(burgersc)?.symbols;\n    if (!symbolsc) {\n      throw new Error(`Symbol not found for: ${burgersc}`);\n    }\n\n    const burgers = symbolsc.get(\"burgers\") as Symbol;\n    const burgers_invocations = invocationResolver.getInvocationsForSymbol(\n      burgers,\n    );\n    const burgers_resolved = Array.from(burgers_invocations.resolved.keys());\n    expect(burgers_resolved.length).toBe(2);\n    expect(burgers_resolved).toContain(\"Burger\");\n    expect(burgers_resolved).toContain(\"MAX_BURGERS\");\n  });\n\n  test(\"resolves invocations for personnel.c\", () => {\n    const symbols = registry.get(personnelc)?.symbols;\n    if (!symbols) {\n      throw new Error(`Symbol not found for: ${personnelc}`);\n    }\n    const create_employee = symbols.get(\"create_employee\") as Symbol;\n    const create_employee_invocations = invocationResolver\n      .getInvocationsForSymbol(create_employee);\n    const create_resolved = Array.from(\n      create_employee_invocations.resolved.keys(),\n    );\n    expect(create_resolved.length).toBe(6);\n    expect(create_resolved).toContain(\"create_employee\");\n    expect(create_resolved).toContain(\"Employee\");\n    expect(create_resolved).toContain(\"Department\");\n    expect(create_resolved).toContain(\"MAX_EMPLOYEES\");\n    expect(create_resolved).toContain(\"employee_count\");\n    expect(create_resolved).toContain(\"employees\");\n  });\n\n  test(\"resolves invocations for main.c\", () => {\n    const symbols = registry.get(main)?.symbols;\n    if (!symbols) {\n      throw new Error(`Symbol not found for: ${main}`);\n    }\n    const main_func = symbols.get(\"main\") as Symbol;\n    const main_invocations = invocationResolver.getInvocationsForSymbol(\n      main_func,\n    );\n    const main_resolved = Array.from(main_invocations.resolved.keys());\n    expect(main_resolved.length).toBe(9);\n    expect(main_resolved).toContain(\"create_burger\");\n    expect(main_resolved).toContain(\"Burger\");\n    expect(main_resolved).toContain(\"create_employee\");\n    expect(main_resolved).toContain(\"Employee\");\n    expect(main_resolved).toContain(\"Condiment\");\n    expect(main_resolved).toContain(\"Sauce\");\n  });\n\n  test(\"Crash Cases\", () => {\n    const symbols = registry.get(crashcasesh)?.symbols;\n    if (!symbols) {\n      throw new Error(`Symbol not found for: ${crashcasesh}`);\n    }\n    const crash = symbols.get(\"CpuFastFill\") as Symbol;\n    const crash_invocations = invocationResolver.getInvocationsForSymbol(crash);\n    const crash_resolved = Array.from(crash_invocations.resolved.keys());\n    expect(crash_resolved.length).toBe(2);\n    expect(crash_resolved).toContain(\"CpuFastSet\");\n    expect(crash_resolved).toContain(\"CPU_FAST_SET_SRC_FIXED\");\n  });\n\n  test(\"Errors\", () => {\n    const symbols = registry.get(errorsh)?.symbols;\n    if (!symbols) {\n      throw new Error(`Symbol not found for: ${errorsh}`);\n    }\n    const xbox = symbols.get(\"xbox\") as Symbol;\n    const xbox_invocations = invocationResolver.getInvocationsForSymbol(xbox);\n    const xbox_resolved = Array.from(xbox_invocations.resolved.keys());\n    expect(xbox_resolved.length).toBe(1);\n    expect(xbox_resolved).toContain(\"#NAPI_UNNAMED_ENUM_0\");\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/invocationResolver/index.ts",
    "content": "import type { Invocations } from \"./types.ts\";\nimport { C_INVOCATION_QUERY, C_MACRO_CONTENT_QUERY } from \"./queries.ts\";\nimport type { CIncludeResolver } from \"../includeResolver/index.ts\";\nimport {\n  type CFile,\n  DataType,\n  EnumMember,\n  FunctionDefinition,\n  FunctionSignature,\n  type Symbol,\n} from \"../symbolRegistry/types.ts\";\nimport type Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport type { IncludedSymbol } from \"../includeResolver/types.ts\";\n\nexport class CInvocationResolver {\n  includeResolver: CIncludeResolver;\n\n  constructor(includeResolver: CIncludeResolver) {\n    this.includeResolver = includeResolver;\n  }\n\n  getInvocationsForNode(\n    node: Parser.SyntaxNode,\n    filepath: string,\n    symbolname: string | undefined = undefined,\n  ): Invocations {\n    const availableSymbols = this.includeResolver\n      .getInclusions()\n      .get(filepath)?.symbols;\n    const currentfile = this.includeResolver.symbolRegistry.get(filepath)!;\n    const localSymbols = currentfile.symbols;\n    const unresolved = new Set<string>();\n    const resolved = new Map<string, IncludedSymbol>();\n    const captures = C_INVOCATION_QUERY.captures(node);\n    for (const capture of captures) {\n      const name = capture.node.text;\n      // if the symbol name is the same as the one we are looking at, skip it\n      if (symbolname && name === symbolname) {\n        continue;\n      }\n      if (availableSymbols && availableSymbols.has(name)) {\n        const availableSymbol = availableSymbols.get(name);\n        if (!availableSymbol) {\n          unresolved.add(name);\n          continue;\n        }\n        resolved.set(name, availableSymbol);\n      } else if (localSymbols && localSymbols.has(name)) {\n        const localSymbol = localSymbols.get(name);\n        if (!localSymbol) {\n          unresolved.add(name);\n          continue;\n        }\n        resolved.set(name, {\n          includefile: currentfile,\n          symbol: localSymbol,\n        });\n      } else {\n        unresolved.add(name);\n      }\n    }\n    // Check for macro invocations\n    // The logic of a macro is set in a single (preproc_arg) node.\n    // If we parse that node's text, we can find the invocations.\n    const macroCaptures = C_MACRO_CONTENT_QUERY.captures(node);\n    for (const capture of macroCaptures) {\n      const contentNode = cParser.parse(capture.node.text).rootNode;\n      const contentInvocations = this.getInvocationsForNode(\n        contentNode,\n        filepath,\n        symbolname,\n      );\n      for (const [key, value] of contentInvocations.resolved) {\n        if (!resolved.has(key)) {\n          resolved.set(key, value);\n        }\n      }\n      for (const value of contentInvocations.unresolved) {\n        unresolved.add(value);\n      }\n    }\n    return {\n      resolved,\n      unresolved,\n    };\n  }\n\n  #getSymbolFile(symbol: Symbol): CFile {\n    const file = this.includeResolver.symbolRegistry.get(\n      symbol.declaration.filepath,\n    );\n    if (!file) {\n      throw Error(`File ${symbol.declaration.filepath} not found!`);\n      // Exclamation point because that's really out of the ordinary\n    }\n    return file;\n  }\n\n  getInvocationsForSymbol(symbol: Symbol) {\n    const filepath = symbol.declaration.filepath;\n    const node = symbol.declaration.node;\n    const name = symbol.name;\n    const invocations = this.getInvocationsForNode(node, filepath, name);\n    const resolved = invocations.resolved;\n    if (symbol instanceof FunctionSignature && symbol.definition) {\n      resolved.set(symbol.name, {\n        includefile: this.#getSymbolFile(symbol.definition),\n        symbol: symbol.definition,\n      });\n    }\n    if (symbol instanceof FunctionDefinition && symbol.signature) {\n      resolved.set(symbol.name, {\n        includefile: this.#getSymbolFile(symbol.signature),\n        symbol: symbol.signature,\n      });\n    }\n    if (symbol instanceof DataType) {\n      const typedefs = symbol.typedefs;\n      for (const [key, value] of typedefs) {\n        resolved.set(key, {\n          includefile: this.#getSymbolFile(value),\n          symbol: value,\n        });\n      }\n    }\n    // Replace enum members with their parent enum\n    for (const [key, value] of resolved) {\n      if (value.symbol instanceof EnumMember) {\n        const parent = value.symbol.parent;\n        if (!resolved.has(parent.name) && parent.name !== symbol.name) {\n          resolved.set(parent.name, {\n            includefile: value.includefile,\n            symbol: parent,\n          });\n        }\n        resolved.delete(key);\n      }\n    }\n    return {\n      resolved: invocations.resolved,\n      unresolved: invocations.unresolved,\n    };\n  }\n\n  getInvocationsForFile(filepath: string): Invocations {\n    let symbols = this.includeResolver.symbolRegistry.get(filepath)?.symbols;\n    if (!symbols) {\n      symbols = new Map();\n    }\n    let unresolved = new Set<string>();\n    const resolved = new Map<string, IncludedSymbol>();\n    for (const symbol of symbols.values()) {\n      const invocations = this.getInvocationsForSymbol(symbol);\n      unresolved = new Set([...unresolved, ...invocations.unresolved]);\n      for (const [key, value] of invocations.resolved) {\n        if (!resolved.has(key)) {\n          resolved.set(key, value);\n        }\n      }\n    }\n    return {\n      resolved,\n      unresolved,\n    };\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/invocationResolver/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\nexport const C_INVOCATION_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n  (type_identifier) @type\n  (identifier) @id\n  `, // The nuclear option, but unlike C# we can afford to do it.\n);\n\nexport const C_MACRO_CONTENT_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n  (preproc_arg) @macro\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/c/invocationResolver/types.ts",
    "content": "import type { IncludedSymbol } from \"../includeResolver/types.ts\";\n\n/** Interface representing the invoked symbols in a file */\nexport interface Invocations {\n  /** Symbols that are part of the project */\n  resolved: Map<string, IncludedSymbol>;\n  /** Symbols that are not part of the project or local variables */\n  unresolved: Set<string>;\n}\n"
  },
  {
    "path": "src/languagePlugins/c/metrics/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CMetricsAnalyzer } from \"./index.ts\";\nimport { cFilesFolder, getCFilesMap } from \"../testFiles/index.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CMetricsAnalyzer\", () => {\n  const analyzer = new CMetricsAnalyzer();\n  const files = getCFilesMap();\n\n  const analyzeFile = (filePath: string) => {\n    const absolutePath = join(cFilesFolder, filePath);\n    const file = files.get(absolutePath);\n    if (!file) {\n      throw new Error(`File not found: ${absolutePath}`);\n    }\n    return analyzer.analyzeNode(file.rootNode);\n  };\n\n  test(\"burgers.c\", () => {\n    const metrics = analyzeFile(\"burgers.c\");\n    expect(metrics.characterCount >= 1485).toBe(true);\n    expect(metrics.codeCharacterCount < 1485).toBe(true);\n    expect(metrics.linesCount >= 53).toBe(true);\n    expect(metrics.codeLinesCount < 53).toBe(true);\n    expect(metrics.cyclomaticComplexity >= 6).toBe(true);\n  });\n\n  test(\"burgers.h\", () => {\n    const metrics = analyzeFile(\"burgers.h\");\n    expect(metrics.characterCount >= 1267).toBe(true);\n    expect(metrics.codeCharacterCount < 1267).toBe(true);\n    expect(metrics.linesCount >= 71).toBe(true);\n    expect(metrics.codeLinesCount < 71).toBe(true);\n    expect(metrics.cyclomaticComplexity).toBe(1);\n  });\n\n  test(\"crashcases.h\", () => {\n    const metrics = analyzeFile(\"crashcases.h\");\n    expect(metrics.characterCount >= 956).toBe(true);\n    expect(metrics.codeCharacterCount < 956).toBe(true);\n    expect(metrics.linesCount >= 33).toBe(true);\n    expect(metrics.codeLinesCount < 33).toBe(true);\n    expect(metrics.cyclomaticComplexity).toBe(0);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/metrics/index.ts",
    "content": "import { C_COMMENT_QUERY, C_COMPLEXITY_QUERY } from \"./queries.ts\";\nimport type { CComplexityMetrics, CodeCounts, CommentSpan } from \"./types.ts\";\nimport type Parser from \"tree-sitter\";\n\nexport class CMetricsAnalyzer {\n  /**\n   * Calculates metrics for a C symbol.\n   * @param node - The syntax node to analyze.\n   * @returns An object containing the complexity metrics.\n   */\n  public analyzeNode(node: Parser.SyntaxNode): CComplexityMetrics {\n    if (node.type === \"preproc_function_def\") {\n      const value = node.childForFieldName(\"value\");\n      if (value) {\n        node = value;\n      }\n    }\n    const complexityCount = this.getComplexityCount(node);\n    const linesCount = node.endPosition.row - node.startPosition.row + 1;\n    const codeCounts = this.getCodeCounts(node);\n    const codeLinesCount = codeCounts.lines;\n    const characterCount = node.endIndex - node.startIndex;\n    const codeCharacterCount = codeCounts.characters;\n\n    return {\n      cyclomaticComplexity: complexityCount,\n      linesCount,\n      codeLinesCount,\n      characterCount,\n      codeCharacterCount,\n    };\n  }\n\n  private getComplexityCount(node: Parser.SyntaxNode): number {\n    const complexityMatches = C_COMPLEXITY_QUERY.captures(node);\n    return complexityMatches.length;\n  }\n\n  /**\n   * Finds comments in the given node and returns their spans.\n   * @param node - The AST node to analyze\n   * @returns An object containing pure comment lines and comment spans\n   */\n  private findComments(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): {\n    pureCommentLines: Set<number>;\n    commentSpans: CommentSpan[];\n  } {\n    const pureCommentLines = new Set<number>();\n    const commentSpans: CommentSpan[] = [];\n\n    const commentCaptures = C_COMMENT_QUERY.captures(node);\n\n    for (const capture of commentCaptures) {\n      const commentNode = capture.node;\n\n      // Record the comment span for character counting\n      commentSpans.push({\n        start: {\n          row: commentNode.startPosition.row,\n          column: commentNode.startPosition.column,\n        },\n        end: {\n          row: commentNode.endPosition.row,\n          column: commentNode.endPosition.column,\n        },\n      });\n\n      // Check if the comment starts at the beginning of the line (ignoring whitespace)\n      const lineIdx = commentNode.startPosition.row - node.startPosition.row;\n      if (lineIdx >= 0 && lineIdx < lines.length) {\n        const lineText = lines[lineIdx];\n        const textBeforeComment = lineText.substring(\n          0,\n          commentNode.startPosition.column,\n        );\n\n        // If there's only whitespace before the comment, it's a pure comment line\n        if (textBeforeComment.trim().length === 0) {\n          for (\n            let line = commentNode.startPosition.row;\n            line <= commentNode.endPosition.row;\n            line++\n          ) {\n            pureCommentLines.add(line);\n          }\n        }\n      }\n    }\n\n    return { pureCommentLines, commentSpans };\n  }\n\n  /**\n   * Finds all empty lines in a node\n   *\n   * @param node The syntax node to analyze\n   * @param lines The lines of text in the node\n   * @returns Set of line numbers that are empty\n   */\n  private findEmptyLines(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): Set<number> {\n    const emptyLines = new Set<number>();\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      if (lines[i].trim().length === 0) {\n        emptyLines.add(lineIndex);\n      }\n    }\n\n    return emptyLines;\n  }\n\n  private getCodeCounts(node: Parser.SyntaxNode): CodeCounts {\n    const lines = node.text.split(/\\r?\\n/);\n    const linesCount = lines.length;\n    // Find comments and their spans\n    const { pureCommentLines, commentSpans } = this.findComments(node, lines);\n\n    // Find empty lines\n    const emptyLines = this.findEmptyLines(node, lines);\n\n    // Calculate code lines\n    const nonCodeLines = new Set([...pureCommentLines, ...emptyLines]);\n    const codeLinesCount = linesCount - nonCodeLines.size;\n\n    let codeCharCount = 0;\n\n    // Process each line individually\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      const line = lines[i];\n\n      // Skip empty lines and pure comment lines\n      if (emptyLines.has(lineIndex) || pureCommentLines.has(lineIndex)) {\n        continue;\n      }\n\n      // Process line for code characters\n      let lineText = line;\n\n      // Remove comment content from the line if present\n      for (const span of commentSpans) {\n        if (span.start.row === lineIndex) {\n          // Comment starts on this line\n          lineText = lineText.substring(0, span.start.column);\n        }\n      }\n\n      // Count normalized code characters (trim excessive whitespace)\n      const normalizedText = lineText.trim().replace(/\\s+/g, \" \");\n      codeCharCount += normalizedText.length;\n    }\n\n    return {\n      lines: codeLinesCount,\n      characters: codeCharCount,\n    };\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/metrics/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\n// Tree-sitter query to find complexity-related nodes\nexport const C_COMPLEXITY_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n    (if_statement) @complexity\n    (while_statement) @complexity\n    (for_statement) @complexity\n    (do_statement) @complexity\n    (case_statement) @complexity\n    (conditional_expression) @complexity\n    (preproc_ifdef) @complexity\n  `,\n);\n\nexport const C_COMMENT_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n    (comment) @comment\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/c/metrics/types.ts",
    "content": "/**\n * Interface for code volumes\n */\nexport interface CodeCounts {\n  /** Number of lines of code */\n  lines: number;\n  /** Number of characters of code */\n  characters: number;\n}\n\nexport interface CommentSpan {\n  start: { row: number; column: number };\n  end: { row: number; column: number };\n}\n\n/**\n * Represents complexity metrics for a C symbol\n */\nexport interface CComplexityMetrics {\n  /** Cyclomatic complexity (McCabe complexity) */\n  cyclomaticComplexity: number;\n  /** Code lines (not including whitespace or comments) */\n  codeLinesCount: number;\n  /** Total lines (including whitespace and comments) */\n  linesCount: number;\n  /** Characters of actual code (excluding comments and excessive whitespace) */\n  codeCharacterCount: number;\n  /** Total characters in the entire symbol */\n  characterCount: number;\n}\n"
  },
  {
    "path": "src/languagePlugins/c/symbolRegistry/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { cFilesFolder, getCFilesMap } from \"../testFiles/index.ts\";\nimport { CSymbolRegistry } from \"./index.ts\";\nimport {\n  DataType,\n  FunctionDefinition,\n  FunctionSignature,\n  Typedef,\n  Variable,\n} from \"./types.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CSymbolRegistry\", () => {\n  const cFilesMap = getCFilesMap();\n  const registry = new CSymbolRegistry(cFilesMap).getRegistry();\n  const burgersh = join(cFilesFolder, \"burgers.h\");\n  const burgersc = join(cFilesFolder, \"burgers.c\");\n  const oldmanh = join(cFilesFolder, \"oldman.h\");\n  const hsymbols = registry.get(burgersh);\n  if (!hsymbols) {\n    throw new Error(`File not found: ${burgersh}`);\n  }\n  const csymbols = registry.get(burgersc);\n  if (!csymbols) {\n    throw new Error(`File not found: ${burgersc}`);\n  }\n\n  test(\"registers symbols for burgers.h\", () => {\n    expect(hsymbols.type).toBe(\".h\");\n    expect(hsymbols.symbols.size).toBe(32);\n  });\n\n  test(\"registers datatypes for burgers.h\", () => {\n    expect(hsymbols.symbols.get(\"Burger\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"Sauce\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"Condiment\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"ClassicSauces\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"Drink_t\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"Burger\")).toBeInstanceOf(DataType);\n    expect(hsymbols.symbols.get(\"Sauce\")).toBeInstanceOf(DataType);\n    expect(hsymbols.symbols.get(\"Condiment\")).toBeInstanceOf(DataType);\n    expect(hsymbols.symbols.get(\"ClassicSauces\")).toBeInstanceOf(DataType);\n    expect(hsymbols.symbols.get(\"Drink_t\")).toBeInstanceOf(DataType);\n  });\n\n  test(\"registers enum members for burgers.h\", () => {\n    expect(hsymbols.symbols.get(\"NONE\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"SALAD\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"TOMATO\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"ONION\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"CHEESE\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"PICKLE\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"KETCHUP\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"MUSTARD\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"BBQ\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"MAYO\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"SPICY\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"COKE\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"ICED_TEA\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"LEMONADE\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"WATER\")).toBeDefined();\n    expect(hsymbols.symbols.get(\"COFFEE\")).toBeDefined();\n  });\n\n  test(\"registers typedefs for burgers.h\", () => {\n    const drink = hsymbols.symbols.get(\"Drink\");\n    const fries = hsymbols.symbols.get(\"Fries\");\n    expect(drink).toBeDefined();\n    expect(fries).toBeDefined();\n    expect(drink).toBeInstanceOf(Typedef);\n    expect(fries).toBeInstanceOf(Typedef);\n    expect((drink as Typedef).datatype).toBeDefined();\n    expect((fries as Typedef).datatype).not.toBeDefined();\n    expect((drink as Typedef).datatype).toBeInstanceOf(DataType);\n    expect((drink as Typedef).datatype?.name).toBe(\"Drink_t\");\n  });\n\n  test(\"registers functions for burgers.h\", () => {\n    const max = hsymbols.symbols.get(\"MAX\");\n    const create = hsymbols.symbols.get(\"create_burger\");\n    const destroy = hsymbols.symbols.get(\"destroy_burger\");\n    const get = hsymbols.symbols.get(\"get_burger_by_id\");\n    const cheapest = hsymbols.symbols.get(\"get_cheapest_burger\");\n    expect(max).toBeDefined();\n    expect(create).toBeDefined();\n    expect(destroy).toBeDefined();\n    expect(get).toBeDefined();\n    expect(cheapest).toBeDefined();\n    expect(max).toBeInstanceOf(FunctionDefinition);\n    expect(create).toBeInstanceOf(FunctionSignature);\n    expect(destroy).toBeInstanceOf(FunctionSignature);\n    expect(get).toBeInstanceOf(FunctionSignature);\n    expect(cheapest).toBeInstanceOf(FunctionSignature);\n    expect((max as FunctionDefinition).isMacro).toBe(true);\n    expect((create as FunctionSignature).isMacro).toBe(false);\n    expect((destroy as FunctionSignature).isMacro).toBe(false);\n    expect((get as FunctionSignature).isMacro).toBe(false);\n    expect((cheapest as FunctionSignature).isMacro).toBe(false);\n    expect((max as FunctionDefinition).declaration.node.type).toBe(\n      \"preproc_function_def\",\n    );\n    expect((create as FunctionSignature).definition).not.toBeDefined();\n    expect((destroy as FunctionSignature).definition).not.toBeDefined();\n    expect((get as FunctionSignature).definition).not.toBeDefined();\n    expect((cheapest as FunctionSignature).definition).not.toBeDefined();\n  });\n\n  test(\"registers variables for burgers.h\", () => {\n    const burgers_count = hsymbols.symbols.get(\"burger_count\");\n    const max_burgers = hsymbols.symbols.get(\"MAX_BURGERS\");\n    const burgers_h = hsymbols.symbols.get(\"BURGERS_H\");\n    expect(burgers_h).toBeDefined();\n    expect(max_burgers).toBeDefined();\n    expect(burgers_count).toBeDefined();\n    expect(burgers_h).toBeInstanceOf(Variable);\n    expect(max_burgers).toBeInstanceOf(Variable);\n    expect(burgers_count).toBeInstanceOf(Variable);\n    expect((burgers_h as Variable).isMacro).toBe(true);\n    expect((max_burgers as Variable).isMacro).toBe(true);\n    expect((burgers_count as Variable).isMacro).toBe(false);\n  });\n\n  test(\"registers symbols for burgers.c\", () => {\n    expect(csymbols.type).toBe(\".c\");\n    expect(csymbols.symbols.size).toBe(5);\n  });\n\n  test(\"registers variables for burgers.c\", () => {\n    const burgers = csymbols.symbols.get(\"burgers\");\n    expect(burgers).toBeDefined();\n    expect(burgers).toBeInstanceOf(Variable);\n    expect((burgers as Variable).isMacro).toBe(false);\n  });\n\n  test(\"registers main\", () => {\n    const main = join(cFilesFolder, \"main.c\");\n    const mainsymbols = registry.get(main);\n    if (!mainsymbols) {\n      throw new Error(`File not found: ${main}`);\n    }\n    expect(mainsymbols.type).toBe(\".c\");\n    expect(mainsymbols.symbols.size).toBe(1);\n    const mainfunc = mainsymbols.symbols.get(\"main\");\n    expect(mainfunc).toBeDefined();\n    expect(mainfunc).toBeInstanceOf(FunctionDefinition);\n    expect((mainfunc as FunctionDefinition).isMacro).toBe(false);\n    expect((mainfunc as FunctionDefinition).declaration.node.type).toBe(\n      \"function_definition\",\n    );\n    expect((mainfunc as FunctionDefinition).declaration.filepath).toBe(main);\n    expect((mainfunc as FunctionDefinition).declaration.node.type).toBe(\n      \"function_definition\",\n    );\n  });\n\n  test(\"prioritizes typedefs\", () => {\n    const oldmanSymbols = registry.get(oldmanh);\n    if (!oldmanSymbols) {\n      throw new Error(`File not found: ${oldmanh}`);\n    }\n    expect(oldmanSymbols.type).toBe(\".h\");\n    expect(oldmanSymbols.symbols.size).toBe(14);\n    const oldman = oldmanSymbols.symbols.get(\"OldMan\");\n    expect(oldman).toBeDefined();\n    expect(oldman).toBeInstanceOf(Typedef);\n    expect((oldman as Typedef).datatype).not.toBeDefined();\n    expect((oldman as Typedef).name).toBe(\"OldMan\");\n    expect((oldman as Typedef).declaration.node.type).toBe(\"type_definition\");\n    expect((oldman as Typedef).declaration.filepath).toBe(oldmanh);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/symbolRegistry/index.ts",
    "content": "import type { ExportedSymbol } from \"../headerResolver/types.ts\";\nimport { CHeaderResolver } from \"../headerResolver/index.ts\";\nimport {\n  CFile,\n  DataType,\n  Enum,\n  FunctionDefinition,\n  FunctionSignature,\n  type Symbol,\n  Typedef,\n  Variable,\n} from \"./types.ts\";\nimport { C_TYPEDEF_TYPE_QUERY } from \"./queries.ts\";\nimport type Parser from \"tree-sitter\";\n\nexport class CSymbolRegistry {\n  headerResolver: CHeaderResolver;\n  files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n  #registry?: Map<string, CFile>;\n\n  constructor(\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n  ) {\n    this.headerResolver = new CHeaderResolver();\n    this.files = files;\n  }\n\n  /**\n   * Converts an ExportedSymbol to a Symbol.\n   * @param es The ExportedSymbol to convert.\n   * @returns The converted Symbol.\n   */\n  #convertSymbol(es: ExportedSymbol): Symbol {\n    if ([\"struct\", \"union\"].includes(es.type)) {\n      return new DataType(\n        es.name,\n        es,\n      );\n    }\n    if (es.type === \"enum\") {\n      return new Enum(\n        es.name,\n        es,\n      );\n    }\n    if (es.type === \"typedef\") {\n      return new Typedef(\n        es.name,\n        es,\n      );\n    }\n    if (es.type === \"function_definition\" || es.type === \"macro_function\") {\n      return new FunctionDefinition(\n        es.name,\n        es,\n        es.node.type === \"preproc_function_def\",\n      );\n    }\n    if (es.type === \"function_signature\") {\n      return new FunctionSignature(\n        es.name,\n        es,\n        es.node.type === \"preproc_function_def\",\n      );\n    }\n    if (es.type === \"variable\" || es.type === \"macro_constant\") {\n      return new Variable(\n        es.name,\n        es,\n        es.node.type === \"preproc_def\",\n      );\n    }\n    throw new Error(`Could not find symbol type for ${es.name} (${es.type})`);\n  }\n\n  /**\n   * Builds the symbol registry by iterating over all header and source files.\n   * It resolves the symbols in the header files and associates them with their\n   * corresponding source files.\n   */\n  #buildRegistry() {\n    this.#registry = new Map();\n    // Iterate over all header files and build the registry\n    const headerFiles = Array.from(this.files.values()).filter((file) =>\n      file.path.endsWith(\".h\")\n    );\n    for (const file of headerFiles) {\n      const exportedSymbols = this.headerResolver.resolveSymbols(file);\n      const header = new CFile(file, \".h\");\n      for (const es of exportedSymbols) {\n        const symbol = this.#convertSymbol(es);\n        if (symbol instanceof DataType) {\n          // Typedefs get priority over structs if they both have the same name\n          // That is because if they both have the same name, that means that the typedef\n          // also includes the struct's definition\n          if (!header.symbols.has(symbol.name)) {\n            header.symbols.set(symbol.name, symbol);\n          }\n        } else {\n          header.symbols.set(symbol.name, symbol);\n        }\n        if (symbol instanceof Enum) {\n          for (const [, enumMember] of symbol.members) {\n            header.symbols.set(enumMember.name, enumMember);\n          }\n        }\n      }\n      // Add the header file to the registry\n      this.#registry.set(file.path, header);\n    }\n\n    // Iterate over all source files to find function definitions.\n    const sourceFiles = Array.from(this.files.values()).filter((file) =>\n      file.path.endsWith(\".c\")\n    );\n    for (const file of sourceFiles) {\n      // We still need to resolve the symbols in the source files\n      // because they can depend on eachother.\n      // However they are not global.\n      const exportedSymbols = this.headerResolver.resolveSymbols(file);\n      const source = new CFile(file, \".c\");\n      for (const es of exportedSymbols) {\n        const symbol = this.#convertSymbol(es);\n        if (symbol instanceof DataType && !source.symbols.has(symbol.name)) {\n          // Typedefs get priority over structs if they both have the same name\n          // That is because if they both have the same name, that means that the typedef\n          // also includes the struct's definition\n          source.symbols.set(symbol.name, symbol);\n        } else {\n          source.symbols.set(symbol.name, symbol);\n        }\n        if (symbol instanceof Enum) {\n          for (const [name, enumMember] of symbol.members) {\n            source.symbols.set(name, enumMember);\n          }\n        }\n      }\n      // Add the source file to the registry\n      this.#registry.set(file.path, source);\n    }\n\n    // Associate typedefs with their corresponding data types\n    for (const [, header] of this.#registry.entries()) {\n      for (\n        const symbol of header.symbols\n          .values()\n          .filter((s) => s instanceof Typedef)\n          .map((s) => s as Typedef)\n      ) {\n        const typedefNode = symbol.declaration.node;\n        const typeCaptures = C_TYPEDEF_TYPE_QUERY.captures(typedefNode);\n        if (typeCaptures.length > 0) {\n          const typeCapture = typeCaptures[0];\n          const typeNode = typeCapture.node;\n          const typeName = typeNode.text;\n          for (const [, header] of this.#registry.entries()) {\n            if (header.symbols.has(typeName)) {\n              const dataType = header.symbols.get(typeName);\n              if (dataType instanceof DataType) {\n                symbol.datatype = dataType;\n                dataType.typedefs.set(symbol.name, symbol);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Returns the symbol registry. If the registry has not been built yet, it builds it first.\n   * @returns The symbol registry.\n   */\n  getRegistry(): Map<string, CFile> {\n    if (!this.#registry) {\n      this.#buildRegistry();\n    }\n    if (!this.#registry) {\n      throw new Error(\"Couldn't build registry for project\");\n    }\n    return this.#registry;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/symbolRegistry/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\nexport const C_FUNCTION_DEF_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n    (function_definition) @fdef\n  `,\n);\n\nexport const C_TYPEDEF_TYPE_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n    (type_definition\n    type: [\n  \t(type_identifier) @name\n        (struct_specifier name: (type_identifier) @name)\n        (enum_specifier name: (type_identifier) @name)\n        (union_specifier name: (type_identifier) @name)\n    ])\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/c/symbolRegistry/types.ts",
    "content": "import {\n  C_VARIABLE_TYPE,\n  type ExportedSymbol,\n} from \"../headerResolver/types.ts\";\nimport type Parser from \"tree-sitter\";\n\n/** Interface representing a C symbol */\nexport class Symbol {\n  /** The name of the symbol */\n  name: string;\n  /** The corresponding ExportedSymbol object */\n  declaration: ExportedSymbol;\n  constructor(name: string, declaration: ExportedSymbol) {\n    this.name = name;\n    this.declaration = declaration;\n  }\n}\n\n/** Interface representing a C function signature */\nexport class FunctionSignature extends Symbol {\n  /** The definition of the function in a source file */\n  definition?: FunctionDefinition;\n  /** Whether the function is a macro or not */\n  isMacro: boolean;\n  constructor(\n    name: string,\n    declaration: ExportedSymbol,\n    isMacro: boolean,\n  ) {\n    super(name, declaration);\n    this.isMacro = isMacro;\n    this.definition = undefined;\n  }\n}\n\n/** Interface representing a C function definition */\nexport class FunctionDefinition extends Symbol {\n  /** The declaration of the function in a header file */\n  signature?: FunctionSignature;\n  /** Whether the function is a macro or not */\n  isMacro: boolean;\n  constructor(name: string, declaration: ExportedSymbol, isMacro: boolean) {\n    super(name, declaration);\n    this.isMacro = isMacro;\n    this.signature = undefined;\n  }\n}\n\n/** Interface representing a C variable */\nexport class DataType extends Symbol {\n  typedefs: Map<string, Typedef>;\n  constructor(name: string, declaration: ExportedSymbol) {\n    super(name, declaration);\n    this.typedefs = new Map();\n  }\n}\n\n/** Interface representing a C typedef */\nexport class Typedef extends Symbol {\n  datatype?: DataType;\n}\n\nexport class Variable extends Symbol {\n  /** Whether the variable is a macro or not */\n  isMacro: boolean;\n  constructor(name: string, declaration: ExportedSymbol, isMacro: boolean) {\n    super(name, declaration);\n    this.isMacro = isMacro;\n  }\n}\n\nexport class EnumMember extends Symbol {\n  parent: Enum;\n  constructor(name: string, declaration: ExportedSymbol, parent: Enum) {\n    super(name, declaration);\n    this.parent = parent;\n  }\n}\n\nexport class Enum extends DataType {\n  members: Map<string, EnumMember>;\n  constructor(name: string, declaration: ExportedSymbol) {\n    super(name, declaration);\n    const enumerators = declaration.node.childForFieldName(\"body\");\n    this.members = new Map();\n    if (enumerators) {\n      for (\n        const enumerator of enumerators.children.filter((c) =>\n          c.type === \"enumerator\"\n        )\n      ) {\n        const idNode = enumerator.childForFieldName(\"name\");\n        if (!idNode) {\n          continue;\n        }\n        const member = new EnumMember(\n          idNode.text,\n          {\n            name: idNode.text,\n            type: C_VARIABLE_TYPE,\n            filepath: declaration.filepath,\n            specifiers: [],\n            qualifiers: [\"const\"],\n            node: enumerator,\n            identifierNode: idNode,\n          },\n          this,\n        );\n        this.members.set(enumerator.text, member);\n      }\n    }\n  }\n}\n\nexport const C_SOURCE_FILE = \".c\";\nexport const C_HEADER_FILE = \".h\";\nexport type CFileType = typeof C_SOURCE_FILE | typeof C_HEADER_FILE;\n\n/** Interface representing a C file */\nexport class CFile {\n  /** The corresponding header file */\n  file: {\n    path: string;\n    rootNode: Parser.SyntaxNode;\n  };\n  /** The symbols defined in the header file */\n  symbols: Map<string, Symbol>;\n  /** The type of the file (i.e. .c or .h) */\n  type: CFileType;\n  constructor(\n    file: { path: string; rootNode: Parser.SyntaxNode },\n    filetype: CFileType,\n  ) {\n    this.file = file;\n    this.type = filetype;\n    this.symbols = new Map();\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/.clangd",
    "content": "CompileFlags:\n  Add: [-xc, -std=c99]\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/.napirc",
    "content": "{\n  \"language\": \"c\",\n  \"project\": {\n    \"include\": [\n      \"**/*.c\",\n      \"**/*.h\"\n    ],\n    \"exclude\": [\n      \".git/**\",\n      \"**/dist/**\",\n      \"**/build/**\",\n      \"**/bin/**\",\n      \"**/obj/**\",\n      \"**/packages/**\",\n      \"**/.vs/**\",\n      \"**/TestResults/**\",\n      \"**/*.user\",\n      \"**/*.suo\",\n      \"**/.nuget/**\",\n      \"**/artifacts/**\",\n      \"**/packages/**\",\n      \"**/util/**\",\n      \"**/test/**\",\n      \"**/perf/**\",\n      \"**/napi_out/**\",\n      \"**/rapports/**\"\n    ]\n  },\n  \"outDir\": \"napi_out\",\n  \"metrics\": {\n    \"file\": {\n      \"maxChar\": 100000,\n      \"maxLine\": 1000,\n      \"maxDep\": 10\n    },\n    \"symbol\": {\n      \"maxChar\": 50000,\n      \"maxLine\": 500,\n      \"maxDep\": 5\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/all.h",
    "content": "#include \"burgers.h\"\n#include \"personnel.h\"\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/burgers.c",
    "content": "// TOTAL SYMBOL COUNT : 1\n// TOTAL FUNCTION COUNT : 0\n#include \"burgers.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nstatic struct Burger* burgers[MAX_BURGERS];\n\nstruct Burger* create_burger(char name[50], enum Condiment condiments[5], union Sauce sauce) {\n    struct Burger* new_burger = malloc(sizeof(struct Burger));\n    if (new_burger == NULL) {\n        return NULL; // Memory allocation failed\n    }\n\n    new_burger->id = ++burger_count;\n    strncpy(new_burger->name, name, sizeof(new_burger->name) - 1);\n    new_burger->name[sizeof(new_burger->name) - 1] = '\\0'; // Ensure null-termination\n    memcpy(new_burger->condiments, condiments, sizeof(new_burger->condiments));\n    new_burger->sauce = sauce;\n\n    return new_burger;\n}\n\nvoid destroy_burger(struct Burger* burger) {\n    if (burger != NULL) {\n        free(burger);\n    }\n}\n\nstruct Burger* get_burger_by_id(int id) {\n    for (int i = 0; i < burger_count; i++) {\n        if (burgers[i] != NULL && burgers[i]->id == id) {\n            return burgers[i];\n        }\n    }\n    return NULL; // Burger not found\n}\n\nstruct Burger* get_cheapest_burger() {\n    struct Burger* cheapest_burger = NULL;\n    float min_price = __FLT_MAX__; // Initialize to maximum float value\n\n    for (int i = 0; i < burger_count; i++) {\n        if (burgers[i] != NULL && burgers[i]->price < min_price) {\n            min_price = burgers[i]->price;\n            cheapest_burger = burgers[i];\n        }\n    }\n\n    return cheapest_burger;\n}\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/burgers.h",
    "content": "// TOTAL SYMBOL COUNT : 16\n// TOTAL FUNCTION COUNT : 5\n#ifndef BURGERS_H\n#define BURGERS_H\n#include <stdbool.h>\n\n#define MAX_BURGERS 100\n#define MAX(x, y) ((x) > (y) ? (x) : (y))\n\nenum Condiment {\n    NONE = 0,\n    SALAD = 30,\n    TOMATO = 40,\n    ONION = 50,\n    CHEESE = 60,\n    PICKLE = 70,\n};\n\nenum ClassicSauces {\n    KETCHUP = 0,\n    MAYO = 1,\n    MUSTARD = 2,\n    BBQ = 3,\n    SPICY = 4,\n};\n\nunion Sauce {\n    enum ClassicSauces classic_sauce;\n    char custom_sauce[50];\n};\n\ntypedef struct {\n    int id;\n    union Sauce sauce;\n    _Bool salted;\n    float price;\n} Fries;\n\ntypedef enum Drink_t {\n    COKE = 0,\n    ICED_TEA = 1,\n    LEMONADE = 2,\n    COFFEE = 3,\n    WATER = 4,\n} Drink;\n\nstruct Burger {\n    int id;\n    char name[50];\n    float price;\n    enum Condiment condiments[5];\n    union Sauce sauce;\n};\n\nconst struct Burger classicBurger = {\n    .id = 1,\n    .name = \"Classic Burger\",\n    .price = 5.99,\n    .condiments = {SALAD, TOMATO, ONION, CHEESE},\n    .sauce = {.classic_sauce = KETCHUP}\n};\n\nstatic int burger_count = 0;\n\nstruct Burger* create_burger(char name[50], enum Condiment condiments[5], union Sauce sauce);\nvoid destroy_burger(struct Burger* burger);\nstruct Burger* get_burger_by_id(int id);\nstruct Burger* get_cheapest_burger();\n\n#endif\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/crashcases.h",
    "content": "// Code segments taken from the Pokémon Fire Red/Leaf Green decomp project\n// https://github.com/pret/pokefirered\n\n#define CPU_FAST_SET_SRC_FIXED 0x01000000\n\nvoid CpuFastSet(const void *src, void *dest, unsigned int control);\n\n#define CpuFastFill(value, dest, size)                               \\\n{                                                                    \\\n    vu32 tmp = (vu32)(value);                                        \\\n    CpuFastSet((void *)&tmp,                                         \\\n               dest,                                                 \\\n               CPU_FAST_SET_SRC_FIXED | ((size)/(32/8) & 0x1FFFFF)); \\\n}\n\nstruct ObjectEvent {\n    int id;\n};\n\nstruct Sprite {\n    int x;\n    int y;\n};\n\nint PlaceholderFunction(struct ObjectEvent *oe, struct Sprite *s);\n\nint (*const gMovementTypeFuncs_WanderAround[])(struct ObjectEvent *, struct Sprite *) = {\n    PlaceholderFunction,\n    PlaceholderFunction,\n    PlaceholderFunction,\n    PlaceholderFunction,\n    PlaceholderFunction,\n    PlaceholderFunction,\n    PlaceholderFunction,\n};\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/errors.h",
    "content": "#include \"thisfiledoesnotexist.h\"\n\n// Unnamed enum\nenum {\n    ONE, TWO, THREE, VIVA, L, ALGERIE\n};\nint xbox = ONE;\n\nstruct {\n    int useless; // USELESS?\n};\n\n// Non-standard typedef\nstruct Salut_t {\n\tint id;\n    int waow;\n} typedef Salut;\n\n// Syntax error\nint x =??;\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/main.c",
    "content": "#include \"all.h\"\n#include <stdio.h>\n\nint main(void) {\n    struct Burger* burger = create_burger(\"Cheeseburger\", (enum Condiment[]){SALAD, ONION}, (union Sauce){KETCHUP});\n    printf(\"Burger ID: %d\\n\", burger->id);\n    printf(\"Burger Name: %s\\n\", burger->name);\n    printf(\"Burger Price: %.2f\\n\", burger->price);\n    printf(\"Burger Condiments: \");\n    for (int i = 0; i < 5; i++) {\n        if (burger->condiments[i] != 0) {\n            printf(\"%d \", burger->condiments[i]);\n        }\n    }\n    printf(\"\\n\");\n    printf(\"Burger Sauce: %d\\n\", burger->sauce.classic_sauce);\n\n    Employee* emp1 = create_employee(1, \"Alice\", \"Manager\", IT, 75000.0);\n    print_employee_details(emp1);\n    return 0;\n}\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/oldman.h",
    "content": "// Code segments taken from the Pokémon Fire Red/Leaf Green decomp project\n// https://github.com/pret/pokefirered\n#include <stdbool.h>\n\n#define BARD_SONG_LENGTH 14\n#define PLAYER_NAME_LENGTH 7\n#define TRAINER_ID_LENGTH 4\n#define NUM_STORYTELLER_TALES 5\n#define GIDDY_MAX_TALES 6\n#define GIDDY_MAX_QUESTIONS 10\n#define NUM_TRADER_ITEMS 5\n\nstruct MauvilleManCommon\n{\n    int id;\n};\n\nstruct MauvilleManBard\n{\n    /*0x00*/ int id;\n    /*0x02*/ int songLyrics[BARD_SONG_LENGTH];\n    /*0x0E*/ int temporaryLyrics[BARD_SONG_LENGTH];\n    /*0x1A*/ int playerName[PLAYER_NAME_LENGTH + 1];\n    /*0x22*/ int filler_2DB6[0x3];\n    /*0x25*/ int playerTrainerId[TRAINER_ID_LENGTH];\n    /*0x29*/ bool hasChangedSong;\n    /*0x2A*/ int language;\n}; /*size = 0x2C*/\n\nstruct MauvilleManStoryteller\n{\n    int id;\n    bool alreadyRecorded;\n    int filler2[2];\n    int gameStatIDs[NUM_STORYTELLER_TALES];\n    int trainerNames[NUM_STORYTELLER_TALES][PLAYER_NAME_LENGTH];\n    int statValues[NUM_STORYTELLER_TALES][4];\n    int language[NUM_STORYTELLER_TALES];\n};\n\nstruct MauvilleManGiddy\n{\n    /*0x00*/ int id;\n    /*0x01*/ int taleCounter;\n    /*0x02*/ int questionNum;\n    /*0x04*/ int randomWords[GIDDY_MAX_TALES];\n    /*0x18*/ int questionList[GIDDY_MAX_QUESTIONS];\n    /*0x20*/ int language;\n}; /*size = 0x2C*/\n\nstruct MauvilleManHipster\n{\n    int id;\n    bool alreadySpoken;\n    int language;\n};\n\nstruct MauvilleOldManTrader\n{\n    int id;\n    int decorIds[NUM_TRADER_ITEMS];\n    int playerNames[NUM_TRADER_ITEMS][11];\n    int alreadyTraded;\n    int language[NUM_TRADER_ITEMS];\n};\n\ntypedef union OldMan\n{\n    struct MauvilleManCommon common;\n    struct MauvilleManBard bard;\n    struct MauvilleManGiddy giddy;\n    struct MauvilleManHipster hipster;\n    struct MauvilleOldManTrader trader;\n    struct MauvilleManStoryteller storyteller;\n    int filler[0x40];\n} OldMan;\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/personnel.c",
    "content": "#include \"personnel.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nEmployee* create_employee(int id, const char* name, const char* position, enum Department department, float salary) {\n    if (employee_count >= MAX_EMPLOYEES) {\n        return NULL; // Maximum employee limit reached\n    }\n\n    Employee* new_employee = malloc(sizeof(Employee));\n    if (new_employee == NULL) {\n        return NULL; // Memory allocation failed\n    }\n\n    new_employee->id = id;\n    strncpy(new_employee->name, name, sizeof(new_employee->name) - 1);\n    new_employee->name[sizeof(new_employee->name) - 1] = '\\0'; // Ensure null-termination\n    strncpy(new_employee->position, position, sizeof(new_employee->position) - 1);\n    new_employee->position[sizeof(new_employee->position) - 1] = '\\0'; // Ensure null-termination\n    new_employee->department = department;\n    new_employee->salary = salary;\n\n    employees[employee_count++] = new_employee;\n\n    return new_employee;\n}\n\nvoid destroy_employee(Employee* employee) {\n    if (employee != NULL) {\n        free(employee);\n    }\n}\n\nEmployee* get_employee_by_id(int id) {\n    for (int i = 0; i < employee_count; i++) {\n        if (employees[i] != NULL && employees[i]->id == id) {\n            return employees[i];\n        }\n    }\n    return NULL; // Employee not found\n}\n\nEmployee* get_highest_paid_employee() {\n    Employee* highest_paid_employee = NULL;\n    float max_salary = -1.0f; // Initialize to a negative value\n\n    for (int i = 0; i < employee_count; i++) {\n        if (employees[i] != NULL && employees[i]->salary > max_salary) {\n            max_salary = employees[i]->salary;\n            highest_paid_employee = employees[i];\n        }\n    }\n\n    return highest_paid_employee;\n}\n\nEmployee** get_employees_by_department(enum Department department, int* count) {\n    Employee** department_employees = malloc(MAX_EMPLOYEES * sizeof(Employee*));\n    if (department_employees == NULL) {\n        *count = 0;\n        return NULL; // Memory allocation failed\n    }\n\n    *count = 0;\n    for (int i = 0; i < employee_count; i++) {\n        if (employees[i] != NULL && employees[i]->department == department) {\n            department_employees[(*count)++] = employees[i];\n        }\n    }\n\n    return department_employees;\n}\n\nvoid print_employee_details(const Employee* employee) {\n    if (employee != NULL) {\n        printf(\"ID: %d\\n\", employee->id);\n        printf(\"Name: %s\\n\", employee->name);\n        printf(\"Position: %s\\n\", employee->position);\n        printf(\"Department: %d\\n\", employee->department);\n        printf(\"Salary: %.2f\\n\", employee->salary);\n    } else {\n        printf(\"Employee not found.\\n\");\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/cFiles/personnel.h",
    "content": "// TOTAL SYMBOL COUNT : 12\n// TOTAL FUNCTION COUNT : 6\n#ifndef PERSONNEL_H\n#define PERSONNEL_H\n#include <stdbool.h>\n\n#define MAX_EMPLOYEES 100\n\nenum Department {\n    HR = 0,\n    IT = 1,\n    SALES = 2,\n    MARKETING = 3,\n    FINANCE = 4,\n};\n\ntypedef struct {\n    int id;\n    char name[50];\n    char position[50];\n    enum Department department;\n    float salary;\n} Employee;\n\nstatic int employee_count = 0;\nstatic Employee* employees[MAX_EMPLOYEES];\n\nEmployee* create_employee(int id, const char* name, const char* position, enum Department department, float salary);\nvoid destroy_employee(Employee* employee);\nEmployee* get_employee_by_id(int id);\nEmployee* get_highest_paid_employee();\nEmployee** get_employees_by_department(enum Department department, int* count);\nvoid print_employee_details(const Employee* employee);\n\n#endif\n"
  },
  {
    "path": "src/languagePlugins/c/testFiles/index.ts",
    "content": "import * as fs from \"node:fs\";\nimport { extname, join } from \"@std/path\";\nimport type Parser from \"tree-sitter\";\nimport { cLanguage, cParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport type z from \"zod\";\nimport type { localConfigSchema } from \"../../../cli/middlewares/napiConfig.ts\";\n\nif (!import.meta.dirname) {\n  throw new Error(\"import.meta.dirname is not defined\");\n}\nexport const cFilesFolder = join(import.meta.dirname, \"cFiles\");\nconst cFilesMap = new Map<\n  string,\n  { path: string; rootNode: Parser.SyntaxNode }\n>();\n\n/**\n * Recursively finds all C files in the given directory and its subdirectories.\n * @param dir - The directory to search in.\n */\nfunction findCFiles(dir: string) {\n  const files = fs.readdirSync(dir);\n  files.forEach((file) => {\n    const fullPath = join(dir, file);\n    const stat = fs.statSync(fullPath);\n    if (stat.isDirectory()) {\n      if (\n        !fullPath.includes(\".extracted/\") &&\n        !fullPath.includes(\"bin/\") &&\n        !fullPath.includes(\"obj/\")\n      ) {\n        findCFiles(fullPath);\n      }\n    } else if (\n      extname(fullPath) === \".c\" ||\n      extname(fullPath) === \".h\"\n    ) {\n      const content = fs.readFileSync(fullPath, \"utf8\");\n      const tree = cParser.parse(content);\n      cFilesMap.set(fullPath, { path: fullPath, rootNode: tree.rootNode });\n    }\n  });\n}\n\nexport function getCFilesMap(): Map<\n  string,\n  { path: string; rootNode: Parser.SyntaxNode }\n> {\n  findCFiles(cFilesFolder);\n  return cFilesMap;\n}\n\nexport function getCFilesContentMap(): Map<\n  string,\n  { path: string; content: string }\n> {\n  findCFiles(cFilesFolder);\n  const contentMap = new Map<string, { path: string; content: string }>();\n  for (const [filePath, file] of cFilesMap) {\n    contentMap.set(filePath, {\n      path: file.path,\n      content: file.rootNode.text,\n    });\n  }\n  return contentMap;\n}\n\nexport const dummyLocalConfig = {\n  language: cLanguage,\n  project: {\n    include: [],\n    exclude: [],\n  },\n  outDir: \"./dist\",\n  c: {\n    includedirs: [],\n  },\n} as z.infer<typeof localConfigSchema>;\n"
  },
  {
    "path": "src/languagePlugins/c/warnings/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CWarningManager } from \"./index.ts\";\nimport { cFilesFolder, getCFilesMap } from \"../testFiles/index.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CWarningManager\", () => {\n  const files = getCFilesMap();\n  const manager = new CWarningManager(files);\n  const diagnostics = manager.diagnostics;\n  test(\"finds all diagnostics\", () => {\n    expect(diagnostics.length).toBe(2);\n  });\n\n  test(\"diagnostics are of correct type\", () => {\n    expect(diagnostics[0].message).toContain(\"Tree-sitter error\");\n    expect(diagnostics[1].message).toContain(\"Unnamed\");\n  });\n\n  test(\"diagnostics are in correct files\", () => {\n    expect(diagnostics[0].filename).toBe(\n      join(cFilesFolder, \"errors.h\"),\n    );\n    expect(diagnostics[1].filename).toBe(\n      join(cFilesFolder, \"errors.h\"),\n    );\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/c/warnings/index.ts",
    "content": "import {\n  type ManifestDiagnostics,\n  TreeSitterError,\n  UnnamedDatatypeWarning,\n} from \"./types.ts\";\nimport type Parser from \"tree-sitter\";\nimport { C_ERROR_QUERY, C_UNNAMED_DATATYPE_QUERY } from \"./queries.ts\";\n\nexport class CWarningManager {\n  diagnostics: ManifestDiagnostics[] = [];\n  constructor(\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n  ) {\n    for (const [, { path, rootNode }] of files) {\n      this.diagnostics.push(\n        ...this.getDiagnostics(path, rootNode),\n      );\n    }\n  }\n\n  private getDiagnostics(\n    path: string,\n    rootNode: Parser.SyntaxNode,\n  ): ManifestDiagnostics[] {\n    const diagnostics: ManifestDiagnostics[] = [];\n    const errorQuery = C_ERROR_QUERY.captures(rootNode);\n    const unnamedDatatypeQuery = C_UNNAMED_DATATYPE_QUERY.captures(rootNode);\n\n    for (const capture of errorQuery) {\n      diagnostics.push(new TreeSitterError(path, capture));\n    }\n\n    for (const capture of unnamedDatatypeQuery) {\n      diagnostics.push(new UnnamedDatatypeWarning(path, capture));\n    }\n\n    return diagnostics;\n  }\n\n  /**\n   * Print the diagnostics to the console.\n   * @param count The number of diagnostics to print. If undefined, all diagnostics will be printed.\n   * @example\n   * ```ts\n   * const manager = new CWarningManager(files);\n   * // Prints up to 5 diagnostics\n   * manager.printDiagnostics(5);\n   * // Prints all diagnostics\n   * manager.printDiagnostics();\n   * ```\n   */\n  public printDiagnostics(count: number | undefined = undefined): void {\n    if (!count) {\n      count = this.diagnostics.length;\n    }\n    if (count > this.diagnostics.length) {\n      count = this.diagnostics.length;\n    }\n    for (let i = 0; i < count; i++) {\n      const diagnostic = this.diagnostics[i];\n      console.warn(diagnostic.message);\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/c/warnings/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { cParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\nexport const C_ERROR_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n  (ERROR) @error\n`,\n);\n\nexport const C_UNNAMED_DATATYPE_QUERY = new Parser.Query(\n  cParser.getLanguage(),\n  `\n  (translation_unit\n  [\n    (struct_specifier\n    !name) @struct\n    (union_specifier\n    !name) @union\n  ])\n  (preproc_ifdef\n  [\n    (struct_specifier\n    !name) @struct\n    (union_specifier\n    !name) @union\n  ])\n`,\n);\n"
  },
  {
    "path": "src/languagePlugins/c/warnings/types.ts",
    "content": "import type Parser from \"tree-sitter\";\n\nexport interface ManifestDiagnostics {\n  filename: string;\n  line: number;\n  column: number;\n  message: string;\n}\n\nexport class TreeSitterError implements ManifestDiagnostics {\n  filename: string;\n  line: number;\n  column: number;\n  message: string;\n\n  constructor(\n    filename: string,\n    capture: Parser.QueryCapture,\n  ) {\n    this.filename = filename;\n    this.line = capture.node.startPosition.row + 1;\n    this.column = capture.node.startPosition.column + 1;\n    this.message =\n      `[WARN] (${filename}:${this.line}:${this.column}) | Tree-sitter error`;\n  }\n}\n\nexport class UnnamedDatatypeWarning implements ManifestDiagnostics {\n  filename: string;\n  line: number;\n  column: number;\n  datatype: string;\n  message: string;\n\n  constructor(\n    filename: string,\n    capture: Parser.QueryCapture,\n  ) {\n    this.filename = filename;\n    this.line = capture.node.startPosition.row + 1;\n    this.column = capture.node.startPosition.column + 1;\n    this.datatype = capture.name;\n    this.message =\n      `[WARN] (${filename}:${this.line}:${this.column}) | Unnamed ${this.datatype}`;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/README.md",
    "content": "# NanoAPI C# Plugin\n\nThis plugin manages parsing and mapping of dependencies in C#/.NET projects.\n\n## Class diagram\n\n```mermaid\nclassDiagram\n    class CSharpDependencyFormatter {\n        - invResolver: CSharpInvocationResolver\n        - usingResolver: CSharpUsingResolver\n        - nsMapper: CSharpNamespaceMapper\n        - projectMapper: CSharpProjectMapper\n        + formatFile(filepath: string): CSharpFile | undefined\n    }\n\n    class CSharpDependency {\n        + id: string\n        + isExternal: boolean\n        + symbols: Record<string, string>\n        + isNamespace?: boolean\n    }\n\n    class CSharpDependent {\n        + id: string\n        + symbols: Record<string, string>\n    }\n\n    class CSharpSymbol {\n        + id: string\n        + type: SymbolType\n        + lineCount: number\n        + characterCount: number\n        + dependents: Record<string, CSharpDependent>\n        + dependencies: Record<string, CSharpDependency>\n    }\n\n    class CSharpFile {\n        + id: string\n        + filepath: string\n        + lineCount: number\n        + characterCount: number\n        + dependencies: Record<string, CSharpDependency>\n        + symbols: Record<string, CSharpSymbol>\n    }\n\n    class CSharpInvocationResolver {\n        - parser: Parser\n        - nsMapper: CSharpNamespaceMapper\n        - usingResolver: CSharpUsingResolver\n        - resolvedImports: ResolvedImports\n        - cache: Map<string, Invocations>\n        - extensions: ExtensionMethodMap\n        + getInvocationsFromFile(filepath: string): Invocations\n        + getInvocationsFromNode(node: Parser.SyntaxNode, filepath: string): Invocations\n        + isUsedInFile(filepath: string, symbol: SymbolNode): boolean\n    }\n\n    class Invocations {\n        + resolvedSymbols: SymbolNode[]\n        + unresolved: string[]\n    }\n\n    class CSharpNamespaceMapper {\n        - files: Map<string, File>\n        - nsResolver: CSharpNamespaceResolver\n        - nsTree: NamespaceNode\n        - exportsCache: Map<string, SymbolNode[]>\n        - fileExports: Map<string, SymbolNode[]>\n        + getFile(key: string): File\n        + buildNamespaceTree(): NamespaceNode\n        + findNamespaceInTree(tree: NamespaceNode, namespaceName: string): NamespaceNode | null\n        + findClassInTree(tree: NamespaceNode, className: string): SymbolNode | null\n        + getExportsForFile(filepath: string): SymbolNode[]\n        + getFullNSName(namespace: NamespaceNode): string\n    }\n\n    class NamespaceNode {\n        + name: string\n        + exports: SymbolNode[]\n        + childrenNamespaces: NamespaceNode[]\n        + parentNamespace?: NamespaceNode\n    }\n\n    class SymbolNode {\n        + name: string\n        + type: SymbolType\n        + namespace: string\n        + filepath: string\n        + node: Parser.SyntaxNode\n    }\n\n    class CSharpNamespaceResolver {\n        - parser: Parser\n        - currentFile: string\n        - cache: Map<string, Namespace[]>\n        + getNamespacesFromFile(file: File): Namespace[]\n        + getExportsFromNamespaces(namespaces: Namespace[]): ExportedSymbol[]\n    }\n\n    class Namespace {\n        + name: string\n        + node: Parser.SyntaxNode\n        + identifierNode?: Parser.SyntaxNode\n        + exports: ExportedSymbol[]\n        + childrenNamespaces: Namespace[]\n    }\n\n    class ExportedSymbol {\n        + name: string\n        + type: SymbolType\n        + node: Parser.SyntaxNode\n        + identifierNode: Parser.SyntaxNode\n        + namespace?: string\n        + filepath: string\n        + parent?: ExportedSymbol\n    }\n\n    class CSharpProjectMapper {\n        + rootFolder: string\n        + subprojects: DotNetProject[]\n        + findSubprojectForFile(filePath: string): DotNetProject | null\n        + updateGlobalUsings(globalUsings: ResolvedImports, subproject: DotNetProject)\n        + getGlobalUsings(filepath: string): ResolvedImports\n    }\n\n    class DotNetProject {\n        + rootFolder: string\n        + csprojPath: string\n        + csprojContent: string\n        + globalUsings: ResolvedImports\n    }\n\n    class CSharpUsingResolver {\n        - nsMapper: CSharpNamespaceMapper\n        - usingDirectives: UsingDirective[]\n        - cachedImports: Map<string, ResolvedImports>\n        - projectmapper: CSharpProjectMapper\n        - cachedExternalDeps: Set<string>\n        + parseUsingDirectives(filepath: string): UsingDirective[]\n        + resolveUsingDirectives(filepath: string): ResolvedImports\n        + getGlobalUsings(filepath: string): ResolvedImports\n        + findClassInImports(imports: ResolvedImports, className: string, filepath: string): SymbolNode | null\n    }\n\n    class UsingDirective {\n        + node: Parser.SyntaxNode\n        + type: UsingType\n        + filepath: string\n        + id: string\n        + alias?: string\n    }\n\n    class InternalSymbol {\n        + usingtype: UsingType\n        + filepath: string\n        + alias?: string\n        + symbol?: SymbolNode\n        + namespace?: NamespaceNode\n    }\n\n    class ExternalSymbol {\n        + usingtype: UsingType\n        + filepath: string\n        + alias?: string\n        + name: string\n    }\n\n    class ResolvedImports {\n        + internal: InternalSymbol[]\n        + external: ExternalSymbol[]\n    }\n\n    class File {\n        + path: string\n        + rootNode: Parser.SyntaxNode\n    }\n\n    class ExtractedFile {\n        + subproject: DotNetProject\n        + namespace: string\n        + symbol: SymbolNode\n        + imports: UsingDirective[]\n        + name: string\n    }\n\n    class CSharpExtractor {\n        - manifest: DependencyManifest\n        - projectMapper: CSharpProjectMapper\n        - nsMapper: CSharpNamespaceMapper\n        - usingResolver: CSharpUsingResolver\n        + extractSymbol(symbol: SymbolNode): ExtractedFile[]\n        + extractAndSaveSymbol(symbol: SymbolNode): void\n        + extractSymbolByName(symbolName: string): ExtractedFile[] | undefined\n        + extractAndSaveSymbolFromFile(filePath:string, symbolName: string): void\n        + getContent(file: ExtractedFile): string\n    }\n\n    class CSharpExtensionResolver {\n        - namespaceMapper: CSharpNamespaceMapper\n        - extensions: ExtensionMethodMap\n        + getExtensions(): ExtensionMethodMap\n    }\n\n    class ExtensionMethod {\n        + node: Parser.SyntaxNode\n        + symbol: SymbolNode\n        + name: string\n        + type: string\n        + extendedType: string\n        + typeParameters?: string[]\n    }\n\n    class ExtensionMethodMap {\n        + [namespace: string]: ExtensionMethod[]\n    }\n\n    enum SymbolType\n    enum UsingType\n    class Parser {\n        + getLanguage(): any\n        + parse(content: string): any\n    }\n\n    CSharpDependencyFormatter --> CSharpInvocationResolver\n    CSharpDependencyFormatter --> CSharpUsingResolver\n    CSharpDependencyFormatter --> CSharpNamespaceMapper\n    CSharpDependencyFormatter --> CSharpProjectMapper\n    CSharpInvocationResolver --> CSharpNamespaceMapper\n    CSharpInvocationResolver --> CSharpUsingResolver\n    CSharpInvocationResolver --> ResolvedImports\n    CSharpNamespaceMapper --> CSharpNamespaceResolver\n    CSharpNamespaceMapper --> NamespaceNode\n    CSharpNamespaceMapper --> SymbolNode\n    CSharpNamespaceResolver --> Namespace\n    CSharpNamespaceResolver --> ExportedSymbol\n    CSharpProjectMapper --> DotNetProject\n    CSharpProjectMapper --> ResolvedImports\n    CSharpUsingResolver --> CSharpNamespaceMapper\n    CSharpUsingResolver --> CSharpProjectMapper\n    CSharpUsingResolver --> UsingDirective\n    CSharpUsingResolver --> ResolvedImports\n    UsingDirective --> UsingType\n    InternalSymbol --> UsingType\n    InternalSymbol --> SymbolNode\n    InternalSymbol --> NamespaceNode\n    ExternalSymbol --> UsingType\n    ResolvedImports --> InternalSymbol\n    ResolvedImports --> ExternalSymbol\n    File --> Parser\n    CSharpExtractor --> DependencyManifest\n    CSharpExtractor --> CSharpProjectMapper\n    CSharpExtractor --> CSharpNamespaceMapper\n    CSharpExtractor --> CSharpUsingResolver\n    ExtractedFile --> DotNetProject\n    ExtractedFile --> SymbolNode\n    ExtractedFile --> UsingDirective\n    CSharpExtensionResolver --> CSharpNamespaceMapper\n    CSharpExtensionResolver --> ExtensionMethodMap\n    ExtensionMethodMap --> ExtensionMethod\n```\n"
  },
  {
    "path": "src/languagePlugins/csharp/dependencyFormatting/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CSharpDependencyFormatter } from \"./index.ts\";\nimport {\n  csharpFilesFolder,\n  getCSharpFilesMap,\n  getCsprojFilesMap,\n} from \"../testFiles/index.ts\";\nimport { join } from \"@std/path\";\nimport type { File } from \"../namespaceResolver/index.ts\";\n\ndescribe(\"Dependency formatting\", () => {\n  const parsedfiles: Map<string, File> = getCSharpFilesMap();\n  const csprojfiles = getCsprojFilesMap();\n  const formatter = new CSharpDependencyFormatter(parsedfiles, csprojfiles);\n\n  test(\"SemiNamespaced.cs\", () => {\n    const snpath = join(csharpFilesFolder, \"SemiNamespaced.cs\");\n    const seminamespaced = formatter.formatFile(snpath);\n    expect(seminamespaced).toBeDefined();\n    if (!seminamespaced) return;\n    expect(seminamespaced.id).toBe(snpath);\n    expect(seminamespaced.dependencies[snpath]).toBeDefined();\n    expect(Object.keys(seminamespaced.symbols).length).toBe(3);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/dependencyFormatting/index.ts",
    "content": "import type { SymbolType } from \"../namespaceResolver/index.ts\";\nimport {\n  CSharpInvocationResolver,\n  type Invocations,\n} from \"../invocationResolver/index.ts\";\nimport {\n  CSharpNamespaceMapper,\n  type SymbolNode,\n} from \"../namespaceMapper/index.ts\";\nimport type Parser from \"tree-sitter\";\nimport {\n  CSharpUsingResolver,\n  type ResolvedImports,\n} from \"../usingResolver/index.ts\";\nimport { CSharpProjectMapper } from \"../projectMapper/index.ts\";\n\n/**\n * Represents a dependency in a C# file.\n */\nexport interface CSharpDependency {\n  /**\n   * The unique identifier of the dependency.\n   */\n  id: string;\n  /**\n   * Indicates whether the dependency is external.\n   */\n  isExternal: boolean;\n  /**\n   * A record of symbols associated with the dependency.\n   */\n  symbols: Record<string, string>;\n  /**\n   * Indicates whether the dependency is a namespace.\n   */\n  isNamespace?: boolean;\n}\n\n/**\n * Represents a dependent in a C# file.\n */\nexport interface CSharpDependent {\n  /**\n   * The unique identifier of the dependent.\n   */\n  id: string;\n  /**\n   * A record of symbols associated with the dependent.\n   */\n  symbols: Record<string, string>;\n}\n\n/**\n * Represents a symbol in a C# file.\n */\nexport interface CSharpSymbol {\n  /**\n   * The unique identifier of the symbol.\n   */\n  id: string;\n  /**\n   * The type of the symbol.\n   */\n  type: SymbolType;\n  /**\n   * The number of lines the symbol spans.\n   */\n  lineCount: number;\n  /**\n   * The number of characters the symbol spans.\n   */\n  characterCount: number;\n  /**\n   * The syntax node representing the symbol.\n   */\n  node: Parser.SyntaxNode;\n  /**\n   * A record of dependents associated with the symbol.\n   */\n  dependents: Record<string, CSharpDependent>;\n  /**\n   * A record of dependencies associated with the symbol.\n   */\n  dependencies: Record<string, CSharpDependency>;\n}\n\n/**\n * Represents a C# file with its metadata and dependencies.\n */\nexport interface CSharpFile {\n  /**\n   * The unique identifier of the file.\n   */\n  id: string;\n  /**\n   * The file path of the C# file.\n   */\n  filepath: string;\n  /**\n   * The root syntax node of the file.\n   */\n  rootNode: Parser.SyntaxNode;\n  /**\n   * The number of lines in the file.\n   */\n  lineCount: number;\n  /**\n   * The number of characters in the file.\n   */\n  characterCount: number;\n  /**\n   * A record of dependencies associated with the file.\n   */\n  dependencies: Record<string, CSharpDependency>;\n  /**\n   * A record of symbols defined in the file.\n   */\n  symbols: Record<string, CSharpSymbol>;\n}\n\nexport class CSharpDependencyFormatter {\n  private invResolver: CSharpInvocationResolver;\n  private usingResolver: CSharpUsingResolver;\n  private nsMapper: CSharpNamespaceMapper;\n  private projectMapper: CSharpProjectMapper;\n\n  /**\n   * Constructs a new CSharpDependencyFormatter.\n   * @param files - A map of file paths to their corresponding syntax nodes.\n   */\n  constructor(\n    parsedFiles: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n    csprojFiles: Map<string, { path: string; content: string }>,\n  ) {\n    this.nsMapper = new CSharpNamespaceMapper(parsedFiles);\n    this.projectMapper = new CSharpProjectMapper(csprojFiles);\n    this.invResolver = new CSharpInvocationResolver(\n      this.nsMapper,\n      this.projectMapper,\n    );\n    this.usingResolver = new CSharpUsingResolver(\n      this.nsMapper,\n      this.projectMapper,\n    );\n    for (const [fp] of parsedFiles) {\n      this.usingResolver.resolveUsingDirectives(fp);\n    }\n  }\n\n  /**\n   * Formats exported symbols into a record of CSharpSymbol.\n   * @param exportedSymbols - An array of exported symbols.\n   * @returns A record of symbol names to their corresponding CSharpSymbol.\n   */\n  private formatSymbols(\n    exportedSymbols: SymbolNode[],\n  ): Record<string, CSharpSymbol> {\n    const symbols: Record<string, CSharpSymbol> = {};\n    for (const symbol of exportedSymbols) {\n      const fullname = (symbol.namespace !== \"\" ? symbol.namespace + \".\" : \"\") +\n        symbol.name;\n      const dependencies = this.invResolver.getInvocationsFromNode(\n        symbol.node,\n        symbol.filepath,\n      );\n      dependencies.resolvedSymbols = dependencies.resolvedSymbols.filter(\n        (sm) => sm.name !== symbol.name,\n      );\n      symbols[fullname] = {\n        id: fullname,\n        type: symbol.type,\n        node: symbol.node,\n        lineCount: symbol.node.endPosition.row - symbol.node.startPosition.row,\n        characterCount: symbol.node.endIndex - symbol.node.startIndex,\n        dependencies: this.formatDependencies(dependencies),\n        dependents: {},\n      };\n    }\n    return symbols;\n  }\n\n  /**\n   * Formats invocations into a record of CSharpDependency.\n   * @param invocations - The invocations to format.\n   * @returns A record of dependency IDs to their corresponding CSharpDependency.\n   */\n  private formatDependencies(\n    invocations: Invocations,\n  ): Record<string, CSharpDependency> {\n    const dependencies: Record<string, CSharpDependency> = {};\n    for (const resolvedSymbol of invocations.resolvedSymbols) {\n      const namespace = resolvedSymbol.namespace;\n      const filepath = resolvedSymbol.filepath;\n      const id = namespace !== \"\"\n        ? namespace + \".\" + resolvedSymbol.name\n        : resolvedSymbol.name;\n      if (!dependencies[filepath]) {\n        dependencies[filepath] = {\n          id: filepath,\n          isExternal: false,\n          symbols: {},\n          isNamespace: true,\n        };\n      }\n      dependencies[filepath].symbols[id] = id;\n    }\n    // Add unresolved symbols as external dependencies\n    // Commented because redundant : if a symbol is unresolved,\n    // then the external dependency is imported through a namespace.\n    // Not removed in case my analysis is inaccurate.\n    // for (const unresolvedSymbol of invocations.unresolved) {\n    //   dependencies[unresolvedSymbol] = {\n    //     id: unresolvedSymbol,\n    //     isExternal: true,\n    //     symbols: {},\n    //   };\n    // }\n    return dependencies;\n  }\n\n  /**\n   * Formats external usings into a record of CSharpDependency.\n   * @param resolvedimports - The resolved imports to format.\n   * @returns A record of dependency IDs to their corresponding CSharpDependency.\n   */\n  private formatExternalUsings(\n    resolvedimports: ResolvedImports,\n  ): Record<string, CSharpDependency> {\n    const dependencies: Record<string, CSharpDependency> = {};\n    for (const unresolvedSymbol of resolvedimports.external) {\n      dependencies[unresolvedSymbol.name] = {\n        id: unresolvedSymbol.name,\n        isExternal: true,\n        symbols: {},\n        isNamespace: true,\n      };\n    }\n    return dependencies;\n  }\n\n  /**\n   * Formats a file into a CSharpFile object.\n   * @param filepath - The path of the file to format.\n   * @returns The formatted CSharpFile object.\n   */\n  public formatFile(filepath: string): CSharpFile | undefined {\n    const file = this.invResolver.nsMapper.getFile(filepath);\n    if (!file) {\n      return undefined;\n    }\n    const fileSymbols = this.nsMapper.getExportsForFile(filepath);\n    const fileDependencies = this.invResolver.getInvocationsFromFile(filepath);\n    const formattedFile: CSharpFile = {\n      id: file.path,\n      filepath: file.path,\n      rootNode: file.rootNode,\n      lineCount: file.rootNode.endPosition.row,\n      characterCount: file.rootNode.endIndex - file.rootNode.startIndex,\n      dependencies: this.formatDependencies(fileDependencies),\n      symbols: this.formatSymbols(fileSymbols),\n    };\n    // Add global usings to dependencies\n    const globalUsings = this.formatExternalUsings(\n      this.usingResolver.getGlobalUsings(filepath),\n    );\n    for (const key in globalUsings) {\n      if (!formattedFile.dependencies[key]) {\n        formattedFile.dependencies[key] = globalUsings[key];\n      }\n    }\n    // Add local usings to dependencies\n    const localUsings = this.formatExternalUsings(\n      this.usingResolver.resolveUsingDirectives(filepath),\n    );\n    for (const key in localUsings) {\n      if (!formattedFile.dependencies[key]) {\n        formattedFile.dependencies[key] = localUsings[key];\n      }\n    }\n    // If an internal dependency is a symbol of an imported namespace,\n    // then add said symbol to the symbol list of that namespace\n    for (const key in formattedFile.dependencies) {\n      const dep = formattedFile.dependencies[key];\n      if (!dep.isExternal && !dep.isNamespace) {\n        const namespaceParts = dep.id.split(\".\");\n        if (namespaceParts.length > 1) {\n          const parentNamespace = namespaceParts.slice(0, -1).join(\".\");\n          const parentDep = formattedFile.dependencies[parentNamespace];\n          if (parentDep && !parentDep.isExternal) {\n            parentDep.symbols[dep.id] = dep.id;\n            delete formattedFile.dependencies[key];\n          }\n        }\n      }\n    }\n    return formattedFile;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/extensionResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CSharpExtensionResolver } from \"./index.ts\";\nimport { CSharpNamespaceMapper } from \"../namespaceMapper/index.ts\";\nimport { getCSharpFilesMap } from \"../testFiles/index.ts\";\n\ndescribe(\"CSharpExtensionResolver\", () => {\n  const files = getCSharpFilesMap();\n  const nsMapper = new CSharpNamespaceMapper(files);\n  const extensionResolver = new CSharpExtensionResolver(nsMapper);\n  const extensions = extensionResolver.getExtensions();\n\n  test(\"should resolve extension methods in the project\", () => {\n    expect(Object.keys(extensions).length).toBe(2);\n    expect(extensions[\"\"]).toBeDefined();\n    expect(extensions[\"\"].length).toBe(2);\n    expect(extensions[\"\"][0].name).toBeDefined();\n\n    expect(extensions[\"MyApp.BeefBurger\"]).toBeDefined();\n    expect(extensions[\"MyApp.BeefBurger\"].length).toBe(1);\n    expect(extensions[\"MyApp.BeefBurger\"][0].name).toBeDefined();\n    expect(extensions[\"MyApp.BeefBurger\"][0].name).toBe(\"Melt\");\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/extensionResolver/index.ts",
    "content": "import type {\n  CSharpNamespaceMapper,\n  NamespaceNode,\n  SymbolNode,\n} from \"../namespaceMapper/index.ts\";\nimport { csharpParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport Parser from \"tree-sitter\";\n\nconst extensionMethodQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  ((method_declaration\n    parameters : (parameter_list\n    (parameter\n      (modifier) @mod)\n    ))\n  (#eq? @mod \"this\")) @method\n`,\n);\n\nconst typeParameterQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n    (type_parameter\n    name: (_) @cls)\n  `,\n);\n\n/**\n * Interface representing an extension method in C#.\n */\nexport interface ExtensionMethod {\n  /**\n   * The syntax node of the extension method.\n   */\n  node: Parser.SyntaxNode;\n  /**\n   * The symbol associated with the extension method.\n   */\n  symbol: SymbolNode;\n  /**\n   * The name of the extension method.\n   */\n  name: string;\n  /**\n   * The type of the extension method.\n   */\n  type: string;\n  /**\n   * The type that is being extended\n   */\n  extendedType: string;\n  /**\n   * The type parameters of the extension method.\n   */\n  typeParameters?: string[];\n}\n\n/**\n * Record for all extensions in the project.\n * Key : name of a namespace that has extensions.\n * Value : the extensions of said namespace.\n */\nexport type ExtensionMethodMap = Record<string, ExtensionMethod[]>;\n\nexport class CSharpExtensionResolver {\n  private namespaceMapper: CSharpNamespaceMapper;\n  private extensions: ExtensionMethodMap = {};\n\n  constructor(namespaceMapper: CSharpNamespaceMapper) {\n    this.namespaceMapper = namespaceMapper;\n    this.resolveExtensionMethodsInNamespaceTree();\n  }\n\n  /**\n   * Returns the extensions found in the project.\n   * @returns A map of extension methods found in the project.\n   */\n  getExtensions(): ExtensionMethodMap {\n    return this.extensions;\n  }\n\n  /**\n   * Resolves extension methods in a symbol.\n   * @param symbol - the symbol to analyse.\n   * @returns A map of extension methods found in the file.\n   */\n  private resolveExtensionMethods(symbol: SymbolNode): ExtensionMethod[] {\n    const extensions: ExtensionMethod[] = [];\n    const extensionMethods = extensionMethodQuery.captures(symbol.node);\n    for (const ext of extensionMethods) {\n      if (ext.name === \"mod\") continue;\n      const methodNode = ext.node;\n      let methodName = methodNode.childForFieldName(\"name\")?.text;\n      if (!methodName) continue;\n      const typeParameters = methodNode.childForFieldName(\"type_parameters\");\n      const typeParameterNames: string[] = [];\n      if (typeParameters) {\n        const typeParams = typeParameterQuery.captures(typeParameters);\n        for (const param of typeParams) {\n          typeParameterNames.push(param.node.text);\n        }\n      }\n      if (methodName.includes(\"<\")) {\n        const index = methodName.indexOf(\"<\");\n        methodName = methodName.substring(0, index);\n      }\n      const methodType = methodNode.childForFieldName(\"returns\")?.text ||\n        \"void\";\n      const parameters = methodNode.childrenForFieldName(\"parameters\");\n      let extendedType = \"void\";\n      parameters.forEach((param) => {\n        if (param.text.startsWith(\"this \")) {\n          extendedType = param.childForFieldName(\"type\")?.text || \"void\";\n        }\n      });\n      extensions.push({\n        node: methodNode,\n        symbol: symbol,\n        name: methodName,\n        type: methodType,\n        extendedType: extendedType,\n        typeParameters: typeParameterNames.length > 0\n          ? typeParameterNames\n          : undefined,\n      });\n    }\n    return extensions;\n  }\n\n  /**\n   * Resolves extension methods in a namespace.\n   * @param namespace - The namespace to analyze.\n   * @returns A map of extension methods found in the namespace.\n   */\n  private resolveExtensionMethodsInNamespace(\n    namespace: NamespaceNode,\n  ): ExtensionMethod[] {\n    const extensions: ExtensionMethod[] = [];\n    for (const symbol of namespace.exports) {\n      const extMethods = this.resolveExtensionMethods(symbol);\n      if (extMethods.length > 0) {\n        extensions.push(...extMethods);\n      }\n    }\n    return extensions;\n  }\n\n  /**\n   * Resolves extension methods in the namespace tree.\n   * @returns A map of extension methods found in the project.\n   */\n  private resolveExtensionMethodsInNamespaceTree() {\n    this.extensions = {};\n\n    const resolveExtensionsRecursively = (namespace: NamespaceNode) => {\n      const extMethods = this.resolveExtensionMethodsInNamespace(namespace);\n      if (extMethods.length > 0) {\n        this.extensions[this.namespaceMapper.getFullNSName(namespace)] =\n          extMethods;\n      }\n      for (const childNamespace of namespace.childrenNamespaces) {\n        resolveExtensionsRecursively(childNamespace);\n      }\n    };\n\n    resolveExtensionsRecursively(this.namespaceMapper.nsTree);\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/extractor/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CSharpExtractor } from \"./index.ts\";\nimport {\n  csharpFilesFolder,\n  getCSharpFilesMap,\n  getCsprojFilesMap,\n} from \"../testFiles/index.ts\";\nimport { generateCSharpDependencyManifest } from \"../../../manifest/dependencyManifest/csharp/index.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CSharpExtractor\", () => {\n  const parsedfiles = getCSharpFilesMap();\n  const csprojFiles = getCsprojFilesMap();\n  const files = new Map<string, { path: string; content: string }>();\n  for (const [filePath, { path, rootNode }] of parsedfiles) {\n    files.set(filePath, { path, content: rootNode.text });\n  }\n  for (const [filePath, { path, content }] of csprojFiles) {\n    files.set(filePath, { path, content });\n  }\n  const manifest = generateCSharpDependencyManifest(files);\n  const extractor = new CSharpExtractor(files, manifest);\n\n  const programpath = join(csharpFilesFolder, \"Program.cs\");\n  const twonamespacesonefilepath = join(\n    csharpFilesFolder,\n    \"2Namespaces1File.cs\",\n  );\n  const seminamespacedpath = join(csharpFilesFolder, \"SemiNamespaced.cs\");\n\n  test(\"should extract symbols correctly\", () => {\n    expect(\n      extractor.extractSymbolFromFile(programpath, \"Program\")?.length,\n    ).toBe(10);\n    const salad = extractor.extractSymbolFromFile(\n      twonamespacesonefilepath,\n      \"ChickenBurger.Salad\",\n    );\n    expect(salad?.length).toBe(1);\n    const saladfile = salad?.[0];\n    expect(saladfile?.name).toBe(\"Salad\");\n    expect(saladfile?.namespace).toBe(\"ChickenBurger\");\n    expect(saladfile?.subproject.name).toBe(\"TestFiles\");\n    expect(saladfile?.symbol).toMatchObject({\n      name: \"Salad\",\n      type: \"class\",\n      namespace: \"ChickenBurger\",\n    });\n    expect(\n      extractor.extractSymbolFromFile(\n        twonamespacesonefilepath,\n        \"MyApp.BeefBurger.Steak\",\n      )?.length,\n    ).toBe(2);\n    expect(\n      extractor.extractSymbolFromFile(seminamespacedpath, \"HeadCrab\")?.length,\n    ).toBe(2);\n  });\n\n  test(\"should extract global using directives\", () => {\n    const project = extractor.projectMapper.findSubprojectForFile(\n      join(csharpFilesFolder, \"Program.cs\"),\n    );\n    if (!project) {\n      throw new Error(\"Project not found\");\n    }\n    const usingDirectives = extractor.generateGlobalUsings(project);\n    expect(usingDirectives.startsWith(\"global using System.IO;\")).toBe(true);\n\n    const subproject = extractor.projectMapper.findSubprojectForFile(\n      join(csharpFilesFolder, \"Subfolder/GlobalUsings.cs\"),\n    );\n    if (!subproject) {\n      throw new Error(\"Subproject not found\");\n    }\n    const subprojectUsingDirectives = extractor.generateGlobalUsings(\n      subproject,\n    );\n    expect(subprojectUsingDirectives.includes(\"global using System;\")).toBe(\n      true,\n    );\n    expect(\n      subprojectUsingDirectives.includes(\"global using MyApp.Models;\"),\n    ).toBe(true);\n  });\n\n  test(\"should manage nested classes\", () => {\n    const extracted = extractor.extractSymbolFromFile(programpath, \"Program\");\n    const symbols = extracted?.map((file) =>\n      file.symbol.namespace !== \"\"\n        ? file.symbol.namespace + \".\" + file.symbol.name\n        : file.symbol.name\n    );\n    expect(symbols).not.toContain(\"OuterNamespace.OuterInnerClass\");\n    expect(symbols).toContain(\"OuterNamespace.OuterClass\");\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/extractor/index.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport {\n  CSharpProjectMapper,\n  type DotNetProject,\n} from \"../projectMapper/index.ts\";\nimport {\n  CSharpNamespaceMapper,\n  type SymbolNode,\n} from \"../namespaceMapper/index.ts\";\nimport {\n  CSharpUsingResolver,\n  type UsingDirective,\n} from \"../usingResolver/index.ts\";\nimport { csharpParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport type { DependencyManifest } from \"../../../manifest/dependencyManifest/types.ts\";\n\n/**\n * Represents an extracted file containing a symbol.\n */\nexport interface ExtractedFile {\n  /** The subproject to which the file belongs */\n  subproject: DotNetProject;\n  /** The namespace of the symbol */\n  namespace: string;\n  /** The symbol node */\n  symbol: SymbolNode;\n  /** The using directives in the file */\n  imports: UsingDirective[];\n  /** The name of the file */\n  name: string;\n}\n\nexport class CSharpExtractor {\n  private manifest: DependencyManifest;\n  public projectMapper: CSharpProjectMapper;\n  private nsMapper: CSharpNamespaceMapper;\n  public usingResolver: CSharpUsingResolver;\n\n  constructor(\n    files: Map<string, { path: string; content: string }>,\n    manifest: DependencyManifest,\n  ) {\n    this.manifest = manifest;\n    const csprojFiles = new Map<string, { path: string; content: string }>();\n    const parsedFiles = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    for (const [filePath, file] of files) {\n      if (filePath.endsWith(\".csproj\")) {\n        csprojFiles.set(filePath, file);\n      } else if (filePath.endsWith(\".cs\")) {\n        parsedFiles.set(filePath, {\n          path: filePath,\n          rootNode: csharpParser.parse(file.content).rootNode,\n        });\n      }\n    }\n    this.projectMapper = new CSharpProjectMapper(csprojFiles);\n    this.nsMapper = new CSharpNamespaceMapper(parsedFiles);\n    this.usingResolver = new CSharpUsingResolver(\n      this.nsMapper,\n      this.projectMapper,\n    );\n    for (const [filePath] of parsedFiles) {\n      this.usingResolver.resolveUsingDirectives(filePath);\n    }\n  }\n\n  /**\n   * Finds all dependencies of a given symbol.\n   * @param symbol - The symbol for which to find dependencies.\n   * @returns An array of symbols.\n   */\n  private findDependencies(symbol: SymbolNode): SymbolNode[] {\n    const dependencies: SymbolNode[] = [];\n    const symbolfullname = symbol.namespace !== \"\"\n      ? symbol.namespace + \".\" + symbol.name\n      : symbol.name;\n    const symbolDependencies = this.manifest[symbol.filepath]\n      ?.symbols[symbolfullname].dependencies;\n    if (symbolDependencies) {\n      for (const dependency of Object.values(symbolDependencies)) {\n        for (const depsymbol of Object.values(dependency.symbols)) {\n          const depsymbolnode = this.nsMapper.findClassInTree(\n            this.nsMapper.nsTree,\n            depsymbol,\n          );\n          if (depsymbolnode) {\n            dependencies.push(depsymbolnode);\n          }\n        }\n      }\n    }\n    return dependencies;\n  }\n\n  /**\n   * Finds all dependencies of a given symbol, and the dependencies of those dependencies.\n   * @param symbol - The symbol for which to find dependencies.\n   * @returns An array of symbols.\n   */\n  private findAllDependencies(\n    symbol: SymbolNode,\n    visited: Set<SymbolNode> = new Set<SymbolNode>(),\n  ): SymbolNode[] {\n    const allDependencies: Set<SymbolNode> = new Set<SymbolNode>();\n    if (visited.has(symbol)) {\n      return Array.from(allDependencies);\n    }\n    visited.add(symbol);\n    const dependencies = this.findDependencies(symbol);\n    for (const dependency of dependencies) {\n      allDependencies.add(dependency);\n      for (const dep of this.findAllDependencies(dependency, visited)) {\n        allDependencies.add(dep);\n      }\n    }\n    return Array.from(allDependencies);\n  }\n\n  /**\n   * Saves the extracted file containing a symbol into the filesystem.\n   * @param file - The extracted file to save.\n   */\n  public getContent(file: ExtractedFile): string {\n    const usingDirectives = file.imports\n      .map((directive) => directive.node.text)\n      .join(\"\\n\");\n    const namespaceDirective = file.namespace !== \"\"\n      ? `namespace ${file.namespace};`\n      : \"\";\n    const content =\n      `${usingDirectives}\\n${namespaceDirective}\\n${file.symbol.node.text}\\n`;\n    return content;\n  }\n\n  /**\n   * Extracts a given symbol and its dependencies and returns them as files.\n   * @param symbol - The symbol to extract.\n   * @returns An array of extracted files.\n   */\n  public extractSymbol(symbol: SymbolNode): ExtractedFile[] {\n    const subproject = this.projectMapper.findSubprojectForFile(\n      symbol.filepath,\n    );\n    if (!subproject) {\n      throw new Error(`Subproject not found for file: ${symbol.filepath}`);\n    }\n    const extractedFiles: ExtractedFile[] = [];\n    const visitedSymbols = new Set<string>();\n\n    const addExtractedFile = (symbol: SymbolNode) => {\n      // If the symbol is nested, we export the parent.\n      while (symbol.parent) {\n        symbol = symbol.parent;\n      }\n      if (!visitedSymbols.has(symbol.name)) {\n        visitedSymbols.add(symbol.name);\n        const subproject = this.projectMapper.findSubprojectForFile(\n          symbol.filepath,\n        );\n        if (subproject) {\n          const extractedFile: ExtractedFile = {\n            subproject,\n            namespace: symbol.namespace,\n            symbol,\n            imports: this.usingResolver.parseUsingDirectives(symbol.filepath),\n            name: symbol.name,\n          };\n          extractedFiles.push(extractedFile);\n        }\n      }\n    };\n\n    addExtractedFile(symbol);\n    const dependencies = this.findAllDependencies(symbol);\n    for (const dependency of dependencies) {\n      addExtractedFile(dependency);\n    }\n\n    return extractedFiles;\n  }\n\n  /**\n   * Extracts a symbol from a file by its name.\n   * @param filePath - The path to file that contains the symbol to extract\n   * @param symbolName - The name of the symbol to extract\n   * @returns A list of extracted files or undefined if the symbol is not found\n   */\n  public extractSymbolFromFile(\n    filePath: string,\n    symbolName: string,\n  ): ExtractedFile[] | undefined {\n    const fileExports = this.nsMapper.getFileExports(filePath);\n    const symbol = fileExports.find(\n      (symbol) =>\n        symbol.name === symbolName ||\n        symbol.namespace + \".\" + symbol.name === symbolName,\n    );\n    if (symbol) {\n      return this.extractSymbol(symbol);\n    }\n    return undefined;\n  }\n\n  public generateGlobalUsings(subproject: DotNetProject): string {\n    let content = \"\";\n    const directives = subproject.globalUsings.directives;\n    for (const directive of directives) {\n      content += `${directive.node.text}\\n`;\n    }\n    return content;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/invocationResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport type { File } from \"../namespaceResolver/index.ts\";\nimport {\n  CSharpNamespaceMapper,\n  type SymbolNode,\n} from \"../namespaceMapper/index.ts\";\nimport { CSharpNamespaceResolver } from \"../namespaceResolver/index.ts\";\nimport {\n  csharpFilesFolder,\n  getCSharpFilesMap,\n  getCsprojFilesMap,\n} from \"../testFiles/index.ts\";\nimport { join } from \"@std/path\";\nimport { CSharpInvocationResolver } from \"./index.ts\";\nimport type Parser from \"tree-sitter\";\nimport { CSharpProjectMapper } from \"../projectMapper/index.ts\";\n\ndescribe(\"InvocationResolver\", () => {\n  const parsedfiles: Map<string, File> = getCSharpFilesMap();\n  const csprojfiles = getCsprojFilesMap();\n  const nsMapper = new CSharpNamespaceMapper(parsedfiles);\n  const projectMapper = new CSharpProjectMapper(csprojfiles);\n  const nsResolver = new CSharpNamespaceResolver();\n  const invResolver: CSharpInvocationResolver = new CSharpInvocationResolver(\n    nsMapper,\n    projectMapper,\n  );\n\n  test(\"Invocation resolution\", () => {\n    const usedFiles = invResolver.getInvocationsFromFile(\n      join(csharpFilesFolder, \"Program.cs\"),\n    );\n    const resolved = usedFiles.resolvedSymbols.map((s) =>\n      s.namespace !== \"\" ? s.namespace + \".\" + s.name : s.name\n    );\n    const unresolved = usedFiles.unresolved;\n    expect(resolved).toContain(\"MyApp.BeefBurger.Bun\");\n    expect(resolved).toContain(\"ChickenBurger.Bun\");\n    expect(resolved).toContain(\"ChickenBurger.Salad\");\n    expect(resolved).toContain(\"MyNamespace.MyClass\");\n    expect(resolved).toContain(\"HalfNamespace.Gordon\");\n    expect(resolved).toContain(\"Freeman\");\n    expect(resolved).toContain(\"OuterNamespace.InnerNamespace.InnerClass\");\n    expect(resolved).toContain(\"MyApp.Models.OrderStatus\");\n    expect(resolved).toContain(\"HeadCrab\"); // Used through extension Bite()\n    expect(resolved).toContain(\"OuterNamespace.OuterInnerClass\");\n    expect(resolved).not.toContain(\"MyApp.BeefBurger.Salad\");\n    expect(unresolved).toContain(\"System.Math\");\n    expect(unresolved).not.toContain(\"string\");\n    expect(unresolved).not.toContain(\"System\");\n    expect(unresolved).not.toContain(\"Salad<string>\");\n  });\n\n  test(\"isUsedInFile\", () => {\n    const myclass: SymbolNode = {\n      name: \"MyClass\",\n      type: \"class\",\n      filepath: join(csharpFilesFolder, \"Namespaced.cs\"),\n      namespace: \"MyNamespace\",\n      node: {} as Parser.SyntaxNode,\n    };\n    const headcrab: SymbolNode = {\n      name: \"HeadCrab\",\n      type: \"class\",\n      filepath: join(csharpFilesFolder, \"SemiNamespaced.cs\"),\n      namespace: \"\",\n      node: {} as Parser.SyntaxNode,\n    };\n    const iorder: SymbolNode = {\n      name: \"IOrder\",\n      type: \"interface\",\n      filepath: join(csharpFilesFolder, \"Models.cs\"),\n      namespace: \"MyApp.Models\",\n      node: {} as Parser.SyntaxNode,\n    };\n    expect(\n      invResolver.isUsedInFile(\n        join(csharpFilesFolder, \"Program.cs\"),\n        myclass,\n      ),\n    ).toBe(true);\n    expect(\n      invResolver.isUsedInFile(\n        join(csharpFilesFolder, \"Program.cs\"),\n        headcrab,\n      ),\n    ).toBe(true);\n    expect(\n      invResolver.isUsedInFile(\n        join(csharpFilesFolder, \"Program.cs\"),\n        iorder,\n      ),\n    ).toBe(false);\n  });\n\n  test(\"Same-file dependencies\", () => {\n    const seminamespaced = nsResolver.getNamespacesFromFile(\n      parsedfiles.get(\n        join(csharpFilesFolder, \"SemiNamespaced.cs\"),\n      ) as File,\n    );\n    const headcrabnode = seminamespaced[0].exports.find(\n      (exp) => exp.name === \"HeadCrab\",\n    )?.node as Parser.SyntaxNode;\n    const hcinvocations = invResolver.getInvocationsFromNode(\n      headcrabnode,\n      join(csharpFilesFolder, \"SemiNamespaced.cs\"),\n    );\n    expect(hcinvocations).toMatchObject({\n      resolvedSymbols: [\n        {\n          name: \"Freeman\",\n          type: \"class\",\n          namespace: \"\",\n        },\n      ],\n      unresolved: [],\n    });\n  });\n\n  test(\"Finds useless using directives\", () => {\n    const filepath = join(csharpFilesFolder, \"2Namespaces1File.cs\");\n    const usingDirectives = invResolver.usingResolver.parseUsingDirectives(\n      filepath,\n    );\n    const invocations = invResolver.getInvocationsFromFile(filepath);\n    expect(usingDirectives.length).toBe(2);\n    expect(\n      usingDirectives.filter((d) => invResolver.isUsingUseful(invocations, d))\n        .length,\n    ).toBe(1);\n\n    const programpath = join(csharpFilesFolder, \"Program.cs\");\n    const programUsingDirectives = invResolver.usingResolver\n      .parseUsingDirectives(programpath);\n    const programInvocations = invResolver.getInvocationsFromFile(programpath);\n    expect(programUsingDirectives.length).toBe(6);\n    expect(\n      programUsingDirectives.filter((d) =>\n        invResolver.isUsingUseful(programInvocations, d)\n      ).length,\n    ).toBe(6);\n\n    const usagepath = join(csharpFilesFolder, \"Usage.cs\");\n    const usageUsingDirectives = invResolver.usingResolver.parseUsingDirectives(\n      usagepath,\n    );\n    const usageInvocations = invResolver.getInvocationsFromFile(usagepath);\n    expect(usageUsingDirectives.length).toBe(6);\n    expect(\n      usageUsingDirectives.filter((d) =>\n        invResolver.isUsingUseful(usageInvocations, d)\n      ).length,\n    ).toBe(4);\n\n    const globalusingpath = join(\n      csharpFilesFolder,\n      \"Subfolder/GlobalUsings.cs\",\n    );\n    const globalusingDirectives = invResolver.usingResolver\n      .parseUsingDirectives(globalusingpath);\n    const globalusingInvocations = invResolver.getInvocationsFromFile(\n      globalusingpath,\n    );\n    expect(globalusingDirectives.length).toBe(2);\n    expect(\n      globalusingDirectives.filter((d) =>\n        invResolver.isUsingUseful(globalusingInvocations, d)\n      )\n        .length,\n    ).toBe(1); // Every external import is considered useful no matter what\n    // Even if there is no invocation.\n    // It's also not dangerous to remove a global using directive\n    // because they are regrouped in GlobalUsings.cs.\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/invocationResolver/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport type { File } from \"../namespaceResolver/index.ts\";\nimport type {\n  CSharpNamespaceMapper,\n  NamespaceNode,\n  SymbolNode,\n} from \"../namespaceMapper/index.ts\";\nimport { csharpParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport {\n  CSharpUsingResolver,\n  ExternalSymbol,\n  type ResolvedImports,\n  type UsingDirective,\n} from \"../usingResolver/index.ts\";\nimport type { CSharpProjectMapper } from \"../projectMapper/index.ts\";\nimport {\n  CSharpExtensionResolver,\n  type ExtensionMethod,\n  type ExtensionMethodMap,\n} from \"../extensionResolver/index.ts\";\n\n/**\n * Query to identify variable names in the file\n */\nconst variablesQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  (variable_declarator\n    name: (identifier) @varname\n  )\n  (parameter\n    name: (identifier) @varname\n  )\n  `,\n);\n\n/**\n * Query to identify classes that are called in the file\n * for object and variable creation\n */\nconst calledClassesQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  (object_creation_expression\n  type: (identifier) @cls)\n  (object_creation_expression\n  type: (qualified_name) @cls)\n  ((object_creation_expression\n  type: (generic_name) @cls))\n  (variable_declaration\n  type: (identifier) @cls)\n  (variable_declaration\n  type: (qualified_name) @cls)\n  (variable_declaration\n  type: (generic_name) @cls)\n  (parameter\n  type: (identifier) @cls)\n  (parameter\n  type: (qualified_name) @cls)\n  (parameter\n  type: (generic_name) @cls)\n  (type_argument_list\n  (identifier) @cls)\n  (type_argument_list\n  (qualified_name) @cls)\n  (type_argument_list\n  (generic_name) @cls)\n  (base_list\n  (identifier) @cls)\n  (base_list\n  (qualified_name) @cls)\n  (base_list\n  (generic_name) @cls)\n  (property_declaration\n  type: (qualified_name) @cls)\n  (property_declaration\n  type: (identifier) @cls)\n  (property_declaration\n  type: (generic_name) @cls)\n  (typeof_expression\n  type: (_) @cls)\n  (method_declaration\n  returns: (qualified_name) @cls)\n  (method_declaration\n  returns: (identifier) @cls)\n  (method_declaration\n  returns: (generic_name) @cls)\n  (array_type\n  type: (identifier) @cls)\n  (array_type\n  type: (qualified_name) @cls)\n  (array_type\n  type: (generic_name) @cls)\n  (nullable_type\n  type: (identifier) @cls)\n  (nullable_type\n  type: (qualified_name) @cls)\n  (nullable_type\n  type: (generic_name) @cls)\n  `,\n);\n\n/**\n * Query to identify member accesses in the file\n * for function or constant calls\n */\nconst memberAccessQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  (_\n    (member_access_expression\n  ))@cls\n  `,\n);\n\n/**\n * Query to identify attribute uses in the file\n */\nconst attributeQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  (attribute\n  name: (_) @cls)\n  `,\n);\n\n/**\n * Query to identify invocation expressions in the file\n * exclusively for function calls\n */\nconst invocationQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  (invocation_expression\n      function: (member_access_expression\n      name: (_) @cls\n    ))\n  `,\n);\n\n/**\n * Interface representing the invocations in a file\n */\nexport interface Invocations {\n  /** List of resolved symbols */\n  resolvedSymbols: SymbolNode[];\n  /** List of unresolved symbols (usually external imports) */\n  unresolved: string[];\n}\n\nexport class CSharpInvocationResolver {\n  parser: Parser = csharpParser;\n  public nsMapper: CSharpNamespaceMapper;\n  public usingResolver: CSharpUsingResolver;\n  private extensions: ExtensionMethodMap = {};\n  private resolvedImports: ResolvedImports;\n  private cache: Map<string, Invocations> = new Map<string, Invocations>();\n\n  constructor(\n    nsMapper: CSharpNamespaceMapper,\n    projectmapper: CSharpProjectMapper,\n  ) {\n    this.nsMapper = nsMapper;\n    this.usingResolver = new CSharpUsingResolver(nsMapper, projectmapper);\n    this.resolvedImports = {\n      internal: [],\n      external: [],\n    };\n    this.extensions = new CSharpExtensionResolver(nsMapper).getExtensions();\n  }\n\n  /**\n   * Retrieves variable names from the given syntax node.\n   * @param node - The syntax node to extract variable names from.\n   * @returns An array of variable names.\n   */\n  #getVariables(node: Parser.SyntaxNode): string[] {\n    return variablesQuery.captures(node).map((ctc) => ctc.node.text);\n  }\n\n  /**\n   * Resolves a symbol (class or namespace) from the given classname.\n   * @param classname - The name of the class to resolve.\n   * @param namespaceTree - The namespace tree to search within.\n   * @returns The resolved symbol node or null if not found.\n   */\n  private resolveSymbol(\n    classname: string,\n    namespaceTree: NamespaceNode,\n    filepath: string,\n  ): SymbolNode | null {\n    // Remove any generic type information from the classname\n    // Classes in the type argument list are managed on their own.\n    const cleanClassname = classname.split(\"<\")[0];\n    let cutClassname = cleanClassname;\n    // Try to find the class in the resolved imports\n    let ucls = null;\n    while (!ucls) {\n      ucls = this.usingResolver.findClassInImports(\n        this.resolvedImports,\n        cleanClassname,\n        filepath,\n      );\n      if (!cutClassname.includes(\".\")) {\n        break;\n      }\n      cutClassname = cutClassname.split(\".\").slice(0, -1).join(\".\");\n    }\n    if (ucls) {\n      return ucls;\n    }\n    // Try to find the class in the namespace tree\n    let cls = null;\n    cutClassname = cleanClassname;\n    while (!cls) {\n      cls = this.nsMapper.findClassInTree(namespaceTree, cutClassname);\n      if (!cutClassname.includes(\".\")) {\n        break;\n      }\n      cutClassname = cutClassname.split(\".\").slice(0, -1).join(\".\");\n    }\n    if (cls) {\n      return cls;\n    }\n    return null;\n  }\n\n  /**\n   * Gets the classes that are called for variable declarations and object creations.\n   * This does not manage static calls such as System.Math.Abs(-1).\n   * @param node - The syntax node to analyze.\n   * @param namespaceTree - The namespace tree to search within.\n   * @returns An object containing resolved and unresolved symbols.\n   */\n  #getCalledClasses(\n    node: Parser.SyntaxNode,\n    namespaceTree: NamespaceNode,\n    filepath: string,\n  ): Invocations {\n    const invocations: Invocations = {\n      resolvedSymbols: [],\n      unresolved: [],\n    };\n    // Query to capture object creation expressions and variable declarations\n    const catches = calledClassesQuery.captures(node);\n    // Process each captured class name\n    catches.forEach((ctc) => {\n      const classname = ctc.node.text;\n      const resolvedSymbol = this.resolveSymbol(\n        classname,\n        namespaceTree,\n        filepath,\n      );\n      if (resolvedSymbol) {\n        invocations.resolvedSymbols.push(resolvedSymbol);\n      } else {\n        // If class not found, mark as unresolved\n        invocations.unresolved.push(classname);\n      }\n    });\n    return invocations;\n  }\n\n  /**\n   * Resolves member accesses within the given syntax node.\n   * @param node - The syntax node to analyze.\n   * @param namespaceTree - The namespace tree to search within.\n   * @returns An object containing resolved and unresolved symbols.\n   */\n  #resolveMemberAccesses(\n    node: Parser.SyntaxNode,\n    namespaceTree: NamespaceNode,\n    filepath: string,\n  ): Invocations {\n    // Get variable names to filter out variable-based invocations\n    const variablenames = this.#getVariables(node);\n    const invocations: Invocations = {\n      resolvedSymbols: [],\n      unresolved: [],\n    };\n    // Query to capture invocation expressions\n    const catches = memberAccessQuery.captures(node);\n    // Process each captured access expression\n    catches.forEach((ctc) => {\n      // Remove intermediate members (e.g., System.Mario in System.Mario.Bros)\n      if (ctc.node.type === \"member_access_expression\") return;\n      // Get the root member access expression\n      const mae = ctc.node.children.filter(\n        (child) => child.type === \"member_access_expression\",\n      );\n      let func = mae.map((m) => m.text);\n      // Among all the matches, even the functions dont have parentheses\n      // That means that nodes with parentheses in it are intermediate members\n      // They get through the net because their type is invocation_expression.\n      func = func.filter((f) => !f.includes(\"(\"));\n      func.forEach((f) => {\n        // The query gives us a full invocation,\n        // but we only want a class or namespace name for the called class.\n        const funcParts = f.split(\".\");\n        const classname = funcParts.slice(0, -1).join(\".\");\n        // If the function is called from a variable, then we ignore it.\n        // (Because the dependency will already be managed by the variable creation)\n        if (variablenames.includes(classname)) {\n          return;\n        }\n        const resolvedSymbol = this.resolveSymbol(\n          classname,\n          namespaceTree,\n          filepath,\n        );\n        if (resolvedSymbol) {\n          invocations.resolvedSymbols.push(resolvedSymbol);\n        } else {\n          // If class not found, mark as unresolved\n          invocations.unresolved.push(classname);\n        }\n      });\n    });\n    return invocations;\n  }\n\n  /**\n   * Resolves extension uses within the given syntax node.\n   * @param node - The syntax node to analyze.\n   * @param namespaceTree - The namespace tree to search within.\n   * @returns An object containing resolved classes the extensions come from.\n   */\n  #resolveExtensionUses(\n    node: Parser.SyntaxNode,\n    filepath: string,\n  ): Invocations {\n    const invocations: Invocations = {\n      resolvedSymbols: [],\n      unresolved: [],\n    };\n    const catches = invocationQuery.captures(node);\n    catches.forEach((ctc) => {\n      let method = ctc.node.text;\n      if (ctc.node.type === \"generic_name\") {\n        const index = method.indexOf(\"<\");\n        method = method.substring(0, index);\n      }\n      const extMethods = this.#findExtension(method, filepath);\n      for (const extMethod of extMethods) {\n        // TODO : check if there is the correct amount of type arguments\n        invocations.resolvedSymbols.push(extMethod.symbol);\n      }\n    });\n    return invocations;\n  }\n\n  #resolveAttributeUses(\n    node: Parser.SyntaxNode,\n    filepath: string,\n  ): Invocations {\n    const invocations: Invocations = {\n      resolvedSymbols: [],\n      unresolved: [],\n    };\n    const catches = attributeQuery.captures(node);\n    catches.forEach((ctc) => {\n      let method = ctc.node.text;\n      if (ctc.node.type === \"generic_name\") {\n        const index = method.indexOf(\"<\");\n        method = method.substring(0, index);\n      }\n      const resolvedSymbol = this.resolveSymbol(\n        method,\n        this.nsMapper.nsTree,\n        filepath,\n      );\n      if (resolvedSymbol) {\n        invocations.resolvedSymbols.push(resolvedSymbol);\n      } else {\n        // If class not found, try again by adding \"Attribute\" to the name\n        const resolvedSymbol = this.resolveSymbol(\n          method + \"Attribute\",\n          this.nsMapper.nsTree,\n          filepath,\n        );\n        if (resolvedSymbol) {\n          invocations.resolvedSymbols.push(resolvedSymbol);\n        } else {\n          // If class not found, mark as unresolved\n          invocations.unresolved.push(ctc.node.text);\n        }\n      }\n    });\n    return invocations;\n  }\n\n  /**\n   * Finds an extension among the available extension methods.\n   * The available extension methods are only in used namespaces.\n   * @param ext - The extension method to find.\n   * @returns The resolved symbol node or null if not found.\n   */\n  #findExtension(ext: string, filepath: string): ExtensionMethod[] {\n    const methods: ExtensionMethod[] = [];\n    const usedNamespaces =\n      this.usingResolver.resolveUsingDirectives(filepath).internal;\n    // Check if the extension method is in the extensions map\n    for (const ns of usedNamespaces) {\n      if (\n        ns.namespace &&\n        this.extensions[this.nsMapper.getFullNSName(ns.namespace)]\n      ) {\n        const extensions =\n          this.extensions[this.nsMapper.getFullNSName(ns.namespace)];\n        const extMethods = extensions.filter((method) => method.name === ext);\n        if (extMethods.length > 0) {\n          methods.push(...extMethods);\n        }\n      }\n    }\n    return methods;\n  }\n\n  /**\n   * Gets the invocations from a file.\n   * @param filepath - The path of the file to analyze.\n   * @returns An object containing resolved and unresolved symbols.\n   */\n  getInvocationsFromFile(filepath: string): Invocations {\n    if (this.cache.has(filepath)) {\n      return this.cache.get(filepath) as Invocations;\n    }\n    const file: File | undefined = this.nsMapper.getFile(filepath);\n    if (!file) {\n      return {\n        resolvedSymbols: [],\n        unresolved: [],\n      };\n    }\n    const invocations = this.getInvocationsFromNode(file.rootNode, filepath);\n    this.cache.set(filepath, invocations);\n    return invocations;\n  }\n\n  /**\n   * Gets the classes used in a file.\n   * @param node - The syntax node to analyze.\n   * @param filepath - The path of the file being analyzed.\n   * @returns An object containing resolved and unresolved symbols.\n   */\n  getInvocationsFromNode(\n    node: Parser.SyntaxNode,\n    filepath: string,\n  ): Invocations {\n    this.resolvedImports = this.usingResolver.resolveUsingDirectives(filepath);\n    const invocations: Invocations = {\n      resolvedSymbols: [],\n      unresolved: [],\n    };\n    // Get classes called in variable declarations and object creations\n    const calledClasses = this.#getCalledClasses(\n      node,\n      this.nsMapper.nsTree,\n      filepath,\n    );\n    // Resolve member accesses expressions\n    const memberAccesses = this.#resolveMemberAccesses(\n      node,\n      this.nsMapper.nsTree,\n      filepath,\n    );\n    // Resolve extension uses\n    const extensions = this.#resolveExtensionUses(node, filepath);\n    // Resolve attribute uses\n    const attributes = this.#resolveAttributeUses(node, filepath);\n\n    // Combine results from both methods, ensuring uniqueness with Set\n    invocations.resolvedSymbols = [\n      ...new Set([\n        ...calledClasses.resolvedSymbols,\n        ...memberAccesses.resolvedSymbols,\n        ...extensions.resolvedSymbols,\n        ...attributes.resolvedSymbols,\n      ]),\n    ];\n    invocations.unresolved = [\n      ...new Set([\n        ...calledClasses.unresolved,\n        ...memberAccesses.unresolved,\n        ...extensions.unresolved,\n        ...attributes.unresolved,\n      ]),\n    ];\n    return invocations;\n  }\n\n  /**\n   * Checks if a symbol is used in a file.\n   * @param filepath - The path of the file to check.\n   * @param symbol - The symbol to check for.\n   * @returns True if the symbol is used in the file, false otherwise.\n   */\n  public isUsedInFile(filepath: string, symbol: SymbolNode): boolean {\n    const invocations = this.getInvocationsFromFile(filepath);\n    return invocations.resolvedSymbols.some((inv) => inv.name === symbol.name);\n  }\n\n  /**\n   * Checks if a using directive is useful in a file.\n   * @param invocations - The invocations in the file.\n   * @param using - The using directive to check for.\n   * @returns True if the using directive is useful, false otherwise.\n   */\n  public isUsingUseful(\n    invocations: Invocations,\n    usingD: UsingDirective,\n  ): boolean {\n    const usedNamespace = this.usingResolver.resolveUsingDirective(usingD);\n    if (usedNamespace instanceof ExternalSymbol) return true;\n    return invocations.resolvedSymbols.some(\n      (inv) =>\n        (usedNamespace.namespace &&\n          inv.namespace ===\n            this.nsMapper.getFullNSName(usedNamespace.namespace)) ||\n        (usedNamespace.symbol && inv.name === usedNamespace.symbol.name) ||\n        (usedNamespace.symbol &&\n          inv.parent &&\n          inv.parent.name === usedNamespace.symbol.name),\n    );\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/metricsAnalyzer/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CSharpMetricsAnalyzer } from \"./index.ts\";\nimport { csharpFilesFolder, getCSharpFilesMap } from \"../testFiles/index.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CSharpMetricsAnalyzer\", () => {\n  const analyzer = new CSharpMetricsAnalyzer();\n  const files = getCSharpFilesMap();\n\n  const analyzeFile = (filePath: string) => {\n    const absolutePath = join(csharpFilesFolder, filePath);\n    const file = files.get(absolutePath);\n    if (!file) {\n      throw new Error(`File not found: ${absolutePath}`);\n    }\n    return analyzer.analyzeNode(file.rootNode);\n  };\n\n  test(\"Analyzes 2Namespaces1File.cs\", () => {\n    const metrics = analyzeFile(\"2Namespaces1File.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 0, // No control flow statements in this file\n      linesCount: 42, // Total lines in the file\n      codeLinesCount: 38, // Excluding blank lines and comments\n      characterCount: expect.any(Number), // Total characters in the file\n      codeCharacterCount: expect.any(Number), // Characters excluding comments and whitespace\n    });\n  });\n\n  test(\"Analyzes Models.cs\", () => {\n    const metrics = analyzeFile(\"Models.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 0, // No control flow statements in this file\n      linesCount: 23, // Total lines in the file\n      codeLinesCount: 22, // Excluding blank lines and comments\n      characterCount: expect.any(Number),\n      codeCharacterCount: expect.any(Number),\n    });\n  });\n\n  test(\"Analyzes Namespaced.cs\", () => {\n    const metrics = analyzeFile(\"Namespaced.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 4,\n      linesCount: 18,\n      codeLinesCount: 17,\n      characterCount: expect.any(Number),\n      codeCharacterCount: expect.any(Number),\n    });\n  });\n\n  test(\"Analyzes Nested.cs\", () => {\n    const metrics = analyzeFile(\"Nested.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 1,\n      linesCount: 32,\n      codeLinesCount: 31,\n      characterCount: expect.any(Number),\n      codeCharacterCount: expect.any(Number),\n    });\n  });\n\n  test(\"Analyzes OtherFileSameNamespace.cs\", () => {\n    const metrics = analyzeFile(\"OtherFileSameNamespace.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 0, // No control flow statements\n      linesCount: 3,\n      codeLinesCount: 2,\n      characterCount: expect.any(Number),\n      codeCharacterCount: expect.any(Number),\n    });\n  });\n\n  test(\"Analyzes Program.cs\", () => {\n    const metrics = analyzeFile(\"Program.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 0, // No control flow statements in the main method\n      linesCount: 35,\n      codeLinesCount: 27,\n      characterCount: expect.any(Number),\n      codeCharacterCount: expect.any(Number),\n    });\n  });\n\n  test(\"Analyzes SemiNamespaced.cs\", () => {\n    const metrics = analyzeFile(\"SemiNamespaced.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 0,\n      linesCount: 32,\n      codeLinesCount: 29,\n      characterCount: expect.any(Number),\n      codeCharacterCount: expect.any(Number),\n    });\n  });\n\n  test(\"Analyzes Usage.cs\", () => {\n    const metrics = analyzeFile(\"Usage.cs\");\n    expect(metrics).toMatchObject({\n      cyclomaticComplexity: 2,\n      linesCount: 25,\n      codeLinesCount: 24,\n      characterCount: expect.any(Number),\n      codeCharacterCount: expect.any(Number),\n    });\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/metricsAnalyzer/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { csharpParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\n/**\n * Interface for code volumes\n */\ninterface CodeCounts {\n  /** Number of lines of code */\n  lines: number;\n  /** Number of characters of code */\n  characters: number;\n}\n\ninterface CommentSpan {\n  start: { row: number; column: number };\n  end: { row: number; column: number };\n}\n\n/**\n * Represents complexity metrics for a C# symbol\n */\nexport interface CSharpComplexityMetrics {\n  /** Cyclomatic complexity (McCabe complexity) */\n  cyclomaticComplexity: number;\n  /** Code lines (not including whitespace or comments) */\n  codeLinesCount: number;\n  /** Total lines (including whitespace and comments) */\n  linesCount: number;\n  /** Characters of actual code (excluding comments and excessive whitespace) */\n  codeCharacterCount: number;\n  /** Total characters in the entire symbol */\n  characterCount: number;\n}\n\n// Tree-sitter query to find complexity-related nodes\nconst complexityQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n    (if_statement) @complexity\n    (while_statement) @complexity\n    (for_statement) @complexity\n    (do_statement) @complexity\n    (switch_section) @complexity\n    (conditional_expression) @complexity\n    (try_statement) @complexity\n    (catch_clause) @complexity\n    (finally_clause) @complexity\n  `,\n);\n\nconst commentQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n    (comment) @comment\n  `,\n);\n\nexport class CSharpMetricsAnalyzer {\n  /**\n   * Calculates metrics for a C# symbol.\n   * @param node - The AST node to analyze\n   * @returns The complexity metrics for the node\n   */\n  public analyzeNode(node: Parser.SyntaxNode): CSharpComplexityMetrics {\n    const complexityCount = this.getComplexityCount(node);\n    const linesCount = node.endPosition.row - node.startPosition.row + 1;\n    const codeCounts = this.getCodeCounts(node);\n    const codeLinesCount = codeCounts.lines;\n    const characterCount = node.endIndex - node.startIndex;\n    const codeCharacterCount = codeCounts.characters;\n\n    return {\n      cyclomaticComplexity: complexityCount,\n      linesCount,\n      codeLinesCount,\n      characterCount,\n      codeCharacterCount,\n    };\n  }\n\n  private getComplexityCount(node: Parser.SyntaxNode): number {\n    const complexityMatches = complexityQuery.captures(node);\n    return complexityMatches.length;\n  }\n\n  /**\n   * Finds comments in the given node and returns their spans.\n   * @param node - The AST node to analyze\n   * @returns An object containing pure comment lines and comment spans\n   */\n  private findComments(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): {\n    pureCommentLines: Set<number>;\n    commentSpans: CommentSpan[];\n  } {\n    const pureCommentLines = new Set<number>();\n    const commentSpans: CommentSpan[] = [];\n\n    const commentCaptures = commentQuery.captures(node);\n\n    for (const capture of commentCaptures) {\n      const commentNode = capture.node;\n\n      // Record the comment span for character counting\n      commentSpans.push({\n        start: {\n          row: commentNode.startPosition.row,\n          column: commentNode.startPosition.column,\n        },\n        end: {\n          row: commentNode.endPosition.row,\n          column: commentNode.endPosition.column,\n        },\n      });\n\n      // Check if the comment starts at the beginning of the line (ignoring whitespace)\n      const lineIdx = commentNode.startPosition.row - node.startPosition.row;\n      if (lineIdx >= 0 && lineIdx < lines.length) {\n        const lineText = lines[lineIdx];\n        const textBeforeComment = lineText.substring(\n          0,\n          commentNode.startPosition.column,\n        );\n\n        // If there's only whitespace before the comment, it's a pure comment line\n        if (textBeforeComment.trim().length === 0) {\n          for (\n            let line = commentNode.startPosition.row;\n            line <= commentNode.endPosition.row;\n            line++\n          ) {\n            pureCommentLines.add(line);\n          }\n        }\n      }\n    }\n\n    return { pureCommentLines, commentSpans };\n  }\n\n  /**\n   * Finds all empty lines in a node\n   *\n   * @param node The syntax node to analyze\n   * @param lines The lines of text in the node\n   * @returns Set of line numbers that are empty\n   */\n  private findEmptyLines(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): Set<number> {\n    const emptyLines = new Set<number>();\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      if (lines[i].trim().length === 0) {\n        emptyLines.add(lineIndex);\n      }\n    }\n\n    return emptyLines;\n  }\n\n  private getCodeCounts(node: Parser.SyntaxNode): CodeCounts {\n    const lines = node.text.split(/\\r?\\n/);\n    const linesCount = lines.length;\n    // Find comments and their spans\n    const { pureCommentLines, commentSpans } = this.findComments(node, lines);\n\n    // Find empty lines\n    const emptyLines = this.findEmptyLines(node, lines);\n\n    // Calculate code lines\n    const nonCodeLines = new Set([...pureCommentLines, ...emptyLines]);\n    const codeLinesCount = linesCount - nonCodeLines.size;\n\n    let codeCharCount = 0;\n\n    // Process each line individually\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      const line = lines[i];\n\n      // Skip empty lines and pure comment lines\n      if (emptyLines.has(lineIndex) || pureCommentLines.has(lineIndex)) {\n        continue;\n      }\n\n      // Process line for code characters\n      let lineText = line;\n\n      // Remove comment content from the line if present\n      for (const span of commentSpans) {\n        if (span.start.row === lineIndex) {\n          // Comment starts on this line\n          lineText = lineText.substring(0, span.start.column);\n        }\n      }\n\n      // Count normalized code characters (trim excessive whitespace)\n      const normalizedText = lineText.trim().replace(/\\s+/g, \" \");\n      codeCharCount += normalizedText.length;\n    }\n\n    return {\n      lines: codeLinesCount,\n      characters: codeCharCount,\n    };\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/namespaceMapper/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CSharpNamespaceMapper } from \"./index.ts\";\nimport { csharpFilesFolder, getCSharpFilesMap } from \"../testFiles/index.ts\";\nimport { join } from \"@std/path\";\nimport type { File } from \"../namespaceResolver/index.ts\";\n\ndescribe(\"NamespaceMapper\", () => {\n  const files: Map<string, File> = getCSharpFilesMap();\n  const nsMapper = new CSharpNamespaceMapper(files);\n\n  test(\"should build a namespace tree\", () => {\n    const nsTree = nsMapper.buildNamespaceTree();\n\n    // Check root namespace\n    expect(nsTree.name).toBe(\"\");\n    expect(nsTree.exports).toHaveLength(3);\n    expect(nsTree.childrenNamespaces).toHaveLength(6);\n\n    // Check exports in root namespace\n    expect(nsTree.exports).toEqual(\n      expect.arrayContaining([\n        expect.objectContaining({\n          name: \"Freeman\",\n          filepath: join(csharpFilesFolder, \"SemiNamespaced.cs\"),\n        }),\n        expect.objectContaining({\n          name: \"HeadCrab\",\n          filepath: join(csharpFilesFolder, \"SemiNamespaced.cs\"),\n        }),\n        expect.objectContaining({\n          name: \"Usage\",\n          filepath: join(csharpFilesFolder, \"Usage.cs\"),\n        }),\n      ]),\n    );\n\n    // Check ChickenBurger namespace\n    const chickenBurgerNs = nsTree.childrenNamespaces.find(\n      (ns) => ns.name === \"ChickenBurger\",\n    );\n    expect(chickenBurgerNs).toBeDefined();\n    if (!chickenBurgerNs) return;\n    expect(chickenBurgerNs.exports).toHaveLength(3);\n    expect(chickenBurgerNs.childrenNamespaces).toHaveLength(0);\n\n    // Check MyApp namespace\n    const myAppNs = nsTree.childrenNamespaces.find((ns) => ns.name === \"MyApp\");\n    expect(myAppNs).toBeDefined();\n    if (!myAppNs) return;\n    expect(myAppNs.exports).toHaveLength(0);\n    expect(myAppNs.childrenNamespaces).toHaveLength(2);\n\n    // Check Models namespace under MyApp\n    const modelsNs = myAppNs.childrenNamespaces.find(\n      (ns) => ns.name === \"Models\",\n    );\n    expect(modelsNs).toBeDefined();\n    if (!modelsNs) return;\n    expect(modelsNs.exports).toHaveLength(5);\n    expect(modelsNs.childrenNamespaces).toHaveLength(0);\n\n    // Check BeefBurger namespace under MyApp\n    const beefBurgerNs = myAppNs.childrenNamespaces.find(\n      (ns) => ns.name === \"BeefBurger\",\n    );\n    expect(beefBurgerNs).toBeDefined();\n    if (!beefBurgerNs) return;\n    expect(beefBurgerNs.exports).toHaveLength(4);\n    expect(beefBurgerNs.childrenNamespaces).toHaveLength(0);\n\n    // Check MyNamespace namespace\n    const myNamespaceNs = nsTree.childrenNamespaces.find(\n      (ns) => ns.name === \"MyNamespace\",\n    );\n    expect(myNamespaceNs).toBeDefined();\n    if (!myNamespaceNs) return;\n    expect(myNamespaceNs.exports).toHaveLength(1);\n    expect(myNamespaceNs.childrenNamespaces).toHaveLength(0);\n\n    // Check OuterNamespace namespace\n    const outerNamespaceNs = nsTree.childrenNamespaces.find(\n      (ns) => ns.name === \"OuterNamespace\",\n    );\n    expect(outerNamespaceNs).toBeDefined();\n    if (!outerNamespaceNs) return;\n    expect(outerNamespaceNs.exports).toHaveLength(2);\n    expect(outerNamespaceNs.childrenNamespaces).toHaveLength(1);\n\n    // Check InnerNamespace under OuterNamespace\n    const innerNamespaceNs = outerNamespaceNs.childrenNamespaces.find(\n      (ns) => ns.name === \"InnerNamespace\",\n    );\n    expect(innerNamespaceNs).toBeDefined();\n    if (!innerNamespaceNs) return;\n    expect(innerNamespaceNs.exports).toHaveLength(1);\n    expect(innerNamespaceNs.childrenNamespaces).toHaveLength(0);\n\n    // Check Tests namespace\n    const testsNs = nsTree.childrenNamespaces.find((ns) => ns.name === \"Tests\");\n    expect(testsNs).toBeDefined();\n    if (!testsNs) return;\n    expect(testsNs.exports).toHaveLength(1);\n    expect(testsNs.childrenNamespaces).toHaveLength(0);\n\n    // Check HalfNamespace namespace\n    const halfNamespaceNs = nsTree.childrenNamespaces.find(\n      (ns) => ns.name === \"HalfNamespace\",\n    );\n    expect(halfNamespaceNs).toBeDefined();\n    if (!halfNamespaceNs) return;\n    expect(halfNamespaceNs.exports).toHaveLength(1);\n    expect(halfNamespaceNs.childrenNamespaces).toHaveLength(0);\n  });\n\n  test(\"Finds classes accurately in the tree\", () => {\n    const nsTree = nsMapper.buildNamespaceTree();\n    const order = nsMapper.findClassInTree(nsTree, \"Order\");\n    expect(order).toMatchObject({\n      name: \"Order\",\n      filepath: join(csharpFilesFolder, \"Models.cs\"),\n    });\n    const innerClass = nsMapper.findClassInTree(nsTree, \"InnerClass\");\n    expect(innerClass).toMatchObject({\n      name: \"InnerClass\",\n      filepath: join(csharpFilesFolder, \"Nested.cs\"),\n    });\n    const chickenbun = nsMapper.findClassInTree(nsTree, \"ChickenBurger.Bun\");\n    expect(chickenbun).toMatchObject({\n      name: \"Bun\",\n      filepath: join(csharpFilesFolder, \"2Namespaces1File.cs\"),\n    });\n  });\n\n  test(\"Finds namespaces accurately in the tree\", () => {\n    const nsTree = nsMapper.buildNamespaceTree();\n    const myapp = nsMapper.findNamespaceInTree(nsTree, \"MyApp\");\n    expect(myapp).toBeDefined();\n    if (!myapp) return;\n    expect(myapp.childrenNamespaces.length).toBe(2);\n    const halfnamespace = nsMapper.findNamespaceInTree(nsTree, \"HalfNamespace\");\n    expect(halfnamespace).toMatchObject({\n      name: \"HalfNamespace\",\n      exports: [{ name: \"Gordon\" }],\n      childrenNamespaces: [],\n    });\n    const models = nsMapper.findNamespaceInTree(nsTree, \"MyApp.Models\");\n    expect(models).toMatchObject({\n      name: \"Models\",\n      exports: expect.any(Array),\n      childrenNamespaces: expect.any(Array),\n    });\n    const innernamespace = nsMapper.findNamespaceInTree(\n      nsTree,\n      \"OuterNamespace.InnerNamespace\",\n    );\n    expect(innernamespace).toMatchObject({\n      name: \"InnerNamespace\",\n      exports: [{ name: \"InnerClass\" }],\n      childrenNamespaces: [],\n    });\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/namespaceMapper/index.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport {\n  CSharpNamespaceResolver,\n  type SymbolType,\n} from \"../namespaceResolver/index.ts\";\n\n/**\n * Interface representing a namespace node in the namespace tree.\n */\nexport interface NamespaceNode {\n  /** The name of the namespace */\n  name: string;\n  /** List of classes and types exported by the namespace */\n  exports: SymbolNode[];\n  /** List of child namespaces */\n  childrenNamespaces: NamespaceNode[];\n  /** Parent namespace */\n  parentNamespace?: NamespaceNode;\n}\n\n/**\n * Interface representing a symbol node in the namespace tree.\n */\nexport interface SymbolNode {\n  /** The name of the symbol */\n  name: string;\n  /** The type of the symbol (class, interface, etc.) */\n  type: SymbolType;\n  /** Kept for ambiguity resolution */\n  namespace: string;\n  /** The file path where the symbol is defined */\n  filepath: string;\n  /** The syntax node corresponding to the symbol */\n  node: Parser.SyntaxNode;\n  /** The parent of the symbol if it is nested */\n  parent?: SymbolNode;\n}\n\nconst DEBUG_NAMESPACE = \"namespace\";\nconst DEBUG_SYMBOL = \"symbol\";\ntype DebugType = typeof DEBUG_NAMESPACE | typeof DEBUG_SYMBOL;\n\n/**\n * Interface representing a debug node in the namespace tree.\n */\nexport interface DebugNode {\n  /** The name of the debug node */\n  name: string;\n  /** The type of the debug node */\n  type: DebugType;\n  /** The children of the debug node */\n  children: DebugNode[];\n}\n\nexport class CSharpNamespaceMapper {\n  files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n  #nsResolver: CSharpNamespaceResolver;\n  nsTree: NamespaceNode;\n  #exportsCache: Map<string, SymbolNode[]> = new Map<string, SymbolNode[]>();\n  fileExports: Map<string, SymbolNode[]> = new Map<string, SymbolNode[]>();\n\n  constructor(\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n  ) {\n    this.files = files;\n    this.#nsResolver = new CSharpNamespaceResolver();\n    this.nsTree = this.buildNamespaceTree();\n  }\n\n  /**\n   * Gets a file object from the files map.\n   * @param key - The key of the file.\n   * @returns The file object.\n   */\n  getFile(key: string) {\n    return this.files.get(key);\n  }\n\n  /**\n   * Gets the exports for a given filepath.\n   * @param filepath - The path of the file to get exports for.\n   * @returns An array of exported symbols.\n   */\n  getFileExports(filepath: string): SymbolNode[] {\n    return this.fileExports.get(filepath) ?? [];\n  }\n\n  /**\n   * Builds the fileExports map from the namespace tree.\n   * @param tree - The root of the namespace tree.\n   */\n  #buildFileExports(tree: NamespaceNode) {\n    tree.exports.forEach((symbol) => {\n      if (!this.fileExports.has(symbol.filepath)) {\n        this.fileExports.set(symbol.filepath, []);\n      }\n      this.fileExports.get(symbol.filepath)?.push(symbol);\n    });\n\n    tree.childrenNamespaces.forEach((ns) => {\n      this.#buildFileExports(ns);\n    });\n  }\n\n  /**\n   * Adds a namespace to the final tree.\n   * @param namespace - The namespace node to add.\n   * @param tree - The root of the namespace tree.\n   */\n  #addNamespaceToTree(namespace: NamespaceNode, tree: NamespaceNode) {\n    // Deconstruct the namespace's name, so that A.B\n    // becomes B, child of A.\n    const parts = namespace.name.split(\".\");\n    let current = tree;\n    let previous = tree;\n\n    // For each part of the namespace, we check if it's\n    // already in the tree. If it is, we go to the next\n    // part. If it isn't, we add it to the tree.\n    if (namespace.name !== \"\") {\n      parts.forEach((part) => {\n        let child = current.childrenNamespaces.find((ns) => ns.name === part);\n        if (!child) {\n          child = {\n            name: part,\n            exports: [],\n            childrenNamespaces: [],\n            parentNamespace: current,\n          };\n          current.childrenNamespaces.push(child);\n        }\n        previous = current;\n        current = child;\n      });\n    }\n\n    // Once we're done with the parts, we add the classes\n    // and children namespaces to the current namespace.\n    current.exports.push(...namespace.exports);\n    namespace.childrenNamespaces.forEach((ns) => {\n      this.#addNamespaceToTree(ns, current);\n    });\n    // We also set the parent namespace for each child namespace.\n    current.parentNamespace = previous;\n  }\n\n  /**\n   * Assigns namespaces to classes, used for ambiguity resolution.\n   * @param tree - The root of the namespace tree.\n   * @param parentNamespace - The parent namespace name.\n   */\n  #assignNamespacesToClasses(tree: NamespaceNode, parentNamespace = \"\") {\n    const fullNamespace = parentNamespace\n      ? `${parentNamespace}.${tree.name}`\n      : tree.name;\n\n    tree.exports.forEach((cls) => {\n      cls.namespace = fullNamespace;\n    });\n\n    tree.childrenNamespaces.forEach((ns) => {\n      this.#assignNamespacesToClasses(ns, fullNamespace);\n    });\n  }\n\n  #assignParentNamespaces(tree: NamespaceNode) {\n    tree.childrenNamespaces.forEach((ns) => {\n      ns.parentNamespace = tree;\n      this.#assignParentNamespaces(ns);\n    });\n  }\n\n  /**\n   * Builds a tree of namespaces from the parsed files.\n   * @returns The root of the namespace tree.\n   */\n  buildNamespaceTree(): NamespaceNode {\n    const namespaceTree: NamespaceNode = {\n      name: \"\",\n      exports: [],\n      childrenNamespaces: [],\n    };\n\n    // Parse all files.\n    this.files.forEach((file) => {\n      const namespaces = this.#nsResolver\n        .getNamespacesFromFile(file)\n        .map((ns) => ns as NamespaceNode);\n\n      namespaces.forEach((namespace) => {\n        this.#assignParentNamespaces(namespace);\n        this.#addNamespaceToTree(namespace, namespaceTree);\n      });\n    });\n\n    // Assign namespaces to classes.\n    this.#assignNamespacesToClasses(namespaceTree);\n\n    // Build the file exports map.\n    this.#buildFileExports(namespaceTree);\n\n    return namespaceTree;\n  }\n\n  /**\n   * Converts a namespace or symbol node to a debug node.\n   * Used for serialisation.\n   * @param node - The node to convert.\n   * @returns The converted node.\n   */\n  #convertNodeToDebug(node: NamespaceNode | SymbolNode): DebugNode {\n    if (\"childrenNamespaces\" in node) {\n      return {\n        name: node.name,\n        type: DEBUG_NAMESPACE,\n        children: [\n          ...node.childrenNamespaces.map((child: NamespaceNode) =>\n            this.#convertNodeToDebug(child)\n          ),\n          ...node.exports.map((symbol: SymbolNode) =>\n            this.#convertNodeToDebug(symbol)\n          ),\n        ],\n      };\n    } else {\n      return {\n        name: node.name,\n        type: DEBUG_SYMBOL,\n        children: [],\n      };\n    }\n  }\n\n  /**\n   * Saves the namespace tree to a file for debugging purposes.\n   * @param filepath - The path to the file where the debug tree will be saved.\n   * @returns The debug tree.\n   */\n  saveDebugTree(filepath: string): DebugNode {\n    const debugTree: DebugNode = this.#convertNodeToDebug(this.nsTree);\n    Deno.writeTextFileSync(filepath, JSON.stringify(debugTree, null, 2));\n    return debugTree;\n  }\n\n  /**\n   * Finds a namespace in the namespace tree.\n   * @param tree - The root of the namespace tree.\n   * @param namespaceName - The name of the namespace to find.\n   * @returns The namespace node if found, otherwise null.\n   */\n  findNamespaceInTree(\n    tree: NamespaceNode,\n    namespaceName: string,\n  ): NamespaceNode | null {\n    if (namespaceName === \"\") {\n      return tree;\n    }\n    if (namespaceName.includes(\".\")) {\n      const parts = namespaceName.split(\".\");\n      const simpleNamespaceName = parts[0];\n      const rest = parts.slice(1).join(\".\");\n\n      const namespace = tree.childrenNamespaces.find(\n        (ns) => ns.name === simpleNamespaceName,\n      );\n      if (namespace) {\n        return this.findNamespaceInTree(namespace, rest);\n      }\n    }\n\n    return (\n      tree.childrenNamespaces.find((ns) => ns.name === namespaceName) ?? null\n    );\n  }\n\n  /**\n   * Gets the full namespace name of a given namespace node.\n   * @param namespace - The namespace node to get the full name for.\n   * @returns The full namespace name.\n   */\n  getFullNSName(namespace: NamespaceNode): string {\n    if (namespace.name === \"\") {\n      return \"\";\n    }\n    if (!namespace.parentNamespace || namespace.parentNamespace.name === \"\") {\n      return namespace.name;\n    }\n    return `${this.getFullNSName(namespace.parentNamespace)}.${namespace.name}`;\n  }\n\n  /**\n   * Finds a class in the namespace tree.\n   * @param tree - The root of the namespace tree.\n   * @param className - The name of the class to find.\n   * @returns The symbol node if found, otherwise null.\n   */\n  findClassInTree(tree: NamespaceNode, className: string): SymbolNode | null {\n    // Management of qualified names\n    if (className.includes(\".\")) {\n      const parts = className.split(\".\");\n      const simpleClassName = parts[parts.length - 1];\n      const namespaceParts = parts.slice(0, -1).reverse();\n\n      // Find all classes with the same name\n      const matchingClasses: SymbolNode[] = [];\n      const searchClasses = (namespace: NamespaceNode) => {\n        namespace.exports.forEach((cls) => {\n          if (cls.name === simpleClassName) {\n            matchingClasses.push(cls);\n          }\n        });\n\n        namespace.childrenNamespaces.forEach((childNamespace) => {\n          searchClasses(childNamespace);\n        });\n      };\n      searchClasses(tree);\n\n      // Filter classes by walking through the namespace parts backwards\n      for (const cls of matchingClasses) {\n        const currentNamespace = cls.namespace.split(\".\").reverse();\n        let matches = true;\n\n        for (let i = 0; i < namespaceParts.length; i++) {\n          if (namespaceParts[i] !== currentNamespace[i]) {\n            matches = false;\n            break;\n          }\n        }\n\n        if (matches) {\n          return cls;\n        }\n      }\n\n      return null;\n    }\n\n    // Find the class in the current node's classes.\n    if (tree.exports.some((cls) => cls.name === className)) {\n      return tree.exports.find((cls) => cls.name === className) ?? null;\n    }\n\n    // Recursively search in children namespaces.\n    for (const namespace of tree.childrenNamespaces) {\n      const found = this.findClassInTree(namespace, className);\n      if (found) {\n        return found;\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * Gets all the exports for a file from the namespace tree.\n   * @param filepath - The path of the file to get exports for.\n   * @returns An array of exported symbols.\n   */\n  getExportsForFile(filepath: string): SymbolNode[] {\n    if (this.#exportsCache.has(filepath)) {\n      return this.#exportsCache.get(filepath) ?? [];\n    }\n    const exports: SymbolNode[] = [];\n\n    const searchClassesInNamespace = (namespace: NamespaceNode) => {\n      namespace.exports.forEach((symbol) => {\n        if (symbol.filepath === filepath) {\n          exports.push(symbol);\n        }\n      });\n\n      namespace.childrenNamespaces.forEach((childNamespace) => {\n        searchClassesInNamespace(childNamespace);\n      });\n    };\n\n    searchClassesInNamespace(this.nsTree);\n    this.#exportsCache.set(filepath, exports);\n    return exports;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/namespaceResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CSharpNamespaceResolver, type File } from \"./index.ts\";\nimport { join } from \"@std/path\";\nimport { csharpFilesFolder, getCSharpFilesMap } from \"../testFiles/index.ts\";\n\ndescribe(\"NamespaceResolver\", () => {\n  const files: Map<string, File> = getCSharpFilesMap();\n  const nsResolver: CSharpNamespaceResolver = new CSharpNamespaceResolver();\n\n  test(\"2Namespaces1File.cs\", () => {\n    const file = files.get(\n      join(csharpFilesFolder, \"2Namespaces1File.cs\"),\n    ) as File;\n    const namespaces = nsResolver.getNamespacesFromFile(file);\n    expect(namespaces).toMatchObject([\n      {\n        name: \"\",\n        exports: [],\n        childrenNamespaces: [\n          {\n            name: \"MyApp.BeefBurger\",\n            exports: [{ name: \"Steak\" }, { name: \"Cheese\" }, { name: \"Bun\" }],\n            childrenNamespaces: [],\n          },\n          {\n            name: \"ChickenBurger\",\n            exports: [{ name: \"Chicken\" }, { name: \"Salad\" }, { name: \"Bun\" }],\n            childrenNamespaces: [],\n          },\n        ],\n      },\n    ]);\n  });\n\n  test(\"Models.cs\", () => {\n    const file = files.get(join(csharpFilesFolder, \"Models.cs\")) as File;\n    const namespaces = nsResolver.getNamespacesFromFile(file);\n    expect(namespaces).toMatchObject([\n      {\n        name: \"MyApp.Models\",\n        exports: [\n          { name: \"User\", type: \"class\" },\n          { name: \"Order\", type: \"struct\" },\n          { name: \"OrderStatus\", type: \"enum\" },\n          { name: \"IOrder\", type: \"interface\" },\n          { name: \"OrderDelegate\", type: \"delegate\" },\n        ],\n        childrenNamespaces: [],\n      },\n    ]);\n  });\n\n  test(\"Namespaced.cs\", () => {\n    const file = files.get(join(csharpFilesFolder, \"Namespaced.cs\")) as File;\n    const namespaces = nsResolver.getNamespacesFromFile(file);\n    expect(namespaces).toMatchObject([\n      {\n        name: \"\",\n        exports: [],\n        childrenNamespaces: [\n          {\n            name: \"MyNamespace\",\n            exports: [{ name: \"MyClass\" }],\n            childrenNamespaces: [],\n          },\n        ],\n      },\n    ]);\n  });\n\n  test(\"Nested.cs\", () => {\n    const file = files.get(join(csharpFilesFolder, \"Nested.cs\")) as File;\n    const namespaces = nsResolver.getNamespacesFromFile(file);\n    expect(namespaces).toMatchObject([\n      {\n        name: \"\",\n        exports: [],\n        childrenNamespaces: [\n          {\n            name: \"OuterNamespace\",\n            exports: [\n              { name: \"OuterClass\" },\n              {\n                name: \"OuterInnerClass\",\n                parent: { name: \"OuterClass\" },\n              },\n            ],\n            childrenNamespaces: [\n              {\n                name: \"InnerNamespace\",\n                exports: [{ name: \"InnerClass\" }],\n                childrenNamespaces: [],\n              },\n            ],\n          },\n        ],\n      },\n    ]);\n  });\n\n  test(\"SemiNamespaced.cs\", () => {\n    const file = files.get(\n      join(csharpFilesFolder, \"SemiNamespaced.cs\"),\n    ) as File;\n    const namespaces = nsResolver.getNamespacesFromFile(file);\n    expect(namespaces).toMatchObject([\n      {\n        name: \"\",\n        exports: [{ name: \"Freeman\" }, { name: \"HeadCrab\" }],\n        childrenNamespaces: [\n          {\n            name: \"HalfNamespace\",\n            exports: [{ name: \"Gordon\" }],\n            childrenNamespaces: [],\n          },\n        ],\n      },\n    ]);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/namespaceResolver/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { csharpParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\n// Constants representing different types of symbols in C#\nexport const CSHARP_CLASS_TYPE = \"class\";\nexport const CSHARP_STRUCT_TYPE = \"struct\";\nexport const CSHARP_ENUM_TYPE = \"enum\";\nexport const CSHARP_INTERFACE_TYPE = \"interface\";\nexport const CSHARP_RECORD_TYPE = \"record\";\nexport const CSHARP_DELEGATE_TYPE = \"delegate\";\n\n/**  Type alias for the different symbol types */\nexport type SymbolType =\n  | typeof CSHARP_CLASS_TYPE\n  | typeof CSHARP_STRUCT_TYPE\n  | typeof CSHARP_ENUM_TYPE\n  | typeof CSHARP_INTERFACE_TYPE\n  | typeof CSHARP_RECORD_TYPE\n  | typeof CSHARP_DELEGATE_TYPE;\n\nconst fscopednamespacedeclquery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  (file_scoped_namespace_declaration\n    name: (qualified_name) @id\n  )\n  `,\n);\n\n/**\n * Interface representing a file\n */\nexport interface File {\n  /** The path of the file */\n  path: string;\n  /** The root node of the file */\n  rootNode: Parser.SyntaxNode;\n}\n\n/**\n * Interface representing an exported symbol\n */\nexport interface ExportedSymbol {\n  /** The name of the symbol */\n  name: string;\n  /** The type of the symbol (i.e. class, struct, enum, etc.) */\n  type: SymbolType;\n  /** The syntax node corresponding to the symbol */\n  node: Parser.SyntaxNode;\n  /** The syntax node corresponding to the identifier */\n  identifierNode: Parser.SyntaxNode;\n  /** The namespace of the symbol */\n  namespace?: string;\n  /** The file path where the symbol is defined */\n  filepath: string;\n  /** The parent symbol if this is a nested class */\n  parent?: ExportedSymbol;\n}\n\n/**\n * Interface representing a namespace\n */\nexport interface Namespace {\n  /** The name of the namespace */\n  name: string;\n  /** The syntax node corresponding to the namespace */\n  node: Parser.SyntaxNode;\n  /** Optional because the root namespace doesn't have an identifier */\n  identifierNode?: Parser.SyntaxNode;\n  /** List of classes and types exported by the namespace */\n  exports: ExportedSymbol[];\n  /** List of child namespaces */\n  childrenNamespaces: Namespace[];\n}\n\nexport class CSharpNamespaceResolver {\n  parser: Parser = csharpParser;\n  #currentFile: string;\n  #cache: Map<string, Namespace[]> = new Map<string, Namespace[]>();\n\n  constructor() {\n    this.#currentFile = \"\";\n  }\n\n  /**\n   * Parses namespaces from a file along with the exported classes\n   * @param file The file to parse\n   * @returns An array of namespaces found in the file\n   */\n  getNamespacesFromFile(file: File): Namespace[] {\n    // Check if the file is already in the cache\n    const cacheValue = this.#cache.get(file.path);\n    if (cacheValue) {\n      return cacheValue;\n    }\n    // Set the current file, so that we can keep track of it in the recursive functions\n    this.#currentFile = file.path;\n    // Different behavior if the file has a file-scoped namespace\n    const fileScopedNamespace = this.#getFileScopedNamespaceDeclaration(\n      file.rootNode,\n    );\n    let namespaces: Namespace[];\n    if (fileScopedNamespace) {\n      // Get the namespaces from the file-scoped namespace\n      namespaces = [\n        {\n          name: fileScopedNamespace,\n          node: file.rootNode,\n          exports: this.#getExportsFromNode(file.rootNode),\n          childrenNamespaces: [],\n        },\n      ];\n      // Cache the namespaces\n      this.#cache.set(file.path, namespaces);\n      return namespaces;\n    } else {\n      // Get the namespaces from the root node\n      namespaces = [\n        {\n          name: \"\",\n          node: file.rootNode,\n          exports: this.#getExportsFromNode(file.rootNode),\n          childrenNamespaces: this.#getNamespacesFromNode(file.rootNode),\n        },\n      ];\n    }\n    // Cache the namespaces\n    this.#cache.set(file.path, namespaces);\n    return namespaces;\n  }\n\n  /**\n   * Gets the file-scoped namespace declaration from a node\n   * @param node The syntax node to search for a file-scoped namespace declaration\n   * @returns The name of the file-scoped namespace, if any\n   */\n  #getFileScopedNamespaceDeclaration(\n    node: Parser.SyntaxNode,\n  ): string | undefined {\n    return fscopednamespacedeclquery\n      .captures(node)\n      .map((capture) => capture.node.text)[0];\n  }\n\n  /**\n   * Recursively parses namespaces from a node\n   * @param node The syntax node to parse for namespaces\n   * @returns An array of namespaces found in the node\n   */\n  #getNamespacesFromNode(node: Parser.SyntaxNode): Namespace[] {\n    return node.children\n      .filter((child) => child.type === \"namespace_declaration\")\n      .map((child) => ({\n        name: this.#getIdentifierNode(child).text,\n        node: child,\n        identifierNode: this.#getIdentifierNode(child),\n        exports: this.#getExportsFromNode(this.#getDeclarationList(child)),\n        childrenNamespaces: this.#getNamespacesFromNode(\n          this.#getDeclarationList(child),\n        ),\n      }));\n  }\n\n  /**\n   * Gets the declaration list from a node\n   * i.e. where the classes, namespaces and methods are declared\n   * @param node The syntax node to search for a declaration list\n   * @returns The declaration list node\n   */\n  #getDeclarationList(node: Parser.SyntaxNode): Parser.SyntaxNode {\n    const result = node.children.find(\n      (child) => child.type === \"declaration_list\",\n    );\n    if (!result) {\n      throw new Error(\"Declaration list node not found\");\n    }\n    return result;\n  }\n\n  /**\n   * Gets the name from a node\n   * @param node The syntax node to search for an identifier\n   * @returns The identifier node\n   */\n  #getIdentifierNode(node: Parser.SyntaxNode): Parser.SyntaxNode {\n    const result = node.childForFieldName(\"name\");\n    if (!result) {\n      throw new Error(\"Identifier node not found\");\n    }\n    return result;\n  }\n\n  /**\n   * Gets the classes, structs and enums from a node\n   * @param node The syntax node to search for exported symbols\n   * @param parent The parent symbol if this is a nested class\n   * @returns An array of exported symbols found in the node\n   */\n  #getExportsFromNode(\n    node: Parser.SyntaxNode,\n    parent?: ExportedSymbol,\n  ): ExportedSymbol[] {\n    const exports: ExportedSymbol[] = [];\n    node.children.forEach((child) => {\n      if (\n        child.type === \"class_declaration\" ||\n        child.type === \"struct_declaration\" ||\n        child.type === \"enum_declaration\" ||\n        child.type === \"interface_declaration\" ||\n        child.type === \"record_declaration\" ||\n        child.type === \"delegate_declaration\"\n      ) {\n        let name = this.#getIdentifierNode(child).text;\n        // Handle generic types\n        // Kind of dirty, needs to be corrected\n        if (name.includes(\"<\")) {\n          const index = name.indexOf(\"<\");\n          name = name.substring(0, index);\n        }\n        const symbol: ExportedSymbol = {\n          name: name,\n          type: child.type.replace(\"_declaration\", \"\") as SymbolType,\n          node: child,\n          identifierNode: this.#getIdentifierNode(child),\n          filepath: this.#currentFile,\n          parent,\n        };\n        exports.push(symbol);\n        // Recursively get nested classes\n        if (\n          child.children.some(\n            (grandChild) => grandChild.type === \"declaration_list\",\n          )\n        ) {\n          exports.push(\n            ...this.#getExportsFromNode(\n              this.#getDeclarationList(child),\n              symbol,\n            ),\n          );\n        }\n      }\n    });\n    return exports;\n  }\n\n  /**\n   * Recursively gets all the exported classes from a list of namespaces\n   * @param namespaces The list of namespaces to search for exported symbols\n   * @returns An array of all exported symbols found in the namespaces\n   */\n  getExportsFromNamespaces(namespaces: Namespace[]): ExportedSymbol[] {\n    let classes: ExportedSymbol[] = [];\n    namespaces.forEach((ns) => {\n      classes = classes.concat(ns.exports);\n      classes = classes.concat(\n        this.getExportsFromNamespaces(ns.childrenNamespaces),\n      );\n    });\n    return classes;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/projectMapper/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { CSharpProjectMapper } from \"./index.ts\";\nimport {\n  csharpFilesFolder,\n  getCSharpFilesMap,\n  getCsprojFilesMap,\n} from \"../testFiles/index.ts\";\nimport { CSharpUsingResolver } from \"../usingResolver/index.ts\";\nimport { CSharpNamespaceMapper } from \"../namespaceMapper/index.ts\";\nimport { join } from \"@std/path\";\n\ndescribe(\"CSharpProjectMapper\", () => {\n  const csprojfiles = getCsprojFilesMap();\n  const parsedfiles = getCSharpFilesMap();\n  const projectMapper = new CSharpProjectMapper(csprojfiles);\n\n  test(\"Project mapper definition\", () => {\n    expect(projectMapper.rootFolder).toBeDefined();\n    expect(projectMapper.subprojects).toBeDefined();\n    expect(projectMapper.subprojects.length).toBe(2);\n  });\n\n  const nsmapper = new CSharpNamespaceMapper(parsedfiles);\n  const usingResolver = new CSharpUsingResolver(nsmapper, projectMapper);\n  const usagecsFile = join(csharpFilesFolder, \"Usage.cs\");\n  const globalusingcsFile = join(\n    csharpFilesFolder,\n    \"Subfolder/GlobalUsings.cs\",\n  );\n  test(\"Global using resolution\", () => {\n    usingResolver.resolveUsingDirectives(usagecsFile);\n    usingResolver.resolveUsingDirectives(globalusingcsFile);\n    expect(usingResolver.getGlobalUsings(usagecsFile).internal.length).toBe(0);\n    expect(usingResolver.getGlobalUsings(usagecsFile).external.length).toBe(1);\n    expect(\n      usingResolver.getGlobalUsings(globalusingcsFile).internal.length,\n    ).toBe(1);\n    expect(\n      usingResolver.getGlobalUsings(globalusingcsFile).external.length,\n    ).toBe(1);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/projectMapper/index.ts",
    "content": "import { basename, dirname, join } from \"@std/path\";\nimport type {\n  ExternalSymbol,\n  InternalSymbol,\n  ResolvedImports,\n  UsingDirective,\n} from \"../usingResolver/index.ts\";\n\n/**\n * Represents a .NET project.\n */\nexport interface DotNetProject {\n  /**\n   * The root folder of the project.\n   */\n  rootFolder: string;\n\n  /**\n   * The name of the project.\n   */\n  name: string;\n\n  /**\n   * The path to the .csproj file of the project.\n   */\n  csprojPath: string;\n\n  /**\n   * The content of the .csproj file of the project\n   */\n  csprojContent: string;\n\n  /**\n   * The global usings resolved for the project.\n   */\n  globalUsings: GlobalUsings;\n}\n\n/**\n * Interface for a subproject's global usings\n */\nexport interface GlobalUsings {\n  /**\n   * The internal symbols used in the project.\n   */\n  internal: InternalSymbol[];\n  /**\n   * The external symbols used in the project.\n   */\n  external: ExternalSymbol[];\n  /**\n   * The using directives that define the global usings.\n   */\n  directives: UsingDirective[];\n}\n\nexport class CSharpProjectMapper {\n  rootFolder: string;\n  subprojects: DotNetProject[];\n\n  constructor(csprojFiles: Map<string, { path: string; content: string }>) {\n    this.rootFolder = this.#getRootFolder(\n      Array.from(csprojFiles.values()).map((csproj) => csproj.path),\n    );\n    this.subprojects = this.#makeSubprojects(csprojFiles);\n  }\n\n  /**\n   * Recursively finds all .csproj files in the given directory and its subdirectories.\n   * @param dir - The directory to search in.\n   * @returns An array of dotnet projects (path to project and csproj file).\n   */\n  #makeSubprojects(\n    csprojFiles: Map<string, { path: string; content: string }>,\n  ): DotNetProject[] {\n    const subprojects: DotNetProject[] = [];\n    for (const [csprojPath, csprojContent] of csprojFiles) {\n      const subproject: DotNetProject = {\n        rootFolder: dirname(csprojPath).replace(/\\\\/g, \"/\"), // Ensure UNIX format\n        name: basename(csprojPath, \".csproj\").replace(/\\\\/g, \"/\"), // Ensure UNIX format\n        csprojPath,\n        csprojContent: csprojContent.content,\n        globalUsings: { internal: [], external: [], directives: [] },\n      };\n      subprojects.push(subproject);\n    }\n    return subprojects;\n  }\n\n  /**\n   * Gets the root folder of the project based on the provided file paths.\n   * @param filepaths - An array of file paths.\n   * @returns The root folder path.\n   */\n  #getRootFolder(filepaths: string[]): string {\n    if (filepaths.length === 0) {\n      throw new Error(\n        \"No .csproj files found. Make sure to include .csproj files along with .cs files in .napirc and that such files exist in your project.\",\n      );\n    }\n    const splitPaths = filepaths.map((filepath) => filepath.split(\"/\"));\n    const commonPath = splitPaths[0].slice();\n    for (let i = 0; i < commonPath.length; i++) {\n      for (let j = 1; j < splitPaths.length; j++) {\n        if (splitPaths[j][i] !== commonPath[i]) {\n          commonPath.splice(i);\n          let rootFolder = join(\"\", ...commonPath).replace(/\\\\/g, \"/\");\n          if (filepaths[0].startsWith(\"/\")) {\n            rootFolder = \"/\" + rootFolder;\n          }\n          return rootFolder;\n        }\n      }\n    }\n    let rootFolder = join(\"\", ...commonPath).replace(/\\\\/g, \"/\");\n    if (filepaths[0].startsWith(\"/\")) {\n      rootFolder = \"/\" + rootFolder;\n    }\n    return rootFolder;\n  }\n\n  /**\n   * Finds the subproject that contains the given file.\n   * @param filePath - The path to the file.\n   * @returns The subproject that contains the file, or null if not found.\n   */\n  findSubprojectForFile(filePath: string): DotNetProject | null {\n    let mostPreciseProject: DotNetProject | null = null;\n    // Check what subprojects contains the file\n    // We assume that the most precise project is the one with the longest rootFolder\n    // (In case there are nested projects)\n    for (const project of this.subprojects) {\n      if (\n        filePath.startsWith(project.rootFolder) ||\n        project.rootFolder === \".\"\n      ) {\n        if (\n          mostPreciseProject === null ||\n          project.rootFolder.length > mostPreciseProject.rootFolder.length\n        ) {\n          mostPreciseProject = project;\n        }\n      }\n    }\n    return mostPreciseProject;\n  }\n\n  /**\n   * Updates the global usings for the subprojects\n   * @param globalUsings - The global usings to set for the subprojects\n   */\n  updateGlobalUsings(globalUsings: GlobalUsings, subproject: DotNetProject) {\n    globalUsings.internal.forEach((symbol) => {\n      subproject.globalUsings.internal.push(symbol);\n    });\n    globalUsings.external.forEach((symbol) => {\n      subproject.globalUsings.external.push(symbol);\n    });\n    globalUsings.directives.forEach((directive) => {\n      subproject.globalUsings.directives.push(directive);\n    });\n  }\n\n  /**\n   * Gets the global usings for the given file.\n   * @param filepath - The path to the file.\n   * @returns The global usings for the file.\n   */\n  getGlobalUsings(filepath: string): ResolvedImports {\n    const subproject = this.findSubprojectForFile(filepath);\n    if (subproject) {\n      return subproject.globalUsings;\n    }\n    return { internal: [], external: [] };\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/2Namespaces1File.cs",
    "content": "using HalfNamespace;\nusing MyApp.Models; // Useless using directive\n\nnamespace MyApp.BeefBurger\n{\n    public class Steak\n    {\n        private Gordon gordon;\n\n        public Steak(Gordon gordon)\n        {\n            this.gordon = gordon;\n        }\n\n        public void Cook()\n        {\n            Console.WriteLine(\"Gordon, we need to cook.\");\n        }\n    }\n    public static class Cheese\n    {\n        public static void Melt(this Steak steak)\n        {\n            Console.WriteLine(\"Cheese melted.\");\n        }\n    }\n    public class Bun { }\n}\nnamespace ChickenBurger\n{\n    public class Chicken { }\n    public class Salad<T>\n    {\n        public T Item { get; set; }\n        public void Add(T item)\n        {\n            Item = item;\n        }\n    }\n    public class Bun { }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/Models.cs",
    "content": "namespace MyApp.Models;\npublic class User\n{\n    public string Name { get; set; }\n    private string Password { get; set; }\n    string Email { get; set; }\n}\npublic struct Order\n{\n    public int OrderId;\n    public string Description;\n}\npublic enum OrderStatus\n{\n    Pending,\n    Completed\n}\npublic interface IOrder\n{\n    void Process();\n}\npublic delegate void OrderDelegate(int orderId);\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/Namespaced.cs",
    "content": "namespace MyNamespace\n{\n    public class MyClass\n    {\n        public void MyMethod()\n        {\n            Console.WriteLine(\"MyMethod\");\n            switch (5)\n            {\n                case 1:\n                case 2:\n                case 3:\n                case 4:\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/Nested.cs",
    "content": "namespace OuterNamespace\n{\n    public class OuterClass\n    {\n        public void OuterMethod()\n        {\n            Console.WriteLine(\"OuterMethod\");\n            if (true)\n            {\n                Console.WriteLine(\"This is an if statement\");\n            }\n        }\n        public class OuterInnerClass\n        {\n            public void OuterInnerMethod()\n            {\n                Console.WriteLine(\"OuterInnerMethod\");\n            }\n        }\n    }\n    namespace InnerNamespace\n    {\n        public class InnerClass\n        {\n            public void InnerMethod()\n            {\n                Console.WriteLine(\"InnerMethod\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/OtherFileSameNamespace.cs",
    "content": "namespace MyApp.BeefBurger;\npublic class Salad { }\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/Program.cs",
    "content": "using MyNamespace;\nusing HalfNamespace;\nusing static OuterNamespace.OuterClass;\nusing OuterNamespace.InnerNamespace;\nusing MyApp.Models;\nusing MyApp.BeefBurger;\n\nnamespace Tests\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            // Test for ambiguity resolution\n            Bun beefBun = new Bun();\n            ChickenBurger.Bun chickenBun = new ChickenBurger.Bun();\n            ChickenBurger.Salad<string> salad = new ChickenBurger.Salad<string>();\n            // Regular usage of imported namespaces\n            MyClass myClass = new MyClass();\n            Gordon gordon = new Gordon();\n            gordon.Crowbar();\n            // Class that is in no namespace\n            Freeman freeman = new Freeman();\n            freeman.Bite();\n            // Nested classes\n            OuterInnerClass outerInnerClass = new OuterInnerClass();\n            InnerClass innerClass = new InnerClass();\n            // Enum\n            OrderStatus orderStatus = OrderStatus.Pending;\n            // Static class\n            System.Math.Abs(-1).Equals(1).ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/SemiNamespaced.cs",
    "content": "namespace HalfNamespace\n{\n    public class Gordon\n    {\n        public void Crowbar()\n        {\n            Console.WriteLine(\"MyMethod\");\n        }\n    }\n}\n\npublic class Freeman\n{\n    public int Health { get; set; }\n    public void Shotgun()\n    {\n        Console.WriteLine(\"MyMethod\");\n    }\n}\n\nstatic class HeadCrab\n{\n    public static void Bite(this Freeman freeman)\n    {\n        freeman.Health -= 10;\n    }\n    public static void Heal(this Freeman freeman)\n    {\n        freeman.Health += 10;\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/Subfolder/GlobalUsings.cs",
    "content": "global using System;\nglobal using MyApp.Models;\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/Subfolder/TestFiles.Subfolder.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/TestFiles.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/csharpFiles/Usage.cs",
    "content": "global using System.IO;\nusing System;\nusing System.Collections.Generic;\nusing static System.Math;\nusing Guy = MyApp.Models.User;\nusing Valve = HalfNamespace;\nclass Usage\n{\n    public void ReadFile()\n    {\n        using (var reader = new System.IO.StreamReader(\"file.txt\"))\n        {\n            try\n            {\n                string content = reader.ReadToEnd();\n                Console.WriteLine(content);\n            }\n            catch (Exception ex)\n            {\n                Console.WriteLine($\"Error reading file: {ex.Message}\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/testFiles/index.ts",
    "content": "import { extname, join } from \"@std/path\";\nimport type Parser from \"tree-sitter\";\nimport { csharpParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\nexport const csharpFilesFolder = join(\n  import.meta.dirname as string,\n  \"csharpFiles\",\n);\n\nconst csharpFilesMap = new Map<\n  string,\n  { path: string; rootNode: Parser.SyntaxNode }\n>();\nconst csprojFiles = new Map<string, { path: string; content: string }>();\n\n/**\n * Recursively finds all C# files in the given directory and its subdirectories.\n * @param dir - The directory to search in.\n */\nfunction findCSharpFiles(dir: string) {\n  const files = Deno.readDirSync(dir);\n  files.forEach((file) => {\n    const fullPath = join(dir, file.name);\n    const stat = Deno.statSync(fullPath);\n    if (stat.isDirectory) {\n      if (\n        !fullPath.includes(\".extracted\") &&\n        !fullPath.includes(\"bin\") &&\n        !fullPath.includes(\"obj\")\n      ) {\n        findCSharpFiles(fullPath);\n      }\n    } else if (extname(fullPath) === \".cs\") {\n      const content = Deno.readTextFileSync(fullPath);\n      const tree = csharpParser.parse(content);\n      csharpFilesMap.set(fullPath, { path: fullPath, rootNode: tree.rootNode });\n    }\n  });\n}\n\n/**\n * Recursively finds all .csproj files in the given directory and its subdirectories.\n * @param dir - The directory to search in.\n */\nfunction findCsprojFiles(dir: string): void {\n  const files = Deno.readDirSync(dir);\n  files.forEach((file) => {\n    const fullPath = join(dir, file.name);\n    const stat = Deno.statSync(fullPath);\n    if (stat.isDirectory) {\n      if (\n        !fullPath.includes(\".extracted\") &&\n        !fullPath.includes(\"bin\") &&\n        !fullPath.includes(\"obj\")\n      ) {\n        findCsprojFiles(fullPath);\n      }\n    } else if (extname(fullPath) === \".csproj\") {\n      const content = Deno.readTextFileSync(fullPath);\n      csprojFiles.set(fullPath, { path: fullPath, content: content });\n    }\n  });\n}\n\n/**\n * Retrieves the map of C# files and their corresponding syntax trees.\n * @returns A map where the keys are file paths and the values are objects containing the file path and its syntax tree.\n */\nexport function getCSharpFilesMap() {\n  findCSharpFiles(csharpFilesFolder);\n  return csharpFilesMap;\n}\n\n/**\n * Retrieves the map of .csproj files and their content.\n * @returns A map where the keys are file paths and the values are the content of the .csproj files.\n */\nexport function getCsprojFilesMap() {\n  findCsprojFiles(csharpFilesFolder);\n  return csprojFiles;\n}\n"
  },
  {
    "path": "src/languagePlugins/csharp/usingResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport {\n  CSharpUsingResolver,\n  ExternalSymbol,\n  GLOBAL_USING,\n  InternalSymbol,\n  LOCAL_USING,\n  USING_ALIAS,\n  USING_CURRENT,\n  USING_STATIC,\n} from \"./index.ts\";\nimport { CSharpNamespaceMapper } from \"../namespaceMapper/index.ts\";\nimport {\n  csharpFilesFolder,\n  getCSharpFilesMap,\n  getCsprojFilesMap,\n} from \"../testFiles/index.ts\";\nimport { join } from \"@std/path\";\nimport type { File } from \"../namespaceResolver/index.ts\";\nimport { CSharpProjectMapper } from \"../projectMapper/index.ts\";\n\ndescribe(\"UsingResolver\", () => {\n  const parsedfiles: Map<string, File> = getCSharpFilesMap();\n  const csprojfiles = getCsprojFilesMap();\n  const nsmapper = new CSharpNamespaceMapper(parsedfiles);\n  const projectMapper = new CSharpProjectMapper(csprojfiles);\n  const resolver = new CSharpUsingResolver(nsmapper, projectMapper);\n\n  test(\"Directive simple parsing\", () => {\n    const usingDirectives = resolver.parseUsingDirectives(\n      join(csharpFilesFolder, \"Usage.cs\"),\n    );\n    expect(usingDirectives).toMatchObject([\n      {\n        type: GLOBAL_USING,\n        id: \"System.IO\",\n      },\n      {\n        type: LOCAL_USING,\n        id: \"System\",\n      },\n      {\n        type: LOCAL_USING,\n        id: \"System.Collections.Generic\",\n      },\n      {\n        type: USING_STATIC,\n        id: \"System.Math\",\n      },\n      {\n        type: USING_ALIAS,\n        id: \"MyApp.Models.User\",\n        alias: \"Guy\",\n      },\n      {\n        type: USING_ALIAS,\n        id: \"HalfNamespace\",\n        alias: \"Valve\",\n      },\n    ]);\n  });\n  test(\"Directive resolving\", () => {\n    const resolved = resolver.resolveUsingDirectives(\n      join(csharpFilesFolder, \"Usage.cs\"),\n    );\n    expect(resolved).toMatchObject({\n      internal: [\n        {\n          usingtype: USING_ALIAS,\n          alias: \"Guy\",\n          symbol: {\n            name: \"User\",\n            type: \"class\",\n            namespace: \"MyApp.Models\",\n          },\n        },\n        {\n          usingtype: USING_ALIAS,\n          alias: \"Valve\",\n          namespace: {\n            name: \"HalfNamespace\",\n            exports: expect.any(Array),\n            childrenNamespaces: expect.any(Array),\n          },\n        },\n        {\n          usingtype: USING_CURRENT,\n          namespace: {\n            name: \"\",\n            exports: expect.any(Array),\n            childrenNamespaces: expect.any(Array),\n          },\n        },\n      ],\n      external: [\n        {\n          usingtype: GLOBAL_USING,\n          name: \"System.IO\",\n        },\n        {\n          usingtype: LOCAL_USING,\n          name: \"System\",\n        },\n        {\n          usingtype: LOCAL_USING,\n          name: \"System.Collections.Generic\",\n        },\n        {\n          usingtype: USING_STATIC,\n          name: \"System.Math\",\n        },\n      ],\n    });\n  });\n\n  test(\"Class resolution\", () => {\n    const filepath = join(csharpFilesFolder, \"Usage.cs\");\n    const imports = resolver.resolveUsingDirectives(filepath);\n    const user = resolver.findClassInImports(imports, \"User\", filepath);\n    expect(user).toMatchObject({\n      name: \"User\",\n      type: \"class\",\n      namespace: \"MyApp.Models\",\n    });\n    const gordon = resolver.findClassInImports(imports, \"Gordon\", filepath);\n    expect(gordon).toMatchObject({\n      name: \"Gordon\",\n      type: \"class\",\n      namespace: \"HalfNamespace\",\n    });\n    const guy = resolver.findClassInImports(imports, \"Guy\", filepath);\n    expect(guy).toMatchObject({\n      name: \"User\",\n      type: \"class\",\n      namespace: \"MyApp.Models\",\n    });\n  });\n\n  test(\"Current namespace resolution\", () => {\n    const filepath = join(csharpFilesFolder, \"Models.cs\");\n    const imports = resolver.resolveUsingDirectives(filepath).internal;\n    expect(imports).toMatchObject([\n      {\n        usingtype: USING_CURRENT,\n        namespace: {\n          name: \"Models\",\n          exports: expect.any(Array),\n          childrenNamespaces: expect.any(Array),\n        },\n      },\n      {\n        usingtype: USING_CURRENT,\n        namespace: {\n          name: \"\",\n          exports: expect.any(Array),\n          childrenNamespaces: expect.any(Array),\n        },\n      },\n    ]);\n  });\n\n  test(\"instanceof functions correctly\", () => {\n    const filepath = join(csharpFilesFolder, \"Usage.cs\");\n    const directives = resolver.parseUsingDirectives(filepath);\n    expect(\n      directives.filter(\n        (d) => resolver.resolveUsingDirective(d) instanceof ExternalSymbol,\n      ).length,\n    ).toBe(4);\n    expect(\n      directives.filter(\n        (d) => resolver.resolveUsingDirective(d) instanceof InternalSymbol,\n      ).length,\n    ).toBe(2);\n\n    const programpath = join(csharpFilesFolder, \"Program.cs\");\n    const progDirectives = resolver.parseUsingDirectives(programpath);\n    expect(\n      progDirectives.filter(\n        (d) => resolver.resolveUsingDirective(d) instanceof ExternalSymbol,\n      ).length,\n    ).toBe(0);\n    expect(\n      progDirectives.filter(\n        (d) => resolver.resolveUsingDirective(d) instanceof InternalSymbol,\n      ).length,\n    ).toBe(6);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/csharp/usingResolver/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport type {\n  CSharpNamespaceMapper,\n  NamespaceNode,\n  SymbolNode,\n} from \"../namespaceMapper/index.ts\";\nimport type {\n  CSharpProjectMapper,\n  GlobalUsings,\n} from \"../projectMapper/index.ts\";\nimport { csharpParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\nconst namespaceDeclarationQuery = new Parser.Query(\n  csharpParser.getLanguage(),\n  `\n  (file_scoped_namespace_declaration\n  name : (_) @name)\n  (namespace_declaration\n  name: (_) @name)\n`,\n);\n\n// Constants representing different types of 'using' directives in C#\nexport const GLOBAL_USING = \"global\";\nexport const LOCAL_USING = \"local\";\nexport const USING_STATIC = \"static\";\nexport const USING_ALIAS = \"alias\";\n// Not for using directives, but for the namespace which is currently being worked on.\nexport const USING_CURRENT = \"current\";\n\n/** Type alias for the different 'using' directive types */\nexport type UsingType =\n  | typeof GLOBAL_USING\n  | typeof LOCAL_USING\n  | typeof USING_STATIC\n  | typeof USING_ALIAS\n  | typeof USING_CURRENT;\n\n/**\n * Interface representing a 'using' directive in the code\n */\nexport interface UsingDirective {\n  /** The syntax node corresponding to the 'using' directive */\n  node: Parser.SyntaxNode;\n  /** The type of 'using' directive */\n  type: UsingType;\n  /** The filepath it is imported in */\n  filepath: string;\n  /** The identifier or qualified name being imported */\n  id: string;\n  /** Optional alias for the imported identifier */\n  alias?: string;\n}\n\n/**\n * Interface representing an internal symbol resolved from a 'using' directive\n */\nexport class InternalSymbol {\n  /** The type of 'using' directive */\n  usingtype: UsingType;\n  /** The filepath it is imported in */\n  filepath: string;\n  /** Optional alias for the symbol */\n  alias?: string;\n  /** The symbol node if it is a class or type */\n  symbol?: SymbolNode;\n  /** The namespace node if it is a namespace */\n  namespace?: NamespaceNode;\n\n  constructor(\n    usingtype: UsingType,\n    filepath: string,\n    alias?: string,\n    symbol?: SymbolNode,\n    namespace?: NamespaceNode,\n  ) {\n    this.usingtype = usingtype;\n    this.filepath = filepath;\n    this.alias = alias;\n    this.symbol = symbol;\n    this.namespace = namespace;\n  }\n}\n\n/**\n * Interface representing an external symbol resolved from a 'using' directive\n */\nexport class ExternalSymbol {\n  /** The type of 'using' directive */\n  usingtype: UsingType;\n  /** The filepath it is imported in */\n  filepath: string;\n  /** Optional alias for the symbol */\n  alias?: string;\n  /** The name of the external symbol */\n  name: string;\n\n  constructor(\n    usingtype: UsingType,\n    filepath: string,\n    alias?: string,\n    name?: string,\n  ) {\n    this.usingtype = usingtype;\n    this.filepath = filepath;\n    this.alias = alias;\n    this.name = name ?? \"\";\n  }\n}\n\n/**\n * Interface representing the resolved imports from a file\n */\nexport interface ResolvedImports {\n  /** List of internal symbols */\n  internal: InternalSymbol[];\n  /** List of external symbols */\n  external: ExternalSymbol[];\n}\n\n/**\n * Class responsible for resolving 'using' directives in C# files\n */\nexport class CSharpUsingResolver {\n  /** Mapper for namespaces and symbols */\n  private nsMapper: CSharpNamespaceMapper;\n  /** List of parsed 'using' directives */\n  private usingDirectives: UsingDirective[] = [];\n  private cachedImports: Map<string, ResolvedImports> = new Map<\n    string,\n    ResolvedImports\n  >();\n  private cachedDirectives: Map<string, UsingDirective[]> = new Map<\n    string,\n    UsingDirective[]\n  >();\n  public projectmapper: CSharpProjectMapper;\n  private cachedExternalDeps: Set<string> = new Set<string>();\n\n  constructor(\n    nsMapper: CSharpNamespaceMapper,\n    projectmapper: CSharpProjectMapper,\n  ) {\n    this.nsMapper = nsMapper;\n    this.projectmapper = projectmapper;\n  }\n\n  /**\n   * Parses the file and returns all using directives.\n   * @param filepath - The path to the file to parse.\n   * @returns An array of UsingDirective objects.\n   */\n  public parseUsingDirectives(filepath: string): UsingDirective[] {\n    if (this.cachedDirectives.has(filepath)) {\n      return this.cachedDirectives.get(filepath) as UsingDirective[];\n    }\n    const file = this.nsMapper.getFile(filepath);\n    if (!file) {\n      return [];\n    }\n    const usingNodes = file.rootNode.descendantsOfType(\"using_directive\");\n    this.usingDirectives = usingNodes.map((node) => {\n      const type = this.getUsingType(node);\n      // The imported namespace or class is an identifier or a qualified name.\n      // Only the alias has the field name \"name\".\n      // The imported namespace isn't named in the tree, so we have to pull\n      // this kind of black magic to find it.\n      const importNode = node.children.find(\n        (child) =>\n          (child.type === \"identifier\" || child.type === \"qualified_name\") &&\n          child !== node.childForFieldName(\"name\"),\n      );\n      let id = importNode ? importNode.text : \"\";\n      // Remove the :: prefix from the id if it exists\n      id = id.includes(\"::\") ? (id.split(\"::\").pop() as string) : id;\n      const aliasNode = node.childForFieldName(\"name\");\n      const alias = aliasNode ? aliasNode.text : undefined;\n      return { node, type, filepath, id, alias };\n    });\n    // Cache the using directives for the file\n    this.cachedDirectives.set(filepath, this.usingDirectives);\n    return this.usingDirectives;\n  }\n\n  /**\n   * Determines the type of 'using' directive based on its text content.\n   * @param node - The syntax node representing the 'using' directive.\n   * @returns The type of 'using' directive.\n   */\n  private getUsingType(node: Parser.SyntaxNode): UsingType {\n    // There is probably a cleaner way to do this.\n    if (node.text.includes(\"using static\")) {\n      return USING_STATIC;\n    }\n    if (node.text.includes(\"=\")) {\n      return USING_ALIAS;\n    }\n    if (node.text.includes(\"global using\")) {\n      return GLOBAL_USING;\n    }\n    return LOCAL_USING;\n  }\n\n  /**\n   * Resolves a single 'using' directive to either an internal or external symbol.\n   * @param directive - The 'using' directive to resolve.\n   * @returns An InternalSymbol or ExternalSymbol object.\n   */\n  public resolveUsingDirective(\n    directive: UsingDirective,\n  ): InternalSymbol | ExternalSymbol {\n    const { type, filepath, id, alias } = directive;\n    // Check if the using directive is a known external dependency\n    if (this.cachedExternalDeps.has(id)) {\n      return new ExternalSymbol(type, filepath, alias, id);\n    }\n    const symbol = this.nsMapper.findClassInTree(this.nsMapper.nsTree, id);\n    if (symbol) {\n      return new InternalSymbol(type, filepath, alias, symbol);\n    }\n    const namespace = this.nsMapper.findNamespaceInTree(\n      this.nsMapper.nsTree,\n      id,\n    );\n    if (namespace) {\n      return new InternalSymbol(type, filepath, alias, undefined, namespace);\n    }\n    // If the directive is not found in the namespace tree, treat it as an external symbol\n    // and cache it\n    this.cachedExternalDeps.add(id);\n    return new ExternalSymbol(type, filepath, alias, id);\n  }\n\n  private getCurrentNamespaces(filepath: string): string[] {\n    const file = this.nsMapper.getFile(filepath);\n    if (!file) {\n      return [];\n    }\n    const currentNamespaces: string[] = [];\n    const namespaces = namespaceDeclarationQuery.captures(file.rootNode);\n    for (const nsName of namespaces) {\n      currentNamespaces.push(nsName.node.text);\n    }\n    return currentNamespaces;\n  }\n\n  /**\n   * Resolves all 'using' directives in a file and categorizes them into internal and external symbols.\n   * @param filepath - The path to the file to resolve.\n   * @returns A ResolvedImports object containing internal and external symbols.\n   */\n  public resolveUsingDirectives(filepath: string): ResolvedImports {\n    const globalUsings: GlobalUsings = {\n      internal: [],\n      external: [],\n      directives: [],\n    };\n    if (this.cachedImports.has(filepath)) {\n      return this.cachedImports.get(filepath) as ResolvedImports;\n    }\n    const internal: InternalSymbol[] = [];\n    const external: ExternalSymbol[] = [];\n    this.parseUsingDirectives(filepath).forEach((directive) => {\n      const resolved = this.resolveUsingDirective(directive);\n      if (\"symbol\" in resolved || \"namespace\" in resolved) {\n        internal.push(resolved);\n        if (directive.type === GLOBAL_USING) {\n          globalUsings.internal.push(resolved);\n          globalUsings.directives.push(directive);\n        }\n      } else {\n        external.push(resolved as ExternalSymbol);\n        if (directive.type === GLOBAL_USING) {\n          globalUsings.external.push(resolved as ExternalSymbol);\n          globalUsings.directives.push(directive);\n        }\n      }\n    });\n    const currentNamespaces = this.getCurrentNamespaces(filepath).concat(\"\");\n    // Add the current namespaces to the internal symbols\n    for (const ns of currentNamespaces) {\n      const namespace = this.nsMapper.findNamespaceInTree(\n        this.nsMapper.nsTree,\n        ns,\n      );\n      if (namespace) {\n        internal.push({\n          usingtype: USING_CURRENT,\n          filepath,\n          namespace,\n        });\n      }\n    }\n    const resolvedimports = { internal, external };\n    // Update the global usings for the project\n    const subproject = this.projectmapper.findSubprojectForFile(filepath);\n    if (subproject) {\n      this.projectmapper.updateGlobalUsings(globalUsings, subproject);\n    }\n    this.cachedImports.set(filepath, resolvedimports);\n    return resolvedimports;\n  }\n\n  /**\n   * Gets the global usings for a file.\n   * @param filepath - The path to the file to analyse.\n   * @returns A ResolvedImports object containing internal and external symbols imported through global using directives.\n   */\n  public getGlobalUsings(filepath: string): ResolvedImports {\n    return this.projectmapper.getGlobalUsings(filepath);\n  }\n\n  /**\n   * Finds a class in the resolved imports.\n   * @param imports - The resolved imports to search.\n   * @param className - The name of the class to find.\n   * @returns The SymbolNode of the class if found, otherwise null.\n   */\n  public findClassInImports(\n    imports: ResolvedImports,\n    className: string,\n    filepath: string,\n  ): SymbolNode | null {\n    // Handle qualified class names with aliases\n    const parts = className.split(\".\");\n    for (let i = 0; i < parts.length; i++) {\n      const aliasMatch = imports.internal.find(\n        (symbol) => symbol.alias === parts[i],\n      );\n      if (aliasMatch && aliasMatch.symbol) {\n        parts[i] = aliasMatch.symbol.name;\n      }\n    }\n    const reconstructedClassName = parts.join(\".\");\n    // Check if the class is directly imported\n    const found = imports.internal.find(\n      (symbol) =>\n        \"symbol\" in symbol &&\n        (symbol.symbol?.name === reconstructedClassName ||\n          symbol.alias === reconstructedClassName),\n    );\n    if (found) {\n      return found.symbol ?? null;\n    }\n    // Check if the class is imported through a namespace\n    for (const symbol of imports.internal) {\n      if (\"namespace\" in symbol && symbol.namespace) {\n        const nsFound = this.nsMapper.findClassInTree(\n          symbol.namespace,\n          reconstructedClassName,\n        );\n        if (nsFound) {\n          return nsFound;\n        }\n      }\n    }\n    // Also check in global usings\n    for (const symbol of this.getGlobalUsings(filepath).internal) {\n      if (\"namespace\" in symbol && symbol.namespace) {\n        const nsFound = this.nsMapper.findClassInTree(\n          symbol.namespace,\n          reconstructedClassName,\n        );\n        if (nsFound) {\n          return nsFound;\n        }\n      }\n    }\n\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/dependencyFormatting/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { getJavaFilesContentMap } from \"../testFiles/index.ts\";\nimport { JavaDependencyFormatter } from \"./index.ts\";\nimport { PEBBLE, STEAK, WORMKILLER } from \"../testFiles/constants.ts\";\n\ndescribe(\"Java dependency formatter\", () => {\n  const files = getJavaFilesContentMap();\n  const formatter = new JavaDependencyFormatter(files);\n\n  test(\"formats Wormkiller.java\", () => {\n    const killer = formatter.formatFile(WORMKILLER);\n    expect(killer).toBeDefined();\n    expect(killer.lineCount >= 11).toBe(true);\n    expect(killer.characterCount > 250).toBe(true);\n    expect(killer.dependencies[STEAK]).toBeDefined();\n    expect(killer.dependencies[PEBBLE]).toBeDefined();\n    expect(killer.dependencies[STEAK].symbols[\"Steak\"]).toBeDefined();\n    expect(killer.dependencies[PEBBLE].symbols[\"Pebble\"]).toBeDefined();\n    expect(killer.filePath).toBe(WORMKILLER);\n    expect(killer.id).toBe(WORMKILLER);\n    expect(killer.symbols[\"Wormkiller\"]).toBeDefined();\n    expect(killer.symbols[\"Wormkiller\"].dependencies[STEAK]).toBeDefined();\n    expect(killer.symbols[\"Wormkiller\"].dependencies[PEBBLE]).toBeDefined();\n    expect(killer.symbols[\"Wormkiller\"].type).toBe(\"class\");\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/java/dependencyFormatting/index.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport { javaParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { JavaImportResolver } from \"../importResolver/index.ts\";\nimport { JavaInvocationResolver } from \"../invocationResolver/index.ts\";\nimport { JavaPackageMapper } from \"../packageMapper/index.ts\";\nimport type { JavaDependency, JavaDepFile, JavaDepSymbol } from \"./types.ts\";\nimport type { Invocations } from \"../invocationResolver/types.ts\";\nimport type { ExportedSymbol } from \"../packageResolver/types.ts\";\n\n/**\n * Class responsible for formatting Java dependencies by analyzing parsed files,\n * resolving imports, invocations, and symbols.\n */\nexport class JavaDependencyFormatter {\n  mapper: JavaPackageMapper;\n  importResolver: JavaImportResolver;\n  invocationResolver: JavaInvocationResolver;\n\n  /**\n   * Constructs a JavaDependencyFormatter instance.\n   *\n   * @param files - A map of file identifiers to their path and content.\n   */\n  constructor(files: Map<string, { path: string; content: string }>) {\n    const parsedFiles: Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    > = new Map();\n    for (const [k, f] of files) {\n      try {\n        const rootNode = javaParser.parse(f.content, undefined, {\n          bufferSize: f.content.length + 10,\n        }).rootNode;\n        parsedFiles.set(k, {\n          path: f.path,\n          rootNode: rootNode,\n        });\n      } catch (e) {\n        console.error(`Failed to parse ${f.path}, skipping`);\n        console.error(e);\n      }\n    }\n    this.mapper = new JavaPackageMapper(parsedFiles);\n    this.importResolver = new JavaImportResolver(this.mapper);\n    this.invocationResolver = new JavaInvocationResolver(this.importResolver);\n  }\n\n  /**\n   * Formats the dependencies of a file based on its invocations.\n   *\n   * @param fileDependencies - The invocations of the file to process.\n   * @returns A record of Java dependencies.\n   */\n  #formatDependencies(\n    fileDependencies: Invocations,\n  ): Record<string, JavaDependency> {\n    const dependencies: Record<string, JavaDependency> = {};\n    const resolved = fileDependencies.resolved;\n    for (const [, node] of resolved) {\n      const filepath = node.file.path;\n      // Getting the file's symbol to get the oldest ancestor in case of nested symbol\n      const id = node.file.symbol.name;\n      if (!dependencies[filepath]) {\n        dependencies[filepath] = {\n          id: filepath,\n          isExternal: false,\n          symbols: {},\n        };\n      }\n      dependencies[filepath].symbols[id] = id;\n    }\n    return dependencies;\n  }\n\n  /**\n   * Formats standard library imports into dependency records.\n   *\n   * @param stdimports - An array of unresolved standard imports.\n   * @returns A record of Java dependencies for standard imports.\n   */\n  #formatStandardImports(stdimports: string[]): Record<string, JavaDependency> {\n    const dependencies: Record<string, JavaDependency> = {};\n    for (const id of stdimports) {\n      if (!dependencies[id]) {\n        dependencies[id] = {\n          id: id,\n          isExternal: true,\n          symbols: {},\n        };\n      }\n    }\n    return dependencies;\n  }\n\n  /**\n   * Formats a symbol exported by a file into a dependency symbol record.\n   *\n   * @param fileSymbol - The exported symbol to format.\n   * @returns A record containing the formatted dependency symbol.\n   */\n  #formatSymbol(fileSymbol: ExportedSymbol): Record<string, JavaDepSymbol> {\n    // Record despite files only having 1 symbol since it's needed for manifest\n    const symbol: Record<string, JavaDepSymbol> = {};\n    const id = fileSymbol.name;\n    const dependencies = this.invocationResolver.getInvocations(\n      this.mapper.files.get(fileSymbol.filepath)!,\n    );\n    symbol[id] = {\n      id: id,\n      type: fileSymbol.type,\n      lineCount: fileSymbol.node.endPosition.row -\n        fileSymbol.node.startPosition.row,\n      characterCount: fileSymbol.node.endIndex -\n        fileSymbol.node.startIndex,\n      node: fileSymbol.node,\n      dependents: {},\n      dependencies: this.#formatDependencies(dependencies),\n    };\n    return symbol;\n  }\n\n  /**\n   * Formats a file into a JavaDepFile object, including its dependencies and symbols.\n   *\n   * @param filepath - The path of the file to format.\n   * @returns A formatted JavaDepFile object.\n   */\n  formatFile(filepath: string): JavaDepFile {\n    const file = this.mapper.files.get(filepath)!;\n    const imports = this.importResolver.imports.get(filepath)!;\n    const stdDependencies = this.#formatStandardImports(imports.unresolved);\n    const invocations = this.invocationResolver.invocations.get(filepath)!;\n    const invokedDependencies = this.#formatDependencies(invocations);\n    const allDependencies = {\n      ...invokedDependencies,\n      ...stdDependencies,\n    };\n    const formattedFile: JavaDepFile = {\n      id: filepath,\n      filePath: file.path,\n      rootNode: file.rootNode,\n      lineCount: file.rootNode.endPosition.row,\n      characterCount: file.rootNode.endIndex,\n      dependencies: allDependencies,\n      symbols: this.#formatSymbol(file.symbol),\n    };\n    return formattedFile;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/dependencyFormatting/types.ts",
    "content": "import type { SymbolType } from \"../packageResolver/types.ts\";\nimport type Parser from \"tree-sitter\";\n\n/**\n * Represents a dependency in a Java file\n */\nexport interface JavaDependency {\n  id: string;\n  isExternal: boolean;\n  symbols: Record<string, string>;\n}\n\n/**\n * Represents a dependent in a Java file\n */\nexport interface JavaDependent {\n  id: string;\n  symbols: Record<string, string>;\n}\n\n/**\n * Represents a symbol in a Java file\n */\nexport interface JavaDepSymbol {\n  id: string;\n  type: SymbolType;\n  lineCount: number;\n  characterCount: number;\n  node: Parser.SyntaxNode;\n  dependents: Record<string, JavaDependent>;\n  dependencies: Record<string, JavaDependency>;\n}\n\n/**\n * Represents a Java file with its dependencies and symbols.\n */\nexport interface JavaDepFile {\n  id: string;\n  filePath: string;\n  rootNode: Parser.SyntaxNode;\n  lineCount: number;\n  characterCount: number;\n  dependencies: Record<string, JavaDependency>;\n  symbols: Record<string, JavaDepSymbol>;\n}\n"
  },
  {
    "path": "src/languagePlugins/java/extractor/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { getJavaFilesContentMap } from \"../testFiles/index.ts\";\nimport { generateJavaDependencyManifest } from \"../../../manifest/dependencyManifest/java/index.ts\";\nimport { JavaExtractor } from \"./index.ts\";\nimport { WORMKILLER } from \"../testFiles/constants.ts\";\n\ndescribe(\"Java extractor\", () => {\n  const files = getJavaFilesContentMap();\n  const manifest = generateJavaDependencyManifest(files);\n  const extractor = new JavaExtractor(files, manifest);\n  test(\"extracts wormkiller\", () => {\n    const symbolsMap: Map<string, { filePath: string; symbols: Set<string> }> =\n      new Map();\n    symbolsMap.set(WORMKILLER, {\n      filePath: WORMKILLER,\n      symbols: new Set(\"Wormkiller\"),\n    });\n    const extracted = extractor.extractSymbols(symbolsMap);\n    expect(extracted.size).toBe(4);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/java/extractor/index.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport type { DependencyManifest } from \"../../../manifest/dependencyManifest/types.ts\";\nimport { JavaInvocationResolver } from \"../invocationResolver/index.ts\";\nimport { javaParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { JavaPackageMapper } from \"../packageMapper/index.ts\";\nimport { JavaImportResolver } from \"../importResolver/index.ts\";\nimport { ExportedFile } from \"./types.ts\";\n\n/**\n * Responsible for extracting and managing Java symbols\n * from a set of files, using dependency resolution and package mapping.\n */\nexport class JavaExtractor {\n  manifest: DependencyManifest;\n  resolver: JavaInvocationResolver;\n\n  /**\n   * Constructs a new instance of `JavaExtractor`.\n   *\n   * @param files - A map of file paths to their respective content and metadata.\n   * @param manifest - The dependency manifest containing information about project dependencies.\n   */\n  constructor(\n    files: Map<string, { path: string; content: string }>,\n    manifest: DependencyManifest,\n  ) {\n    this.manifest = manifest;\n    const parsedFiles = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    for (const [filepath, file] of files) {\n      parsedFiles.set(filepath, {\n        path: file.path,\n        rootNode: javaParser.parse(file.content).rootNode,\n      });\n    }\n    const mapper = new JavaPackageMapper(parsedFiles);\n    const importresolver = new JavaImportResolver(mapper);\n    this.resolver = new JavaInvocationResolver(importresolver);\n  }\n\n  /**\n   * Extracts symbols from the provided symbol map and returns a map of files\n   * that should be kept, including their paths and content.\n   *\n   * @param symbolsMap - A map where keys are symbol names and values contain file paths\n   * and sets of symbols associated with those files.\n   * @returns A map of file paths to their respective content and metadata for files to keep.\n   */\n  extractSymbols(\n    symbolsMap: Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >,\n  ): Map<string, { path: string; content: string }> {\n    const exportedfiles = Array.from(\n      symbolsMap.keys().map((k) => new ExportedFile(k, this.resolver)),\n    );\n    const filesToKeep: Map<string, { path: string; content: string }> =\n      new Map();\n    for (const f of exportedfiles) {\n      if (!filesToKeep.has(f.file.path)) {\n        filesToKeep.set(f.file.path, {\n          path: f.file.path,\n          content: f.file.rootNode.text,\n        });\n      }\n      for (const [k, v] of f.dependencies) {\n        if (!filesToKeep.has(k)) {\n          filesToKeep.set(k, {\n            path: v.path,\n            content: v.rootNode.text,\n          });\n        }\n      }\n    }\n    return filesToKeep;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/extractor/types.ts",
    "content": "import type { JavaInvocationResolver } from \"../invocationResolver/index.ts\";\nimport type { JavaFile } from \"../packageResolver/types.ts\";\n\n/**\n * Represents a file that has been exported along with its dependencies.\n */\nexport class ExportedFile {\n  /**\n   * A map of dependencies where the key is the file path and the value is the corresponding JavaFile.\n   */\n  dependencies: Map<string, JavaFile>;\n\n  /**\n   * The main JavaFile instance associated with this exported file.\n   */\n  file: JavaFile;\n\n  /**\n   * Constructs an ExportedFile instance and resolves its dependencies.\n   *\n   * @param filepath - The file path of the Java file to be exported.\n   * @param resolver - An instance of JavaInvocationResolver used to resolve file dependencies.\n   */\n  constructor(filepath: string, resolver: JavaInvocationResolver) {\n    this.file = resolver.mapper.files.get(filepath)!;\n    this.dependencies = new Map();\n    const stack = [this.file];\n    while (stack.length > 0) {\n      const current = stack.pop()!;\n      if (this.dependencies.has(current.path)) {\n        continue;\n      }\n      this.dependencies.set(current.path, current);\n      const currentDep = resolver.getInvocations(current).resolved;\n      stack.push(...currentDep.values().map((v) => v.file));\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/importResolver/index.test.ts",
    "content": "import { getJavaFilesMap } from \"../testFiles/index.ts\";\nimport { BURGER, PEBBLE, WORMKILLER } from \"../testFiles/constants.ts\";\nimport { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { JavaImportResolver } from \"./index.ts\";\nimport { JavaPackageMapper } from \"../packageMapper/index.ts\";\n\ndescribe(\"Java Import Resolver\", () => {\n  const files = getJavaFilesMap();\n  const mapper = new JavaPackageMapper(files);\n  const resolver = new JavaImportResolver(mapper);\n\n  test(\"unresolved external dependencies\", () => {\n    const burgerimports = resolver.imports.get(BURGER)!;\n    expect(burgerimports.unresolved.length).toBe(2);\n    expect(burgerimports.unresolved).toContain(\"java.io.File\");\n    expect(burgerimports.unresolved).toContain(\"java.lang.System\");\n  });\n\n  test(\"resolved internal dependencies\", () => {\n    const killerimports = resolver.imports.get(WORMKILLER)!;\n    expect(killerimports.resolved.size).toBe(3);\n    expect(killerimports.unresolved.length).toBe(0);\n    expect(killerimports.resolved.get(\"Steak\")).toBeDefined();\n    expect(killerimports.resolved.get(\"Pebble\")).toBeDefined();\n    expect(killerimports.resolved.get(\"Wormkiller\")).toBeDefined();\n    expect(killerimports.resolved.get(\"Pebble.Sandworm\")).toBeUndefined();\n    expect(killerimports.resolved.get(\"Sandworm\")).toBeUndefined();\n\n    const pebbleimports = resolver.imports.get(PEBBLE)!;\n    expect(pebbleimports.resolved.size).toBe(2);\n    expect(pebbleimports.unresolved.length).toBe(0);\n    expect(pebbleimports.resolved.get(\"Food\")).toBeDefined();\n    expect(pebbleimports.resolved.get(\"Pebble\")).toBeDefined();\n\n    const burgerimports = resolver.imports.get(BURGER)!;\n    expect(burgerimports.resolved.get(\"Food\")).toBeDefined();\n    expect(burgerimports.resolved.get(\"Condiment\")).toBeDefined();\n    expect(burgerimports.resolved.get(\"DoubleBurger\")).toBeDefined();\n    expect(burgerimports.resolved.get(\"Steak\")).toBeDefined();\n    expect(burgerimports.resolved.get(\"Pebble\")).toBeUndefined();\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/java/importResolver/index.ts",
    "content": "import type { JavaPackageMapper } from \"../packageMapper/index.ts\";\nimport type { JavaFile } from \"../packageResolver/types.ts\";\nimport type { JavaImports } from \"./types.ts\";\n\n/**\n * Resolves and organizes Java imports for a given set of files.\n */\nexport class JavaImportResolver {\n  mapper: JavaPackageMapper;\n  imports: Map<string, JavaImports>;\n\n  /**\n   * Creates an instance of JavaImportResolver.\n   * @param {JavaPackageMapper} mapper - The package mapper containing file and tree information.\n   */\n  constructor(mapper: JavaPackageMapper) {\n    this.mapper = mapper;\n    this.imports = new Map();\n    for (const [key, f] of this.mapper.files) {\n      this.imports.set(key, this.getImports(f));\n    }\n  }\n\n  /**\n   * Retrieves and categorizes imports for a given Java file.\n   * @param {JavaFile} file - The Java file for which imports need to be resolved.\n   * @returns {JavaImports} An object containing resolved and unresolved imports.\n   */\n  getImports(file: JavaFile): JavaImports {\n    const imports: JavaImports = {\n      resolved: new Map(),\n      unresolved: [],\n    };\n    const importstrings = file.imports;\n    for (const impstr of importstrings) {\n      const foundimport = this.mapper.tree.getImport(impstr);\n      if (!foundimport) {\n        imports.unresolved.push(impstr);\n      } else {\n        for (const [key, dep] of foundimport) {\n          imports.resolved.set(key, dep);\n        }\n      }\n    }\n    const currentPackageImports = this.mapper.tree.getImport(\n      file.package + \".*\",\n    );\n    if (currentPackageImports) {\n      for (const [key, dep] of currentPackageImports) {\n        imports.resolved.set(key, dep);\n      }\n    }\n    return imports;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/importResolver/types.ts",
    "content": "import type { ConcreteNode } from \"../packageMapper/types.ts\";\n\n/**\n * Represents a collection of Java imports, categorized into resolved and unresolved imports.\n */\nexport interface JavaImports {\n  /**\n   * A map of resolved imports where the key is the import name and the value is the corresponding ConcreteNode.\n   */\n  resolved: Map<string, ConcreteNode>;\n\n  /**\n   * An array of unresolved import names.\n   */\n  unresolved: string[];\n}\n"
  },
  {
    "path": "src/languagePlugins/java/invocationResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { getJavaFilesMap } from \"../testFiles/index.ts\";\nimport { JavaInvocationResolver } from \"./index.ts\";\nimport { JavaPackageMapper } from \"../packageMapper/index.ts\";\nimport { JavaImportResolver } from \"../importResolver/index.ts\";\nimport { APP, WORMKILLER } from \"../testFiles/constants.ts\";\n\ndescribe(\"Java Invocation Resolver\", () => {\n  const files = getJavaFilesMap();\n  const mapper = new JavaPackageMapper(files);\n  const impResolver = new JavaImportResolver(mapper);\n  const resolver = new JavaInvocationResolver(impResolver);\n  const invocations = resolver.invocations;\n\n  test(\"resolves Wormkiller.java\", () => {\n    const killerinv = invocations.get(WORMKILLER)!;\n    expect(killerinv.unresolved.has(\"Sandworm\")).toBe(true);\n    expect(killerinv.unresolved.has(\"Pebble.Sandworm\")).toBe(true);\n    const resolved = Array.from(killerinv.resolved.keys());\n    expect(resolved).toContain(\"Steak\");\n    expect(resolved).toContain(\"Pebble\");\n  });\n\n  test(\"resolves App.java\", () => {\n    const appinv = invocations.get(APP)!;\n    console.log(impResolver.imports.get(APP)!);\n    expect(appinv.unresolved.has(\"System.out\")).toBe(false);\n    expect(appinv.unresolved.has(\"System\")).toBe(true);\n    const resolved = Array.from(appinv.resolved.keys());\n    expect(resolved).toContain(\"restaurantCount\");\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/java/invocationResolver/index.ts",
    "content": "import type { JavaImportResolver } from \"../importResolver/index.ts\";\nimport type { JavaImports } from \"../importResolver/types.ts\";\nimport type { JavaPackageMapper } from \"../packageMapper/index.ts\";\nimport { ConcreteNode } from \"../packageMapper/types.ts\";\nimport type { JavaFile } from \"../packageResolver/types.ts\";\nimport { JAVA_INVOCATION_QUERY, JAVA_VARIABLES_QUERY } from \"./queries.ts\";\nimport type { Invocations } from \"./types.ts\";\n\n/**\n * Resolves Java method invocations and maps them to their corresponding imports or project nodes.\n */\nexport class JavaInvocationResolver {\n  mapper: JavaPackageMapper;\n  imports: Map<string, JavaImports>;\n  invocations: Map<string, Invocations>;\n\n  /**\n   * Constructs a new JavaInvocationResolver.\n   *\n   * @param resolver - An instance of JavaImportResolver containing the package mapper and imports.\n   */\n  constructor(resolver: JavaImportResolver) {\n    this.mapper = resolver.mapper;\n    this.imports = resolver.imports;\n    this.invocations = new Map();\n    for (const [key, f] of this.mapper.files) {\n      this.invocations.set(key, this.getInvocations(f));\n    }\n  }\n\n  /**\n   * Extracts and resolves method invocations from a given Java file.\n   *\n   * @param file - The Java file to analyze for method invocations.\n   * @returns An object containing resolved and unresolved method invocations.\n   */\n  getInvocations(file: JavaFile): Invocations {\n    const variables = new Set(\n      JAVA_VARIABLES_QUERY.captures(file.symbol.node).map((c) => c.node.text),\n    );\n    const captures = JAVA_INVOCATION_QUERY.captures(file.symbol.node).map((c) =>\n      c.node.text\n    );\n    const resolvedImports = this.imports.get(file.path)!.resolved;\n    const resolved = new Map<string, ConcreteNode>();\n    const unresolved = new Set<string>();\n    for (const c of captures) {\n      // Check variables\n      if (variables.has(c)) {\n        continue;\n      }\n      // Check imports\n      if (resolvedImports.has(c)) {\n        resolved.set(c, resolvedImports.get(c)!);\n      } else {\n        // Check project\n        const cnode = this.mapper.tree.getNode(c);\n        if (cnode && cnode instanceof ConcreteNode) {\n          resolved.set(c, cnode);\n        } else {\n          unresolved.add(c);\n        }\n      }\n    }\n    return { resolved, unresolved };\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/invocationResolver/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { javaParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\n/**\n * A Tree-sitter query to match Java method or type invocations.\n * This query captures scoped type identifiers, type identifiers, and identifiers.\n */\nexport const JAVA_INVOCATION_QUERY = new Parser.Query(\n  javaParser.getLanguage(),\n  `\n  [\n  (scoped_type_identifier)\n  (type_identifier)\n  (identifier)\n  ] @any\n  `,\n);\n\n/**\n * A Tree-sitter query to match Java variable declarations and formal parameters.\n * This query captures variable declarators and formal parameter names.\n */\nexport const JAVA_VARIABLES_QUERY = new Parser.Query(\n  javaParser.getLanguage(),\n  `\n  (variable_declarator name: (_) @var)\n  (formal_parameter name: (_) @var)\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/java/invocationResolver/types.ts",
    "content": "import type { ConcreteNode } from \"../packageMapper/types.ts\";\n\n/**\n * Represents a collection of invocations.\n */\nexport interface Invocations {\n  /**\n   * A map of resolved invocations, where the key is a string identifier\n   * and the value is a `ConcreteNode` object.\n   */\n  resolved: Map<string, ConcreteNode>;\n\n  /**\n   * A set of unresolved invocation identifiers.\n   */\n  unresolved: Set<string>;\n}\n"
  },
  {
    "path": "src/languagePlugins/java/metrics/index.ts",
    "content": "import { JAVA_COMMENT_QUERY, JAVA_COMPLEXITY_QUERY } from \"./queries.ts\";\nimport type {\n  CodeCounts,\n  CommentSpan,\n  JavaComplexityMetrics,\n} from \"./types.ts\";\nimport type Parser from \"tree-sitter\";\n\n// This class has no tests because it is copy-pasted from C.\n// If the C metrics work, then the Java ones do.\n\nexport class JavaMetricsAnalyzer {\n  /**\n   * Calculates metrics for a Java symbol.\n   * @param node - The syntax node to analyze.\n   * @returns An object containing the complexity metrics.\n   */\n  public analyzeNode(node: Parser.SyntaxNode): JavaComplexityMetrics {\n    if (node.type === \"preproc_function_def\") {\n      const value = node.childForFieldName(\"value\");\n      if (value) {\n        node = value;\n      }\n    }\n    const complexityCount = this.getComplexityCount(node);\n    const linesCount = node.endPosition.row - node.startPosition.row + 1;\n    const codeCounts = this.getCodeCounts(node);\n    const codeLinesCount = codeCounts.lines;\n    const characterCount = node.endIndex - node.startIndex;\n    const codeCharacterCount = codeCounts.characters;\n\n    return {\n      cyclomaticComplexity: complexityCount,\n      linesCount,\n      codeLinesCount,\n      characterCount,\n      codeCharacterCount,\n    };\n  }\n\n  private getComplexityCount(node: Parser.SyntaxNode): number {\n    const complexityMatches = JAVA_COMPLEXITY_QUERY.captures(node);\n    return complexityMatches.length;\n  }\n\n  /**\n   * Finds comments in the given node and returns their spans.\n   * @param node - The AST node to analyze\n   * @returns An object containing pure comment lines and comment spans\n   */\n  private findComments(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): {\n    pureCommentLines: Set<number>;\n    commentSpans: CommentSpan[];\n  } {\n    const pureCommentLines = new Set<number>();\n    const commentSpans: CommentSpan[] = [];\n\n    const commentCaptures = JAVA_COMMENT_QUERY.captures(node);\n\n    for (const capture of commentCaptures) {\n      const commentNode = capture.node;\n\n      // Record the comment span for character counting\n      commentSpans.push({\n        start: {\n          row: commentNode.startPosition.row,\n          column: commentNode.startPosition.column,\n        },\n        end: {\n          row: commentNode.endPosition.row,\n          column: commentNode.endPosition.column,\n        },\n      });\n\n      // Check if the comment starts at the beginning of the line (ignoring whitespace)\n      const lineIdx = commentNode.startPosition.row - node.startPosition.row;\n      if (lineIdx >= 0 && lineIdx < lines.length) {\n        const lineText = lines[lineIdx];\n        const textBeforeComment = lineText.substring(\n          0,\n          commentNode.startPosition.column,\n        );\n\n        // If there's only whitespace before the comment, it's a pure comment line\n        if (textBeforeComment.trim().length === 0) {\n          for (\n            let line = commentNode.startPosition.row;\n            line <= commentNode.endPosition.row;\n            line++\n          ) {\n            pureCommentLines.add(line);\n          }\n        }\n      }\n    }\n\n    return { pureCommentLines, commentSpans };\n  }\n\n  /**\n   * Finds all empty lines in a node\n   *\n   * @param node The syntax node to analyze\n   * @param lines The lines of text in the node\n   * @returns Set of line numbers that are empty\n   */\n  private findEmptyLines(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): Set<number> {\n    const emptyLines = new Set<number>();\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      if (lines[i].trim().length === 0) {\n        emptyLines.add(lineIndex);\n      }\n    }\n\n    return emptyLines;\n  }\n\n  private getCodeCounts(node: Parser.SyntaxNode): CodeCounts {\n    const lines = node.text.split(/\\r?\\n/);\n    const linesCount = lines.length;\n    // Find comments and their spans\n    const { pureCommentLines, commentSpans } = this.findComments(node, lines);\n\n    // Find empty lines\n    const emptyLines = this.findEmptyLines(node, lines);\n\n    // Calculate code lines\n    const nonCodeLines = new Set([...pureCommentLines, ...emptyLines]);\n    const codeLinesCount = linesCount - nonCodeLines.size;\n\n    let codeCharCount = 0;\n\n    // Process each line individually\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      const line = lines[i];\n\n      // Skip empty lines and pure comment lines\n      if (emptyLines.has(lineIndex) || pureCommentLines.has(lineIndex)) {\n        continue;\n      }\n\n      // Process line for code characters\n      let lineText = line;\n\n      // Remove comment content from the line if present\n      for (const span of commentSpans) {\n        if (span.start.row === lineIndex) {\n          // Comment starts on this line\n          lineText = lineText.substring(0, span.start.column);\n        }\n      }\n\n      // Count normalized code characters (trim excessive whitespace)\n      const normalizedText = lineText.trim().replace(/\\s+/g, \" \");\n      codeCharCount += normalizedText.length;\n    }\n\n    return {\n      lines: codeLinesCount,\n      characters: codeCharCount,\n    };\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/metrics/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { javaParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\n// Tree-sitter query to find complexity-related nodes\nexport const JAVA_COMPLEXITY_QUERY = new Parser.Query(\n  javaParser.getLanguage(),\n  `\n    (if_statement) @complexity\n    (while_statement) @complexity\n    (for_statement) @complexity\n    (do_statement) @complexity\n    (switch_block_statement_group) @complexity\n    (ternary_expression) @complexity\n  `,\n);\n\nexport const JAVA_COMMENT_QUERY = new Parser.Query(\n  javaParser.getLanguage(),\n  `\n    (block_comment) @comment\n    (line_comment) @comment\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/java/metrics/types.ts",
    "content": "/**\n * Interface for code volumes\n */\nexport interface CodeCounts {\n  /** Number of lines of code */\n  lines: number;\n  /** Number of characters of code */\n  characters: number;\n}\n\nexport interface CommentSpan {\n  start: { row: number; column: number };\n  end: { row: number; column: number };\n}\n\n/**\n * Represents complexity metrics for a C symbol\n */\nexport interface JavaComplexityMetrics {\n  /** Cyclomatic complexity (McCabe complexity) */\n  cyclomaticComplexity: number;\n  /** Code lines (not including whitespace or comments) */\n  codeLinesCount: number;\n  /** Total lines (including whitespace and comments) */\n  linesCount: number;\n  /** Characters of actual code (excluding comments and excessive whitespace) */\n  codeCharacterCount: number;\n  /** Total characters in the entire symbol */\n  characterCount: number;\n}\n"
  },
  {
    "path": "src/languagePlugins/java/packageMapper/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { JavaPackageMapper } from \"./index.ts\";\nimport { getJavaFilesMap } from \"../testFiles/index.ts\";\n\ndescribe(\"Java Package Mapper\", () => {\n  const files = getJavaFilesMap();\n  const mapper = new JavaPackageMapper(files);\n  const tree = mapper.tree;\n\n  test(\"maps the project correctly\", () => {\n    expect(Array.from(tree.children.keys())).toContain(\"io\");\n    expect(Array.from(tree.children.get(\"io\")!.children.keys())).toContain(\n      \"nanoapi\",\n    );\n    expect(\n      Array.from(\n        tree.children.get(\"io\")!.children.get(\"nanoapi\")!.children.keys(),\n      ),\n    )\n      .toContain(\"testfiles\");\n    expect(\n      Array.from(tree.children.get(\"io\")!.children.get(\"nanoapi\")!.children.get(\n        \"testfiles\",\n      )!.children.keys()),\n    ).toContain(\"food\");\n    const food = tree.children.get(\"io\")!.children.get(\"nanoapi\")!.children.get(\n      \"testfiles\",\n    )!.children.get(\"food\")!;\n    const classes = Array.from(food.children.keys());\n    expect(classes).toContain(\"Burger\");\n    expect(classes).toContain(\"Condiment\");\n    expect(classes).toContain(\"DoubleBurger\");\n    expect(classes).toContain(\"Food\");\n    expect(classes).toContain(\"Steak\");\n    expect(classes).toContain(\"goron\");\n    const goron = food.children.get(\"goron\")!;\n    expect(goron.children.get(\"Pebble\")).toBeDefined();\n    expect(goron.children.get(\"Pebble\")!.children.get(\"Sandworm\"))\n      .toBeDefined();\n  });\n\n  test(\"finds nodes\", () => {\n    expect(tree.getNode(\"\")).toBeDefined();\n    expect(tree.getNode(\"io\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.App\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Burger\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Burger.advertisement\"))\n      .toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Burger.restaurantCount\"))\n      .toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Condiment\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.DoubleBurger\"))\n      .toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Food\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Steak\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Steak.Tapeworm\"))\n      .toBeUndefined(); // Private nested classes aren't registered\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.Screws\")).toBeUndefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.goron\")).toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.goron.Pebble\"))\n      .toBeDefined();\n    expect(tree.getNode(\"io.nanoapi.testfiles.food.goron.Pebble.Sandworm\"))\n      .toBeDefined();\n  });\n\n  test(\"resolves imports\", () => {\n    const ioimport = tree.getImport(\"io.*\");\n    expect(ioimport).toBeDefined();\n    if (!ioimport) {\n      throw Error(\"dummy error, not supposed to happen\");\n    }\n    expect(ioimport.size).toBe(0);\n    const foodimport = tree.getImport(\"io.nanoapi.testfiles.food.*\")!;\n    expect(foodimport.size >= 5).toBe(true);\n    expect(foodimport.get(\"goron\")).toBeUndefined();\n    expect(foodimport.get(\"Pebble\")).toBeUndefined();\n    const burgerimport = tree.getImport(\"io.nanoapi.testfiles.food.Burger\")!;\n    expect(burgerimport.size).toBe(1);\n    const burgerstarimport = tree.getImport(\n      \"io.nanoapi.testfiles.food.Burger.*\",\n    );\n    expect(burgerstarimport).toBeDefined();\n    expect(burgerstarimport!.size).toBe(2);\n    const advertisementimport = tree.getImport(\n      \"io.nanoapi.testfiles.food.Burger.advertisement\",\n    )!;\n    expect(advertisementimport).toBeDefined();\n    expect(advertisementimport.size).toBe(1);\n\n    expect(tree.getImport(\"io.nanoapi.testfiles\")).toBeUndefined();\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/java/packageMapper/index.ts",
    "content": "import { JavaTree } from \"./types.ts\";\nimport type Parser from \"tree-sitter\";\nimport { JavaPackageResolver } from \"../packageResolver/index.ts\";\nimport type { JavaFile } from \"../packageResolver/types.ts\";\n\n/**\n * Maps Java files to their respective packages and builds a tree representation.\n */\nexport class JavaPackageMapper {\n  /** A tree structure representing the Java package hierarchy. */\n  tree: JavaTree;\n\n  /** A map of file paths to their resolved JavaFile representations. */\n  files: Map<string, JavaFile>;\n\n  /**\n   * Constructs a new JavaPackageMapper instance.\n   *\n   * @param files - A map where each key is a file path and each value is an object containing\n   *                the file path and its root syntax node.\n   */\n  constructor(\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n  ) {\n    this.files = new Map();\n    this.tree = new JavaTree();\n    const resolver = new JavaPackageResolver();\n    for (const f of files.values()) {\n      const resolvedFile = resolver.resolveFile(f);\n      if (!resolvedFile) {\n        continue;\n      }\n      this.tree.addFile(resolvedFile);\n      this.files.set(f.path, resolvedFile);\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/packageMapper/types.ts",
    "content": "import type { ExportedSymbol, JavaFile } from \"../packageResolver/types.ts\";\n\n/**\n * Represents a node in a Java tree structure.\n */\nexport interface JavaNode {\n  /** The name of the node. */\n  name: string;\n  /** The children of the node, stored as a map. */\n  children: Map<string, JavaNode>;\n}\n\n/**\n * A basic implementation of the JavaNode interface.\n */\nexport class AbstractNode implements JavaNode {\n  /** The name of the node. */\n  name: string;\n  /** The children of the node, stored as a map. */\n  children: Map<string, JavaNode>;\n\n  /**\n   * Creates an instance of AbstractNode.\n   * @param name - The name of the node.\n   */\n  constructor(name: string) {\n    this.name = name;\n    this.children = new Map();\n  }\n}\n\n/**\n * An abstract class representing a concrete node in the Java tree structure.\n */\nexport abstract class ConcreteNode implements JavaNode {\n  /** The name of the node. */\n  name: string;\n  /** The children of the node, stored as a map of NestedSymbol. */\n  children: Map<string, NestedSymbol>;\n  /** The declaration associated with this node. */\n  declaration: ExportedSymbol;\n  /** The file associated with this node. */\n  file: JavaFile;\n\n  /**\n   * Creates an instance of ConcreteNode.\n   * @param declaration - The exported symbol declaration for this node.\n   * @param file - The Java file associated with this node.\n   */\n  constructor(declaration: ExportedSymbol, file: JavaFile) {\n    this.declaration = declaration;\n    this.file = file;\n    this.name = declaration.name;\n    this.children = new Map();\n  }\n}\n\n/**\n * Represents the root of a Java tree structure.\n */\nexport class JavaTree extends AbstractNode {\n  /**\n   * Creates an instance of JavaTree.\n   */\n  constructor() {\n    super(\"\");\n  }\n\n  /**\n   * Adds a Java file to the tree structure.\n   * @param file - The Java file to add.\n   */\n  addFile(file: JavaFile) {\n    const packageparts = file.package.split(\".\");\n    let current = this.children;\n    for (const p of packageparts) {\n      if (!current.has(p)) {\n        current.set(p, new AbstractNode(p));\n      }\n      current = current.get(p)!.children;\n    }\n    current.set(file.symbol.name, new FileNode(file));\n  }\n\n  /**\n   * Retrieves a node from the tree by its name.\n   * @param name - The fully qualified name of the node.\n   * @returns The JavaNode if found, otherwise undefined.\n   */\n  getNode(name: string): JavaNode | undefined {\n    if (name === \"\") {\n      return this;\n    } else {\n      const packageparts = name.split(\".\").reverse();\n      let current = this.children.get(packageparts.pop()!);\n      if (!current) {\n        return undefined;\n      }\n      while (packageparts.length > 0) {\n        if (!current) {\n          return undefined;\n        }\n        current = current.children.get(packageparts.pop()!);\n      }\n      return current;\n    }\n  }\n\n  /**\n   * Retrieves a map of importable nodes by their name.\n   * @param name - The name of the import, optionally ending with `*` for wildcard imports.\n   * @returns A map of ConcreteNode objects if found, otherwise undefined.\n   */\n  getImport(name: string): Map<string, ConcreteNode> | undefined {\n    if (name.endsWith(\"*\")) {\n      const node = this.getNode(name.substring(0, name.length - 2));\n      if (node) {\n        const map = new Map<string, ConcreteNode>();\n        for (const v of node.children.values()) {\n          if (v instanceof ConcreteNode) {\n            map.set(v.name, v);\n          }\n        }\n        return map;\n      }\n    } else {\n      const node = this.getNode(name);\n      if (node && node instanceof ConcreteNode) {\n        const map = new Map<string, ConcreteNode>();\n        map.set(node.name, node);\n        return map;\n      }\n    }\n    return undefined;\n  }\n}\n\n/**\n * Represents a file node in the Java tree structure.\n */\nexport class FileNode extends ConcreteNode {\n  /**\n   * Creates an instance of FileNode.\n   * @param file - The Java file associated with this node.\n   */\n  constructor(file: JavaFile) {\n    super(file.symbol, file);\n    for (const c of this.declaration.children) {\n      if (!c.modifiers.includes(\"private\")) {\n        this.children.set(c.name, new NestedSymbol(c, file));\n      }\n    }\n  }\n}\n\n/**\n * Represents a nested symbol within a Java file.\n */\nexport class NestedSymbol extends ConcreteNode {\n  /**\n   * Creates an instance of NestedSymbol.\n   * @param symbol - The exported symbol associated with this node.\n   * @param file - The Java file associated with this node.\n   */\n  constructor(symbol: ExportedSymbol, file: JavaFile) {\n    super(symbol, file);\n    for (const c of this.declaration.children) {\n      if (!c.modifiers.includes(\"private\")) {\n        this.children.set(c.name, new NestedSymbol(c, file));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/packageResolver/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { getJavaFilesMap } from \"../testFiles/index.ts\";\nimport {\n  BURGER,\n  CONDIMENT,\n  DOUBLEBURGER,\n  FOOD,\n  STEAK,\n} from \"../testFiles/constants.ts\";\nimport { JavaPackageResolver } from \"./index.ts\";\nimport type { JavaClass } from \"./types.ts\";\n\ndescribe(\"Java Package Resolver\", () => {\n  const files = getJavaFilesMap();\n  const condiment = files.get(CONDIMENT)!;\n  const food = files.get(FOOD)!;\n  const steak = files.get(STEAK)!;\n  const burger = files.get(BURGER)!;\n  const doubleburger = files.get(DOUBLEBURGER)!;\n  const packageResolver = new JavaPackageResolver();\n\n  test(\"parses Food.java\", () => {\n    const result = packageResolver.resolveFile(food)!;\n    expect(result).toBeDefined();\n    expect(result.path).toStrictEqual(FOOD);\n    expect(result.package).toBe(\"io.nanoapi.testfiles.food\");\n    expect(result.imports.length).toBe(0);\n    const symbol = result.symbol as JavaClass;\n    expect(symbol).toBeDefined();\n    expect(symbol.name).toBe(\"Food\");\n    expect(symbol.type).toBe(\"interface\");\n    expect(symbol.modifiers).toContain(\"public\");\n    expect(symbol.modifiers.length).toBe(1);\n    expect(symbol.typeParamCount).toBe(0);\n    expect(symbol.superclass).toBeUndefined();\n    expect(symbol.interfaces.length).toBe(0);\n    expect(symbol.children.length).toBe(0);\n  });\n\n  test(\"parses Condiment.java\", () => {\n    const result = packageResolver.resolveFile(condiment)!;\n    expect(result).toBeDefined();\n    expect(result.path).toStrictEqual(CONDIMENT);\n    expect(result.package).toBe(\"io.nanoapi.testfiles.food\");\n    expect(result.imports.length).toBe(0);\n    const symbol = result.symbol as JavaClass;\n    expect(symbol).toBeDefined();\n    expect(symbol.name).toBe(\"Condiment\");\n    expect(symbol.type).toBe(\"enum\");\n    expect(symbol.modifiers).toContain(\"public\");\n    expect(symbol.modifiers.length).toBe(1);\n    expect(symbol.typeParamCount).toBe(0);\n    expect(symbol.superclass).toBeUndefined();\n    expect(symbol.interfaces.length).toBe(0);\n    expect(symbol.children.length).toBe(0);\n  });\n\n  test(\"parses Steak.java\", () => {\n    const result = packageResolver.resolveFile(steak)!;\n    expect(result).toBeDefined();\n    expect(result.path).toStrictEqual(STEAK);\n    expect(result.package).toBe(\"io.nanoapi.testfiles.food\");\n    expect(result.imports.length).toBe(0);\n    const symbol = result.symbol as JavaClass;\n    expect(symbol).toBeDefined();\n    expect(symbol.name).toBe(\"Steak\");\n    expect(symbol.type).toBe(\"class\");\n    expect(symbol.modifiers).toContain(\"public\");\n    expect(symbol.modifiers.length).toBe(1);\n    expect(symbol.typeParamCount).toBe(0);\n    expect(symbol.superclass).toBeUndefined();\n    expect(symbol.interfaces.length).toBe(1);\n    expect(symbol.interfaces[0]).toBe(\"Food\");\n    expect(symbol.children.length).toBe(1);\n    const child = symbol.children[0] as JavaClass;\n    expect(child).toBeDefined();\n    expect(child.name).toBe(\"Tapeworm\");\n    expect(child.type).toBe(\"class\");\n    expect(child.modifiers).toContain(\"private\");\n    expect(child.modifiers).toContain(\"static\");\n    expect(child.modifiers.length).toBe(2);\n    expect(child.typeParamCount).toBe(0);\n    expect(child.superclass).toBeUndefined();\n    expect(child.interfaces.length).toBe(0);\n    expect(child.children.length).toBe(0);\n  });\n\n  test(\"parses Burger.java\", () => {\n    const result = packageResolver.resolveFile(burger)!;\n    expect(result).toBeDefined();\n    expect(result.path).toStrictEqual(BURGER);\n    expect(result.package).toBe(\"io.nanoapi.testfiles.food\");\n    expect(result.imports.length).toBe(2);\n    const symbol = result.symbol as JavaClass;\n    expect(symbol).toBeDefined();\n    expect(symbol.name).toBe(\"Burger\");\n    expect(symbol.type).toBe(\"class\");\n    expect(symbol.typeParamCount).toBe(1);\n    expect(symbol.superclass).toBeUndefined();\n    expect(symbol.interfaces.length).toBe(1);\n    expect(symbol.children.length).toBe(2);\n    expect(symbol.children.filter((c) => c.name === \"restaurantCount\"))\n      .toHaveLength(1);\n    expect(symbol.children.filter((c) => c.type === \"field\")).toHaveLength(1);\n    expect(symbol.children.filter((c) => c.name === \"advertisement\"))\n      .toHaveLength(1);\n    expect(symbol.children.filter((c) => c.type === \"method\")).toHaveLength(1);\n  });\n\n  test(\"parses DoubleBurger.java\", () => {\n    const result = packageResolver.resolveFile(doubleburger)!;\n    expect(result).toBeDefined();\n    expect(result.path).toStrictEqual(DOUBLEBURGER);\n    expect(result.package).toBe(\"io.nanoapi.testfiles.food\");\n    const symbol = result.symbol as JavaClass;\n    expect(symbol).toBeDefined();\n    expect(symbol.name).toBe(\"DoubleBurger\");\n    expect(symbol.type).toBe(\"class\");\n    expect(symbol.typeParamCount).toBe(2);\n    expect(symbol.superclass).toBeDefined();\n    expect(symbol.superclass).toBe(\"Burger\");\n    expect(symbol.interfaces.length).toBe(0);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/java/packageResolver/index.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport { JavaClass, type JavaFile, type SymbolType } from \"./types.ts\";\nimport { JAVA_PROGRAM_QUERY } from \"./queries.ts\";\nimport { javaParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\n/**\n * Resolves Java package and import information from a parsed Java file.\n */\nexport class JavaPackageResolver {\n  /**\n   * The Tree-sitter parser instance for Java.\n   */\n  parser: Parser = javaParser;\n\n  /**\n   * Resolves the package, imports, and main declaration of a Java file.\n   *\n   * @param file - An object containing the file path and the root syntax node of the parsed Java file.\n   * @returns A JavaFile object containing the file's path, root node, package name, imports, and main symbol declaration.\n   * @throws Will throw an error if no declaration is found in the file.\n   */\n  resolveFile(file: {\n    path: string;\n    rootNode: Parser.SyntaxNode;\n  }): JavaFile | undefined {\n    const captures = JAVA_PROGRAM_QUERY.captures(file.rootNode);\n    const packagename = captures.find((c) => c.name === \"package\")?.node.text ??\n      \"\";\n    const imports = captures.filter((c) => c.name === \"import\").map((c) =>\n      // Unholy string manipulation because wildcard imports break tree-sitter queries.\n      c.node.text.split(\" \").findLast((s) => s.length > 0 && s !== \";\")!\n        .replace(\";\", \"\")\n    );\n    const declaration = captures.find((c) =>\n      ![\"package\", \"import\"].includes(c.name)\n    );\n    if (!declaration) {\n      return undefined;\n    }\n    const symbol = new JavaClass(\n      declaration.node,\n      declaration.name as SymbolType,\n      file.path,\n    );\n    return {\n      path: file.path,\n      rootNode: file.rootNode,\n      symbol,\n      package: packagename,\n      imports,\n    };\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/packageResolver/queries.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { javaParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\n/**\n * A Tree-sitter query to extract high-level program elements from Java source code.\n * This query captures:\n * - Package declarations\n * - Import statements\n * - Class declarations\n * - Interface declarations\n * - Enum declarations\n * - Record declarations\n * - Annotation type declarations\n */\nexport const JAVA_PROGRAM_QUERY = new Parser.Query(\n  javaParser.getLanguage(),\n  `(program [\n  (package_declaration (_) @package)\n  (import_declaration) @import\n  (class_declaration) @class\n  (interface_declaration) @interface\n  (enum_declaration) @enum\n  (record_declaration) @record\n  (annotation_type_declaration) @annotation\n  ])`,\n);\n\n/**\n * A Tree-sitter query to extract public static members from Java source code.\n * This query captures:\n * - Public static field declarations\n * - Public static method declarations\n */\nexport const JAVA_STATIC_MEMBERS_QUERY = new Parser.Query(\n  javaParser.getLanguage(),\n  `\n  (field_declaration (modifiers \"public\" \"static\")) @field\n  (method_declaration (modifiers \"public\" \"static\")) @method\n  `,\n);\n"
  },
  {
    "path": "src/languagePlugins/java/packageResolver/types.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport { JAVA_STATIC_MEMBERS_QUERY } from \"./queries.ts\";\n\n/**\n * Represents the \"public\" modifier in Java.\n */\nexport const JAVA_PUBLIC_MODIFIER = \"public\";\n\n/**\n * Represents the \"private\" modifier in Java.\n */\nexport const JAVA_PRIVATE_MODIFIER = \"private\";\n\n/**\n * Represents the \"protected\" modifier in Java.\n */\nexport const JAVA_PROTECTED_MODIFIER = \"protected\";\n\n/**\n * Represents the \"abstract\" modifier in Java.\n */\nexport const JAVA_ABSTRACT_MODIFIER = \"abstract\";\n\n/**\n * Represents the \"final\" modifier in Java.\n */\nexport const JAVA_FINAL_MODIFIER = \"final\";\n\n/**\n * Represents the \"static\" modifier in Java.\n */\nexport const JAVA_STATIC_MODIFIER = \"static\";\n\n/**\n * Represents the \"strictfp\" modifier in Java.\n */\nexport const JAVA_STRICTFP_MODIFIER = \"strictfp\";\n\n/**\n * Represents the \"sealed\" modifier in Java.\n */\nexport const JAVA_SEALED_MODIFIER = \"sealed\";\n\n/**\n * Represents the \"non-sealed\" modifier in Java.\n */\nexport const JAVA_NONSEALED_MODIFIER = \"non-sealed\";\n\nexport type Modifier =\n  | typeof JAVA_PUBLIC_MODIFIER\n  | typeof JAVA_PRIVATE_MODIFIER\n  | typeof JAVA_PROTECTED_MODIFIER\n  | typeof JAVA_ABSTRACT_MODIFIER\n  | typeof JAVA_FINAL_MODIFIER\n  | typeof JAVA_STATIC_MODIFIER\n  | typeof JAVA_STRICTFP_MODIFIER\n  | typeof JAVA_SEALED_MODIFIER\n  | typeof JAVA_NONSEALED_MODIFIER;\n\n/**\n * Represents the \"class\" type in Java.\n */\nexport const JAVA_CLASS_TYPE = \"class\";\n\n/**\n * Represents the \"interface\" type in Java.\n */\nexport const JAVA_INTERFACE_TYPE = \"interface\";\n\n/**\n * Represents the \"enum\" type in Java.\n */\nexport const JAVA_ENUM_TYPE = \"enum\";\n\n/**\n * Represents the \"record\" type in Java.\n */\nexport const JAVA_RECORD_TYPE = \"record\";\n\n/**\n * Represents the \"annotation\" type in Java.\n */\nexport const JAVA_ANNOTATION_TYPE = \"annotation\";\n\n/**\n * Represents the \"field\" type in Java.\n */\nexport const JAVA_FIELD_TYPE = \"field\";\n\n/**\n * Represents the \"method\" type in Java.\n */\nexport const JAVA_METHOD_TYPE = \"method\";\n\nexport type SymbolType =\n  | typeof JAVA_CLASS_TYPE\n  | typeof JAVA_INTERFACE_TYPE\n  | typeof JAVA_ENUM_TYPE\n  | typeof JAVA_RECORD_TYPE\n  | typeof JAVA_ANNOTATION_TYPE\n  | typeof JAVA_FIELD_TYPE\n  | typeof JAVA_METHOD_TYPE;\n\n/**\n * Represents a Java file with its metadata and structure.\n */\nexport interface JavaFile {\n  /** The file path of the Java file. */\n  path: string;\n  /** The root syntax node of the file. */\n  rootNode: Parser.SyntaxNode;\n  /** The main exported symbol in the file. */\n  symbol: ExportedSymbol;\n  /** The package name of the file. */\n  package: string;\n  /** The list of imports in the file. */\n  imports: string[];\n}\n\n/**\n * Represents an exported symbol in a Java file.\n */\nexport interface ExportedSymbol {\n  /** The name of the symbol. */\n  name: string;\n  /** The type of the symbol (e.g., class, method). */\n  type: SymbolType;\n  /** The modifiers applied to the symbol. */\n  modifiers: Modifier[];\n  /** The child symbols of this symbol. */\n  children: ExportedSymbol[];\n  /** The file path where the symbol is located. */\n  filepath: string;\n  /** The syntax node representing the symbol. */\n  node: Parser.SyntaxNode;\n  /** The syntax node representing the identifier of the symbol. */\n  idNode: Parser.SyntaxNode;\n}\n\n/**\n * Represents a Java class or similar construct (e.g., interface, enum).\n */\nexport class JavaClass implements ExportedSymbol {\n  /** The name of the class. */\n  name: string;\n  /** The type of the class (e.g., class, interface). */\n  type: SymbolType;\n  /** The modifiers applied to the class. */\n  modifiers: Modifier[];\n  /** The child symbols of the class. */\n  children: ExportedSymbol[];\n  /** The file path where the class is located. */\n  filepath: string;\n  /** The syntax node representing the class. */\n  node: Parser.SyntaxNode;\n  /** The syntax node representing the identifier of the class. */\n  idNode: Parser.SyntaxNode;\n  /** The number of type parameters the class has. */\n  typeParamCount: number;\n  /** The name of the superclass, if any. */\n  superclass?: string;\n  /** The list of interfaces implemented by the class. */\n  interfaces: string[];\n\n  /**\n   * Constructs a new JavaClass instance.\n   * @param node - The syntax node representing the class.\n   * @param type - The type of the class (e.g., class, interface).\n   * @param filepath - The file path where the class is located.\n   */\n  constructor(node: Parser.SyntaxNode, type: SymbolType, filepath: string) {\n    const modifiersNode = node.children.find((c) => c.type === \"modifiers\");\n    this.modifiers = [];\n    if (modifiersNode) {\n      this.modifiers.push(\n        ...modifiersNode.children.map((c) => c.text as Modifier),\n      );\n    }\n    this.idNode = node.childForFieldName(\"name\")!;\n    this.name = this.idNode.text;\n    const typeParams = node.childForFieldName(\"type_parameters\");\n    this.typeParamCount = 0;\n    if (typeParams) {\n      this.typeParamCount = typeParams.namedChildren.length;\n    }\n    this.superclass = undefined;\n    const superclassNode = node.childForFieldName(\"superclass\");\n    if (superclassNode) {\n      this.superclass = superclassNode.namedChildren[0].text;\n    }\n    const interfacesNode = node.childForFieldName(\"interfaces\");\n    this.interfaces = [];\n    if (interfacesNode) {\n      this.interfaces.push(\n        ...interfacesNode.namedChildren[0].namedChildren.map((c) => c.text),\n      );\n    }\n    this.children = [];\n    const bodyNode = node.childForFieldName(\"body\");\n    if (bodyNode) {\n      this.children.push(\n        ...bodyNode.children.filter((c) =>\n          [\n            \"class_declaration\",\n            \"interface_declaration\",\n            \"enum_declaration\",\n            \"record_declaration\",\n            \"annotation_type_declaration\",\n          ].includes(c.type)\n        ).map((c) =>\n          new JavaClass(c, c.type.split(\"_\")[0] as SymbolType, filepath)\n        ),\n      );\n      this.children.push(\n        ...JAVA_STATIC_MEMBERS_QUERY.captures(bodyNode).map((c) =>\n          new JavaMember(c, filepath)\n        ),\n      );\n    }\n    this.type = type;\n    this.node = node;\n    this.filepath = filepath;\n  }\n}\n\n/**\n * Represents a member of a Java class (e.g., method, field).\n */\nexport class JavaMember implements ExportedSymbol {\n  /** The name of the member. */\n  name: string;\n  /** The type of the member (e.g., method, field). */\n  type: SymbolType;\n  /** The modifiers applied to the member. */\n  modifiers: Modifier[];\n  /** The child symbols of the member. */\n  children: ExportedSymbol[];\n  /** The syntax node representing the member. */\n  node: Parser.SyntaxNode;\n  /** The syntax node representing the identifier of the member. */\n  idNode: Parser.SyntaxNode;\n  /** The file path where the member is located. */\n  filepath: string;\n\n  /**\n   * Constructs a new JavaMember instance.\n   * @param capture - The query capture representing the member.\n   * @param filepath - The file path where the member is located.\n   */\n  constructor(capture: Parser.QueryCapture, filepath: string) {\n    this.type = capture.name as SymbolType;\n    this.node = capture.node;\n    this.filepath = filepath;\n    const modifiersNode = this.node.children.find((c) =>\n      c.type === \"modifiers\"\n    )!;\n    this.modifiers = modifiersNode.children.map((c) => c.text as Modifier),\n      this.children = [];\n    if (this.type === \"method\") {\n      this.idNode = this.node.childForFieldName(\"name\")!;\n      this.name = this.idNode.text;\n    } else {\n      const declarator = this.node.childForFieldName(\"declarator\")!;\n      this.idNode = declarator.childForFieldName(\"name\")!;\n      this.name = this.idNode.text;\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/constants.ts",
    "content": "import { javaFilesFolder } from \"./index.ts\";\nimport { join } from \"@std/path\";\n\n// Root\nexport const APP = join(javaFilesFolder, \"App.java\");\n\n// Food package\nexport const CONDIMENT = join(javaFilesFolder, \"food/Condiment.java\");\nexport const FOOD = join(javaFilesFolder, \"food/Food.java\");\nexport const STEAK = join(javaFilesFolder, \"food/Steak.java\");\nexport const BURGER = join(javaFilesFolder, \"food/Burger.java\");\nexport const DOUBLEBURGER = join(javaFilesFolder, \"food/DoubleBurger.java\");\nexport const PEBBLE = join(javaFilesFolder, \"food/goron/Pebble.java\");\n\n// Medication package\nexport const WORMKILLER = join(javaFilesFolder, \"medication/Wormkiller.java\");\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/index.ts",
    "content": "import * as fs from \"node:fs\";\nimport { extname, join } from \"@std/path\";\nimport type Parser from \"tree-sitter\";\nimport {\n  javaLanguage,\n  javaParser,\n} from \"../../../helpers/treeSitter/parsers.ts\";\n\nif (!import.meta.dirname) {\n  throw new Error(\"import.meta.dirname is not defined\");\n}\nexport const javaFilesFolder = join(\n  import.meta.dirname,\n  \"napi-tests/src/main/java/io/nanoapi/testfiles\",\n);\nconst javaFilesMap = new Map<\n  string,\n  { path: string; rootNode: Parser.SyntaxNode }\n>();\n\n/**\n * Recursively finds all C files in the given directory and its subdirectories.\n * @param dir - The directory to search in.\n */\nfunction findJavaFiles(dir: string) {\n  const files = fs.readdirSync(dir);\n  files.forEach((file) => {\n    const fullPath = join(dir, file);\n    const stat = fs.statSync(fullPath);\n    if (stat.isDirectory()) {\n      if (\n        !fullPath.includes(\".extracted/\") &&\n        !fullPath.includes(\"bin/\") &&\n        !fullPath.includes(\"obj/\")\n      ) {\n        findJavaFiles(fullPath);\n      }\n    } else if (\n      extname(fullPath) === \".java\"\n    ) {\n      const content = fs.readFileSync(fullPath, \"utf8\");\n      const tree = javaParser.parse(content);\n      javaFilesMap.set(fullPath, { path: fullPath, rootNode: tree.rootNode });\n    }\n  });\n}\n\nexport function getJavaFilesMap(): Map<\n  string,\n  { path: string; rootNode: Parser.SyntaxNode }\n> {\n  findJavaFiles(javaFilesFolder);\n  return javaFilesMap;\n}\n\nexport function getJavaFilesContentMap(): Map<\n  string,\n  { path: string; content: string }\n> {\n  findJavaFiles(javaFilesFolder);\n  const contentMap = new Map<string, { path: string; content: string }>();\n  for (const [filePath, file] of javaFilesMap) {\n    contentMap.set(filePath, {\n      path: file.path,\n      content: file.rootNode.text,\n    });\n  }\n  return contentMap;\n}\n\nexport const dummyLocalConfig = {\n  language: javaLanguage,\n  project: {\n    include: [],\n    exclude: [],\n  },\n  outDir: \"./dist\",\n};\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>napi-tests</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n\t<filteredResources>\n\t\t<filter>\n\t\t\t<id>1749122394405</id>\n\t\t\t<name></name>\n\t\t\t<type>30</type>\n\t\t\t<matcher>\n\t\t\t\t<id>org.eclipse.core.resources.regexFilterMatcher</id>\n\t\t\t\t<arguments>node_modules|\\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>\n\t\t\t</matcher>\n\t\t</filter>\n\t</filteredResources>\n</projectDescription>\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/App.java",
    "content": "package io.nanoapi.testfiles;\n\nimport static io.nanoapi.testfiles.food.Burger.*;\n\n/**\n * Hello world!\n */\npublic class App {\n\n    public static void main(String[] args) {\n        restaurantCount++;\n        System.out.println(\"Hello World!\");\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/food/Burger.java",
    "content": "package io.nanoapi.testfiles.food;\n\nimport java.io.File;\nimport java.lang.System;\n\npublic class Burger<T> implements Food {\n\n    public static int restaurantCount = 1;\n\n    public void eat() {\n        System.out.println(\"Yummy !\");\n    }\n\n    public double getPrice() {\n        return 2.0;\n    }\n\n    public double getCalories() {\n        return 500.0;\n    }\n\n    public static void advertisement() {\n        System.out.println(\"Mmmmm Burger King\");\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/food/Condiment.java",
    "content": "package io.nanoapi.testfiles.food;\n\npublic enum Condiment {\n    SALAD,\n    TOMATO,\n    ONION,\n}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/food/DoubleBurger.java",
    "content": "package io.nanoapi.testfiles.food;\n\npublic class DoubleBurger<T, N> extends Burger {}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/food/Food.java",
    "content": "package io.nanoapi.testfiles.food;\n\npublic interface Food {\n    public void eat();\n\n    public double getPrice();\n\n    public double getCalories();\n}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/food/Steak.java",
    "content": "package io.nanoapi.testfiles.food;\n\npublic class Steak implements Food {\n\n    public void eat() {\n        System.out.println(\"Yum !\");\n    }\n\n    public double getPrice() {\n        return 1.5;\n    }\n\n    public double getCalories() {\n        return 200;\n    }\n\n    private static class Tapeworm {\n        // Uh oh !!\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/food/goron/Pebble.java",
    "content": "package io.nanoapi.testfiles.food.goron;\n\nimport io.nanoapi.testfiles.food.Food;\n\npublic class Pebble implements Food {\n\n    public void eat() {\n        System.out.println(\"crunch !\");\n    }\n\n    public double getPrice() {\n        return 0.01;\n    }\n\n    public double getCalories() {\n        return 1;\n    }\n\n    public class Sandworm {\n        // Gorons get tapeworms too ?!\n    }\n}\n"
  },
  {
    "path": "src/languagePlugins/java/testFiles/napi-tests/src/main/java/io/nanoapi/testfiles/medication/Wormkiller.java",
    "content": "package io.nanoapi.testfiles.medication;\n\nimport io.nanoapi.testfiles.food.Steak;\nimport io.nanoapi.testfiles.food.goron.Pebble;\n\npublic class Wormkiller {\n\n    public void applyTo(Steak steak) {}\n\n    public void killWorm(Pebble.Sandworm sandworm) {}\n}\n"
  },
  {
    "path": "src/languagePlugins/python/dependencyResolver/index.test.ts",
    "content": "import { beforeEach, describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport type Parser from \"tree-sitter\";\nimport { pythonParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport { PythonModuleResolver } from \"../moduleResolver/index.ts\";\nimport { PythonUsageResolver } from \"../usageResolver/index.ts\";\nimport { PythonImportExtractor } from \"../importExtractor/index.ts\";\nimport { PythonItemResolver } from \"../itemResolver/index.ts\";\nimport { PythonDependencyResolver } from \"../dependencyResolver/index.ts\";\nimport type { FileDependencies } from \"./types.ts\";\nimport { PYTHON_VARIABLE_TYPE } from \"../exportExtractor/types.ts\";\nimport { PythonMetricsAnalyzer } from \"../metricAnalyzer/index.ts\";\ndescribe(\"DependencyResolver\", () => {\n  let parser: Parser;\n  let exportExtractor: PythonExportExtractor;\n  let importExtractor: PythonImportExtractor;\n  let usageResolver: PythonUsageResolver;\n  let moduleResolver: PythonModuleResolver;\n  let itemResolver: PythonItemResolver;\n  let metricsAnalyzer: PythonMetricsAnalyzer;\n  let dependencyResolver: PythonDependencyResolver;\n  let files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n\n  beforeEach(() => {\n    parser = pythonParser;\n\n    // Create test files\n    files = new Map([\n      // Original module with function\n      [\n        \"original.py\",\n        {\n          path: \"original.py\",\n          rootNode: parser.parse(`\ndef original_func():\n    return \"Original function\"\n          `).rootNode,\n        },\n      ],\n      // Re-exporter module\n      [\n        \"reexporter.py\",\n        {\n          path: \"reexporter.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\n          `).rootNode,\n        },\n      ],\n      // Consumer module\n      [\n        \"consumer.py\",\n        {\n          path: \"consumer.py\",\n          rootNode: parser.parse(`\nfrom reexporter import original_func\n\ndef consumer_func():\n    return original_func()\n          `).rootNode,\n        },\n      ],\n      // Another module for testing\n      [\n        \"another.py\",\n        {\n          path: \"another.py\",\n          rootNode: parser.parse(`\ndef another_func():\n    return \"Another function\"\n          `).rootNode,\n        },\n      ],\n      // Direct consumer module that imports directly from original\n      [\n        \"direct_consumer.py\",\n        {\n          path: \"direct_consumer.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\n\ndef direct_func():\n    return original_func()\n        `).rootNode,\n        },\n      ],\n      // Files for multi-level re-export test\n      [\n        \"level1.py\",\n        {\n          path: \"level1.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\n        `).rootNode,\n        },\n      ],\n      [\n        \"level2.py\",\n        {\n          path: \"level2.py\",\n          rootNode: parser.parse(`\nfrom level1 import original_func\n        `).rootNode,\n        },\n      ],\n      [\n        \"level3_consumer.py\",\n        {\n          path: \"level3_consumer.py\",\n          rootNode: parser.parse(`\nfrom level2 import original_func\n\ndef level3_func():\n    return original_func()\n        `).rootNode,\n        },\n      ],\n      // Module with modified variables and different dependencies at each modification\n      [\n        \"modified_variables.py\",\n        {\n          path: \"modified_variables.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\n\n# Initial declaration with dependency on original_func\ncounter = original_func()\n\nfrom another import another_func\n\n# Modification with dependency on another_func\ncounter += len(another_func())\n\n# Another modification with both dependencies\ncounter = counter + len(original_func()) + len(another_func())\n          `).rootNode,\n        },\n      ],\n      // Module with multiple function definitions\n      [\n        \"multi_function.py\",\n        {\n          path: \"multi_function.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\n\n# First definition with dependency on original\ndef multi_func():\n    return original_func()\n\nfrom another import another_func\n\n# Second definition with dependency on another\ndef multi_func(param):\n    return another_func() + param\n          `).rootNode,\n        },\n      ],\n      // Module with sequential variable dependencies\n      [\n        \"sequential_vars.py\",\n        {\n          path: \"sequential_vars.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\n\n# First variable depends on original_func\nvar1 = original_func()\n\n# Second variable depends on var1\nvar2 = var1 + \" modified\"\n\nfrom another import another_func\n\n# Third variable depends on another_func and var2\nvar3 = another_func() + var2\n\n# Modification of var1 now depends on another_func\nvar1 = another_func()\n          `).rootNode,\n        },\n      ],\n      // Module with complex list and dictionary variables\n      [\n        \"complex_vars.py\",\n        {\n          path: \"complex_vars.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\nfrom another import another_func\n\n# List with dependency on original_func\nmy_list = [original_func()]\n\n# Augmented assignment with dependency on another_func\nmy_list += [another_func()]\n\n# Dictionary with dependencies on both functions\nmy_dict = {\n    \"original\": original_func(),\n    \"another\": another_func()\n}\n\n# Dictionary modification\nmy_dict[\"third\"] = original_func() + another_func()\n\n# List reassignment with both dependencies\nmy_list = [original_func(), another_func()]\n          `).rootNode,\n        },\n      ],\n    ]);\n\n    exportExtractor = new PythonExportExtractor(parser, files);\n    importExtractor = new PythonImportExtractor(parser, files);\n    moduleResolver = new PythonModuleResolver(new Set(files.keys()), \"3.10\");\n    usageResolver = new PythonUsageResolver(parser, exportExtractor);\n    itemResolver = new PythonItemResolver(\n      exportExtractor,\n      importExtractor,\n      moduleResolver,\n    );\n    metricsAnalyzer = new PythonMetricsAnalyzer(parser);\n    dependencyResolver = new PythonDependencyResolver(\n      files,\n      exportExtractor,\n      importExtractor,\n      itemResolver,\n      usageResolver,\n      moduleResolver,\n      metricsAnalyzer,\n    );\n  });\n\n  describe(\"Re-exporting modules\", () => {\n    test(\"should track both original and re-exporting modules as dependencies\", () => {\n      // Analyze dependencies for the consumer module\n      const dependencies: FileDependencies = dependencyResolver\n        .getFileDependencies(\"consumer.py\");\n\n      // We expect the consumer to depend on both original.py and reexporter.py\n      expect(dependencies.dependencies.size).toBe(2);\n\n      // Check dependency on original.py\n      expect(dependencies.dependencies.has(\"original.py\")).toBeTruthy();\n      const originalDep = dependencies.dependencies.get(\"original.py\");\n      expect(originalDep?.isExternal).toBe(false);\n      expect(originalDep?.symbols.has(\"original_func\")).toBeTruthy();\n\n      // Check dependency on reexporter.py (as a re-exporting module)\n      expect(dependencies.dependencies.has(\"reexporter.py\")).toBeTruthy();\n      const reexporterDep = dependencies.dependencies.get(\"reexporter.py\");\n      expect(reexporterDep?.isExternal).toBe(false);\n      // Re-exporting modules are tracked without symbols\n      expect(reexporterDep?.symbols.size).toBe(0);\n    });\n\n    test(\"should not track reexporter as dependency if it's not used\", () => {\n      // Analyze dependencies\n      const dependencies: FileDependencies = dependencyResolver\n        .getFileDependencies(\n          \"direct_consumer.py\",\n        );\n\n      // Should only depend on original.py\n      expect(dependencies.dependencies.size).toBe(1);\n      expect(dependencies.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(dependencies.dependencies.has(\"reexporter.py\")).toBeFalsy();\n    });\n\n    test(\"should handle multiple levels of re-exports\", () => {\n      // Analyze dependencies\n      const dependencies: FileDependencies = dependencyResolver\n        .getFileDependencies(\n          \"level3_consumer.py\",\n        );\n\n      // Should depend on all three modules in the chain\n      expect(dependencies.dependencies.size).toBe(3);\n      expect(dependencies.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(dependencies.dependencies.has(\"level1.py\")).toBeTruthy();\n      expect(dependencies.dependencies.has(\"level2.py\")).toBeTruthy();\n\n      // Original should have the symbol\n      const originalDep = dependencies.dependencies.get(\"original.py\");\n      expect(originalDep?.symbols.has(\"original_func\")).toBeTruthy();\n\n      // Re-exporters should have no symbols\n      const level1Dep = dependencies.dependencies.get(\"level1.py\");\n      const level2Dep = dependencies.dependencies.get(\"level2.py\");\n      expect(level1Dep?.symbols.size).toBe(0);\n      expect(level2Dep?.symbols.size).toBe(0);\n    });\n  });\n\n  describe(\"Variable modifications\", () => {\n    test(\"should track dependencies in each variable modification\", () => {\n      // Analyze dependencies for the module with modified variables\n      const dependencies: FileDependencies = dependencyResolver\n        .getFileDependencies(\n          \"modified_variables.py\",\n        );\n\n      // Should depend on both original.py and another.py\n      expect(dependencies.dependencies.size).toBe(2);\n      expect(dependencies.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(dependencies.dependencies.has(\"another.py\")).toBeTruthy();\n\n      // Both dependencies should have their functions as used symbols\n      const originalDep = dependencies.dependencies.get(\"original.py\");\n      const anotherDep = dependencies.dependencies.get(\"another.py\");\n      expect(originalDep?.symbols.has(\"original_func\")).toBeTruthy();\n      expect(anotherDep?.symbols.has(\"another_func\")).toBeTruthy();\n\n      // Find counter variable in symbols\n      const counterSymbol = dependencies.symbols.find(\n        (s) => s.id === \"counter\" && s.type === PYTHON_VARIABLE_TYPE,\n      );\n      expect(counterSymbol).toBeDefined();\n\n      // Counter should have dependencies on both modules\n      expect(counterSymbol?.dependencies.size).toBe(2);\n      expect(counterSymbol?.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(counterSymbol?.dependencies.has(\"another.py\")).toBeTruthy();\n\n      // Both dependencies should have their functions as used symbols\n      const counterOriginalDep = counterSymbol?.dependencies.get(\"original.py\");\n      const counterAnotherDep = counterSymbol?.dependencies.get(\"another.py\");\n      expect(counterOriginalDep?.symbols.has(\"original_func\")).toBeTruthy();\n      expect(counterAnotherDep?.symbols.has(\"another_func\")).toBeTruthy();\n    });\n\n    test(\"should track dependencies in multiple function definitions\", () => {\n      // Analyze dependencies for the module with multiple function definitions\n      const dependencies: FileDependencies = dependencyResolver\n        .getFileDependencies(\n          \"multi_function.py\",\n        );\n\n      // Should depend on both original.py and another.py\n      expect(dependencies.dependencies.size).toBe(2);\n      expect(dependencies.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(dependencies.dependencies.has(\"another.py\")).toBeTruthy();\n\n      // Find multi_func function in symbols\n      const multiFuncSymbol = dependencies.symbols.find(\n        (s) => s.id === \"multi_func\",\n      );\n      expect(multiFuncSymbol).toBeDefined();\n\n      // multi_func should have dependencies on both modules\n      expect(multiFuncSymbol?.dependencies.size).toBe(2);\n      expect(multiFuncSymbol?.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(multiFuncSymbol?.dependencies.has(\"another.py\")).toBeTruthy();\n    });\n\n    test(\"should handle sequential variable dependencies\", () => {\n      // Analyze dependencies for the module with sequential variable dependencies\n      const dependencies: FileDependencies = dependencyResolver\n        .getFileDependencies(\n          \"sequential_vars.py\",\n        );\n\n      // Module should depend on both original.py and another.py\n      expect(dependencies.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(dependencies.dependencies.has(\"another.py\")).toBeTruthy();\n\n      // Find all three variables in symbols\n      const variables = dependencies.symbols.filter(\n        (s) => s.type === PYTHON_VARIABLE_TYPE,\n      );\n\n      // Should have three variable symbols: var1, var2, var3\n      const varIds = variables.map((v) => v.id);\n      expect(varIds).toContain(\"var1\");\n      expect(varIds).toContain(\"var2\");\n      expect(varIds).toContain(\"var3\");\n\n      // Each variable should have at least some dependencies\n      for (const varSymbol of variables) {\n        expect(varSymbol.dependencies.size).toBeGreaterThan(0);\n      }\n\n      // At least one variable should depend on another.py\n      const hasAnotherDependency = variables.some((v) =>\n        v.dependencies.has(\"another.py\")\n      );\n      expect(hasAnotherDependency).toBeTruthy();\n\n      // At least one variable should depend on original.py\n      const hasOriginalDependency = variables.some((v) =>\n        v.dependencies.has(\"original.py\")\n      );\n      expect(hasOriginalDependency).toBeTruthy();\n    });\n\n    test(\"should handle complex list and dictionary variables\", () => {\n      // Analyze dependencies for the module with complex list and dictionary variables\n      const dependencies: FileDependencies = dependencyResolver\n        .getFileDependencies(\n          \"complex_vars.py\",\n        );\n\n      // Should depend on both original.py and another.py\n      expect(dependencies.dependencies.size).toBe(2);\n\n      // Find my_list variable in symbols\n      const myListSymbol = dependencies.symbols.find(\n        (s) => s.id === \"my_list\" && s.type === PYTHON_VARIABLE_TYPE,\n      );\n      expect(myListSymbol).toBeDefined();\n\n      // my_list should depend on both modules\n      expect(myListSymbol?.dependencies.size).toBe(2);\n      expect(myListSymbol?.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(myListSymbol?.dependencies.has(\"another.py\")).toBeTruthy();\n\n      // Find my_dict variable in symbols\n      const myDictSymbol = dependencies.symbols.find(\n        (s) => s.id === \"my_dict\" && s.type === PYTHON_VARIABLE_TYPE,\n      );\n      expect(myDictSymbol).toBeDefined();\n\n      // my_dict should depend on both modules\n      expect(myDictSymbol?.dependencies.size).toBe(2);\n      expect(myDictSymbol?.dependencies.has(\"original.py\")).toBeTruthy();\n      expect(myDictSymbol?.dependencies.has(\"another.py\")).toBeTruthy();\n    });\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/dependencyResolver/index.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport type { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport type { PythonUsageResolver } from \"../usageResolver/index.ts\";\nimport type { ExternalUsage, InternalUsage } from \"../usageResolver/types.ts\";\nimport type { PythonItemResolver } from \"../itemResolver/index.ts\";\nimport {\n  PYTHON_INTERNAL_MODULE_TYPE,\n  type ResolvedExternalModule,\n  type ResolvedExternalSymbol,\n  type ResolvedInternalModule,\n  type ResolvedInternalSymbol,\n} from \"../itemResolver/types.ts\";\nimport type { PythonImportExtractor } from \"../importExtractor/index.ts\";\nimport type { PythonModuleResolver } from \"../moduleResolver/index.ts\";\nimport {\n  PYTHON_NAMESPACE_MODULE_TYPE,\n  type PythonModule,\n} from \"../moduleResolver/types.ts\";\nimport type {\n  FileDependencies,\n  ModuleDependency,\n  SymbolDependency,\n} from \"./types.ts\";\nimport {\n  FROM_IMPORT_STATEMENT_TYPE,\n  type ImportStatement,\n  NORMAL_IMPORT_STATEMENT_TYPE,\n} from \"../importExtractor/types.ts\";\nimport type { PythonMetricsAnalyzer } from \"../metricAnalyzer/index.ts\";\n/**\n * PythonDependencyResolver analyzes a Python file's AST to build a dependency manifest.\n * It uses the PythonExportExtractor to determine exported symbols and the PythonUsageResolver\n * to determine which imports are used and how they are used within the file.\n *\n * Dependencies are computed at two levels:\n *\n * 1. File-level: Based on the file's root AST node.\n * 2. Symbol-level: For each exported symbol, by analyzing its AST subtree.\n *\n * The resolver distinguishes between internal (project) and external dependencies,\n * and tracks which symbols from each module are actually used.\n *\n * Results are cached to avoid re-computation.\n */\nexport class PythonDependencyResolver {\n  private files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n  private exportExtractor: PythonExportExtractor;\n  private importExtractor: PythonImportExtractor;\n  private itemResolver: PythonItemResolver;\n  private usageResolver: PythonUsageResolver;\n  private moduleResolver: PythonModuleResolver;\n  private metricsAnalyzer: PythonMetricsAnalyzer;\n  private fileDependenciesCache = new Map<string, FileDependencies>();\n\n  constructor(\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n    exportExtractor: PythonExportExtractor,\n    importExtractor: PythonImportExtractor,\n    itemResolver: PythonItemResolver,\n    usageResolver: PythonUsageResolver,\n    moduleResolver: PythonModuleResolver,\n    metricsAnalyzer: PythonMetricsAnalyzer,\n  ) {\n    this.files = files;\n    this.exportExtractor = exportExtractor;\n    this.importExtractor = importExtractor;\n    this.itemResolver = itemResolver;\n    this.usageResolver = usageResolver;\n    this.moduleResolver = moduleResolver;\n    this.metricsAnalyzer = metricsAnalyzer;\n  }\n\n  public getFileDependencies(path: string) {\n    if (this.fileDependenciesCache.has(path)) {\n      return this.fileDependenciesCache.get(path) as FileDependencies;\n    }\n\n    const file = this.files.get(path);\n    if (!file) {\n      throw new Error(`File not found: ${path}`);\n    }\n    const complexityMetrics = this.metricsAnalyzer.analyzeNodes([\n      file.rootNode,\n    ]);\n\n    const fileDependencies: FileDependencies = {\n      filePath: file.path,\n      metrics: {\n        characterCount: complexityMetrics.characterCount,\n        codeCharacterCount: complexityMetrics.codeCharacterCount,\n        linesCount: complexityMetrics.linesCount,\n        codeLineCount: complexityMetrics.codeLinesCount,\n        cyclomaticComplexity: complexityMetrics.cyclomaticComplexity,\n      },\n      dependencies: new Map<string, ModuleDependency>(),\n      symbols: [],\n    };\n\n    // Get module from file path\n    const fileModule = this.moduleResolver.getModuleFromFilePath(file.path);\n\n    // Get all imports\n    const importStmts = this.importExtractor.getImportStatements(file.path);\n\n    // Analyze dependencies for the file-level node\n    const { internalUsageMap, externalUsageMap } = this\n      .analyzeDependenciesForNode(\n        file.rootNode,\n        fileModule,\n        importStmts,\n      );\n\n    // Convert usage maps to dependencies\n    fileDependencies.dependencies = new Map([\n      ...this.convertInternalUsageToDependencies(internalUsageMap),\n      ...this.convertExternalUsageToDependencies(externalUsageMap),\n    ]);\n\n    // get all symbols\n    const { symbols } = this.exportExtractor.getSymbols(file.path);\n\n    // For each symbol, resolve its dependencies\n    for (const symbol of symbols) {\n      const complexityMetrics = this.metricsAnalyzer.analyzeNodes(symbol.nodes);\n\n      const symbolDependencies: SymbolDependency = {\n        id: symbol.id,\n        type: symbol.type,\n        positions: symbol.nodes.map((node) => ({\n          start: {\n            index: node.startIndex,\n            row: node.startPosition.row,\n            column: node.startPosition.column,\n          },\n          end: {\n            index: node.endIndex,\n            row: node.endPosition.row,\n            column: node.endPosition.column,\n          },\n        })),\n        metrics: {\n          characterCount: complexityMetrics.characterCount,\n          codeCharacterCount: complexityMetrics.codeCharacterCount,\n          linesCount: complexityMetrics.linesCount,\n          codeLineCount: complexityMetrics.codeLinesCount,\n          cyclomaticComplexity: complexityMetrics.cyclomaticComplexity,\n        },\n        dependencies: new Map<string, ModuleDependency>(),\n      };\n\n      // Process each node of the symbol independently, then merge results\n      const symbolInternalUsageMap = new Map<string, InternalUsage>();\n      const symbolExternalUsageMap = new Map<string, ExternalUsage>();\n\n      // For each node of the symbol, analyze its dependencies\n      for (const symbolNode of symbol.nodes) {\n        // Find import statements that are relevant for this specific node\n        const nodeImportStmts = this.filterImportStatementsForNode(\n          symbolNode,\n          importStmts,\n          symbols,\n        );\n\n        // Analyze dependencies for the current node\n        const nodeResult = this.analyzeDependenciesForNode(\n          symbolNode,\n          fileModule,\n          nodeImportStmts,\n        );\n\n        // Merge the results into the symbol's usage maps\n        this.mergeUsageMaps(\n          nodeResult.internalUsageMap,\n          symbolInternalUsageMap,\n        );\n        this.mergeUsageMaps(\n          nodeResult.externalUsageMap,\n          symbolExternalUsageMap,\n        );\n\n        // Resolve internal usage for other symbols in the file for this node\n        for (const otherSymbol of symbols) {\n          if (otherSymbol.id === symbol.id) {\n            continue;\n          }\n\n          this.usageResolver.resolveInternalUsageForSymbol(\n            symbolNode,\n            nodeImportStmts.map((stmt) => stmt.node),\n            fileModule,\n            otherSymbol,\n            otherSymbol.identifierNode.text,\n            symbolInternalUsageMap,\n          );\n        }\n      }\n\n      // Convert usage maps to dependencies\n      symbolDependencies.dependencies = new Map([\n        ...this.convertInternalUsageToDependencies(symbolInternalUsageMap),\n        ...this.convertExternalUsageToDependencies(symbolExternalUsageMap),\n      ]);\n\n      fileDependencies.symbols.push(symbolDependencies);\n    }\n\n    // Cache the file dependencies\n    this.fileDependenciesCache.set(path, fileDependencies);\n\n    return fileDependencies;\n  }\n\n  /**\n   * Filters import statements that are relevant for a specific node of a symbol\n   * @param symbolNode The specific node of a symbol\n   * @param allImportStmts All import statements in the file\n   * @param allSymbols All symbols in the file\n   * @returns Filtered import statements relevant for this node\n   */\n  private filterImportStatementsForNode(\n    symbolNode: Parser.SyntaxNode,\n    allImportStmts: ImportStatement[],\n    allSymbols: {\n      id: string;\n      nodes: Parser.SyntaxNode[];\n      identifierNode: Parser.SyntaxNode;\n      type: string;\n    }[],\n  ): ImportStatement[] {\n    return allImportStmts.filter((importStmt) => {\n      // Include only imports that come before this node\n      const isBeforeNode = importStmt.node.endIndex < symbolNode.endIndex;\n\n      // Exclude imports that are contained within other symbols\n      let isWithinOtherSymbols = false;\n\n      for (const otherSymbol of allSymbols) {\n        for (const otherNode of otherSymbol.nodes) {\n          // Skip the current node we're analyzing\n          if (otherNode === symbolNode) {\n            continue;\n          }\n\n          // Check if the import is contained within another symbol's node\n          if (\n            importStmt.node.startIndex >= otherNode.startIndex &&\n            importStmt.node.endIndex <= otherNode.endIndex\n          ) {\n            isWithinOtherSymbols = true;\n            break;\n          }\n        }\n\n        if (isWithinOtherSymbols) {\n          break;\n        }\n      }\n\n      return isBeforeNode && !isWithinOtherSymbols;\n    });\n  }\n\n  /**\n   * Merges two usage maps together, combining their contents\n   */\n  private mergeUsageMaps<T extends InternalUsage | ExternalUsage>(\n    source: Map<string, T>,\n    target: Map<string, T>,\n  ): void {\n    for (const [key, sourceValue] of source.entries()) {\n      if (!target.has(key)) {\n        target.set(key, sourceValue);\n      } else {\n        const targetValue = target.get(key);\n\n        if (targetValue) {\n          // Handle internal usage maps\n          if (\"symbols\" in sourceValue && \"symbols\" in targetValue) {\n            const sourceInternal = sourceValue as InternalUsage;\n            const targetInternal = targetValue as InternalUsage;\n\n            // Merge symbols\n            for (const [symbolId, symbol] of sourceInternal.symbols.entries()) {\n              targetInternal.symbols.set(symbolId, symbol);\n            }\n\n            // Merge re-exporting modules if they exist\n            if (sourceInternal.reExportingModules) {\n              if (!targetInternal.reExportingModules) {\n                targetInternal.reExportingModules = new Map();\n              }\n\n              for (\n                const [\n                  modulePath,\n                  module,\n                ] of sourceInternal.reExportingModules.entries()\n              ) {\n                targetInternal.reExportingModules.set(modulePath, module);\n              }\n            }\n          } // Handle external usage maps\n          else if (\"itemNames\" in sourceValue && \"itemNames\" in targetValue) {\n            const sourceExternal = sourceValue as ExternalUsage;\n            const targetExternal = targetValue as ExternalUsage;\n\n            // Merge item names\n            for (const itemName of sourceExternal.itemNames) {\n              targetExternal.itemNames.add(itemName);\n            }\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Analyzes dependencies for a given AST node (can be a file or symbol node)\n   * @param node The AST node to analyze\n   * @param contextModule The module context for resolving imports\n   * @param importStmts The import statements to consider\n   * @returns Maps containing internal and external usage information\n   */\n  private analyzeDependenciesForNode(\n    node: Parser.SyntaxNode,\n    contextModule: PythonModule, // Using any here for simplicity\n    importStmts: ImportStatement[],\n  ): {\n    internalUsageMap: Map<string, InternalUsage>;\n    externalUsageMap: Map<string, ExternalUsage>;\n  } {\n    // Initialize usage result\n    const internalUsageMap = new Map<string, InternalUsage>();\n    const externalUsageMap = new Map<string, ExternalUsage>();\n\n    const nodesToExclude = importStmts.map((importStmt) => importStmt.node);\n\n    // Process all explicit imports first - both normal imports and from-imports\n    // (excluding wildcards) in the order they appear in the file\n    for (const importStmt of importStmts) {\n      if (importStmt.type === FROM_IMPORT_STATEMENT_TYPE) {\n        // For \"from X import Y\" statements\n        const member = importStmt.members[0];\n\n        if (member.isWildcardImport) {\n          // process wildcard import last\n          continue;\n        }\n\n        // Try to resolve the source module (X in \"from X import Y\")\n        // This could be an internal module or an external one\n        const sourceModule = this.moduleResolver.resolveModule(\n          contextModule,\n          member.identifierNode.text,\n        );\n\n        for (const item of member.items || []) {\n          // Internal module\n          if (sourceModule) {\n            const resolvedItem = this.itemResolver.resolveItem(\n              sourceModule,\n              item.identifierNode.text,\n            );\n            if (!resolvedItem) {\n              // Skip items that can't be resolved instead of throwing\n              continue;\n            }\n            if (resolvedItem.type === PYTHON_INTERNAL_MODULE_TYPE) {\n              const internalResolvedModule =\n                resolvedItem as ResolvedInternalModule;\n              if (internalResolvedModule.symbol) {\n                const internalResolvedSymbol =\n                  internalResolvedModule as ResolvedInternalSymbol;\n\n                // Pass the immediate re-exporting module if it's different from the original module\n                const reExportingModule =\n                  internalResolvedSymbol.module.path !== sourceModule.path\n                    ? sourceModule\n                    : undefined;\n\n                this.usageResolver.resolveInternalUsageForSymbol(\n                  node,\n                  nodesToExclude,\n                  internalResolvedSymbol.module,\n                  internalResolvedSymbol.symbol,\n                  item.aliasNode?.text || item.identifierNode.text,\n                  internalUsageMap,\n                  reExportingModule,\n                );\n\n                // Add all modules in the re-export chain as dependencies\n                if (\n                  internalResolvedSymbol.reExportChain &&\n                  internalResolvedSymbol.reExportChain.length > 0\n                ) {\n                  for (\n                    const reExportModule of internalResolvedSymbol.reExportChain\n                  ) {\n                    if (reExportModule.path !== sourceModule.path) {\n                      // Skip the immediate re-exporter (already handled)\n                      this.usageResolver.resolveInternalUsageForSymbol(\n                        node,\n                        nodesToExclude,\n                        internalResolvedSymbol.module, // Original module\n                        internalResolvedSymbol.symbol, // Original symbol\n                        item.aliasNode?.text || item.identifierNode.text,\n                        internalUsageMap,\n                        reExportModule, // Intermediate re-exporting module\n                      );\n                    }\n                  }\n                }\n              } else {\n                this.usageResolver.resolveInternalUsageForModule(\n                  node,\n                  nodesToExclude,\n                  internalResolvedModule.module,\n                  item.aliasNode?.text || item.identifierNode.text,\n                  internalUsageMap,\n                );\n              }\n            }\n          } else {\n            // external module\n            this.usageResolver.resolveExternalUsageForItem(\n              node,\n              nodesToExclude,\n              {\n                moduleName: member.identifierNode.text,\n                itemName: item.identifierNode.text,\n              },\n              item.aliasNode?.text || item.identifierNode.text,\n              externalUsageMap,\n            );\n          }\n        }\n      }\n\n      if (importStmt.type === NORMAL_IMPORT_STATEMENT_TYPE) {\n        importStmt.members.forEach((member) => {\n          const resolvedItem = this.itemResolver.resolveItem(\n            contextModule,\n            member.aliasNode?.text || member.identifierNode.text,\n          );\n          if (!resolvedItem) {\n            // Skip items that can't be resolved\n            return;\n          }\n\n          if (resolvedItem.type === PYTHON_INTERNAL_MODULE_TYPE) {\n            const internalResolvedModule =\n              resolvedItem as ResolvedInternalModule;\n            this.usageResolver.resolveInternalUsageForModule(\n              node,\n              nodesToExclude,\n              internalResolvedModule.module,\n              member.aliasNode?.text || member.identifierNode.text,\n              internalUsageMap,\n            );\n          } else {\n            const externalResolvedModule =\n              resolvedItem as ResolvedExternalModule;\n            this.usageResolver.resolveExternalUsageForItem(\n              node,\n              nodesToExclude,\n              {\n                moduleName: externalResolvedModule.moduleName,\n              },\n              member.aliasNode?.text || member.identifierNode.text,\n              externalUsageMap,\n            );\n          }\n        });\n      }\n    }\n\n    // Process wildcard imports last\n    for (const importStmt of importStmts) {\n      if (importStmt.type === FROM_IMPORT_STATEMENT_TYPE) {\n        const member = importStmt.members[0];\n        if (!member.isWildcardImport) {\n          continue;\n        }\n\n        const sourceModule = this.moduleResolver.resolveModule(\n          contextModule,\n          member.identifierNode.text,\n        );\n\n        // For external modules, we can't resolve items\n        // Only thing we can do is to add the module as a dependency\n        if (!sourceModule) {\n          // External module processing is done when converting usage map to dependencies\n          continue;\n        } else {\n          // internal module, we can resolve items\n          const resolvedItem = this.itemResolver.resolveItem(\n            sourceModule,\n            member.identifierNode.text,\n          );\n          if (!resolvedItem) {\n            continue;\n          }\n          if (resolvedItem.type === PYTHON_INTERNAL_MODULE_TYPE) {\n            const internalResolvedModule =\n              resolvedItem as ResolvedInternalModule;\n            if (!internalResolvedModule.symbol) {\n              continue;\n            }\n            const internalResolvedSymbol =\n              internalResolvedModule as ResolvedInternalSymbol;\n\n            this.usageResolver.resolveInternalUsageForSymbol(\n              node,\n              nodesToExclude,\n              internalResolvedSymbol.module,\n              internalResolvedSymbol.symbol,\n              internalResolvedSymbol.symbol.identifierNode.text,\n              internalUsageMap,\n            );\n          } else {\n            const externalResolvedModule =\n              resolvedItem as ResolvedExternalModule;\n            if (!externalResolvedModule.symbol) {\n              continue;\n            }\n            const externalResolvedSymbol =\n              externalResolvedModule as ResolvedExternalSymbol;\n            this.usageResolver.resolveExternalUsageForItem(\n              node,\n              nodesToExclude,\n              {\n                moduleName: externalResolvedSymbol.moduleName,\n                itemName: externalResolvedSymbol.symbolName,\n              },\n              externalResolvedSymbol.symbolName,\n              externalUsageMap,\n            );\n          }\n        }\n      }\n    }\n\n    return { internalUsageMap, externalUsageMap };\n  }\n\n  /**\n   * Converts internal usage map to module dependencies\n   */\n  private convertInternalUsageToDependencies(\n    internalUsageMap: Map<string, InternalUsage>,\n  ): Map<string, ModuleDependency> {\n    const dependencies = new Map<string, ModuleDependency>();\n\n    for (const [modulePath, usage] of internalUsageMap.entries()) {\n      const dependency: ModuleDependency = {\n        id: modulePath,\n        isExternal: false,\n        isNamespaceModule: usage.module.type === PYTHON_NAMESPACE_MODULE_TYPE,\n        symbols: new Set(),\n      };\n\n      // Add all used symbols\n      for (const symbol of usage.symbols.values()) {\n        dependency.symbols.add(symbol.identifierNode.text);\n      }\n\n      dependencies.set(modulePath, dependency);\n\n      // Add re-exporting modules as dependencies without symbols\n      if (usage.reExportingModules) {\n        for (\n          const [\n            reExportingModulePath,\n            reExportingModule,\n          ] of usage.reExportingModules.entries()\n        ) {\n          if (!dependencies.has(reExportingModulePath)) {\n            dependencies.set(reExportingModulePath, {\n              id: reExportingModulePath,\n              isExternal: false,\n              isNamespaceModule:\n                reExportingModule.type === PYTHON_NAMESPACE_MODULE_TYPE,\n              symbols: new Set(), // Empty set since we don't need specific symbols\n            });\n          }\n        }\n      }\n    }\n\n    return dependencies;\n  }\n\n  /**\n   * Converts external usage map to module dependencies\n   */\n  private convertExternalUsageToDependencies(\n    externalUsageMap: Map<string, ExternalUsage>,\n  ): Map<string, ModuleDependency> {\n    const dependencies = new Map<string, ModuleDependency>();\n\n    for (const [modulePath, usage] of externalUsageMap.entries()) {\n      const dependency: ModuleDependency = {\n        id: modulePath,\n        isExternal: true,\n        isNamespaceModule: false,\n        symbols: new Set(),\n      };\n\n      // Add all used items\n      for (const itemName of usage.itemNames) {\n        dependency.symbols.add(itemName);\n      }\n\n      dependencies.set(modulePath, dependency);\n    }\n\n    return dependencies;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/dependencyResolver/types.ts",
    "content": "import type { PythonSymbolType } from \"../exportExtractor/types.ts\";\n\n/**\n * Represents a dependency on a specific module.\n * This includes both internal project modules and external libraries.\n */\nexport interface ModuleDependency {\n  /** Module identifier (typically the file path) */\n  id: string;\n  /** True if this is an external dependency (stdlib/third-party) */\n  isExternal: boolean;\n  /**\n   * True if this module is a namespace module\n   * Usefull to have because namespace modules are not files\n   */\n  isNamespaceModule: boolean;\n  /** Symbols used from this module, mapping symbol ID to symbol name */\n  symbols: Set<string>;\n}\n\n/**\n * Represents dependency information for a specific exported symbol (function, class, variable).\n * This is used for fine-grained code splitting.\n */\nexport interface SymbolDependency {\n  /** Symbol identifier */\n  id: string;\n  /** Symbol type (class, function, variable) */\n  type: PythonSymbolType;\n  /** Positions of the symbol in the file */\n  positions: {\n    start: {\n      index: number;\n      row: number;\n      column: number;\n    };\n    end: {\n      index: number;\n      row: number;\n      column: number;\n    };\n  }[];\n  /** Size metrics for the symbol */\n  metrics: {\n    /** Total character count in the symbol */\n    characterCount: number;\n    /** Total character count in the symbol */\n    codeCharacterCount: number;\n    /** Total line count in the symbol */\n    linesCount: number;\n    /** Total line count in the symbol */\n    codeLineCount: number;\n    /** Total cyclomatic complexity of the symbol */\n    cyclomaticComplexity: number;\n  };\n  /** Map of modules this symbol depends on */\n  dependencies: Map<string, ModuleDependency>;\n}\n\n/**\n * Represents comprehensive dependency information for a file.\n */\nexport interface FileDependencies {\n  /** The path to the analyzed file */\n  filePath: string;\n  /** File size metrics */\n  metrics: {\n    /** Total character count in the symbol */\n    characterCount: number;\n    /** Total character count in the symbol */\n    codeCharacterCount: number;\n    /** Total line count in the symbol */\n    linesCount: number;\n    /** Total line count in the symbol */\n    codeLineCount: number;\n    /** Total cyclomatic complexity of the symbol */\n    cyclomaticComplexity: number;\n  };\n  /** Module-level dependencies for the entire file */\n  dependencies: Map<string, ModuleDependency>;\n  /** Exported symbols with their individual dependency info */\n  symbols: SymbolDependency[];\n}\n\n/**\n * Complete module dependency graph for an entire project.\n * Maps file paths to their dependency information.\n */\nexport type FileDependencyMap = Map<string, FileDependencies>;\n"
  },
  {
    "path": "src/languagePlugins/python/exportExtractor/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport type Parser from \"tree-sitter\";\nimport { PythonExportExtractor } from \"./index.ts\";\nimport {\n  PYTHON_CLASS_TYPE,\n  PYTHON_FUNCTION_TYPE,\n  PYTHON_VARIABLE_TYPE,\n} from \"./types.ts\";\nimport { pythonParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\ndescribe(\"PythonExportExtractor\", () => {\n  const parser = pythonParser;\n\n  test(\"should extract simple class definitions\", () => {\n    const code = `\n    class MyClass:\n        pass\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"class_test.py\", {\n      path: \"class_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"class_test.py\");\n    const classSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_CLASS_TYPE,\n    );\n\n    expect(classSymbols.length).toBeGreaterThanOrEqual(1);\n    const myClass = classSymbols.find((s) => s.id === \"MyClass\");\n    expect(myClass).toBeDefined();\n    // Optionally, check that the syntax node's text matches expectations.\n    expect(myClass?.identifierNode.text).toBe(\"MyClass\");\n  });\n\n  test(\"should extract decorated class definitions\", () => {\n    const code = `\n    @decorator\n    class DecoratedClass:\n        pass\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"decorated_class_test.py\", {\n      path: \"decorated_class_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"decorated_class_test.py\");\n    const classSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_CLASS_TYPE,\n    );\n\n    expect(classSymbols.length).toBeGreaterThanOrEqual(1);\n    const decClass = classSymbols.find((s) => s.id === \"DecoratedClass\");\n    expect(decClass).toBeDefined();\n  });\n\n  test(\"should extract simple function definitions\", () => {\n    const code = `\n    def my_function():\n        pass\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"function_test.py\", {\n      path: \"function_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"function_test.py\");\n    const functionSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_FUNCTION_TYPE,\n    );\n\n    expect(functionSymbols.length).toBeGreaterThanOrEqual(1);\n    const func = functionSymbols.find((s) => s.id === \"my_function\");\n    expect(func).toBeDefined();\n  });\n\n  test(\"should extract decorated function definitions\", () => {\n    const code = `\n    @decorator\n    def decorated_function():\n        pass\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"decorated_function_test.py\", {\n      path: \"decorated_function_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"decorated_function_test.py\");\n    const functionSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_FUNCTION_TYPE,\n    );\n\n    expect(functionSymbols.length).toBeGreaterThanOrEqual(1);\n    const decFunc = functionSymbols.find((s) => s.id === \"decorated_function\");\n    expect(decFunc).toBeDefined();\n  });\n\n  test(\"should extract variable assignments and not include __all__\", () => {\n    const code = `\n    x = 10\n    y, z = 20, 30\n    __all__ = [\"x\", \"y\", \"z\"]\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"variable_test.py\", {\n      path: \"variable_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"variable_test.py\");\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n\n    // Expect that x, y, z are extracted as variables.\n    const varX = variableSymbols.find((s) => s.id === \"x\");\n    const varY = variableSymbols.find((s) => s.id === \"y\");\n    const varZ = variableSymbols.find((s) => s.id === \"z\");\n    expect(varX).toBeDefined();\n    expect(varY).toBeDefined();\n    expect(varZ).toBeDefined();\n\n    // __all__ should not be among exported symbols.\n    const exportedIds = result.symbols.map((s) => s.id);\n    expect(exportedIds).not.toContain(\"__all__\");\n  });\n\n  test(\"should extract __all__ elements correctly\", () => {\n    const code = `\n    __all__ = [\"a\", \"b\", \"c\"]\n    a = 1\n    b = 2\n    c = 3\n    d = 4\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"all_test.py\", { path: \"all_test.py\", rootNode: tree.rootNode });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"all_test.py\");\n\n    // publicSymbols should reflect the __all__ definition.\n    expect(result.publicSymbols).toContain(\"a\");\n    expect(result.publicSymbols).toContain(\"b\");\n    expect(result.publicSymbols).toContain(\"c\");\n    // 'd' is not public because it's not in __all__\n    expect(result.publicSymbols).not.toContain(\"d\");\n  });\n\n  test(\"should extract multiple function definitions with the same name\", () => {\n    const code = `\n    def my_function():\n        pass\n\n    # Some comment in between\n\n    def my_function(a, b):\n        return a + b\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"multi_function_test.py\", {\n      path: \"multi_function_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"multi_function_test.py\");\n    const functionSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_FUNCTION_TYPE,\n    );\n\n    // Should find one symbol for my_function with two nodes\n    const func = functionSymbols.find((s) => s.id === \"my_function\");\n    expect(func).toBeDefined();\n    expect(func?.nodes.length).toBe(2);\n  });\n\n  test(\"should extract multiple class definitions with the same name\", () => {\n    const code = `\n    class MyClass:\n        def method1(self):\n            pass\n\n    # Some comment in between\n\n    class MyClass:\n        def method2(self):\n            pass\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"multi_class_test.py\", {\n      path: \"multi_class_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"multi_class_test.py\");\n    const classSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_CLASS_TYPE,\n    );\n\n    // Should find one symbol for MyClass with two nodes\n    const myClass = classSymbols.find((s) => s.id === \"MyClass\");\n    expect(myClass).toBeDefined();\n    expect(myClass?.nodes.length).toBe(2);\n  });\n\n  test(\"should capture variable modifications\", () => {\n    const code = `\n    counter = 0\n    counter += 1\n    counter = counter + 10\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"variable_mod_test.py\", {\n      path: \"variable_mod_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"variable_mod_test.py\");\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n\n    // There should be only one symbol for counter\n    expect(variableSymbols.length).toBe(1);\n\n    // The one symbol should be counter\n    const counter = variableSymbols[0];\n    expect(counter.id).toBe(\"counter\");\n\n    // After deduplication, we expect 3 nodes: initial assignment + two modifications\n    expect(counter.nodes.length).toBe(3);\n\n    // Verify that all the expected nodes are included\n    const nodeCaptured = (text: string) =>\n      counter.nodes.some((node) => node.text.includes(text));\n    expect(nodeCaptured(\"counter = 0\")).toBe(true);\n    expect(nodeCaptured(\"counter += 1\")).toBe(true);\n    expect(nodeCaptured(\"counter = counter + 10\")).toBe(true);\n  });\n\n  test(\"should not create new symbols for variable modifications\", () => {\n    const code = `\n    # Only modify a variable without initializing it\n    existing_var += 5\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"var_mod_only_test.py\", {\n      path: \"var_mod_only_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"var_mod_only_test.py\");\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n\n    // Should not find a symbol for existing_var as it's only modified without initialization\n    const existingVar = variableSymbols.find((s) => s.id === \"existing_var\");\n    expect(existingVar).toBeUndefined();\n  });\n\n  test(\"should handle mixed symbol types and multiple modifications\", () => {\n    const code = `\n    x = 10\n\n    def func():\n        pass\n\n    x += 5\n\n    class MyClass:\n        pass\n\n    x = 20\n\n    def func(a):\n        return a\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"mixed_symbols_test.py\", {\n      path: \"mixed_symbols_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"mixed_symbols_test.py\");\n\n    // Variable x should have 3 nodes\n    const x = result.symbols.find(\n      (s) => s.id === \"x\" && s.type === PYTHON_VARIABLE_TYPE,\n    );\n    expect(x).toBeDefined();\n    expect(x?.nodes.length).toBe(3);\n\n    // Function func should have 2 nodes\n    const func = result.symbols.find(\n      (s) => s.id === \"func\" && s.type === PYTHON_FUNCTION_TYPE,\n    );\n    expect(func).toBeDefined();\n    expect(func?.nodes.length).toBe(2);\n\n    // Class MyClass should have 1 node\n    const myClass = result.symbols.find(\n      (s) => s.id === \"MyClass\" && s.type === PYTHON_CLASS_TYPE,\n    );\n    expect(myClass).toBeDefined();\n    expect(myClass?.nodes.length).toBe(1);\n  });\n\n  test(\"should capture list variable modifications\", () => {\n    const code = `\n    my_list = [1, 2, 3]\n    my_list.append(4)\n    my_list += [5, 6]\n    my_list.extend([7, 8])\n    my_list[0] = 0\n    my_list = my_list + [9, 10]\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"list_mod_test.py\", {\n      path: \"list_mod_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"list_mod_test.py\");\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n\n    // There should be only one symbol for my_list\n    const myList = variableSymbols.find((s) => s.id === \"my_list\");\n    expect(myList).toBeDefined();\n\n    // Should have nodes for the initial assignment and += operation\n    // Method calls like append and extend are not captured as they're not direct assignments\n    // But reassignment (my_list = my_list + [9, 10]) should be captured\n    expect(myList?.nodes.length).toBeGreaterThanOrEqual(3);\n\n    // Verify that key operations are included\n    const nodeCaptured = (text: string) =>\n      myList?.nodes.some((node) => node.text.includes(text));\n    expect(nodeCaptured(\"my_list = [1, 2, 3]\")).toBe(true);\n    expect(nodeCaptured(\"my_list += [5, 6]\")).toBe(true);\n    expect(nodeCaptured(\"my_list = my_list + [9, 10]\")).toBe(true);\n  });\n\n  test(\"should capture dictionary variable modifications\", () => {\n    const code = `\n    my_dict = {\"a\": 1, \"b\": 2}\n    my_dict[\"c\"] = 3\n    my_dict.update({\"d\": 4})\n    my_dict |= {\"e\": 5}\n    del my_dict[\"a\"]\n    my_dict = {**my_dict, \"f\": 6}\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"dict_mod_test.py\", {\n      path: \"dict_mod_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"dict_mod_test.py\");\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n\n    // There should be only one symbol for my_dict\n    const myDict = variableSymbols.find((s) => s.id === \"my_dict\");\n    expect(myDict).toBeDefined();\n\n    // Should have at least the initial assignment, |= operation, and reassignment\n    // Method calls like update and subscript assignments aren't captured as direct assignments\n    expect(myDict?.nodes.length).toBeGreaterThanOrEqual(3);\n\n    // Verify that key operations are included\n    const nodeCaptured = (text: string) =>\n      myDict?.nodes.some((node) => node.text.includes(text));\n    expect(nodeCaptured('my_dict = {\"a\": 1, \"b\": 2}')).toBe(true);\n    expect(nodeCaptured('my_dict |= {\"e\": 5}')).toBe(true);\n    expect(nodeCaptured('my_dict = {**my_dict, \"f\": 6}')).toBe(true);\n  });\n\n  test(\"should capture object attribute modifications\", () => {\n    const code = `\n    class Person:\n        pass\n\n    person = Person()\n    person.name = \"John\"\n    person.age = 30\n    person = Person()\n    person.name = \"Jane\"\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"object_mod_test.py\", {\n      path: \"object_mod_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"object_mod_test.py\");\n\n    // There should be a class and a variable\n    const personClass = result.symbols.find(\n      (s) => s.id === \"Person\" && s.type === PYTHON_CLASS_TYPE,\n    );\n    expect(personClass).toBeDefined();\n\n    const personVar = result.symbols.find(\n      (s) => s.id === \"person\" && s.type === PYTHON_VARIABLE_TYPE,\n    );\n    expect(personVar).toBeDefined();\n\n    // Should have at least the two assignments to person\n    // Attribute assignments (person.name, person.age) aren't captured as direct variable assignments\n    expect(personVar?.nodes.length).toBeGreaterThanOrEqual(2);\n\n    // Verify that direct assignments to the person variable are included\n    const nodeCaptured = (text: string) =>\n      personVar?.nodes.some((node) => node.text.includes(text));\n    expect(nodeCaptured(\"person = Person()\")).toBe(true);\n  });\n\n  test(\"should ignore variable modifications inside functions\", () => {\n    const code = `\n    # Module-level variable\n    counter = 0\n\n    def increment():\n        # Local variable with same name as module-level one\n        counter = 0\n        counter += 1\n        return counter\n\n    # Module-level modification\n    counter += 10\n\n    def modify_global():\n        global counter\n        # This modifies the module-level variable\n        counter += 100\n\n    # Another module-level variable\n    total = 50\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"function_scope_test.py\", {\n      path: \"function_scope_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"function_scope_test.py\");\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n\n    // There should be two variables: counter and total\n    expect(variableSymbols.length).toBe(2);\n\n    // The counter variable should have 2 nodes (initial assignment and module-level +=)\n    const counter = variableSymbols.find((s) => s.id === \"counter\");\n    expect(counter).toBeDefined();\n    expect(counter?.nodes.length).toBe(2);\n\n    // Verify that only module-level operations are included, not the ones inside functions\n    const counterNodeTexts = counter?.nodes.map((node) => node.text.trim());\n    expect(counterNodeTexts).toContain(\"counter = 0\");\n    expect(counterNodeTexts).toContain(\"counter += 10\");\n    // The counter += 1 inside increment() should not be captured\n    const hasLocalIncrement = counter?.nodes.some(\n      (node) =>\n        node.text.includes(\"counter += 1\") &&\n        node.parent?.parent?.type === \"function_definition\",\n    );\n    expect(hasLocalIncrement).toBeFalsy();\n\n    // The counter += 100 inside modify_global() should not be captured\n    // even though it modifies the global variable\n    const hasGlobalModification = counter?.nodes.some((node) =>\n      node.text.includes(\"counter += 100\")\n    );\n    expect(hasGlobalModification).toBeFalsy();\n\n    // The total variable should have 1 node\n    const total = variableSymbols.find((s) => s.id === \"total\");\n    expect(total).toBeDefined();\n    expect(total?.nodes.length).toBe(1);\n  });\n\n  test(\"should ignore variable modifications inside classes\", () => {\n    const code = `\n    # Module-level variable\n    data = []\n\n    class DataProcessor:\n        # Class variable with same name\n        data = {}\n\n        def __init__(self):\n            # Instance variable\n            self.data = []\n\n        def process(self):\n            # Local variable\n            data = []\n            data.append(\"processed\")\n            return data\n\n        @classmethod\n        def reset(cls):\n            # Modifying class variable\n            cls.data = {}\n\n    # Module-level modification\n    data.append(\"module\")\n    data = data + [\"level\"]\n\n    # Another module-level variable\n    config = {}\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"class_scope_test.py\", {\n      path: \"class_scope_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"class_scope_test.py\");\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n\n    // There should be two variables: data and config\n    expect(variableSymbols.length).toBe(2);\n\n    // The data variable should have 2 nodes (initial assignment and module-level reassignment)\n    const data = variableSymbols.find((s) => s.id === \"data\");\n    expect(data).toBeDefined();\n    expect(data?.nodes.length).toBeGreaterThanOrEqual(2);\n\n    // Verify that only module-level operations are included, not the ones inside the class\n    const dataNodeTexts = data?.nodes.map((node) => node.text.trim());\n    expect(dataNodeTexts).toContain(\"data = []\");\n    expect(dataNodeTexts).toContain('data = data + [\"level\"]');\n\n    // Class-level data = {} should not be captured\n    const hasClassVariable = data?.nodes.some(\n      (node) =>\n        node.text.includes(\"data = {}\") &&\n        node.parent?.parent?.type === \"class_definition\",\n    );\n    expect(hasClassVariable).toBeFalsy();\n\n    // The config variable should have 1 node\n    const config = variableSymbols.find((s) => s.id === \"config\");\n    expect(config).toBeDefined();\n    expect(config?.nodes.length).toBe(1);\n  });\n\n  test(\"should capture only top-level nodes in a complex nested structure\", () => {\n    const code = `\n    # Module-level variable\n    count = 0\n\n    class Counter:\n        # Class variable\n        count = 0\n\n        def __init__(self, initial=0):\n            # Instance variable\n            self.count = initial\n\n        def increment(self):\n            # Local method operation\n            count = 0  # Local variable shadowing\n            count += 1\n            self.count += 1\n\n        @classmethod\n        def increment_class(cls):\n            # Class variable operation\n            cls.count += 1\n\n        @staticmethod\n        def process():\n            # Local function operation\n            def nested():\n                # Nested function variable\n                count = 100\n                count *= 2\n                return count\n            return nested()\n\n    # Another module-level function\n    def operate():\n        # Local variable\n        count = 5\n\n        # Nested function\n        def nested_modifier():\n            nonlocal count\n            count += 5\n\n            # Double-nested function\n            def deep_nested():\n                # Local to deep_nested\n                count = 1000\n                return count\n\n            return deep_nested()\n\n        nested_modifier()\n        return count\n\n    # Module-level modifications\n    count += 1\n    count *= 2\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"complex_nested_test.py\", {\n      path: \"complex_nested_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"complex_nested_test.py\");\n\n    // There should be one class, one function and one variable\n    const classSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_CLASS_TYPE,\n    );\n    expect(classSymbols.length).toBe(1);\n\n    const functionSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_FUNCTION_TYPE,\n    );\n    expect(functionSymbols.length).toBe(1);\n\n    const variableSymbols = result.symbols.filter(\n      (s) => s.type === PYTHON_VARIABLE_TYPE,\n    );\n    expect(variableSymbols.length).toBe(1);\n\n    // The count variable should have 3 nodes (initial assignment and two module-level modifications)\n    const count = variableSymbols[0];\n    expect(count.id).toBe(\"count\");\n    expect(count.nodes.length).toBe(3);\n\n    // Verify that only module-level operations are included\n    const countNodeTexts = count.nodes.map((node) => node.text.trim());\n    expect(countNodeTexts).toContain(\"count = 0\");\n    expect(countNodeTexts).toContain(\"count += 1\");\n    expect(countNodeTexts).toContain(\"count *= 2\");\n\n    // No inner node should be included\n    const hasNestedNodes = count.nodes.some((node) => {\n      const parentTypes = [];\n      let current = node.parent;\n      while (current) {\n        parentTypes.push(current.type);\n        current = current.parent;\n      }\n\n      return (\n        parentTypes.includes(\"function_definition\") ||\n        parentTypes.includes(\"class_definition\")\n      );\n    });\n\n    expect(hasNestedNodes).toBeFalsy();\n  });\n\n  test(\"should capture method calls and attribute assignments for app configuration\", () => {\n    const code = `\n    import os\n    from celery import Celery\n\n    os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"Project.settings\")\n    app = Celery(\"Project\")\n    app.config_from_object(\"django.conf:settings\", namespace=\"CELERY\")\n    app.conf.update(app.conf.get(\"CELERY_CONFIG\"))\n    app.autodiscover_tasks()\n\n    app.conf.task_queue_max_priority = 10\n    app.conf.task_default_priority = 5\n\n    app.conf.beat_schedule = {\n        \"task1\": {\n            \"task\": \"module.tasks.task1\",\n            \"schedule\": 60,\n        },\n        \"task2\": {\n            \"task\": \"module.tasks.task2\",\n            \"schedule\": 300,\n        },\n    }\n    `;\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"celery_app_test.py\", {\n      path: \"celery_app_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"celery_app_test.py\");\n    const appSymbol = result.symbols.find((s) => s.id === \"app\");\n\n    // Verify app symbol exists and is captured correctly\n    expect(appSymbol).toBeDefined();\n    expect(appSymbol?.type).toBe(PYTHON_VARIABLE_TYPE);\n\n    // Should have multiple nodes for the app variable\n    expect(appSymbol?.nodes.length).toBeGreaterThanOrEqual(6); // Initial definition + 5 modification statements\n\n    // Verify that all app operations are included\n    const nodeCaptured = (text: string) =>\n      appSymbol?.nodes.some((node) => node.text.includes(text));\n\n    // Initial assignment\n    expect(nodeCaptured('app = Celery(\"Project\")')).toBe(true);\n\n    // Method calls\n    expect(nodeCaptured(\"app.config_from_object\")).toBe(true);\n    expect(nodeCaptured(\"app.autodiscover_tasks()\")).toBe(true);\n\n    // Attribute assignments\n    expect(nodeCaptured(\"app.conf.task_queue_max_priority = 10\")).toBe(true);\n    expect(nodeCaptured(\"app.conf.task_default_priority = 5\")).toBe(true);\n    expect(nodeCaptured(\"app.conf.beat_schedule = {\")).toBe(true);\n  });\n\n  test(\"should capture complex object initialization and modifications\", () => {\n    const code = `\n    from flask import Flask\n    from flask_cors import CORS\n    from config import Config\n\n    server = Flask(__name__)\n    server.config.from_object(Config)\n    server.config.update({\n        \"DEBUG\": True,\n        \"SECRET_KEY\": \"dev-key\",\n    })\n    server.register_blueprint(api_bp, url_prefix=\"/api\")\n\n    CORS(server)\n\n    server.logger.setLevel(\"INFO\")\n    server.config[\"MAX_CONTENT_LENGTH\"] = 16 * 1024 * 1024\n\n    server.session_interface = CustomSessionInterface()\n\n    server.route_map = {\n        \"home\": \"/\",\n        \"dashboard\": \"/dashboard\",\n        \"profile\": \"/profile\",\n        \"settings\": {\n            \"general\": \"/settings/general\",\n            \"security\": \"/settings/security\",\n            \"notifications\": \"/settings/notifications\",\n        }\n    }\n\n    @server.before_request\n    def before_request():\n        pass\n\n    if __name__ == \"__main__\":\n        server.run(host=\"0.0.0.0\", port=5000)\n    `;\n\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"flask_app.py\", {\n      path: \"flask_app.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"flask_app.py\");\n    const serverSymbol = result.symbols.find((s) => s.id === \"server\");\n\n    // Verify server symbol exists and is a variable\n    expect(serverSymbol).toBeDefined();\n    expect(serverSymbol?.type).toBe(PYTHON_VARIABLE_TYPE);\n\n    // Should have multiple nodes for the server variable\n    expect(serverSymbol?.nodes.length).toBeGreaterThan(5);\n\n    // Check for specific operations\n    const nodeCaptured = (text: string) =>\n      serverSymbol?.nodes.some((node) => node.text.includes(text));\n\n    // Initial declaration\n    expect(nodeCaptured(\"server = Flask(__name__)\")).toBe(true);\n\n    // Method calls\n    expect(nodeCaptured(\"server.config.from_object\")).toBe(true);\n    expect(nodeCaptured(\"server.config.update\")).toBe(true);\n    expect(nodeCaptured(\"server.register_blueprint\")).toBe(true);\n\n    // Attribute assignments\n    expect(nodeCaptured(\"server.logger.setLevel\")).toBe(true);\n\n    // Subscript assignments should now be detected\n    expect(nodeCaptured('server.config[\"MAX_CONTENT_LENGTH\"]')).toBe(true);\n\n    expect(nodeCaptured(\"server.session_interface =\")).toBe(true);\n    expect(nodeCaptured(\"server.route_map =\")).toBe(true);\n  });\n\n  test(\"should capture variable modifications through subscript operations\", () => {\n    const code = `\n    # Direct subscript operations\n    data = {}\n    data[\"key1\"] = \"value1\"\n    data[\"key2\"] = \"value2\"\n\n    # Nested subscript operations\n    config = {\"db\": {}}\n    config[\"db\"][\"host\"] = \"localhost\"\n    config[\"db\"][\"port\"] = 5432\n\n    # Attribute + subscript operations\n    class Storage:\n        def __init__(self):\n            self.items = {}\n\n    storage = Storage()\n    storage.items[\"item1\"] = {\"name\": \"Item 1\", \"price\": 10}\n    storage.items[\"item2\"] = {\"name\": \"Item 2\", \"price\": 20}\n\n    # Mixed operations\n    options = {}\n    options[\"debug\"] = True\n    options.update({\"verbose\": True})\n    options[\"logging\"] = {\"level\": \"info\"}\n    `;\n\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(\"subscript_test.py\", {\n      path: \"subscript_test.py\",\n      rootNode: tree.rootNode,\n    });\n\n    const resolver = new PythonExportExtractor(parser, files);\n    const result = resolver.getSymbols(\"subscript_test.py\");\n\n    // Check data variable\n    const dataSymbol = result.symbols.find((s) => s.id === \"data\");\n    expect(dataSymbol).toBeDefined();\n    expect(dataSymbol?.type).toBe(PYTHON_VARIABLE_TYPE);\n    expect(dataSymbol?.nodes.length).toBeGreaterThan(1);\n\n    // Check config variable\n    const configSymbol = result.symbols.find((s) => s.id === \"config\");\n    expect(configSymbol).toBeDefined();\n    expect(configSymbol?.nodes.length).toBeGreaterThanOrEqual(1);\n\n    // Check storage variable\n    const storageSymbol = result.symbols.find((s) => s.id === \"storage\");\n    expect(storageSymbol).toBeDefined();\n    expect(storageSymbol?.nodes.length).toBeGreaterThanOrEqual(1);\n\n    // Check options variable\n    const optionsSymbol = result.symbols.find((s) => s.id === \"options\");\n    expect(optionsSymbol).toBeDefined();\n    expect(optionsSymbol?.nodes.length).toBeGreaterThan(1);\n\n    // Check specific subscript operations were captured\n    const nodeCaptured = (\n      symbol: { nodes: Parser.SyntaxNode[] } | undefined,\n      text: string,\n    ) =>\n      symbol?.nodes.some((node: Parser.SyntaxNode) => node.text.includes(text));\n\n    // Verify that subscript-related nodes were captured\n    expect(nodeCaptured(dataSymbol, \"data[\")).toBe(true);\n    expect(nodeCaptured(storageSymbol, \"storage.items\")).toBe(true);\n    expect(nodeCaptured(optionsSymbol, \"options[\")).toBe(true);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/exportExtractor/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport {\n  PYTHON_CLASS_TYPE,\n  PYTHON_FUNCTION_TYPE,\n  PYTHON_VARIABLE_TYPE,\n  type PythonSymbol,\n  type PythonSymbolType,\n} from \"./types.ts\";\n\n/**\n * PythonExportExtractor extracts exported symbols from a Python source file using Tree-sitter.\n *\n * This class performs the following:\n * - Analyzes Python source code to detect top-level definitions.\n * - Supports both plain and decorated class and function definitions.\n * - Extracts top-level variable assignments (ignoring __all__).\n * - Additionally, detects the __all__ assignment to extract public symbol names.\n * - Combines all these patterns in a single Tree-sitter query.\n * - Uses caching to improve performance by avoiding redundant computation.\n * - Tracks all nodes for each symbol, including multiple definitions and modifications.\n */\nexport class PythonExportExtractor {\n  private files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n  private parser: Parser;\n  private symbolQuery: Parser.Query;\n  private cache = new Map<\n    string,\n    { symbols: PythonSymbol[]; publicSymbols: undefined | string[] }\n  >(); // Cache results for efficiency\n\n  constructor(\n    parser: Parser,\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n  ) {\n    this.parser = parser;\n    this.files = files;\n    // This single query combines multiple patterns:\n    // - Top-level class definitions (plain and decorated) with their name captured as @classIdentifier\n    // - Top-level function definitions (plain and decorated) with their name captured as @functionIdentifier\n    // - Top-level variable assignments (excluding __all__) with their identifier captured as @variableIdentifier\n    // - Top-level variable modifications with their identifier captured as @variableModifier\n    // - Attribute assignments (app.attr = value)\n    // - Nested attribute assignments (app.conf.attr = value)\n    // - Method calls (app.method())\n    // - Nested method calls (app.conf.update())\n    // - An assignment for __all__ where the left side is captured as @allIdentifier (ensuring it equals \"__all__\")\n    //   and the public element strings from the list are captured as @publicElement.\n    this.symbolQuery = new Parser.Query(\n      this.parser.getLanguage(),\n      `\n        (module\n          ([\n            ; Top-level classes (plain and decorated)\n          \t(class_definition\n          \t\tname: (identifier) @classIdentifier\n          \t) @class\n            (decorated_definition\n              definition: (class_definition\n                name: (identifier) @classIdentifier\n              )\n            ) @class\n            \n            ; Top-level functions (plain and decorated)\n          \t(function_definition\n              name: (identifier) @functionIdentifier\n            ) @function\n            (decorated_definition\n              definition: (function_definition\n                name: (identifier) @functionIdentifier\n              )\n            ) @function\n\n            ; Top-level variables (ignoring __all__)\n            (expression_statement\n              (assignment\n                left: (\n                  (identifier) @variableIdentifier\n                  (#not-eq? @variableIdentifier \"__all__\")\n                )\n              )\n            ) @variable\n            (expression_statement\n              (assignment\n                left: (pattern_list\n                  (\n                    (identifier) @variableIdentifier\n                    (#not-eq? @variableIdentifier \"__all__\")\n                  )\n                )\n              )\n            ) @variable\n            \n            ; Top-level variable modifications (augmented assignments like +=, -=, etc.)\n            (expression_statement\n              (augmented_assignment\n                left: (identifier) @variableModifier\n              )\n            ) @variableMod\n            \n            ; Top-level variable modifications in regular assignments (reassignments)\n            (expression_statement\n              (assignment\n                left: (\n                  (identifier) @variableModifier\n                  (#not-eq? @variableModifier \"__all__\")\n                )\n              )\n            ) @variableMod\n            \n            ; Attribute assignments (app.attr = value)\n            (expression_statement\n              (assignment\n                left: (attribute\n                  object: (identifier) @variableModifier\n                )\n              )\n            ) @variableMod\n\n            ; Nested attribute assignments (app.conf.attr = value)\n            (expression_statement\n              (assignment\n                left: (attribute\n                  object: (attribute\n                    object: (identifier) @variableModifier\n                  )\n                )\n              )\n            ) @variableMod\n\n            ; Method calls (app.method())\n            (expression_statement\n              (call\n                function: (attribute\n                  object: (identifier) @variableModifier\n                )\n              )\n            ) @variableMod\n\n            ; Nested method calls (app.conf.update())\n            (expression_statement\n              (call\n                function: (attribute\n                  object: (attribute\n                    object: (identifier) @variableModifier\n                  )\n                )\n              )\n            ) @variableMod\n            \n            ; Subscript assignments (obj[key] = value)\n            (expression_statement\n              (assignment\n                left: (subscript\n                  value: (identifier) @variableModifier\n                )\n              )\n            ) @variableMod\n            \n            ; Nested subscript assignments (obj.attr[key] = value)\n            (expression_statement\n              (assignment\n                left: (subscript\n                  value: (attribute\n                    object: (identifier) @variableModifier\n                  )\n                )\n              )\n            ) @variableMod\n            \n            ; __all__ assignment to capture public symbols\n            (expression_statement\n              (assignment\n                left: (identifier) @allIdentifier (#eq? @allIdentifier \"__all__\")\n                right: (list\n                  (string (string_content) @publicElement)\n                )\n              )\n            )\n          ])        \n        )\n      `,\n    );\n  }\n\n  /**\n   * Retrieves all exported symbols from the specified file.\n   *\n   * This method performs the following:\n   * - Runs the combined Tree-sitter query on the file's AST to extract:\n   *   - Top-level classes, functions, and variable definitions.\n   *   - Variable modifications at the top level.\n   *   - The __all__ assignment to obtain public symbol names.\n   * - Constructs Symbol objects for each definition using:\n   *   - The outer node capture (e.g. @class, @function, or @variable).\n   *   - Its associated identifier capture (e.g. @classIdentifier, @functionIdentifier, or @variableIdentifier).\n   * - Tracks all nodes for each symbol, including multiple definitions and modifications.\n   * - Caches the result to prevent redundant parsing.\n   *\n   * @param filePath The path of the Python file to analyze.\n   * @returns An object with two properties:\n   *   - symbols: Array of extracted Symbol objects.\n   *   - publicSymbols: Array of public symbol names from __all__, or undefined if not defined.\n   */\n  public getSymbols(filePath: string) {\n    const cacheKey = `${filePath}|symbols`;\n\n    // Return cached result if available\n    const cacheValue = this.cache.get(cacheKey);\n    if (cacheValue) {\n      return cacheValue;\n    }\n\n    // Ensure the file exists in the provided map\n    const file = this.files.get(filePath);\n    if (!file) {\n      throw new Error(`File not found: ${filePath}`);\n    }\n\n    // Use a map to collect all nodes for each symbol\n    const symbolsMap = new Map<\n      string,\n      {\n        id: string;\n        type: PythonSymbolType;\n        nodes: Parser.SyntaxNode[];\n        identifierNode: Parser.SyntaxNode;\n      }\n    >();\n\n    let publicSymbols: string[] | undefined = undefined;\n\n    // Execute the combined query on the AST\n    const matches = this.symbolQuery.matches(file.rootNode);\n\n    matches.forEach(({ captures }) => {\n      let symbolType: PythonSymbolType | undefined;\n      let symbolNode: Parser.SyntaxNode | undefined;\n      let identifierNode: Parser.SyntaxNode | undefined;\n      let isModification = false;\n\n      captures.forEach((capture) => {\n        // If the capture name indicates an outer definition,\n        // record its node and determine its type.\n        if (\n          [\"class\", \"function\", \"variable\", \"variableMod\"].includes(\n            capture.name,\n          )\n        ) {\n          symbolNode = capture.node;\n\n          if (capture.name === \"class\") {\n            symbolType = PYTHON_CLASS_TYPE;\n          } else if (capture.name === \"function\") {\n            symbolType = PYTHON_FUNCTION_TYPE;\n          } else if (\n            capture.name === \"variable\" ||\n            capture.name === \"variableMod\"\n          ) {\n            symbolType = PYTHON_VARIABLE_TYPE;\n            isModification = capture.name === \"variableMod\";\n          }\n        }\n\n        // If the capture is an identifier for the definition or modification,\n        // store it as the identifier node.\n        if (\n          [\n            \"classIdentifier\",\n            \"functionIdentifier\",\n            \"variableIdentifier\",\n            \"variableModifier\",\n          ].includes(capture.name)\n        ) {\n          identifierNode = capture.node;\n        }\n\n        // Capture public symbol elements from the __all__ assignment.\n        if (capture.name === \"publicElement\") {\n          if (!publicSymbols) {\n            publicSymbols = [capture.node.text];\n          } else {\n            publicSymbols.push(capture.node.text);\n          }\n        }\n      });\n\n      // When we have all parts of a symbol, update the symbols map\n      if (symbolType && symbolNode && identifierNode) {\n        const symbolId = identifierNode.text;\n\n        if (symbolsMap.has(symbolId)) {\n          // If this symbol already exists, add this node to its nodes array\n          const existingSymbol = symbolsMap.get(symbolId);\n\n          // For classes, functions, and variable modifications\n          if (existingSymbol) {\n            // Check if this node is already in the array (to avoid duplicates)\n            const isDuplicate = existingSymbol.nodes.some(\n              (node) =>\n                node.startPosition.row === symbolNode?.startPosition.row &&\n                node.startPosition.column === symbolNode?.startPosition.column,\n            );\n\n            if (!isDuplicate) {\n              // Only add modifications to existing variable symbols, not creating a new variable symbol\n              if (isModification && symbolType === PYTHON_VARIABLE_TYPE) {\n                existingSymbol.nodes.push(symbolNode);\n              } // For classes and functions with multiple definitions, add each occurrence\n              else if (\n                !isModification &&\n                (symbolType === PYTHON_CLASS_TYPE ||\n                  symbolType === PYTHON_FUNCTION_TYPE)\n              ) {\n                existingSymbol.nodes.push(symbolNode);\n              } // For non-modification variable assignments, if the symbol already exists,\n              // add the node to track reassignments\n              else if (!isModification && symbolType === PYTHON_VARIABLE_TYPE) {\n                existingSymbol.nodes.push(symbolNode);\n              }\n            }\n          }\n        } else {\n          // If it's a new symbol or not a modification, add it to the map\n          if (!isModification) {\n            symbolsMap.set(symbolId, {\n              id: symbolId,\n              type: symbolType,\n              nodes: [symbolNode],\n              identifierNode,\n            });\n          }\n        }\n      }\n    });\n\n    // Convert the map to an array of symbols\n    const symbols: PythonSymbol[] = Array.from(symbolsMap.values());\n\n    const result = {\n      symbols,\n      publicSymbols,\n    };\n\n    // Cache the result for future lookups.\n    this.cache.set(cacheKey, result);\n\n    return result;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/exportExtractor/types.ts",
    "content": "import type Parser from \"tree-sitter\";\n\n/**\n * Constants for Python symbol types\n */\nexport const PYTHON_CLASS_TYPE = \"class\";\nexport const PYTHON_FUNCTION_TYPE = \"function\";\nexport const PYTHON_VARIABLE_TYPE = \"variable\";\n\n/**\n * Represents the types of symbols that can be exported from a Python module.\n *\n * - class: A Python class definition\n * - function: A Python function definition\n * - variable: A Python variable assignment at module level\n */\nexport type PythonSymbolType =\n  | typeof PYTHON_CLASS_TYPE\n  | typeof PYTHON_FUNCTION_TYPE\n  | typeof PYTHON_VARIABLE_TYPE;\n\n/**\n * Represents an exported symbol in a Python module.\n * Exported symbols include top-level classes, functions, and variables.\n *\n * This interface captures not only the symbol's identifier (name) but also\n * its AST nodes for detailed analysis.\n */\nexport interface PythonSymbol {\n  /** The identifier (name) of the symbol */\n  id: string;\n  /** The full AST nodes for the symbol definition */\n  nodes: Parser.SyntaxNode[];\n  /** The AST node for just the identifier part of the symbol */\n  identifierNode: Parser.SyntaxNode;\n  /** The type of the symbol (class, function, or variable) */\n  type: PythonSymbolType;\n}\n"
  },
  {
    "path": "src/languagePlugins/python/importExtractor/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { PythonImportExtractor } from \"./index.ts\";\nimport {\n  FROM_IMPORT_STATEMENT_TYPE,\n  type ImportItem,\n  NORMAL_IMPORT_STATEMENT_TYPE,\n} from \"./types.ts\";\nimport { pythonParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\ndescribe(\"Python Import Extractor\", () => {\n  const parser = pythonParser;\n\n  test(\"should extract multiple simple normal import statements\", () => {\n    const importExtractor = new PythonImportExtractor(\n      parser,\n      new Map([\n        [\n          \"file.py\",\n          {\n            path: \"file.py\",\n            rootNode: parser.parse(`\n            import os\n            import sys\n            `).rootNode,\n          },\n        ],\n      ]),\n    );\n\n    const importStatements = importExtractor.getImportStatements(\"file.py\");\n    expect(importStatements).toHaveLength(2);\n\n    const firstImportStatement = importStatements[0];\n    expect(firstImportStatement.type).toEqual(NORMAL_IMPORT_STATEMENT_TYPE);\n    expect(firstImportStatement.node.text).toEqual(\"import os\");\n    expect(firstImportStatement.members).toHaveLength(1);\n\n    const firstImportStatementMember = firstImportStatement.members[0];\n    expect(firstImportStatementMember.isWildcardImport).toEqual(false);\n    expect(firstImportStatementMember.items).toBeUndefined();\n    expect(firstImportStatementMember.node.text).toEqual(\"os\");\n    expect(firstImportStatementMember.identifierNode.text).toEqual(\"os\");\n    expect(firstImportStatementMember.aliasNode).toBeUndefined();\n\n    const secondImportStatement = importStatements[1];\n    expect(secondImportStatement.type).toEqual(NORMAL_IMPORT_STATEMENT_TYPE);\n    expect(secondImportStatement.node.text).toEqual(\"import sys\");\n    expect(secondImportStatement.members).toHaveLength(1);\n\n    const secondImportStatementMember = secondImportStatement.members[0];\n    expect(secondImportStatementMember.isWildcardImport).toEqual(false);\n    expect(secondImportStatementMember.items).toBeUndefined();\n    expect(secondImportStatementMember.node.text).toEqual(\"sys\");\n    expect(secondImportStatementMember.identifierNode.text).toEqual(\"sys\");\n    expect(secondImportStatementMember.aliasNode).toBeUndefined();\n  });\n\n  test(\"should extract aliased normal import statements\", () => {\n    const importExtractor = new PythonImportExtractor(\n      parser,\n      new Map([\n        [\n          \"file.py\",\n          {\n            path: \"file.py\",\n            rootNode: parser.parse(\"import os as operating_system\").rootNode,\n          },\n        ],\n      ]),\n    );\n\n    const importStatements = importExtractor.getImportStatements(\"file.py\");\n    expect(importStatements).toHaveLength(1);\n\n    const importStatement = importStatements[0];\n    expect(importStatement.type).toEqual(NORMAL_IMPORT_STATEMENT_TYPE);\n    expect(importStatement.members).toHaveLength(1);\n    const importStatementMember = importStatement.members[0];\n    expect(importStatementMember.isWildcardImport).toEqual(false);\n    expect(importStatementMember.items).toBeUndefined();\n    expect(importStatementMember.node.text).toEqual(\"os as operating_system\");\n    expect(importStatementMember.identifierNode.text).toEqual(\"os\");\n    expect(importStatementMember.aliasNode?.text).toEqual(\"operating_system\");\n  });\n\n  test(\"should extract multilple from import statements\", () => {\n    const importExtractor = new PythonImportExtractor(\n      parser,\n      new Map([\n        [\n          \"file.py\",\n          {\n            path: \"file.py\",\n            rootNode: parser.parse(`\n            from os import path, environ\n            from sys import argv\n            `).rootNode,\n          },\n        ],\n      ]),\n    );\n\n    const importStatements = importExtractor.getImportStatements(\"file.py\");\n    expect(importStatements).toHaveLength(2);\n\n    const firstImportStatement = importStatements[0];\n    expect(firstImportStatement.type).toEqual(FROM_IMPORT_STATEMENT_TYPE);\n    expect(firstImportStatement.node.text).toEqual(\n      \"from os import path, environ\",\n    );\n    expect(firstImportStatement.members).toHaveLength(1);\n\n    const firstImportStatementMember = firstImportStatement.members[0];\n    expect(firstImportStatementMember.isWildcardImport).toEqual(false);\n    expect(firstImportStatementMember.node.text).toEqual(\"os\");\n    expect(firstImportStatementMember.identifierNode.text).toEqual(\"os\");\n    expect(firstImportStatementMember.aliasNode).toBeUndefined();\n    expect(firstImportStatementMember.items).toHaveLength(2);\n\n    const firstImportStatementMemberFirstSymbol = (\n      firstImportStatementMember.items as ImportItem[]\n    )[0];\n    expect(firstImportStatementMemberFirstSymbol.node.text).toEqual(\"path\");\n    expect(firstImportStatementMemberFirstSymbol.identifierNode.text).toEqual(\n      \"path\",\n    );\n    expect(firstImportStatementMemberFirstSymbol.aliasNode).toBeUndefined();\n\n    const firstImportStatementMemberSecondSymbol = (\n      firstImportStatementMember.items as ImportItem[]\n    )[1];\n    expect(firstImportStatementMemberSecondSymbol.node.text).toEqual(\"environ\");\n    expect(firstImportStatementMemberSecondSymbol.identifierNode.text).toEqual(\n      \"environ\",\n    );\n    expect(firstImportStatementMemberSecondSymbol.aliasNode).toBeUndefined();\n\n    const secondImportStatement = importStatements[1];\n    expect(secondImportStatement.type).toEqual(FROM_IMPORT_STATEMENT_TYPE);\n    expect(secondImportStatement.node.text).toEqual(\"from sys import argv\");\n    expect(secondImportStatement.members).toHaveLength(1);\n\n    const secondImportStatementMember = secondImportStatement.members[0];\n    expect(secondImportStatementMember.isWildcardImport).toEqual(false);\n    expect(secondImportStatementMember.node.text).toEqual(\"sys\");\n    expect(secondImportStatementMember.identifierNode.text).toEqual(\"sys\");\n    expect(secondImportStatementMember.aliasNode).toBeUndefined();\n    expect(secondImportStatementMember.items).toHaveLength(1);\n\n    const secondImportStatementMemberSymbol = (\n      secondImportStatementMember.items as ImportItem[]\n    )[0];\n    expect(secondImportStatementMemberSymbol.node.text).toEqual(\"argv\");\n    expect(secondImportStatementMemberSymbol.node.text).toEqual(\"argv\");\n    expect(secondImportStatementMemberSymbol.aliasNode).toBeUndefined();\n  });\n\n  test(\"should extract from import statements with aliases\", () => {\n    const importExtractor = new PythonImportExtractor(\n      parser,\n      new Map([\n        [\n          \"file.py\",\n          {\n            path: \"file.py\",\n            rootNode: parser.parse(\"from os import path as os_path\").rootNode,\n          },\n        ],\n      ]),\n    );\n\n    const importStatements = importExtractor.getImportStatements(\"file.py\");\n\n    expect(importStatements).toHaveLength(1);\n\n    const importStatement = importStatements[0];\n    expect(importStatement.type).toEqual(FROM_IMPORT_STATEMENT_TYPE);\n    expect(importStatement.node.text).toEqual(\n      \"from os import path as os_path\",\n    );\n    expect(importStatement.members).toHaveLength(1);\n\n    const importStatementMember = importStatement.members[0];\n    expect(importStatementMember.isWildcardImport).toEqual(false);\n    expect(importStatementMember.node.text).toEqual(\"os\");\n    expect(importStatementMember.identifierNode.text).toEqual(\"os\");\n    expect(importStatementMember.aliasNode).toBeUndefined();\n    expect(importStatementMember.items).toHaveLength(1);\n\n    const importStatementMemberSymbol = (\n      importStatementMember.items as ImportItem[]\n    )[0];\n    expect(importStatementMemberSymbol.node.text).toEqual(\"path as os_path\");\n    expect(importStatementMemberSymbol.identifierNode.text).toEqual(\"path\");\n    expect(importStatementMemberSymbol.aliasNode?.text).toEqual(\"os_path\");\n  });\n\n  test(\"should extract from import statements with wildcard imports\", () => {\n    const importExtractor = new PythonImportExtractor(\n      parser,\n      new Map([\n        [\n          \"file.py\",\n          {\n            path: \"file.py\",\n            rootNode: parser.parse(\"from os import *\").rootNode,\n          },\n        ],\n      ]),\n    );\n\n    const importStatements = importExtractor.getImportStatements(\"file.py\");\n\n    expect(importStatements).toHaveLength(1);\n\n    const importStatement = importStatements[0];\n    expect(importStatement.type).toEqual(FROM_IMPORT_STATEMENT_TYPE);\n    expect(importStatement.node.text).toEqual(\"from os import *\");\n    expect(importStatement.members).toHaveLength(1);\n\n    const importStatementMember = importStatement.members[0];\n    expect(importStatementMember.isWildcardImport).toEqual(true);\n    expect(importStatementMember.items).toBeUndefined();\n    expect(importStatementMember.node.text).toEqual(\"os\");\n    expect(importStatementMember.identifierNode.text).toEqual(\"os\");\n    expect(importStatementMember.aliasNode).toBeUndefined();\n  });\n\n  test(\"should handle mix of normal and from import statements\", () => {\n    const importExtractor = new PythonImportExtractor(\n      parser,\n      new Map([\n        [\n          \"file.py\",\n          {\n            path: \"file.py\",\n            rootNode: parser.parse(`\n            import os\n            from sys import argv\n            `).rootNode,\n          },\n        ],\n      ]),\n    );\n\n    const importStatements = importExtractor.getImportStatements(\"file.py\");\n    expect(importStatements).toHaveLength(2);\n\n    const firstImportStatement = importStatements[0];\n    expect(firstImportStatement.type).toEqual(NORMAL_IMPORT_STATEMENT_TYPE);\n    expect(firstImportStatement.node.text).toEqual(\"import os\");\n    expect(firstImportStatement.members).toHaveLength(1);\n\n    const secondImportStatement = importStatements[1];\n    expect(secondImportStatement.type).toEqual(FROM_IMPORT_STATEMENT_TYPE);\n    expect(secondImportStatement.node.text).toEqual(\"from sys import argv\");\n    expect(secondImportStatement.members).toHaveLength(1);\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/importExtractor/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport {\n  FROM_IMPORT_STATEMENT_TYPE,\n  type ImportMember,\n  type ImportStatement,\n  NORMAL_IMPORT_STATEMENT_TYPE,\n} from \"./types.ts\";\n\n/**\n * PythonImportExtractor parses and extracts Python import statements (normal and from-import).\n *\n * It specifically does the following:\n *  - Parses Python files using Tree-sitter.\n *  - Identifies standard import statements (`import module`) and extracts their identifiers and aliases.\n *  - Identifies from-import statements (`from module import X`) and extracts module names, imported items, aliases, and wildcard imports.\n *  - Caches results to optimize performance on subsequent calls.\n *\n * Dependencies (assumed provided externally):\n *  - Tree-sitter parser for AST parsing.\n *  - A map of files with their paths and parsed AST root nodes.\n *\n * Usage example:\n * ```typescript\n * // Get import statements for a Python file\n * const importStatements = importExtractor.getImportStatements(filePath);\n * // Process them to understand imports and dependencies\n * for (const stmt of importStatements) {\n *   if (stmt.type === NORMAL_IMPORT_STATEMENT_TYPE) {\n *     // Handle regular imports\n *   } else {\n *     // Handle from-imports\n *   }\n * }\n * ```\n */\nexport class PythonImportExtractor {\n  /**\n   * Map of file paths to their parsed AST root nodes\n   * Used to access file content for analysis\n   */\n  private files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n\n  /**\n   * Tree-sitter parser for Python code\n   * Used to parse import statements\n   */\n  private parser: Parser;\n\n  /**\n   * Tree-sitter query to find import statements in Python code\n   * This identifies both normal imports and from-imports\n   */\n  private importQuery: Parser.Query;\n\n  /**\n   * Cache for import statements\n   * Maps file paths to their parsed import statements\n   * Used to avoid reprocessing files already analyzed\n   */\n  private cache = new Map<string, ImportStatement[]>();\n\n  /**\n   * Constructs a new PythonImportExtractor.\n   *\n   * @param parser - A Tree-sitter parser instance for Python.\n   * @param files - A map of file paths to objects containing their AST root nodes.\n   */\n  constructor(\n    parser: Parser,\n    files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n  ) {\n    this.parser = parser;\n    this.files = files;\n\n    // Single query to capture all import statements\n    this.importQuery = new Parser.Query(\n      this.parser.getLanguage(),\n      `[\n        (import_statement) @import\n        (import_from_statement) @import\n      ]`,\n    );\n  }\n\n  /**\n   * Extracts all import statements (both normal and from-import) from the specified file.\n   * Uses caching to optimize repeated calls.\n   *\n   * @param filePath - Path to the Python file being analyzed.\n   * @returns An array of resolved ImportStatement objects for the given file.\n   */\n  public getImportStatements(filePath: string): ImportStatement[] {\n    const cachedValue = this.cache.get(filePath);\n    if (cachedValue) {\n      return cachedValue;\n    }\n\n    const file = this.files.get(filePath);\n    if (!file) {\n      console.error(`File ${filePath} not found in files map`);\n      return [];\n    }\n\n    const importStatements: ImportStatement[] = [];\n\n    // Process all matched import statements\n    this.importQuery.captures(file.rootNode).forEach(({ node }) => {\n      if (node.type === \"import_statement\") {\n        importStatements.push(this.processNormalImport(node));\n      } else if (node.type === \"import_from_statement\") {\n        importStatements.push(this.processFromImport(node));\n      }\n    });\n\n    this.cache.set(filePath, importStatements);\n    return importStatements;\n  }\n\n  /**\n   * Processes a normal import statement (`import module`) by manually extracting\n   * its components from the AST.\n   *\n   * @param node - The import_statement syntax node\n   * @returns A resolved ImportStatement object\n   */\n  private processNormalImport(node: Parser.SyntaxNode): ImportStatement {\n    const importStatement: ImportStatement = {\n      node,\n      type: NORMAL_IMPORT_STATEMENT_TYPE,\n      sourceNode: undefined,\n      members: [],\n    };\n\n    // Retrieve imported modules and optional aliases\n    const memberNodes = node.childrenForFieldName(\"name\");\n    memberNodes.forEach((memberNode) => {\n      let identifierNode: Parser.SyntaxNode;\n      let aliasNode: Parser.SyntaxNode | undefined;\n\n      if (memberNode.type === \"aliased_import\") {\n        const nameNode = memberNode.childForFieldName(\"name\");\n        if (!nameNode) {\n          throw new Error(\"Malformed aliased import: missing name\");\n        }\n        identifierNode = nameNode;\n        aliasNode = memberNode.childForFieldName(\"alias\") || undefined;\n      } else {\n        identifierNode = memberNode;\n        aliasNode = undefined;\n      }\n\n      importStatement.members.push({\n        node: memberNode,\n        identifierNode,\n        aliasNode,\n        isWildcardImport: false,\n        items: undefined,\n      });\n    });\n\n    return importStatement;\n  }\n\n  /**\n   * Processes a from-import statement (`from module import X`) by manually extracting\n   * its components from the AST.\n   *\n   * @param node - The import_from_statement syntax node\n   * @returns A resolved ImportStatement object\n   */\n  private processFromImport(node: Parser.SyntaxNode): ImportStatement {\n    const sourceNode = node.childForFieldName(\"module_name\");\n    if (!sourceNode) {\n      throw new Error(\"Malformed from-import: missing module name\");\n    }\n\n    const importStatement: ImportStatement = {\n      node,\n      type: FROM_IMPORT_STATEMENT_TYPE,\n      sourceNode,\n      members: [],\n    };\n\n    const importMember: ImportMember = {\n      node: sourceNode,\n      identifierNode: sourceNode,\n      aliasNode: undefined,\n      isWildcardImport: false,\n      items: undefined,\n    };\n\n    const wildcardNode = node.descendantsOfType(\"wildcard_import\")[0];\n\n    if (wildcardNode) {\n      importMember.isWildcardImport = true;\n    } else {\n      const itemNodes = node.childrenForFieldName(\"name\");\n      importMember.items = itemNodes.map((itemNode) => {\n        let identifierNode: Parser.SyntaxNode;\n        let aliasNode: Parser.SyntaxNode | undefined;\n\n        if (itemNode.type === \"aliased_import\") {\n          const nameNode = itemNode.childForFieldName(\"name\");\n          if (!nameNode) {\n            throw new Error(\"Malformed aliased import item: missing name\");\n          }\n          identifierNode = nameNode;\n          aliasNode = itemNode.childForFieldName(\"alias\") || undefined;\n        } else {\n          identifierNode = itemNode;\n          aliasNode = undefined;\n        }\n\n        return {\n          node: itemNode,\n          identifierNode,\n          aliasNode,\n        };\n      });\n    }\n\n    importStatement.members.push(importMember);\n    return importStatement;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/importExtractor/types.ts",
    "content": "import type Parser from \"tree-sitter\";\n\n/**\n * Represents an explicitly imported item within an import statement.\n * This item can be a class, function, module, or any valid Python identifier.\n *\n * Example:\n * - In \"from os import path as p\", the item would be \"path\" with alias \"p\"\n * - In \"from os import path\", the item would be \"path\" without an alias\n */\nexport interface ImportItem {\n  /** The syntax node corresponding to the entire imported item (including alias, if present). */\n  node: Parser.SyntaxNode;\n\n  /** The syntax node for the item's original identifier (e.g., \"path\" in \"from os import path as p\"). */\n  identifierNode: Parser.SyntaxNode;\n\n  /** The syntax node for the item's alias, if one is provided (e.g., \"p\" in \"from os import path as p\"). */\n  aliasNode: Parser.SyntaxNode | undefined;\n}\n\n/**\n * Represents a member imported from a module. A member corresponds to either:\n * - A module imported entirely (`import module` or `import module as alias`).\n * - A module or symbol imported from another module (`from module import X` or `from module import *`).\n *\n * This is the core structure that represents what's being imported and how.\n */\nexport interface ImportMember {\n  /** The syntax node corresponding to the imported member (module or item). */\n  node: Parser.SyntaxNode;\n\n  /** The syntax node corresponding to the member's identifier. */\n  identifierNode: Parser.SyntaxNode;\n\n  /** The syntax node for the member's alias, if provided (e.g., \"alias\" in \"import module as alias\"). */\n  aliasNode: Parser.SyntaxNode | undefined;\n\n  /** Indicates if this is a wildcard import (`from module import *`). */\n  isWildcardImport: boolean;\n\n  /**\n   * The list of explicitly imported items from this member.\n   * - Undefined if the import is a wildcard import (`from module import *`).\n   * - Undefined if the import is a standard import statement (`import module`).\n   * - Contains ImportItem objects for each imported symbol in a from-import statement.\n   */\n  items?: ImportItem[];\n}\n\n/**\n * Constants representing the two types of Python import statements\n */\nexport const NORMAL_IMPORT_STATEMENT_TYPE = \"normal\";\nexport const FROM_IMPORT_STATEMENT_TYPE = \"from\";\n\n/**\n * Defines the possible types of Python import statements:\n * - \"normal\": Standard imports like \"import os\" or \"import os as operating_system\"\n * - \"from\": From-imports like \"from os import path\" or \"from os import *\"\n */\nexport type PythonImportStatementType =\n  | typeof NORMAL_IMPORT_STATEMENT_TYPE\n  | typeof FROM_IMPORT_STATEMENT_TYPE;\n\n/**\n * Represents a fully resolved import statement from a Python source file.\n * It abstracts both normal (`import module`) and from-import (`from module import X`) statements.\n *\n * This structure contains all the necessary information to analyze import relationships\n * and symbol dependencies between Python modules.\n */\nexport interface ImportStatement {\n  /** The syntax node representing the entire import statement. */\n  node: Parser.SyntaxNode;\n\n  /** The type of import statement: either \"normal\" or \"from\". */\n  type: PythonImportStatementType;\n\n  /**\n   * The syntax node representing the source module in a from-import statement (`from module import ...`).\n   * - Undefined for standard import statements (`import module`).\n   */\n  sourceNode: Parser.SyntaxNode | undefined;\n\n  /** The list of imported members or modules. */\n  members: ImportMember[];\n}\n"
  },
  {
    "path": "src/languagePlugins/python/itemResolver/index.test.ts",
    "content": "import { beforeEach, describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport type Parser from \"tree-sitter\";\nimport { pythonParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport { PythonImportExtractor } from \"../importExtractor/index.ts\";\nimport { PythonModuleResolver } from \"../moduleResolver/index.ts\";\nimport { PythonItemResolver } from \"./index.ts\";\nimport { PYTHON_INTERNAL_MODULE_TYPE } from \"./types.ts\";\n\n/**\n * These tests verify the Python symbol resolution system, which handles:\n * 1. Basic symbol resolution through direct and nested imports\n * 2. Wildcard import behavior (from module import *)\n * 3. Alias handling (import x as y)\n * 4. Python's __all__ directive for controlling exported symbols\n * 5. Handling of private symbols (with _ prefix)\n * 6. Multiple import styles and their precedence\n * 7. Symbol visibility and shadowing rules\n * 8. Circular imports\n */\ndescribe(\"PythonItemResolver\", () => {\n  let resolver: PythonItemResolver;\n  let moduleResolver: PythonModuleResolver;\n  let exportExtractor: PythonExportExtractor;\n  let importExtractor: PythonImportExtractor;\n  let files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n\n  beforeEach(() => {\n    // Setup basic test modules\n    files = new Map([\n      // TEST CASE 1: Shadowing in nested imports\n      // Module with original definition\n      [\n        \"shadow_nest_1.py\",\n        {\n          path: \"shadow_nest_1.py\",\n          rootNode: pythonParser.parse(`\n            def shadow_func(): return \"original\"\n          `).rootNode,\n        },\n      ],\n      // Module that imports and overrides with local definition\n      [\n        \"shadow_nest_2.py\",\n        {\n          path: \"shadow_nest_2.py\",\n          rootNode: pythonParser.parse(`\n            from shadow_nest_1 import shadow_func\n            def shadow_func(): return \"override\"  # This local definition should shadow the import\n          `).rootNode,\n        },\n      ],\n      // Module that imports from the module with shadowing\n      [\n        \"shadow_nest_user.py\",\n        {\n          path: \"shadow_nest_user.py\",\n          rootNode: pythonParser.parse(`\n            from shadow_nest_2 import shadow_func\n          `).rootNode,\n        },\n      ],\n\n      // TEST CASE 2: Importing submodules from packages with qualified names\n      [\n        \"deep_package/__init__.py\",\n        {\n          path: \"deep_package/__init__.py\",\n          rootNode: pythonParser.parse(`\n            # Empty init file\n          `).rootNode,\n        },\n      ],\n      [\n        \"deep_package/submod.py\",\n        {\n          path: \"deep_package/submod.py\",\n          rootNode: pythonParser.parse(`\n            def submod_func(): pass\n          `).rootNode,\n        },\n      ],\n      [\n        \"import_submodule.py\",\n        {\n          path: \"import_submodule.py\",\n          rootNode: pythonParser.parse(`\n            # Import the submodule directly\n            import deep_package.submod\n            # Use the submodule qualified name\n            def use_func():\n                return deep_package.submod.submod_func()\n          `).rootNode,\n        },\n      ],\n\n      // TEST CASE 3: Deep package hierarchies with multiple layers of __all__ inheritance\n      [\n        \"nested_all_pkg/__init__.py\",\n        {\n          path: \"nested_all_pkg/__init__.py\",\n          rootNode: pythonParser.parse(`\n            from .level1 import *\n            from .direct import direct_func\n\n            __all__ = ['direct_func', 'level1_func', 'level2_deep_func']\n          `).rootNode,\n        },\n      ],\n      [\n        \"nested_all_pkg/direct.py\",\n        {\n          path: \"nested_all_pkg/direct.py\",\n          rootNode: pythonParser.parse(`\n            def direct_func(): pass\n            def not_exported(): pass\n          `).rootNode,\n        },\n      ],\n      [\n        \"nested_all_pkg/level1.py\",\n        {\n          path: \"nested_all_pkg/level1.py\",\n          rootNode: pythonParser.parse(`\n            from .level2 import *\n\n            def level1_func(): pass\n            def level1_hidden(): pass\n\n            __all__ = ['level1_func', 'level2_deep_func']\n          `).rootNode,\n        },\n      ],\n      [\n        \"nested_all_pkg/level2.py\",\n        {\n          path: \"nested_all_pkg/level2.py\",\n          rootNode: pythonParser.parse(`\n            def level2_func(): pass\n            def level2_deep_func(): pass\n            def level2_hidden(): pass\n\n            __all__ = ['level2_func', 'level2_deep_func']\n          `).rootNode,\n        },\n      ],\n      [\n        \"use_nested_all.py\",\n        {\n          path: \"use_nested_all.py\",\n          rootNode: pythonParser.parse(`\n            from nested_all_pkg import *\n          `).rootNode,\n        },\n      ],\n\n      // Basic modules with symbols\n      [\n        \"moduleA.py\",\n        {\n          path: \"moduleA.py\",\n          rootNode: pythonParser.parse(`\n            def foo(): pass\n            def bar(): pass\n            CONSTANT = 42\n          `).rootNode,\n        },\n      ],\n      [\n        \"moduleB.py\",\n        {\n          path: \"moduleB.py\",\n          rootNode: pythonParser.parse(`from moduleA import foo as f, bar`)\n            .rootNode,\n        },\n      ],\n      // Add test module for testing conflicting imports\n      [\n        \"override_imports.py\",\n        {\n          path: \"override_imports.py\",\n          rootNode: pythonParser.parse(`\n            from moduleA import foo  # foo from moduleA\n            from multi_wildcard1 import common as foo  # This should override the previous import\n          `).rootNode,\n        },\n      ],\n      // Modules for testing different import styles\n      [\n        \"moduleC.py\",\n        {\n          path: \"moduleC.py\",\n          rootNode: pythonParser.parse(`from moduleB import *`).rootNode,\n        },\n      ],\n      [\n        \"moduleD.py\",\n        {\n          path: \"moduleD.py\",\n          rootNode: pythonParser.parse(`\n            from moduleB import f as fooAlias\n            from moduleA import bar as barAlias\n          `).rootNode,\n        },\n      ],\n      [\n        \"moduleE.py\",\n        {\n          path: \"moduleE.py\",\n          rootNode: pythonParser.parse(`import moduleA`).rootNode,\n        },\n      ],\n      [\n        \"moduleF.py\",\n        {\n          path: \"moduleF.py\",\n          rootNode: pythonParser.parse(`\n            from moduleA import foo\n            def local_func(): pass\n          `).rootNode,\n        },\n      ],\n      [\n        \"moduleG.py\",\n        {\n          path: \"moduleG.py\",\n          rootNode: pythonParser.parse(`\n            import moduleF\n            from moduleF import local_func as alias_local\n          `).rootNode,\n        },\n      ],\n      // Modules for import order and precedence testing\n      [\n        \"precedence.py\",\n        {\n          path: \"precedence.py\",\n          rootNode: pythonParser.parse(`\n            # Define a local symbol\n            def duplicate():\n                return \"local\"\n\n            # Import a symbol with same name - this should be shadowed\n            from moduleA import CONSTANT as duplicate\n\n            # Wildcard import - should not override existing names\n            from moduleB import *\n          `).rootNode,\n        },\n      ],\n      [\n        \"wildcard_precedence.py\",\n        {\n          path: \"wildcard_precedence.py\",\n          rootNode: pythonParser.parse(`\n            # First wildcard import\n            from moduleA import *\n\n            # Second wildcard import\n            from moduleB import *\n\n            # Third explicit import - should override any previous wildcards\n            from moduleA import foo as bar\n          `).rootNode,\n        },\n      ],\n      // Circular import test\n      [\n        \"circular1.py\",\n        {\n          path: \"circular1.py\",\n          rootNode: pythonParser.parse(`\n            def circular1_func(): pass\n            from circular2 import circular2_func\n          `).rootNode,\n        },\n      ],\n      [\n        \"circular2.py\",\n        {\n          path: \"circular2.py\",\n          rootNode: pythonParser.parse(`\n            def circular2_func(): pass\n            from circular1 import circular1_func\n          `).rootNode,\n        },\n      ],\n      // Nested package test\n      [\n        \"package/__init__.py\",\n        {\n          path: \"package/__init__.py\",\n          rootNode: pythonParser.parse(`\n            from .submodule import sub_func\n            from .intermediate import *\n          `).rootNode,\n        },\n      ],\n      [\n        \"package/intermediate.py\",\n        {\n          path: \"package/intermediate.py\",\n          rootNode: pythonParser.parse(`\n            def intermediate_func(): pass\n            from .submodule import *\n          `).rootNode,\n        },\n      ],\n      [\n        \"package/submodule.py\",\n        {\n          path: \"package/submodule.py\",\n          rootNode: pythonParser.parse(`\n            def sub_func(): pass\n            def deep_nested_func(): pass\n          `).rootNode,\n        },\n      ],\n      [\n        \"usePackage.py\",\n        {\n          path: \"usePackage.py\",\n          rootNode: pythonParser.parse(`\n            from package import *\n\n            # This should be available from the wildcard import\n            deep_nested_func()\n          `).rootNode,\n        },\n      ],\n      // __all__ directive test modules\n      [\n        \"moduleWithAll.py\",\n        {\n          path: \"moduleWithAll.py\",\n          rootNode: pythonParser.parse(`\n            def public_func(): pass\n            def another_func(): pass\n            def _private_func(): pass\n\n            __all__ = ['public_func']\n          `).rootNode,\n        },\n      ],\n      [\n        \"importAllModule.py\",\n        {\n          path: \"importAllModule.py\",\n          rootNode: pythonParser.parse(`\n            from moduleWithAll import *\n          `).rootNode,\n        },\n      ],\n      [\n        \"importSpecificFromAll.py\",\n        {\n          path: \"importSpecificFromAll.py\",\n          rootNode: pythonParser.parse(`\n            from moduleWithAll import another_func, _private_func\n          `).rootNode,\n        },\n      ],\n      // Private symbol handling\n      [\n        \"privateSymbols.py\",\n        {\n          path: \"privateSymbols.py\",\n          rootNode: pythonParser.parse(`\n            def public_function(): pass\n            def _private_function(): pass\n            _PRIVATE_CONSTANT = 123\n            PUBLIC_CONSTANT = 456\n          `).rootNode,\n        },\n      ],\n      [\n        \"importPrivate.py\",\n        {\n          path: \"importPrivate.py\",\n          rootNode: pythonParser.parse(`\n            from privateSymbols import *\n\n            # Should only import public symbols through wildcard\n          `).rootNode,\n        },\n      ],\n      [\n        \"explicitPrivate.py\",\n        {\n          path: \"explicitPrivate.py\",\n          rootNode: pythonParser.parse(`\n            from privateSymbols import _private_function, PUBLIC_CONSTANT\n\n            # Explicit imports can include private symbols\n          `).rootNode,\n        },\n      ],\n      // Complex __all__ overriding private convention\n      [\n        \"allOverride.py\",\n        {\n          path: \"allOverride.py\",\n          rootNode: pythonParser.parse(`\n            def regular_func(): pass\n            def _private_func(): pass\n\n            # Explicitly export a private symbol through __all__\n            __all__ = ['regular_func', '_private_func']\n          `).rootNode,\n        },\n      ],\n      [\n        \"importAllOverride.py\",\n        {\n          path: \"importAllOverride.py\",\n          rootNode: pythonParser.parse(`\n            from allOverride import *\n\n            # Should import both regular_func and _private_func because they're in __all__\n          `).rootNode,\n        },\n      ],\n      // Namespace packages\n      [\n        \"namespace_pkg/module1.py\",\n        {\n          path: \"namespace_pkg/module1.py\",\n          rootNode: pythonParser.parse(`\n            def ns_func1(): pass\n          `).rootNode,\n        },\n      ],\n      [\n        \"namespace_pkg/module2.py\",\n        {\n          path: \"namespace_pkg/module2.py\",\n          rootNode: pythonParser.parse(`\n            def ns_func2(): pass\n            from .module1 import ns_func1\n          `).rootNode,\n        },\n      ],\n      // Absolute vs relative imports\n      [\n        \"package2/__init__.py\",\n        {\n          path: \"package2/__init__.py\",\n          rootNode: pythonParser.parse(`\n            from .relmod import rel_func\n            absolute_var = \"from init\"\n          `).rootNode,\n        },\n      ],\n      [\n        \"package2/relmod.py\",\n        {\n          path: \"package2/relmod.py\",\n          rootNode: pythonParser.parse(`\n            def rel_func(): pass\n            from . import absolute_var\n          `).rootNode,\n        },\n      ],\n      [\n        \"package2/absmod.py\",\n        {\n          path: \"package2/absmod.py\",\n          rootNode: pythonParser.parse(`\n            def abs_func(): pass\n            from package2 import absolute_var\n          `).rootNode,\n        },\n      ],\n      // Multiple wildcard imports\n      [\n        \"multi_wildcard1.py\",\n        {\n          path: \"multi_wildcard1.py\",\n          rootNode: pythonParser.parse(`\n            def unique1(): pass\n            def common(): return \"from1\"\n          `).rootNode,\n        },\n      ],\n      [\n        \"multi_wildcard2.py\",\n        {\n          path: \"multi_wildcard2.py\",\n          rootNode: pythonParser.parse(`\n            def unique2(): pass\n            def common(): return \"from2\"\n          `).rootNode,\n        },\n      ],\n      [\n        \"multi_wildcards.py\",\n        {\n          path: \"multi_wildcards.py\",\n          rootNode: pythonParser.parse(`\n            from multi_wildcard1 import *\n            from multi_wildcard2 import *\n\n            # Should have unique1, unique2, and common (from multi_wildcard1 since it comes first)\n          `).rootNode,\n        },\n      ],\n    ]);\n\n    // Initialize resolvers\n    exportExtractor = new PythonExportExtractor(pythonParser, files);\n    importExtractor = new PythonImportExtractor(pythonParser, files);\n    moduleResolver = new PythonModuleResolver(new Set(files.keys()), \"3.13\");\n    resolver = new PythonItemResolver(\n      exportExtractor,\n      importExtractor,\n      moduleResolver,\n    );\n  });\n\n  // ========================================================\n  // SECTION: Advanced Import Scenarios and Shadowing\n  // ========================================================\n  describe(\"Advanced Import Scenarios and Shadowing\", () => {\n    test(\"handles shadowing in nested imports\", () => {\n      const shadowNest2 = moduleResolver.getModuleFromFilePath(\n        \"shadow_nest_2.py\",\n      );\n      expect(shadowNest2).toBeDefined();\n\n      // When importing shadow_func, should get the local definition from shadow_nest_2,\n      // not the one imported from shadow_nest_1\n      const directResult = resolver.resolveItem(shadowNest2, \"shadow_func\");\n      expect(directResult).toBeDefined();\n      expect(directResult?.module?.path).toBe(\"shadow_nest_2.py\"); // Local definition, not from shadow_nest_1\n\n      // Test that another module importing from shadow_nest_2 gets the shadowed version\n      const shadowUser = moduleResolver.getModuleFromFilePath(\n        \"shadow_nest_user.py\",\n      );\n      expect(shadowUser).toBeDefined();\n\n      const userResult = resolver.resolveItem(shadowUser, \"shadow_func\");\n      expect(userResult).toBeDefined();\n      expect(userResult?.module?.path).toBe(\"shadow_nest_2.py\"); // Should get the shadowed version\n    });\n\n    test(\"handles deep package hierarchies with multiple layers of __all__ inheritance\", () => {\n      const useNestedAll = moduleResolver.getModuleFromFilePath(\n        \"use_nested_all.py\",\n      );\n      expect(useNestedAll).toBeDefined();\n\n      // Should be able to resolve symbols listed in the top-level __all__\n      const directResult = resolver.resolveItem(useNestedAll, \"direct_func\");\n      expect(directResult).toBeDefined();\n      expect(directResult?.module?.path).toBe(\"nested_all_pkg/direct.py\");\n\n      const level1Result = resolver.resolveItem(useNestedAll, \"level1_func\");\n      expect(level1Result).toBeDefined();\n      expect(level1Result?.module?.path).toBe(\"nested_all_pkg/level1.py\");\n\n      const deepResult = resolver.resolveItem(useNestedAll, \"level2_deep_func\");\n      expect(deepResult).toBeDefined();\n      expect(deepResult?.module?.path).toBe(\"nested_all_pkg/level2.py\");\n\n      // Should NOT import level2_func even though it's in level2's __all__\n      // but not in the top-level package __all__\n      const level2Result = resolver.resolveItem(useNestedAll, \"level2_func\");\n      expect(level2Result).toBeUndefined();\n\n      // Should NOT import any of the hidden functions\n      const level1HiddenResult = resolver.resolveItem(\n        useNestedAll,\n        \"level1_hidden\",\n      );\n      expect(level1HiddenResult).toBeUndefined();\n\n      const level2HiddenResult = resolver.resolveItem(\n        useNestedAll,\n        \"level2_hidden\",\n      );\n      expect(level2HiddenResult).toBeUndefined();\n\n      const notExportedResult = resolver.resolveItem(\n        useNestedAll,\n        \"not_exported\",\n      );\n      expect(notExportedResult).toBeUndefined();\n    });\n  });\n\n  // ========================================================\n  // SECTION: Basic Item Resolution\n  // ========================================================\n  describe(\"Basic Item Resolution\", () => {\n    test(\"resolves symbols defined directly in a module\", () => {\n      const moduleA = moduleResolver.getModuleFromFilePath(\"moduleA.py\");\n      expect(moduleA).toBeDefined();\n\n      const result = resolver.resolveItem(moduleA, \"foo\");\n      expect(result).toBeDefined();\n      expect(result?.type).toBe(PYTHON_INTERNAL_MODULE_TYPE);\n      expect(result?.module?.path).toBe(\"moduleA.py\");\n      expect(result?.symbol?.id).toBe(\"foo\");\n    });\n\n    test(\"resolves symbols via explicit imports\", () => {\n      const moduleB = moduleResolver.getModuleFromFilePath(\"moduleB.py\");\n      expect(moduleB).toBeDefined();\n\n      // Test alias resolution\n      const aliasResult = resolver.resolveItem(moduleB, \"f\");\n      expect(aliasResult).toBeDefined();\n      expect(aliasResult?.type).toBe(PYTHON_INTERNAL_MODULE_TYPE);\n      expect(aliasResult?.module?.path).toBe(\"moduleA.py\");\n      expect(aliasResult?.symbol?.id).toBe(\"foo\");\n\n      // Test direct import resolution\n      const directResult = resolver.resolveItem(moduleB, \"bar\");\n      expect(directResult).toBeDefined();\n      expect(directResult?.type).toBe(PYTHON_INTERNAL_MODULE_TYPE);\n      expect(directResult?.module?.path).toBe(\"moduleA.py\");\n      expect(directResult?.symbol?.id).toBe(\"bar\");\n    });\n\n    test(\"resolves symbols via wildcard imports\", () => {\n      const moduleC = moduleResolver.getModuleFromFilePath(\"moduleC.py\");\n      expect(moduleC).toBeDefined();\n\n      // Test resolving a symbol that was imported via wildcard from moduleB\n      const wildcardResult = resolver.resolveItem(moduleC, \"f\");\n      expect(wildcardResult).toBeDefined();\n      expect(wildcardResult?.type).toBe(PYTHON_INTERNAL_MODULE_TYPE);\n      expect(wildcardResult?.module?.path).toBe(\"moduleA.py\");\n      expect(wildcardResult?.symbol?.id).toBe(\"foo\");\n\n      // Also test bar which was imported into moduleB and then wildcard imported into moduleC\n      const anotherResult = resolver.resolveItem(moduleC, \"bar\");\n      expect(anotherResult).toBeDefined();\n      expect(anotherResult?.module?.path).toBe(\"moduleA.py\");\n      expect(anotherResult?.symbol?.id).toBe(\"bar\");\n    });\n\n    test(\"resolves module imports\", () => {\n      const moduleE = moduleResolver.getModuleFromFilePath(\"moduleE.py\");\n      expect(moduleE).toBeDefined();\n\n      // Test resolving a module import\n      const moduleResult = resolver.resolveItem(moduleE, \"moduleA\");\n      expect(moduleResult).toBeDefined();\n      expect(moduleResult?.type).toBe(PYTHON_INTERNAL_MODULE_TYPE);\n      expect(moduleResult?.module?.path).toBe(\"moduleA.py\");\n      expect(moduleResult?.symbol).toBeUndefined(); // No specific symbol when importing whole module\n    });\n  });\n\n  // ========================================================\n  // SECTION: Import Precedence and Shadowing\n  // ========================================================\n  describe(\"Import Precedence and Shadowing\", () => {\n    test(\"local definitions override imports\", () => {\n      const precedenceModule = moduleResolver.getModuleFromFilePath(\n        \"precedence.py\",\n      );\n      expect(precedenceModule).toBeDefined();\n\n      // The local 'duplicate' function should be returned, not the imported one\n      const result = resolver.resolveItem(precedenceModule, \"duplicate\");\n      expect(result).toBeDefined();\n      expect(result?.module?.path).toBe(\"precedence.py\");\n      expect(result?.symbol?.id).toBe(\"duplicate\");\n    });\n\n    test(\"explicit imports override wildcard imports\", () => {\n      const wildcardPrecedence = moduleResolver.getModuleFromFilePath(\n        \"wildcard_precedence.py\",\n      );\n      expect(wildcardPrecedence).toBeDefined();\n\n      // The 'bar' symbol is explicitly imported from moduleA.foo, overriding any wildcards\n      const result = resolver.resolveItem(wildcardPrecedence, \"bar\");\n      expect(result).toBeDefined();\n      expect(result?.module?.path).toBe(\"moduleA.py\");\n      expect(result?.symbol?.id).toBe(\"foo\");\n    });\n\n    test(\"earlier wildcard imports shadow later ones\", () => {\n      const multiWildcards = moduleResolver.getModuleFromFilePath(\n        \"multi_wildcards.py\",\n      );\n      expect(multiWildcards).toBeDefined();\n\n      // The 'common' function should come from the first wildcard import\n      const wildcardSymbols = resolver.getWildcardSymbols(multiWildcards);\n\n      // Check for the functions from both modules\n      expect(wildcardSymbols.has(\"unique1\")).toBeTruthy();\n      expect(wildcardSymbols.has(\"unique2\")).toBeTruthy();\n\n      // But the common function should come from the first module\n      const commonFunc = resolver.resolveItem(multiWildcards, \"common\");\n      expect(commonFunc).toBeDefined();\n      expect(commonFunc?.module?.path).toBe(\"multi_wildcard1.py\");\n    });\n\n    test(\"later imports override earlier imports with the same name\", () => {\n      const overrideModule = moduleResolver.getModuleFromFilePath(\n        \"override_imports.py\",\n      );\n      expect(overrideModule).toBeDefined();\n\n      // The 'foo' symbol should come from the last import (multi_wildcard1.common), not the first one (moduleA.foo)\n      const result = resolver.resolveItem(overrideModule, \"foo\");\n      expect(result).toBeDefined();\n      expect(result?.module?.path).toBe(\"multi_wildcard1.py\");\n      expect(result?.symbol?.id).toBe(\"common\");\n    });\n  });\n\n  // ========================================================\n  // SECTION: Visibility and __all__ Directive\n  // ========================================================\n  describe(\"Visibility and __all__ Directive\", () => {\n    test(\"respects __all__ in wildcard imports\", () => {\n      const importAllModule = moduleResolver.getModuleFromFilePath(\n        \"importAllModule.py\",\n      );\n      expect(importAllModule).toBeDefined();\n\n      // Should find public_func which is in __all__\n      const publicResult = resolver.resolveItem(importAllModule, \"public_func\");\n      expect(publicResult).toBeDefined();\n      expect(publicResult?.module?.path).toBe(\"moduleWithAll.py\");\n\n      // Should NOT find another_func which is not in __all__\n      const anotherResult = resolver.resolveItem(\n        importAllModule,\n        \"another_func\",\n      );\n      expect(anotherResult).toBeUndefined();\n\n      // Should NOT find _private_func which is neither in __all__ nor would be included due to _ prefix\n      const privateResult = resolver.resolveItem(\n        importAllModule,\n        \"_private_func\",\n      );\n      expect(privateResult).toBeUndefined();\n    });\n\n    test(\"explicit imports override __all__ restrictions\", () => {\n      const importSpecific = moduleResolver.getModuleFromFilePath(\n        \"importSpecificFromAll.py\",\n      );\n      expect(importSpecific).toBeDefined();\n\n      // Should find explicitly imported symbols even if not in __all__\n      const anotherResult = resolver.resolveItem(\n        importSpecific,\n        \"another_func\",\n      );\n      expect(anotherResult).toBeDefined();\n      expect(anotherResult?.module?.path).toBe(\"moduleWithAll.py\");\n\n      // Should also find explicitly imported private symbols\n      const privateResult = resolver.resolveItem(\n        importSpecific,\n        \"_private_func\",\n      );\n      expect(privateResult).toBeDefined();\n      expect(privateResult?.module?.path).toBe(\"moduleWithAll.py\");\n    });\n\n    test(\"excludes private symbols in wildcard imports without __all__\", () => {\n      const importPrivate = moduleResolver.getModuleFromFilePath(\n        \"importPrivate.py\",\n      );\n      expect(importPrivate).toBeDefined();\n\n      // Should find public symbols through wildcard\n      const publicResult = resolver.resolveItem(\n        importPrivate,\n        \"public_function\",\n      );\n      expect(publicResult).toBeDefined();\n\n      const constantResult = resolver.resolveItem(\n        importPrivate,\n        \"PUBLIC_CONSTANT\",\n      );\n      expect(constantResult).toBeDefined();\n\n      // Should NOT find private symbols through wildcard\n      const privateResult = resolver.resolveItem(\n        importPrivate,\n        \"_private_function\",\n      );\n      expect(privateResult).toBeUndefined();\n\n      const privateConstResult = resolver.resolveItem(\n        importPrivate,\n        \"_PRIVATE_CONSTANT\",\n      );\n      expect(privateConstResult).toBeUndefined();\n    });\n\n    test(\"__all__ can override private symbol conventions\", () => {\n      const importAllOverride = moduleResolver.getModuleFromFilePath(\n        \"importAllOverride.py\",\n      );\n      expect(importAllOverride).toBeDefined();\n\n      // Should find both regular and private symbols listed in __all__\n      const regularResult = resolver.resolveItem(\n        importAllOverride,\n        \"regular_func\",\n      );\n      expect(regularResult).toBeDefined();\n\n      // Even though it starts with _, it should be imported because it's in __all__\n      const privateResult = resolver.resolveItem(\n        importAllOverride,\n        \"_private_func\",\n      );\n      expect(privateResult).toBeDefined();\n    });\n  });\n\n  // ========================================================\n  // SECTION: Complex Import Scenarios\n  // ========================================================\n  describe(\"Complex Import Scenarios\", () => {\n    test(\"handles circular imports gracefully\", () => {\n      const circular1 = moduleResolver.getModuleFromFilePath(\"circular1.py\");\n      const circular2 = moduleResolver.getModuleFromFilePath(\"circular1.py\");\n      expect(circular1).toBeDefined();\n      expect(circular2).toBeDefined();\n\n      // Should be able to resolve the function defined in circular1\n      const func1Result = resolver.resolveItem(circular1, \"circular1_func\");\n      expect(func1Result).toBeDefined();\n      expect(func1Result?.module?.path).toBe(\"circular1.py\");\n\n      // Should also resolve the function imported from circular2\n      const func2Result = resolver.resolveItem(circular1, \"circular2_func\");\n      expect(func2Result).toBeDefined();\n      expect(func2Result?.module?.path).toBe(\"circular2.py\");\n\n      // And the reverse should work too\n      const func1FromCircular2 = resolver.resolveItem(\n        circular2,\n        \"circular1_func\",\n      );\n      expect(func1FromCircular2).toBeDefined();\n      expect(func1FromCircular2?.module?.path).toBe(\"circular1.py\");\n    });\n\n    test(\"resolves symbols through package hierarchy with wildcard imports\", () => {\n      const usePackage = moduleResolver.getModuleFromFilePath(\"usePackage\");\n      expect(usePackage).toBeDefined();\n\n      // Should be able to resolve the deeply nested function through multiple wildcards\n      const deepResult = resolver.resolveItem(usePackage, \"deep_nested_func\");\n      expect(deepResult).toBeDefined();\n      expect(deepResult?.module?.path).toBe(\"package/submodule.py\");\n\n      // Also test the intermediate function\n      const intermediateResult = resolver.resolveItem(\n        usePackage,\n        \"intermediate_func\",\n      );\n      expect(intermediateResult).toBeDefined();\n      expect(intermediateResult?.module?.path).toBe(\"package/intermediate.py\");\n\n      // And the explicit import in __init__.py\n      const subFuncResult = resolver.resolveItem(usePackage, \"sub_func\");\n      expect(subFuncResult).toBeDefined();\n      expect(subFuncResult?.module?.path).toBe(\"package/submodule.py\");\n    });\n\n    test(\"handles relative imports in packages\", () => {\n      const package2 = moduleResolver.getModuleFromFilePath(\"package2.py\");\n      const relmod = moduleResolver.getModuleFromFilePath(\"package2/relmod.py\");\n      const absmod = moduleResolver.getModuleFromFilePath(\"package2/absmod.py\");\n\n      expect(package2).toBeDefined();\n      expect(relmod).toBeDefined();\n      expect(absmod).toBeDefined();\n\n      // Test relative import from __init__ to submodule\n      const relFuncFromInit = resolver.resolveItem(package2, \"rel_func\");\n      expect(relFuncFromInit).toBeDefined();\n      expect(relFuncFromInit?.module?.path).toBe(\"package2/relmod.py\");\n\n      // Test relative import from submodule to package\n      const varFromRelmod = resolver.resolveItem(relmod, \"absolute_var\");\n      expect(varFromRelmod).toBeDefined();\n      expect(varFromRelmod?.module?.path).toBe(\"package2/__init__.py\");\n\n      // Test absolute import from submodule to package\n      const varFromAbsmod = resolver.resolveItem(absmod, \"absolute_var\");\n      expect(varFromAbsmod).toBeDefined();\n      expect(varFromAbsmod?.module?.path).toBe(\"package2/__init__.py\");\n    });\n  });\n\n  // ========================================================\n  // SECTION: Wildcard Symbol Collection\n  // ========================================================\n  describe(\"Wildcard Symbol Collection\", () => {\n    test(\"collects all symbols for wildcard exports respecting __all__\", () => {\n      const moduleWithAll = moduleResolver.getModuleFromFilePath(\n        \"moduleWithAll.py\",\n      );\n      expect(moduleWithAll).toBeDefined();\n\n      const symbols = resolver.getWildcardSymbols(moduleWithAll);\n\n      // Should only contain symbols listed in __all__\n      expect(symbols.size).toBe(1);\n      expect(symbols.has(\"public_func\")).toBeTruthy();\n      expect(symbols.has(\"another_func\")).toBeFalsy();\n      expect(symbols.has(\"_private_func\")).toBeFalsy();\n    });\n\n    test(\"collects all non-private symbols for wildcard exports without __all__\", () => {\n      const privateSymbols = moduleResolver.getModuleFromFilePath(\n        \"privateSymbols.py\",\n      );\n      expect(privateSymbols).toBeDefined();\n\n      const symbols = resolver.getWildcardSymbols(privateSymbols);\n\n      // Should only contain public symbols (no _ prefix)\n      expect(symbols.has(\"public_function\")).toBeTruthy();\n      expect(symbols.has(\"PUBLIC_CONSTANT\")).toBeTruthy();\n      expect(symbols.has(\"_private_function\")).toBeFalsy();\n      expect(symbols.has(\"_PRIVATE_CONSTANT\")).toBeFalsy();\n    });\n\n    test(\"combines symbols from wildcard imports with local symbols\", () => {\n      const moduleC = moduleResolver.getModuleFromFilePath(\"moduleC.py\");\n      expect(moduleC).toBeDefined();\n\n      const symbols = resolver.getWildcardSymbols(moduleC);\n\n      // Should have all the symbols from moduleB\n      expect(symbols.has(\"f\")).toBeTruthy(); // alias from moduleB\n      expect(symbols.has(\"bar\")).toBeTruthy(); // direct import in moduleB\n\n      // Verify they resolve to the correct original source\n      const fSymbol = symbols.get(\"f\");\n      expect(fSymbol?.module?.path).toBe(\"moduleA.py\");\n      expect(fSymbol?.symbol?.id).toBe(\"foo\");\n    });\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/itemResolver/index.ts",
    "content": "import type { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport type { PythonImportExtractor } from \"../importExtractor/index.ts\";\nimport {\n  FROM_IMPORT_STATEMENT_TYPE,\n  NORMAL_IMPORT_STATEMENT_TYPE,\n} from \"../importExtractor/types.ts\";\nimport type { PythonModuleResolver } from \"../moduleResolver/index.ts\";\nimport {\n  PYTHON_NAMESPACE_MODULE_TYPE,\n  type PythonModule,\n} from \"../moduleResolver/types.ts\";\nimport {\n  PYTHON_EXTERNAL_MODULE_TYPE,\n  PYTHON_INTERNAL_MODULE_TYPE,\n  type ResolvedExternalModule,\n  type ResolvedExternalSymbol,\n  type ResolvedInternalModule,\n  type ResolvedInternalSymbol,\n  type ResolvedItem,\n  type ResolvedSymbol,\n} from \"./types.ts\";\n\n/**\n * PythonItemResolver resolves items across Python modules following Python's\n * import resolution rules, handling both internal and external dependencies.\n *\n * The resolver implements Python's symbol resolution algorithm:\n * 1. Symbols defined directly in a module take precedence\n * 2. Explicitly imported symbols are checked next\n * 3. Wildcard imports are checked last, in order of appearance\n *\n * It handles both internal modules (analyzable within the project) and external\n * modules (from third-party or standard libraries). The resolver uses caching\n * to improve performance and to handle circular dependencies.\n *\n * This class ties together several components:\n * - ExportExtractor to find symbols defined in modules\n * - ImportExtractor to analyze import statements\n * - ModuleResolver to locate modules within the project\n */\nexport class PythonItemResolver {\n  /**\n   * Cache for final symbol resolution results\n   * Maps a moduleId:symbolName pair to its resolved item\n   */\n  private resolutionCache = new Map<string, ResolvedItem | undefined>();\n\n  /**\n   * Cache for in-progress resolutions to handle circular dependencies.\n   *\n   * When a symbol resolution is in progress but not completed, we store\n   * its partial result here. This prevents infinite recursion when modules\n   * import each other in a circular fashion, which is allowed in Python.\n   *\n   * Maps a moduleId:symbolName pair to its in-progress resolved item\n   */\n  private recursiveCache = new Map<string, ResolvedItem | undefined>();\n\n  /**\n   * Cache for all symbols in a module, used for wildcard imports\n   * Maps a module path to a map of symbol names to resolved symbols\n   *\n   * This caches the complete set of symbols available in a module,\n   * including both directly defined symbols and imported symbols.\n   */\n  private allSymbolsCache = new Map<string, Map<string, ResolvedSymbol>>();\n\n  /**\n   * Creates a new PythonItemResolver\n   *\n   * @param exportExtractor - Used to extract symbols defined in modules\n   * @param importExtractor - Used to analyze import statements\n   * @param moduleResolver - Used to resolve module references\n   */\n  constructor(\n    private exportExtractor: PythonExportExtractor,\n    private importExtractor: PythonImportExtractor,\n    private moduleResolver: PythonModuleResolver,\n  ) {}\n\n  /**\n   * Resolves a symbol name from the perspective of a module.\n   *\n   * This method follows Python's import resolution rules to find the definition\n   * of a symbol, whether it's defined directly in the module, imported from another\n   * module, or comes from a wildcard import.\n   *\n   * @param fromModule - The module context where the symbol is referenced\n   * @param itemName - The name of the item (symbol or module) to resolve\n   * @returns The resolved item information or undefined if not found\n   */\n  public resolveItem(\n    fromModule: PythonModule,\n    itemName: string,\n  ): ResolvedItem | undefined {\n    // Generate cache key based on starting module and symbol name\n    const cacheKey = `${fromModule.path}:${itemName}`;\n\n    // Check if already resolved\n    if (this.resolutionCache.has(cacheKey)) {\n      return this.resolutionCache.get(cacheKey);\n    }\n\n    // Track visited module-symbol pairs to detect cycles\n    const visited = new Set<string>();\n    const result = this.resolveItemImpl(fromModule, itemName, visited);\n\n    // Cache the final result\n    this.resolutionCache.set(cacheKey, result);\n    return result;\n  }\n\n  /**\n   * Implementation of the item resolution logic following Python's resolution order\n   *\n   * Resolution happens in this specific order:\n   * 1. Check if the item is defined directly in this module\n   * 2. Check explicitly imported items (via direct imports or from-imports)\n   * 3. Check wildcard imports last, in order of appearance\n   *\n   * @param module - The module from which to resolve the item\n   * @param itemName - The name of the item (symbol or module) to resolve\n   * @param visited - Set of already visited module:item pairs to prevent circular resolution\n   * @returns The resolved item or undefined if not found\n   */\n  private resolveItemImpl(\n    module: PythonModule,\n    itemName: string,\n    visited: Set<string>,\n  ): ResolvedItem | undefined {\n    const visitKey = `${module.path}:${itemName}`;\n\n    // Check for circular references\n    if (visited.has(visitKey)) {\n      return undefined;\n    }\n\n    // Check recursive resolution cache\n    if (this.recursiveCache.has(visitKey)) {\n      return this.recursiveCache.get(visitKey);\n    }\n\n    visited.add(visitKey);\n\n    // Python resolution order:\n    // 1. Check symbols defined directly in this module\n    // 2. Check explicitly imported items (via direct or from-imports)\n    // 3. Check wildcard imports last, in order of appearance\n\n    // 1. If module is a namespace package,\n    // Cause these have are not files, so they have no\n    // imports or sumbols.\n    if (module.type !== PYTHON_NAMESPACE_MODULE_TYPE) {\n      // 2. Check if symbol is defined directly in this module\n      // Get all symbols defined in this module using the export extractor\n      const exports = this.exportExtractor.getSymbols(module.path);\n\n      // Look for direct symbol match by comparing symbol ids with the requested item name\n      const directSymbol = exports.symbols.find((sym) => sym.id === itemName);\n\n      // Direct symbol resolution:\n      // If the symbol is defined directly in this module, we can return it immediately\n      // Python always gives priority to symbols defined directly in the module,\n      // regardless of any __all__ restrictions (which only affect what's exported)\n      if (directSymbol) {\n        // Create a resolved internal item with the found symbol\n        const result = {\n          type: PYTHON_INTERNAL_MODULE_TYPE,\n          module: module,\n          symbol: directSymbol,\n        } as ResolvedInternalSymbol;\n        this.recursiveCache.set(visitKey, result);\n        return result;\n      }\n\n      // 3. Check imports in order of appearance in the file\n      // Python resolves imports sequentially based on their order in the file,\n      // with the only exception being wildcard imports which are always lowest priority\n      const importStatements = this.importExtractor.getImportStatements(\n        module.path,\n      );\n\n      // Store last resolved item from explicit imports\n      // In Python, later imports with the same name override earlier ones\n      let lastExplicitImport: ResolvedItem | undefined;\n\n      // Process all explicit imports first - both normal imports and from-imports\n      // (excluding wildcards) in the order they appear in the file\n      for (const stmt of importStatements) {\n        if (stmt.type === FROM_IMPORT_STATEMENT_TYPE) {\n          // For \"from X import Y\" statements\n          const member = stmt.members[0];\n\n          if (member.isWildcardImport) {\n            // Process wildcard import last\n            continue;\n          }\n\n          // Try to resolve the source module (X in \"from X import Y\")\n          // This could be an internal module or an external one\n          const sourceModule = this.moduleResolver.resolveModule(\n            module,\n            member.identifierNode.text,\n          );\n\n          // 3.1 Check if explicit import\n          for (const item of member.items || []) {\n            const lookupName = item.aliasNode?.text || item.identifierNode.text;\n\n            // itemName matches the imported name\n            if (itemName === lookupName) {\n              if (sourceModule) {\n                // internal module reference\n                lastExplicitImport = this.resolveItemImpl(\n                  sourceModule,\n                  item.identifierNode.text,\n                  visited,\n                );\n\n                // If we got a result and it's an internal symbol, track the re-export chain\n                if (\n                  lastExplicitImport &&\n                  lastExplicitImport.type === PYTHON_INTERNAL_MODULE_TYPE &&\n                  (lastExplicitImport as ResolvedInternalSymbol).symbol\n                ) {\n                  const resolvedSymbol =\n                    lastExplicitImport as ResolvedInternalSymbol;\n\n                  // Initialize or update the re-export chain\n                  if (!resolvedSymbol.reExportChain) {\n                    resolvedSymbol.reExportChain = [];\n                  }\n\n                  // Add this module to the re-export chain\n                  resolvedSymbol.reExportChain.push(sourceModule);\n                }\n              } else {\n                // external module reference\n                lastExplicitImport = {\n                  type: PYTHON_EXTERNAL_MODULE_TYPE,\n                  moduleName: member.identifierNode.text,\n                  symbolName: item.identifierNode.text,\n                } as ResolvedExternalSymbol;\n              }\n              // Don't return immediately, continue processing for possible overrides\n            }\n          }\n        }\n\n        // 3.2 Check normal import\n        if (stmt.type === NORMAL_IMPORT_STATEMENT_TYPE) {\n          for (const member of stmt.members) {\n            const lookupName = member.aliasNode?.text ||\n              member.identifierNode.text;\n\n            if (itemName === lookupName) {\n              const sourceModule = this.moduleResolver.resolveModule(\n                module,\n                member.identifierNode.text,\n              );\n\n              // Internal module reference\n              if (sourceModule) {\n                lastExplicitImport = {\n                  type: PYTHON_INTERNAL_MODULE_TYPE,\n                  module: sourceModule,\n                } as ResolvedInternalModule;\n              } else {\n                // External module reference\n                lastExplicitImport = {\n                  type: PYTHON_EXTERNAL_MODULE_TYPE,\n                  moduleName: member.identifierNode.text,\n                } as ResolvedExternalModule;\n              }\n              // Don't return immediately, continue processing for possible overrides\n            }\n          }\n        }\n      }\n\n      // After all explicit imports, return the last one found (if any)\n      if (lastExplicitImport) {\n        this.recursiveCache.set(visitKey, lastExplicitImport);\n        return lastExplicitImport;\n      }\n\n      // 3.3 Process wildcard imports last, in order of appearance\n      for (const stmt of importStatements) {\n        if (stmt.type === FROM_IMPORT_STATEMENT_TYPE) {\n          // For \"from X import Y\" statements\n          const member = stmt.members[0];\n\n          // Try to resolve the source module (X in \"from X import Y\")\n          // This could be an internal module or an external one\n          const sourceModule = this.moduleResolver.resolveModule(\n            module,\n            member.identifierNode.text,\n          );\n\n          // Handle wildcard imports (from X import *)\n          if (member.isWildcardImport) {\n            if (sourceModule) {\n              // For internal wildcard imports, we need to respect the __all__ list\n              // Python only imports symbols listed in __all__ if it exists\n              const sourceExports = this.exportExtractor.getSymbols(\n                sourceModule.path,\n              );\n\n              if (sourceExports.publicSymbols) {\n                // If the source module has an __all__ list defined:\n                // Only continue if the requested item is listed in __all__\n                // This matches Python's behavior where wildcard imports only import\n                // symbols explicitly listed in __all__\n                if (!sourceExports.publicSymbols.includes(itemName)) {\n                  continue; // Skip this import as the item isn't in __all__\n                }\n              } else {\n                // If no __all__ list exists, Python only imports non-underscore-prefixed names\n                // Skip private symbols (those starting with underscore)\n                if (itemName.startsWith(\"_\")) {\n                  continue; // Skip private symbols in wildcard imports\n                }\n              }\n\n              // Recursively try to resolve the item from the source module\n              const result = this.resolveItemImpl(\n                sourceModule,\n                itemName,\n                visited,\n              );\n              if (result) {\n                this.recursiveCache.set(visitKey, result);\n                return result;\n              }\n            }\n            // For external wildcard imports (modules we can't analyze):\n            // We can't determine what symbols are available without analyzing the external module\n            // We skip this import and continue checking other imports\n            continue;\n          }\n        }\n      }\n    }\n\n    // Check if the item is defined directly in this module\n    const childModule = module.children.get(itemName);\n    if (childModule) {\n      const result = {\n        type: PYTHON_INTERNAL_MODULE_TYPE,\n        module: childModule,\n      } as ResolvedInternalModule;\n      // Return the resolved child module as an internal item\n      return result;\n    }\n\n    // Not found in this module or any imports\n    this.recursiveCache.set(visitKey, undefined);\n    return undefined;\n  }\n\n  /**\n   * Returns all symbols that would be imported through a wildcard import from this module.\n   *\n   * This method follows Python's wildcard import rules:\n   * - If __all__ is defined in the module, only symbols listed there are included\n   * - Otherwise, all symbols that don't begin with an underscore are included\n   * - Direct definitions in the module take precedence over any imports\n   * - Explicit imports take precedence over wildcard imports\n   * - Wildcard imports have lowest precedence, processed in order of appearance\n   *\n   * @param module - The module to collect wildcard-importable symbols from\n   * @returns Map of symbol names to their resolved definitions\n   */\n  public getWildcardSymbols(module: PythonModule): Map<string, ResolvedSymbol> {\n    // Check cache first\n    const cacheKey = module.path;\n    if (this.allSymbolsCache.has(cacheKey)) {\n      return this.allSymbolsCache.get(cacheKey) as Map<string, ResolvedSymbol>;\n    }\n\n    // Create a new map for storing all symbols\n    const result = new Map<string, ResolvedSymbol>();\n\n    // Track visited modules to prevent infinite recursion\n    const visited = new Set<string>();\n\n    this.collectSymbols(module, result, visited);\n\n    // Cache the result\n    this.allSymbolsCache.set(cacheKey, result);\n    return result;\n  }\n\n  /**\n   * Collects all symbols from a module that would be available for import,\n   * following Python's symbol visibility and precedence rules.\n   *\n   * Python's symbol resolution has specific rules for wildcard imports:\n   * 1. Local definitions take precedence over any imports\n   *    - If __all__ is defined, only symbols in __all__ are included\n   *    - Otherwise, non-underscore-prefixed symbols are included\n   * 2. Explicit imports (both regular and from-imports) are processed next, in order\n   * 3. Wildcard imports have lowest precedence and are processed last, in order\n   *\n   * This ensures consistent symbol resolution matching Python's behavior.\n   *\n   * @param module - The module from which to collect symbols\n   * @param symbolsMap - Map to store the collected symbols\n   * @param visited - Set of already visited modules to prevent circular imports\n   */\n  private collectSymbols(\n    module: PythonModule,\n    symbolsMap: Map<string, ResolvedSymbol>,\n    visited: Set<string>,\n  ): void {\n    // Prevent circular imports\n    if (visited.has(module.path)) {\n      return;\n    }\n    visited.add(module.path);\n\n    // for namespace modules, they don't have direct symbols\n    if (module.type === PYTHON_NAMESPACE_MODULE_TYPE) {\n      return;\n    }\n\n    // 1. Get all symbols defined directly in this module\n    // Python gives highest precedence to local definitions\n    const exports = this.exportExtractor.getSymbols(module.path);\n    for (const symbol of exports.symbols) {\n      if (exports.publicSymbols) {\n        // Check if the symbol is in __all__\n        if (exports.publicSymbols.includes(symbol.id)) {\n          symbolsMap.set(symbol.id, {\n            type: PYTHON_INTERNAL_MODULE_TYPE,\n            module: module,\n            symbol: symbol,\n          } as ResolvedInternalSymbol);\n        }\n      } else {\n        // If no __all__, add all public symbols\n        // unless they are private (starting with underscore)\n        if (!symbol.id.startsWith(\"_\")) {\n          symbolsMap.set(symbol.id, {\n            type: PYTHON_INTERNAL_MODULE_TYPE,\n            module: module,\n            symbol: symbol,\n          } as ResolvedInternalSymbol);\n        }\n      }\n    }\n\n    // 2. Process imports in the correct order of precedence:\n    // First explicit imports, then wildcard imports\n    const importStatements = this.importExtractor.getImportStatements(\n      module.path,\n    );\n\n    // First pass: process all explicit imports in order of appearance\n    // Explicit imports take precedence over wildcard imports in Python\n    for (const stmt of importStatements) {\n      if (stmt.type === FROM_IMPORT_STATEMENT_TYPE) {\n        const member = stmt.members[0];\n\n        // Skip wildcard imports in first pass\n        if (member.isWildcardImport) {\n          continue;\n        }\n\n        const sourceModule = this.moduleResolver.resolveModule(\n          module,\n          member.identifierNode.text,\n        );\n\n        if (member.isWildcardImport) {\n          // Process wildcard import last\n          continue;\n        }\n\n        // Handle explicit imports\n        for (const item of member.items || []) {\n          const lookupName = item.aliasNode?.text || item.identifierNode.text;\n\n          if (sourceModule) {\n            // for internal modules\n            const resolved = this.resolveItem(\n              sourceModule,\n              item.identifierNode.text,\n            );\n            if (resolved) {\n              if (resolved.type === PYTHON_INTERNAL_MODULE_TYPE) {\n                // Explicit imports always override previously imported names\n                symbolsMap.set(lookupName, {\n                  type: PYTHON_INTERNAL_MODULE_TYPE,\n                  module: resolved.module,\n                  symbol: resolved.symbol,\n                } as ResolvedInternalSymbol);\n              } else {\n                // External module reference\n                symbolsMap.set(lookupName, {\n                  type: PYTHON_EXTERNAL_MODULE_TYPE,\n                  moduleName: resolved.moduleName,\n                  symbolName: item.identifierNode.text,\n                } as ResolvedExternalSymbol);\n              }\n            }\n          } else {\n            // External module reference\n            symbolsMap.set(lookupName, {\n              type: PYTHON_EXTERNAL_MODULE_TYPE,\n              moduleName: member.identifierNode.text,\n              symbolName: item.identifierNode.text,\n            } as ResolvedExternalSymbol);\n          }\n        }\n      }\n    }\n\n    // Second pass: process wildcard imports in order of appearance\n    // Wildcard imports have lowest precedence in Python's import system\n    for (const stmt of importStatements) {\n      if (stmt.type === FROM_IMPORT_STATEMENT_TYPE) {\n        const member = stmt.members[0];\n\n        // Only process wildcard imports in second pass\n        if (member.isWildcardImport) {\n          const sourceModule = this.moduleResolver.resolveModule(\n            module,\n            member.identifierNode.text,\n          );\n\n          // Handle wildcard imports\n          if (sourceModule) {\n            // For internal modules, we can get all symbols\n            const sourceSymbols = this.getWildcardSymbols(sourceModule);\n            for (const [name, resolved] of sourceSymbols) {\n              // Check if the symbol is not already defined\n              // In Python, symbols from earlier imports (or local definitions)\n              // always shadow symbols from later wildcard imports\n              if (!symbolsMap.has(name)) {\n                if (resolved.type === PYTHON_INTERNAL_MODULE_TYPE) {\n                  // If it's an internal symbol, add it to the map\n                  symbolsMap.set(name, {\n                    type: PYTHON_INTERNAL_MODULE_TYPE,\n                    module: resolved.module,\n                    symbol: resolved.symbol,\n                  } as ResolvedInternalSymbol);\n                } else {\n                  // If it's an external symbol, add it to the map\n                  symbolsMap.set(name, {\n                    type: PYTHON_EXTERNAL_MODULE_TYPE,\n                    moduleName: resolved.moduleName,\n                    symbolName: name,\n                  } as ResolvedExternalSymbol);\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/itemResolver/types.ts",
    "content": "import type { PythonSymbol } from \"../exportExtractor/types.ts\";\nimport type { PythonModule } from \"../moduleResolver/types.ts\";\n\n/**\n * Constant representing an internal Python module/symbol that can be resolved\n * within the project's source code.\n */\nexport const PYTHON_INTERNAL_MODULE_TYPE = \"internal\";\n\n/**\n * Constant representing an external Python module/symbol that comes from\n * third-party dependencies or standard library and cannot be directly analyzed.\n */\nexport const PYTHON_EXTERNAL_MODULE_TYPE = \"external\";\n\n/**\n * Represents the type of a Python module or symbol - either internal (within the project)\n * or external (from third-party libraries or standard library).\n *\n * Internal items exist within the project's codebase and can be analyzed directly.\n * External items are from third-party libraries or the standard library and only their\n * references can be tracked.\n */\nexport type PythonModuleType =\n  | typeof PYTHON_INTERNAL_MODULE_TYPE\n  | typeof PYTHON_EXTERNAL_MODULE_TYPE;\n\n/**\n * Represents a resolved Python item (module or symbol) after the resolution process.\n *\n * This interface is the base for both internal and external resolved items and contains\n * properties relevant to both types. The concrete type (internal/external) determines\n * which fields will be populated.\n *\n * This is used as the base interface for more specific resolved item types.\n */\nexport interface ResolvedItem {\n  /** Indicates whether the item is internal or external */\n  type: PythonModuleType;\n  /** For internal items, the resolved module reference */\n  module?: PythonModule;\n  /** For internal items that are symbols, the symbol definition */\n  symbol?: PythonSymbol;\n  /** For external items, the name of the module */\n  moduleName?: string;\n  /** For external items that are symbols, the name of the symbol */\n  symbolName?: string;\n}\n\n/**\n * Represents a resolved Python module after the resolution process.\n *\n * This specialized interface is for modules (as opposed to symbols) and serves\n * as a base for both internal and external module references.\n *\n * It includes all properties from ResolvedItem but with a focus on module-specific attributes.\n * Concrete implementations include ResolvedInternalModule and ResolvedExternalModule.\n */\nexport interface ResolvedModule extends ResolvedItem {\n  /** Indicates whether the module is internal or external */\n  type: PythonModuleType;\n  /** For internal modules, the resolved module reference */\n  module?: PythonModule;\n  /** For external modules, the name of the module */\n  moduleName?: string;\n}\n\n/**\n * Represents a resolved internal Python module that exists within the analyzed project.\n *\n * Internal modules are those defined within the project's source code that can be\n * directly analyzed. This interface specifically represents modules, not symbols.\n *\n * Example: A module like \"myproject.utils\" defined within the project's codebase.\n */\nexport interface ResolvedInternalModule extends ResolvedModule {\n  /** Always set to \"internal\" for internal modules */\n  type: typeof PYTHON_INTERNAL_MODULE_TYPE;\n\n  /** Reference to the resolved Python module */\n  module: PythonModule;\n}\n\n/**\n * Represents a resolved external Python module that comes from outside the analyzed project.\n *\n * External modules are those defined in third-party libraries or Python's standard library\n * that cannot be directly analyzed. We can only track their names and references.\n * This interface specifically represents modules, not symbols.\n *\n * Examples: Standard library modules like \"os\" or \"sys\", or third-party modules like \"numpy\" or \"pandas\".\n */\nexport interface ResolvedExternalModule extends ResolvedModule {\n  /** Always set to \"external\" for external modules */\n  type: typeof PYTHON_EXTERNAL_MODULE_TYPE;\n\n  /** The name of the external module */\n  moduleName: string;\n}\n\n/**\n * Represents a resolved Python symbol (not a module) after the resolution process.\n *\n * This specialized interface is for symbols (as opposed to modules) and serves\n * as a base for both internal and external symbol references.\n *\n * Symbols include functions, classes, variables, or any other named entity in Python.\n * Concrete implementations include ResolvedInternalSymbol and ResolvedExternalSymbol.\n */\nexport interface ResolvedSymbol extends ResolvedItem {\n  /** Indicates whether the symbol is internal or external */\n  type: PythonModuleType;\n\n  /** For internal symbols, the module where the symbol is defined */\n  module?: PythonModule;\n\n  /** For internal symbols, the symbol definition */\n  symbol?: PythonSymbol;\n\n  /** For external symbols, the name of the module */\n  moduleName?: string;\n\n  /** For external symbols, the name of the symbol */\n  symbolName?: string;\n}\n\n/**\n * Represents a resolved internal Python symbol defined within the analyzed project.\n *\n * Used specifically for symbols (variables, functions, classes) that are defined\n * within the project's source code and can be directly analyzed.\n *\n * Internal symbols have complete information available including their module location\n * and full symbol definition with metadata.\n *\n * Example: A function called \"process_data\" defined in a module within the project.\n */\nexport interface ResolvedInternalSymbol extends ResolvedSymbol {\n  /** Always set to \"internal\" for internal symbols */\n  type: typeof PYTHON_INTERNAL_MODULE_TYPE;\n\n  /** The module where the symbol is defined */\n  module: PythonModule;\n\n  /** The symbol definition */\n  symbol: PythonSymbol;\n\n  /**\n   * Chain of modules that re-export this symbol\n   * This tracks the re-export path when a symbol is imported and then re-exported by another module.\n   */\n  reExportChain: PythonModule[];\n}\n\n/**\n * Represents a resolved external Python symbol that comes from outside the analyzed project.\n *\n * External symbols are those defined in third-party libraries or Python's standard library\n * that cannot be directly analyzed. We can only track their names and references.\n *\n * Examples: Functions like \"os.path.join\" from the standard library, or third-party\n * classes like \"pandas.DataFrame\".\n */\nexport interface ResolvedExternalSymbol extends ResolvedSymbol {\n  /** Always set to \"external\" for external symbols */\n  type: typeof PYTHON_EXTERNAL_MODULE_TYPE;\n\n  /** The name of the external module */\n  moduleName: string;\n\n  /** The name of the external symbol */\n  symbolName: string;\n}\n"
  },
  {
    "path": "src/languagePlugins/python/metricAnalyzer/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport type Parser from \"tree-sitter\";\nimport { PythonMetricsAnalyzer } from \"./index.ts\";\nimport { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport { pythonParser } from \"../../../helpers/treeSitter/parsers.ts\";\n\ndescribe(\"PythonMetricsAnalyzer\", () => {\n  const parser = pythonParser;\n\n  // Helper function to set up the test environment\n  function setupTest(code: string, filename = \"test.py\") {\n    const tree = parser.parse(code);\n    const files = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    files.set(filename, {\n      path: filename,\n      rootNode: tree.rootNode,\n    });\n\n    const exportExtractor = new PythonExportExtractor(parser, files);\n    const complexityAnalyzer = new PythonMetricsAnalyzer(parser);\n\n    return { exportExtractor, complexityAnalyzer, filename, tree };\n  }\n\n  test(\"should analyze a simple function\", () => {\n    const code = `\ndef simple_function():\n    return 42\n`;\n\n    const { exportExtractor, complexityAnalyzer, filename } = setupTest(code);\n    const { symbols } = exportExtractor.getSymbols(filename);\n\n    expect(symbols.length).toBeGreaterThan(0);\n\n    const functionSymbol = symbols.find((s) => s.id === \"simple_function\");\n    expect(functionSymbol).toBeDefined();\n\n    if (!functionSymbol) {\n      throw new Error(\"Function symbol not found\");\n    }\n\n    // Use the nodes property (not node)\n    const nodes = functionSymbol.nodes;\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    expect(metrics.cyclomaticComplexity).toBe(1);\n    expect(metrics.codeLinesCount).toBeGreaterThan(0);\n    expect(metrics.linesCount).toBeGreaterThan(0);\n    expect(metrics.characterCount).toBeGreaterThan(0);\n    expect(metrics.codeCharacterCount).toBeGreaterThan(0);\n  });\n\n  test(\"should analyze a function with branches\", () => {\n    const code = `\ndef complex_function(a, b):\n    if a > 0:\n        if b > 0:\n            return a + b\n        else:\n            return a - b\n    elif a < 0:\n        return -a + b\n    else:\n        for i in range(10):\n            if i % 2 == 0:\n                continue\n            elif i % 3 == 0:\n                break\n        return 0\n`;\n\n    const { exportExtractor, complexityAnalyzer, filename } = setupTest(code);\n    const { symbols } = exportExtractor.getSymbols(filename);\n\n    const functionSymbol = symbols.find((s) => s.id === \"complex_function\");\n    expect(functionSymbol).toBeDefined();\n\n    if (!functionSymbol) {\n      throw new Error(\"Function symbol not found\");\n    }\n\n    // Use the nodes property (not node)\n    const nodes = functionSymbol.nodes;\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    // Base complexity 1 + branches:\n    // 1(if a>0) + 1(if b>0) + 1(else for b) + 1(elif a<0) + 1(else for a) +\n    // 1(for loop) + 1(if i%2) + 1(elif i%3)\n    // = 1 + 8 = 9\n    expect(metrics.cyclomaticComplexity).toBeGreaterThan(1);\n  });\n\n  test(\"should analyze a function with logical operators\", () => {\n    const code = `\ndef logical_function(a, b, c):\n    if a > 0 and b > 0:\n        return True\n    elif a < 0 or b < 0:\n        if c and (a or b):\n            return False\n    return None\n`;\n\n    const { exportExtractor, complexityAnalyzer, filename } = setupTest(code);\n    const { symbols } = exportExtractor.getSymbols(filename);\n\n    const functionSymbol = symbols.find((s) => s.id === \"logical_function\");\n    expect(functionSymbol).toBeDefined();\n\n    if (!functionSymbol) {\n      throw new Error(\"Function symbol not found\");\n    }\n\n    // Use the nodes property (not node)\n    const nodes = functionSymbol.nodes;\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    // Base complexity 1 + branches:\n    // 1(if) + 1(and) + 1(elif) + 1(or) + 1(if c) + 1(and) + 1(or)\n    // = 1 + 7 = 8\n    expect(metrics.cyclomaticComplexity).toBeGreaterThan(4);\n  });\n\n  test(\"should analyze a class with methods\", () => {\n    const code = `\nclass TestClass:\n    def __init__(self, value):\n        self.value = value\n\n    def simple_method(self):\n        return self.value\n\n    def complex_method(self, factor):\n        if factor > 0:\n            return self.value * factor\n        else:\n            return self.value\n`;\n\n    const { exportExtractor, complexityAnalyzer, filename } = setupTest(code);\n    const { symbols } = exportExtractor.getSymbols(filename);\n\n    const classSymbol = symbols.find((s) => s.id === \"TestClass\");\n    expect(classSymbol).toBeDefined();\n\n    if (!classSymbol) {\n      throw new Error(\"Class symbol not found\");\n    }\n\n    // Use the nodes property (not node)\n    const nodes = classSymbol.nodes;\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    // Should have found the if-else in the complex_method\n    expect(metrics.cyclomaticComplexity).toBeGreaterThan(1);\n  });\n\n  test(\"should handle comments and empty lines correctly\", () => {\n    const code = `\ndef commented_function():\n    # This is a comment\n\n    # Another comment\n    value = 42  # Inline comment\n\n    # Final comment\n    return value\n`;\n\n    const { exportExtractor, complexityAnalyzer, filename } = setupTest(code);\n    const { symbols } = exportExtractor.getSymbols(filename);\n\n    const functionSymbol = symbols.find((s) => s.id === \"commented_function\");\n    expect(functionSymbol).toBeDefined();\n\n    if (!functionSymbol) {\n      throw new Error(\"Function symbol not found\");\n    }\n\n    // Use the nodes property (not node)\n    const nodes = functionSymbol.nodes;\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    // Only actual code lines should be counted\n    expect(metrics.linesCount).toBeGreaterThan(metrics.codeLinesCount);\n    expect(metrics.codeLinesCount).toBeGreaterThan(0);\n    expect(metrics.characterCount).toBeGreaterThan(metrics.codeCharacterCount);\n    expect(metrics.codeCharacterCount).toBeGreaterThan(0);\n  });\n\n  test(\"should analyze try/except blocks\", () => {\n    const code = `\ndef exception_function():\n    try:\n        value = 42\n        return value\n    except ValueError:\n        return \"Value error\"\n    except TypeError:\n        return \"Type error\"\n    finally:\n        print(\"Cleanup\")\n`;\n\n    const { exportExtractor, complexityAnalyzer, filename } = setupTest(code);\n    const { symbols } = exportExtractor.getSymbols(filename);\n\n    const functionSymbol = symbols.find((s) => s.id === \"exception_function\");\n    expect(functionSymbol).toBeDefined();\n\n    if (!functionSymbol) {\n      throw new Error(\"Function symbol not found\");\n    }\n\n    // Use the nodes property (not node)\n    const nodes = functionSymbol.nodes;\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    // Should have complexity from try and except blocks\n    expect(metrics.cyclomaticComplexity).toBeGreaterThan(2);\n  });\n\n  test(\"should analyze an entire file\", () => {\n    const code = `\ndef function1():\n    return 1\n\ndef function2(x):\n    if x > 0:\n        return x\n    return 0\n\nclass TestClass:\n    def method1(self):\n        try:\n            return 42\n        except:\n            return 0\n`;\n\n    const { complexityAnalyzer, tree } = setupTest(code);\n\n    // Analyze the entire file using the root node\n    const nodes = [tree.rootNode];\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    // Should include complexity from all functions and methods\n    expect(metrics.cyclomaticComplexity).toBeGreaterThan(3);\n    expect(metrics.linesCount).toBeGreaterThan(0);\n    expect(metrics.codeLinesCount).toBeGreaterThan(0);\n    expect(metrics.characterCount).toBeGreaterThan(0);\n    expect(metrics.codeCharacterCount).toBeGreaterThan(0);\n  });\n\n  test(\"should analyze multiple nodes\", () => {\n    const code = `\ndef function1():\n    return 1\n\ndef function2(x):\n    if x > 0:\n        return x\n    return 0\n`;\n\n    const { exportExtractor, complexityAnalyzer, filename } = setupTest(code);\n    const { symbols } = exportExtractor.getSymbols(filename);\n\n    // Get both function nodes\n    const function1Symbol = symbols.find((s) => s.id === \"function1\");\n    const function2Symbol = symbols.find((s) => s.id === \"function2\");\n\n    expect(function1Symbol).toBeDefined();\n    expect(function2Symbol).toBeDefined();\n\n    if (!function1Symbol || !function2Symbol) {\n      throw new Error(\"Function symbols not found\");\n    }\n\n    // Use the nodes property (not node)\n    const nodes = [...function1Symbol.nodes, ...function2Symbol.nodes];\n    const metrics = complexityAnalyzer.analyzeNodes(nodes);\n\n    // Should include complexity from both functions\n    expect(metrics.cyclomaticComplexity).toBeGreaterThan(1);\n\n    // Compare with analyzing them separately\n    const metrics1 = complexityAnalyzer.analyzeNodes(function1Symbol.nodes);\n    const metrics2 = complexityAnalyzer.analyzeNodes(function2Symbol.nodes);\n\n    // Total metrics should be sum of individual metrics (except base complexity is counted once)\n    expect(metrics.codeLinesCount).toBe(\n      metrics1.codeLinesCount + metrics2.codeLinesCount,\n    );\n    expect(metrics.linesCount).toBe(metrics1.linesCount + metrics2.linesCount);\n    expect(metrics.characterCount).toBe(\n      metrics1.characterCount + metrics2.characterCount,\n    );\n    expect(metrics.codeCharacterCount).toBe(\n      metrics1.codeCharacterCount + metrics2.codeCharacterCount,\n    );\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/metricAnalyzer/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport type { PythonComplexityMetrics } from \"./types.ts\";\n\n/**\n * Interface for representing comment spans in the code\n */\ninterface CommentSpan {\n  start: { row: number; column: number };\n  end: { row: number; column: number };\n}\n\n/**\n * PythonMetricsAnalyzer calculates complexity metrics for Python symbols.\n *\n * This class performs the following:\n * - Calculates cyclomatic complexity for Python symbols using Tree-sitter\n * - Analyzes code structure to count decision points (if, elif, while, for, and, or, etc.)\n * - Provides detailed metrics for each symbol including line counts and character counts\n *\n * Note: This implementation uses a simplified approach to calculating cyclomatic complexity\n * by counting basic control flow structures and boolean operators.\n */\nexport class PythonMetricsAnalyzer {\n  private parser: Parser;\n  private complexityQuery: Parser.Query;\n  private commentQuery: Parser.Query;\n\n  constructor(parser: Parser) {\n    this.parser = parser;\n\n    // This query captures all nodes that contribute to cyclomatic complexity\n    // Each capture increases the complexity by 1\n    this.complexityQuery = new Parser.Query(\n      this.parser.getLanguage(),\n      `\n        ; Basic control flow structures\n        (if_statement) @dec\n        (elif_clause) @dec\n        (while_statement) @dec\n        (for_statement) @dec\n        (with_statement) @dec\n        (try_statement) @dec\n        (except_clause) @dec\n        (conditional_expression) @dec\n        (boolean_operator) @dec\n        (if_clause) @dec\n      `,\n    );\n\n    // Query to find comments\n    this.commentQuery = new Parser.Query(\n      this.parser.getLanguage(),\n      `\n        (comment) @comment\n      `,\n    );\n  }\n\n  /**\n   * Analyzes a list of AST nodes and calculates its complexity metrics\n   *\n   * @param nodes The AST nodes to analyze\n   * @returns Complexity metrics for the nodes\n   */\n  public analyzeNodes(nodes: Parser.SyntaxNode[]) {\n    // Base complexity starts at 1\n    let cyclomaticComplexity = 1;\n    let codeLinesCount = 0;\n    let linesCount = 0;\n    let characterCount = 0;\n    let codeCharacterCount = 0;\n\n    // Process each node of the symbol to count metrics\n    for (const node of nodes) {\n      const nodeMetrics = this.analyzeNode(node);\n\n      // Accumulate metrics from this node\n      linesCount += nodeMetrics.linesCount;\n      codeLinesCount += nodeMetrics.codeLinesCount;\n      characterCount += nodeMetrics.characterCount;\n      codeCharacterCount += nodeMetrics.codeCharacterCount;\n      cyclomaticComplexity += nodeMetrics.complexityCount;\n    }\n\n    const metrics: PythonComplexityMetrics = {\n      cyclomaticComplexity,\n      codeLinesCount,\n      linesCount,\n      codeCharacterCount,\n      characterCount,\n    };\n\n    return metrics;\n  }\n\n  /**\n   * Analyzes a single syntax node and calculates its metrics\n   *\n   * @param node The syntax node to analyze\n   * @returns Metrics for the node\n   */\n  private analyzeNode(node: Parser.SyntaxNode) {\n    // Count total lines and characters\n    const linesCount = node.endPosition.row - node.startPosition.row + 1;\n    const characterCount = node.text.length;\n\n    // Split the node text into lines for processing\n    const lines = node.text.split(\"\\n\");\n\n    // Find comments and their spans\n    const { pureCommentLines, commentSpans } = this.findComments(node, lines);\n\n    // Find empty lines\n    const emptyLines = this.findEmptyLines(node, lines);\n\n    // Calculate code lines\n    const nonCodeLines = new Set([...pureCommentLines, ...emptyLines]);\n    const codeLinesCount = linesCount - nonCodeLines.size;\n\n    // Calculate code characters\n    const codeCharacterCount = this.calculateCodeCharacters(\n      node,\n      lines,\n      pureCommentLines,\n      emptyLines,\n      commentSpans,\n    );\n\n    // Calculate cyclomatic complexity\n    const complexityCount = this.calculateComplexity(node);\n\n    return {\n      linesCount,\n      codeLinesCount,\n      characterCount,\n      codeCharacterCount,\n      complexityCount,\n    };\n  }\n\n  /**\n   * Finds all comments in a node and categorizes them\n   *\n   * @param node The syntax node to analyze\n   * @param lines The lines of text in the node\n   * @returns Object containing pure comment lines and comment spans\n   */\n  private findComments(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): {\n    pureCommentLines: Set<number>;\n    commentSpans: CommentSpan[];\n  } {\n    const pureCommentLines = new Set<number>();\n    const commentSpans: CommentSpan[] = [];\n\n    const commentCaptures = this.commentQuery.captures(node);\n\n    for (const capture of commentCaptures) {\n      const commentNode = capture.node;\n\n      // Record the comment span for character counting\n      commentSpans.push({\n        start: {\n          row: commentNode.startPosition.row,\n          column: commentNode.startPosition.column,\n        },\n        end: {\n          row: commentNode.endPosition.row,\n          column: commentNode.endPosition.column,\n        },\n      });\n\n      // Check if the comment starts at the beginning of the line (ignoring whitespace)\n      const lineIdx = commentNode.startPosition.row - node.startPosition.row;\n      if (lineIdx >= 0 && lineIdx < lines.length) {\n        const lineText = lines[lineIdx];\n        const textBeforeComment = lineText.substring(\n          0,\n          commentNode.startPosition.column,\n        );\n\n        // If there's only whitespace before the comment, it's a pure comment line\n        if (textBeforeComment.trim().length === 0) {\n          for (\n            let line = commentNode.startPosition.row;\n            line <= commentNode.endPosition.row;\n            line++\n          ) {\n            pureCommentLines.add(line);\n          }\n        }\n      }\n    }\n\n    return { pureCommentLines, commentSpans };\n  }\n\n  /**\n   * Finds all empty lines in a node\n   *\n   * @param node The syntax node to analyze\n   * @param lines The lines of text in the node\n   * @returns Set of line numbers that are empty\n   */\n  private findEmptyLines(\n    node: Parser.SyntaxNode,\n    lines: string[],\n  ): Set<number> {\n    const emptyLines = new Set<number>();\n\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      if (lines[i].trim().length === 0) {\n        emptyLines.add(lineIndex);\n      }\n    }\n\n    return emptyLines;\n  }\n\n  /**\n   * Calculates the number of characters that are actual code\n   *\n   * @param node The syntax node to analyze\n   * @param lines The lines of text in the node\n   * @param pureCommentLines Set of line numbers that are pure comments\n   * @param emptyLines Set of line numbers that are empty\n   * @param commentSpans Array of comment spans to exclude\n   * @returns Number of code characters\n   */\n  private calculateCodeCharacters(\n    node: Parser.SyntaxNode,\n    lines: string[],\n    pureCommentLines: Set<number>,\n    emptyLines: Set<number>,\n    commentSpans: CommentSpan[],\n  ): number {\n    let codeCharCount = 0;\n\n    // Process each line individually\n    for (let i = 0; i < lines.length; i++) {\n      const lineIndex = node.startPosition.row + i;\n      const line = lines[i];\n\n      // Skip empty lines and pure comment lines\n      if (emptyLines.has(lineIndex) || pureCommentLines.has(lineIndex)) {\n        continue;\n      }\n\n      // Process line for code characters\n      let lineText = line;\n\n      // Remove comment content from the line if present\n      for (const span of commentSpans) {\n        if (span.start.row === lineIndex) {\n          // Comment starts on this line\n          lineText = lineText.substring(0, span.start.column);\n        }\n      }\n\n      // Count normalized code characters (trim excessive whitespace)\n      const normalizedText = lineText.trim().replace(/\\s+/g, \" \");\n      codeCharCount += normalizedText.length;\n    }\n\n    return codeCharCount;\n  }\n\n  /**\n   * Calculates the cyclomatic complexity of a node\n   *\n   * @param node The syntax node to analyze\n   * @returns Number of decision points that contribute to complexity\n   */\n  private calculateComplexity(node: Parser.SyntaxNode): number {\n    const captures = this.complexityQuery.captures(node);\n    return captures.length;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/metricAnalyzer/types.ts",
    "content": "/**\n * Represents complexity metrics for a Python symbol\n */\nexport interface PythonComplexityMetrics {\n  /** Cyclomatic complexity (McCabe complexity) */\n  cyclomaticComplexity: number;\n  /** Code lines (not including whitespace or comments) */\n  codeLinesCount: number;\n  /** Total lines (including whitespace and comments) */\n  linesCount: number;\n  /** Characters of actual code (excluding comments and excessive whitespace) */\n  codeCharacterCount: number;\n  /** Total characters in the entire symbol */\n  characterCount: number;\n}\n"
  },
  {
    "path": "src/languagePlugins/python/moduleResolver/index.test.ts",
    "content": "import { beforeEach, describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { PythonModuleResolver } from \"./index.ts\";\nimport {\n  PYTHON_MODULE_TYPE,\n  PYTHON_NAMESPACE_MODULE_TYPE,\n  PYTHON_PACKAGE_MODULE_TYPE,\n} from \"./types.ts\";\nimport { SEPARATOR } from \"@std/path\";\n\ndescribe(\"PythonModuleResolver\", () => {\n  describe(\"Module Map Building\", () => {\n    test(\"should build module map for a single file\", () => {\n      const resolver = new PythonModuleResolver(new Set([\"main.py\"]), \"3.13\");\n      const root = resolver.pythonModule;\n\n      expect(root.name).toBe(\"\");\n      expect(root.type).toBe(PYTHON_NAMESPACE_MODULE_TYPE);\n      expect(root.children.size).toBe(1);\n\n      const mainModule = root.children.get(\"main\");\n      expect(mainModule).toBeDefined();\n      expect(mainModule?.name).toBe(\"main\");\n      expect(mainModule?.type).toBe(PYTHON_MODULE_TYPE);\n      expect(mainModule?.path).toBe(\"main.py\");\n      expect(mainModule?.fullName).toBe(\"main\");\n      expect(mainModule?.parent).toBe(root);\n    });\n\n    test(\"should build module map for multiple files at root level\", () => {\n      const resolver = new PythonModuleResolver(\n        new Set([\"main.py\", \"utils.py\", \"config.py\"]),\n        \"3.13\",\n      );\n      const root = resolver.pythonModule;\n\n      expect(root.children.size).toBe(3);\n\n      const moduleNames = Array.from(root.children.keys());\n      expect(moduleNames).toContain(\"main\");\n      expect(moduleNames).toContain(\"utils\");\n      expect(moduleNames).toContain(\"config\");\n\n      // Check each module has correct properties\n      for (const name of moduleNames) {\n        const module = root.children.get(name);\n        expect(module?.name).toBe(name);\n        expect(module?.type).toBe(PYTHON_MODULE_TYPE);\n        expect(module?.path).toBe(`${name}.py`);\n        expect(module?.fullName).toBe(name);\n      }\n    });\n\n    test(\"should build module map for a simple package\", () => {\n      const resolver = new PythonModuleResolver(\n        new Set([\"pkg/__init__.py\", \"pkg/module.py\"]),\n        \"3.13\",\n      );\n      const root = resolver.pythonModule;\n\n      expect(root.children.size).toBe(1);\n\n      const pkgModule = root.children.get(\"pkg\");\n      expect(pkgModule).toBeDefined();\n      expect(pkgModule?.name).toBe(\"pkg\");\n      expect(pkgModule?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n      expect(pkgModule?.path).toBe(\"pkg/__init__.py\");\n      expect(pkgModule?.fullName).toBe(\"pkg\");\n\n      expect(pkgModule?.children.size).toBe(1);\n      const subModule = pkgModule?.children.get(\"module\");\n      expect(subModule?.name).toBe(\"module\");\n      expect(subModule?.type).toBe(PYTHON_MODULE_TYPE);\n      expect(subModule?.path).toBe(\"pkg/module.py\");\n      expect(subModule?.fullName).toBe(\"pkg.module\");\n      expect(subModule?.parent).toBe(pkgModule);\n    });\n\n    test(\"should build module map for nested packages\", () => {\n      const resolver = new PythonModuleResolver(\n        new Set([\n          \"pkg/__init__.py\",\n          \"pkg/module.py\",\n          \"pkg/subpkg/__init__.py\",\n          \"pkg/subpkg/submodule.py\",\n          \"pkg/subpkg/deeper/__init__.py\",\n          \"pkg/subpkg/deeper/core.py\",\n        ]),\n        \"3.13\",\n      );\n      const root = resolver.pythonModule;\n\n      // Check first level\n      const pkgModule = root.children.get(\"pkg\");\n      expect(pkgModule).toBeDefined();\n      expect(pkgModule?.name).toBe(\"pkg\");\n      expect(pkgModule?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n\n      // Check second level\n      expect(pkgModule?.children.size).toBe(2); // module.py and subpkg\n      const moduleModule = pkgModule?.children.get(\"module\");\n      expect(moduleModule?.name).toBe(\"module\");\n      expect(moduleModule?.fullName).toBe(\"pkg.module\");\n\n      const subpkgModule = pkgModule?.children.get(\"subpkg\");\n      expect(subpkgModule?.name).toBe(\"subpkg\");\n      expect(subpkgModule?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n      expect(subpkgModule?.fullName).toBe(\"pkg.subpkg\");\n\n      // Check third level\n      expect(subpkgModule?.children.size).toBe(2); // submodule.py and deeper\n      const submoduleModule = subpkgModule?.children.get(\"submodule\");\n      expect(submoduleModule?.name).toBe(\"submodule\");\n      expect(submoduleModule?.fullName).toBe(\"pkg.subpkg.submodule\");\n\n      const deeperModule = subpkgModule?.children.get(\"deeper\");\n      expect(deeperModule?.name).toBe(\"deeper\");\n      expect(deeperModule?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n      expect(deeperModule?.fullName).toBe(\"pkg.subpkg.deeper\");\n\n      // Check fourth level\n      const coreModule = deeperModule?.children.get(\"core\");\n      expect(coreModule?.name).toBe(\"core\");\n      expect(coreModule?.fullName).toBe(\"pkg.subpkg.deeper.core\");\n    });\n\n    test(\"should handle package namespaces with multiple modules\", () => {\n      const resolver = new PythonModuleResolver(\n        new Set([\n          \"pkg/__init__.py\",\n          \"pkg/module1.py\",\n          \"pkg/module2.py\",\n          \"pkg/module3.py\",\n        ]),\n        \"3.13\",\n      );\n      const pkgModule = resolver.pythonModule.children.get(\"pkg\");\n\n      expect(pkgModule?.children.size).toBe(3);\n      const moduleNames = Array.from(pkgModule?.children.keys() || []);\n      expect(moduleNames).toContain(\"module1\");\n      expect(moduleNames).toContain(\"module2\");\n      expect(moduleNames).toContain(\"module3\");\n    });\n\n    test(\"should handle multiple packages at root level\", () => {\n      const resolver = new PythonModuleResolver(\n        new Set([\n          \"pkg1/__init__.py\",\n          \"pkg1/module.py\",\n          \"pkg2/__init__.py\",\n          \"pkg2/module.py\",\n          \"main.py\",\n        ]),\n        \"3.13\",\n      );\n      const root = resolver.pythonModule;\n\n      expect(root.children.size).toBe(3); // pkg1, pkg2, main\n\n      const pkg1 = root.children.get(\"pkg1\");\n      const pkg2 = root.children.get(\"pkg2\");\n      const main = root.children.get(\"main\");\n\n      expect(pkg1?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n      expect(pkg2?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n      expect(main?.type).toBe(PYTHON_MODULE_TYPE);\n\n      expect(pkg1?.children.size).toBe(1);\n      expect(pkg2?.children.size).toBe(1);\n    });\n  });\n\n  describe(\"Module Resolution\", () => {\n    let resolver: PythonModuleResolver;\n\n    beforeEach(() => {\n      resolver = new PythonModuleResolver(\n        new Set([\n          \"main.py\",\n          \"utils.py\",\n          \"config.py\",\n          \"pkg/__init__.py\",\n          \"pkg/module1.py\",\n          \"pkg/module2.py\",\n          \"pkg/subpkg/__init__.py\",\n          \"pkg/subpkg/submodule1.py\",\n          \"pkg/subpkg/submodule2.py\",\n          \"pkg/subpkg/deeper/__init__.py\",\n          \"pkg/subpkg/deeper/core.py\",\n          \"anotherpkg/__init__.py\",\n          \"anotherpkg/helper.py\",\n        ]),\n        \"3.13\",\n      );\n    });\n\n    describe(\"getModuleFromFilePath\", () => {\n      test(\"should resolve module from file path for regular modules\", () => {\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        expect(mainModule.name).toBe(\"main\");\n        expect(mainModule.fullName).toBe(\"main\");\n\n        const utilsModule = resolver.getModuleFromFilePath(\"utils.py\");\n        expect(utilsModule.name).toBe(\"utils\");\n        expect(utilsModule.fullName).toBe(\"utils\");\n      });\n\n      test(\"should resolve module from file path for packages\", () => {\n        const pkgModule = resolver.getModuleFromFilePath(\"pkg/__init__.py\");\n        expect(pkgModule.name).toBe(\"pkg\");\n        expect(pkgModule.fullName).toBe(\"pkg\");\n        expect(pkgModule.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n      });\n\n      test(\"should handle package path without __init__.py\", () => {\n        const pkgModule = resolver.getModuleFromFilePath(\"pkg\");\n        expect(pkgModule.name).toBe(\"pkg\");\n        expect(pkgModule.fullName).toBe(\"pkg\");\n      });\n\n      test(\"should handle package path with trailing separator\", () => {\n        const pkgModule = resolver.getModuleFromFilePath(`pkg${SEPARATOR}`);\n        expect(pkgModule.name).toBe(\"pkg\");\n        expect(pkgModule.fullName).toBe(\"pkg\");\n      });\n\n      test(\"should resolve module from file path for nested packages\", () => {\n        const subpkgModule = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/__init__.py\",\n        );\n        expect(subpkgModule.name).toBe(\"subpkg\");\n        expect(subpkgModule.fullName).toBe(\"pkg.subpkg\");\n        expect(subpkgModule.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n\n        const deeperModule = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/deeper/__init__.py\",\n        );\n        expect(deeperModule.name).toBe(\"deeper\");\n        expect(deeperModule.fullName).toBe(\"pkg.subpkg.deeper\");\n      });\n\n      test(\"should resolve module from file path for modules in packages\", () => {\n        const moduleModule = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        expect(moduleModule.name).toBe(\"module1\");\n        expect(moduleModule.fullName).toBe(\"pkg.module1\");\n\n        const submoduleModule = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/submodule1.py\",\n        );\n        expect(submoduleModule.name).toBe(\"submodule1\");\n        expect(submoduleModule.fullName).toBe(\"pkg.subpkg.submodule1\");\n\n        const coreModule = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/deeper/core.py\",\n        );\n        expect(coreModule.name).toBe(\"core\");\n        expect(coreModule.fullName).toBe(\"pkg.subpkg.deeper.core\");\n      });\n\n      test(\"should handle module path without .py extension\", () => {\n        const moduleModule = resolver.getModuleFromFilePath(\"pkg/module1\");\n        expect(moduleModule.name).toBe(\"module1\");\n        expect(moduleModule.fullName).toBe(\"pkg.module1\");\n      });\n\n      test(\"should throw an error for non-existent modules\", () => {\n        expect(() => {\n          resolver.getModuleFromFilePath(\"nonexistent.py\");\n        }).toThrow();\n\n        expect(() => {\n          resolver.getModuleFromFilePath(\"pkg/nonexistent.py\");\n        }).toThrow();\n      });\n\n      test(\"should use cache for repeat lookups\", () => {\n        const initialCacheSize = resolver[\"modulePathCache\"].size;\n        expect(initialCacheSize).toBe(0);\n\n        // First lookup should cache the result\n        const moduleFirst = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        expect(moduleFirst.name).toBe(\"module1\");\n\n        // Get the cache size before second lookup\n        const cacheSizeBefore = resolver[\"modulePathCache\"].size;\n        expect(cacheSizeBefore).toBe(1);\n\n        // Second lookup should use the cache\n        const moduleSecond = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        expect(moduleSecond).toBe(moduleFirst); // Same instance\n\n        // Cache size should not have increased since we used the cached value\n        expect(resolver[\"modulePathCache\"].size).toBe(1);\n      });\n    });\n\n    describe(\"Absolute Import Resolution\", () => {\n      test(\"should resolve top-level module imports\", () => {\n        // From main.py, import utils\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedUtils = resolver.resolveModule(mainModule, \"utils\");\n\n        expect(resolvedUtils).toBeDefined();\n        expect(resolvedUtils?.name).toBe(\"utils\");\n        expect(resolvedUtils?.fullName).toBe(\"utils\");\n      });\n\n      test(\"should resolve package imports\", () => {\n        // From main.py, import pkg\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedPkg = resolver.resolveModule(mainModule, \"pkg\");\n\n        expect(resolvedPkg).toBeDefined();\n        expect(resolvedPkg?.name).toBe(\"pkg\");\n        expect(resolvedPkg?.fullName).toBe(\"pkg\");\n        expect(resolvedPkg?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n      });\n\n      test(\"should resolve submodule imports with dotted names\", () => {\n        // From main.py, import pkg.module1\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedModule = resolver.resolveModule(\n          mainModule,\n          \"pkg.module1\",\n        );\n\n        expect(resolvedModule).toBeDefined();\n        expect(resolvedModule?.name).toBe(\"module1\");\n        expect(resolvedModule?.fullName).toBe(\"pkg.module1\");\n      });\n\n      test(\"should resolve deeply nested imports\", () => {\n        // From main.py, import pkg.subpkg.deeper.core\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedCore = resolver.resolveModule(\n          mainModule,\n          \"pkg.subpkg.deeper.core\",\n        );\n\n        expect(resolvedCore).toBeDefined();\n        expect(resolvedCore?.name).toBe(\"core\");\n        expect(resolvedCore?.fullName).toBe(\"pkg.subpkg.deeper.core\");\n      });\n\n      test(\"should resolve imports from within packages\", () => {\n        // From pkg/module1.py, import anotherpkg.helper\n        const module1 = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        const resolvedHelper = resolver.resolveModule(\n          module1,\n          \"anotherpkg.helper\",\n        );\n\n        expect(resolvedHelper).toBeDefined();\n        expect(resolvedHelper?.name).toBe(\"helper\");\n        expect(resolvedHelper?.fullName).toBe(\"anotherpkg.helper\");\n      });\n\n      test(\"should handle absolute imports that don't exist\", () => {\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedNonExistent = resolver.resolveModule(\n          mainModule,\n          \"nonexistent\",\n        );\n\n        expect(resolvedNonExistent).toBeUndefined();\n\n        const resolvedNestedNonExistent = resolver.resolveModule(\n          mainModule,\n          \"pkg.nonexistent\",\n        );\n        expect(resolvedNestedNonExistent).toBeUndefined();\n      });\n\n      test(\"should not resolve standard library modules\", () => {\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n\n        // Test with common stdlib modules\n        const resolvedOs = resolver.resolveModule(mainModule, \"os\");\n        expect(resolvedOs).toBeUndefined();\n\n        const resolvedSys = resolver.resolveModule(mainModule, \"sys\");\n        expect(resolvedSys).toBeUndefined();\n\n        const resolvedJson = resolver.resolveModule(mainModule, \"json\");\n        expect(resolvedJson).toBeUndefined();\n      });\n\n      test(\"should handle circular imports\", () => {\n        // From main.py, import main (itself)\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedSelf = resolver.resolveModule(mainModule, \"main\");\n\n        // Self-imports should return undefined to avoid circular references\n        expect(resolvedSelf).toBeUndefined();\n      });\n\n      test(\"should use cache for repeated resolution\", () => {\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n\n        // First resolution should cache the result\n        const utils1 = resolver.resolveModule(mainModule, \"utils\");\n        expect(utils1).toBeDefined();\n\n        // Get the cache size before second resolution\n        const cacheSizeBefore = resolver[\"importResolutionCache\"].size;\n        expect(cacheSizeBefore).toBe(1);\n\n        // Second resolution of the same import should use the cache\n        const utils2 = resolver.resolveModule(mainModule, \"utils\");\n        expect(utils2).toBe(utils1); // Same instance\n        // Cache size should not have increased since we used the cached value\n        expect(resolver[\"importResolutionCache\"].size).toBe(1);\n      });\n    });\n\n    describe(\"Relative Import Resolution\", () => {\n      test(\"should resolve same-level relative imports\", () => {\n        // From pkg/module1.py, import .module2\n        const module1 = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        const resolvedModule2 = resolver.resolveModule(module1, \".module2\");\n\n        expect(resolvedModule2).toBeDefined();\n        expect(resolvedModule2?.name).toBe(\"module2\");\n        expect(resolvedModule2?.fullName).toBe(\"pkg.module2\");\n      });\n\n      test(\"should resolve parent-level relative imports\", () => {\n        // From pkg/subpkg/submodule1.py, import ..module1\n        const submodule1 = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/submodule1.py\",\n        );\n        const resolvedModule1 = resolver.resolveModule(submodule1, \"..module1\");\n\n        expect(resolvedModule1).toBeDefined();\n        expect(resolvedModule1?.name).toBe(\"module1\");\n        expect(resolvedModule1?.fullName).toBe(\"pkg.module1\");\n      });\n\n      test(\"should resolve multiple-level parent relative imports\", () => {\n        // From pkg/subpkg/deeper/core.py, import ...module1\n        const core = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/deeper/core.py\",\n        );\n        const resolvedModule1 = resolver.resolveModule(core, \"...module1\");\n\n        expect(resolvedModule1).toBeDefined();\n        expect(resolvedModule1?.name).toBe(\"module1\");\n        expect(resolvedModule1?.fullName).toBe(\"pkg.module1\");\n      });\n\n      test(\"should resolve relative imports to the root level\", () => {\n        // From pkg/subpkg/deeper/core.py, import ....utils\n        const core = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/deeper/core.py\",\n        );\n        const resolvedUtils = resolver.resolveModule(core, \"....utils\");\n\n        expect(resolvedUtils).toBeDefined();\n        expect(resolvedUtils?.name).toBe(\"utils\");\n        expect(resolvedUtils?.fullName).toBe(\"utils\");\n      });\n\n      test(\"should resolve relative imports with subpaths\", () => {\n        // From pkg/module1.py, import .subpkg.submodule1\n        const module1 = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        const resolvedSubmodule = resolver.resolveModule(\n          module1,\n          \".subpkg.submodule1\",\n        );\n\n        expect(resolvedSubmodule).toBeDefined();\n        expect(resolvedSubmodule?.name).toBe(\"submodule1\");\n        expect(resolvedSubmodule?.fullName).toBe(\"pkg.subpkg.submodule1\");\n      });\n\n      test(\"should handle relative imports to non-existent modules\", () => {\n        const module1 = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        const resolvedNonExistent = resolver.resolveModule(\n          module1,\n          \".nonexistent\",\n        );\n\n        expect(resolvedNonExistent).toBeUndefined();\n      });\n\n      test(\"should handle too many dots in relative imports\", () => {\n        // If we go beyond the root level with too many dots\n        const module1 = resolver.getModuleFromFilePath(\"pkg/module1.py\");\n        const resolvedTooManyDots = resolver.resolveModule(\n          module1,\n          \"....toomany\",\n        );\n\n        expect(resolvedTooManyDots).toBeUndefined();\n      });\n\n      test(\"should handle empty remainder in relative imports\", () => {\n        // Just dots means import the package itself at that level\n        // From pkg/subpkg/submodule1.py, import ..\n        const submodule1 = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/submodule1.py\",\n        );\n        const resolvedParentPackage = resolver.resolveModule(submodule1, \"..\");\n\n        expect(resolvedParentPackage).toBeDefined();\n        expect(resolvedParentPackage?.name).toBe(\"pkg\");\n        expect(resolvedParentPackage?.fullName).toBe(\"pkg\");\n      });\n\n      test(\"should use cache for repeated relative imports\", () => {\n        const submodule1 = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/submodule1.py\",\n        );\n\n        // First resolution\n        const module1 = resolver.resolveModule(submodule1, \"..module1\");\n        expect(module1).toBeDefined();\n\n        // Get the cache size before second resolution\n        const cacheSizeBefore = resolver[\"importResolutionCache\"].size;\n        expect(cacheSizeBefore).toBe(1);\n\n        // Second resolution should use cache\n        const module1Again = resolver.resolveModule(submodule1, \"..module1\");\n        expect(module1Again).toBe(module1); // Same instance\n        // Cache size should not have increased since we used the cached value\n        expect(resolver[\"importResolutionCache\"].size).toBe(1);\n      });\n    });\n\n    describe(\"Edge Cases and Special Scenarios\", () => {\n      test(\"should not allow circular references\", () => {\n        // Test various patterns that could create circular references\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        expect(resolver.resolveModule(mainModule, \"main\")).toBeUndefined();\n\n        const pkgInit = resolver.getModuleFromFilePath(\"pkg/__init__.py\");\n        expect(resolver.resolveModule(pkgInit, \"pkg\")).toBeUndefined();\n\n        const submodule = resolver.getModuleFromFilePath(\n          \"pkg/subpkg/submodule1.py\",\n        );\n        expect(\n          resolver.resolveModule(submodule, \"..subpkg.submodule1\"),\n        ).toBeUndefined();\n      });\n\n      test(\"should handle mixed path separators\", () => {\n        // Test with a mix of forward and backward slashes\n        const mixedPath = \"pkg/subpkg\\\\submodule1.py\".replace(/\\\\/g, SEPARATOR);\n        const module = resolver.getModuleFromFilePath(mixedPath);\n        expect(module.name).toBe(\"submodule1\");\n        expect(module.fullName).toBe(\"pkg.subpkg.submodule1\");\n      });\n\n      test(\"should resolve imports when importing a package\", () => {\n        // From main.py, import pkg.subpkg\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const subpkg = resolver.resolveModule(mainModule, \"pkg.subpkg\");\n\n        expect(subpkg).toBeDefined();\n        expect(subpkg?.name).toBe(\"subpkg\");\n        expect(subpkg?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n        expect(subpkg?.fullName).toBe(\"pkg.subpkg\");\n      });\n\n      test(\"should handle namespace packages (PEP 420)\", () => {\n        // Python 3.3+ allows namespace packages without __init__.py\n        const resolver = new PythonModuleResolver(\n          new Set([\"main.py\", \"namespace/pkg/module.py\"]),\n          \"3.13\",\n        );\n\n        // Should create implicit namespace package\n        const root = resolver.pythonModule;\n        const namespaceModule = root.children.get(\"namespace\");\n        expect(namespaceModule).toBeDefined();\n        expect(namespaceModule?.type).toBe(PYTHON_NAMESPACE_MODULE_TYPE);\n\n        const pkgModule = namespaceModule?.children.get(\"pkg\");\n        expect(pkgModule).toBeDefined();\n        expect(pkgModule?.type).toBe(PYTHON_NAMESPACE_MODULE_TYPE);\n\n        // Should be able to resolve the module\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedModule = resolver.resolveModule(\n          mainModule,\n          \"namespace.pkg.module\",\n        );\n        expect(resolvedModule).toBeDefined();\n        expect(resolvedModule?.name).toBe(\"module\");\n        expect(resolvedModule?.fullName).toBe(\"namespace.pkg.module\");\n      });\n\n      test(\"should handle special file names\", () => {\n        // Files with names matching keywords or special patterns\n        const resolver = new PythonModuleResolver(\n          new Set([\n            \"main.py\",\n            \"special/class.py\",\n            \"special/_private.py\",\n            \"special/with.py\",\n          ]),\n          \"3.13\",\n        );\n\n        // These names are valid Python module names despite being keywords\n        const classModule = resolver.getModuleFromFilePath(\"special/class.py\");\n        expect(classModule.name).toBe(\"class\");\n\n        const privateModule = resolver.getModuleFromFilePath(\n          \"special/_private.py\",\n        );\n        expect(privateModule.name).toBe(\"_private\");\n\n        // Import paths should resolve\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedClass = resolver.resolveModule(\n          mainModule,\n          \"special.class\",\n        );\n        expect(resolvedClass).toBeDefined();\n        expect(resolvedClass?.name).toBe(\"class\");\n\n        const resolvedPrivate = resolver.resolveModule(\n          mainModule,\n          \"special._private\",\n        );\n        expect(resolvedPrivate).toBeDefined();\n        expect(resolvedPrivate?.name).toBe(\"_private\");\n      });\n\n      test(\"should handle importing from deeply nested paths\", () => {\n        // Tests the from X.Y.Z import A syntax equivalent\n        const resolver = new PythonModuleResolver(\n          new Set([\n            \"main.py\",\n            \"deep/a/__init__.py\",\n            \"deep/a/b/__init__.py\",\n            \"deep/a/b/c/__init__.py\",\n            \"deep/a/b/c/d.py\",\n          ]),\n          \"3.13\",\n        );\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n\n        // Test importing d from deep.a.b.c\n        const resolvedD = resolver.resolveModule(mainModule, \"deep.a.b.c.d\");\n        expect(resolvedD).toBeDefined();\n        expect(resolvedD?.name).toBe(\"d\");\n        expect(resolvedD?.fullName).toBe(\"deep.a.b.c.d\");\n\n        // Test importing the package itself\n        const resolvedC = resolver.resolveModule(mainModule, \"deep.a.b.c\");\n        expect(resolvedC).toBeDefined();\n        expect(resolvedC?.type).toBe(PYTHON_PACKAGE_MODULE_TYPE);\n        expect(resolvedC?.fullName).toBe(\"deep.a.b.c\");\n      });\n\n      test(\"should handle imports with ..* patterns\", () => {\n        const resolver = new PythonModuleResolver(\n          new Set([\n            \"patterns/a/__init__.py\",\n            \"patterns/a/b/__init__.py\",\n            \"patterns/a/b/module.py\",\n            \"patterns/a/other.py\",\n          ]),\n          \"3.13\",\n        );\n\n        // Get the module point of view\n        const moduleFile = resolver.getModuleFromFilePath(\n          \"patterns/a/b/module.py\",\n        );\n\n        // Test relative import with dots followed by wildcard-like name\n        // In Python, 'from .. import *' would import everything from parent\n        // Here we're testing \"..other\" - the parent's \"other\" module\n        const resolvedOther = resolver.resolveModule(moduleFile, \"..other\");\n        expect(resolvedOther).toBeDefined();\n        expect(resolvedOther?.name).toBe(\"other\");\n        expect(resolvedOther?.fullName).toBe(\"patterns.a.other\");\n      });\n\n      test(\"should handle resolution of _name modules\", () => {\n        // Modules starting with underscore are treated as internal/private\n        const resolver = new PythonModuleResolver(\n          new Set([\n            \"main.py\",\n            \"pkg/__init__.py\",\n            \"pkg/_internal.py\",\n            \"pkg/public.py\",\n          ]),\n          \"3.13\",\n        );\n\n        // Get the internal module\n        const internalModule = resolver.getModuleFromFilePath(\n          \"pkg/_internal.py\",\n        );\n        expect(internalModule.name).toBe(\"_internal\");\n\n        // Should be able to resolve _name from outside\n        const mainModule = resolver.getModuleFromFilePath(\"main.py\");\n        const resolvedInternal = resolver.resolveModule(\n          mainModule,\n          \"pkg._internal\",\n        );\n        expect(resolvedInternal).toBeDefined();\n        expect(resolvedInternal?.name).toBe(\"_internal\");\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/moduleResolver/index.ts",
    "content": "import pythonStdLib from \"../../../scripts/generate_python_stdlib_list/output.json\" with {\n  type: \"json\",\n};\nimport {\n  PYTHON_MODULE_TYPE,\n  PYTHON_NAMESPACE_MODULE_TYPE,\n  PYTHON_PACKAGE_MODULE_TYPE,\n  type PythonModule,\n  type PythonModuleType,\n} from \"./types.ts\";\n\n/**\n * PythonModuleResolver builds a hierarchical tree structure representing\n * the modules of a Python project based on its file structure.\n *\n * It processes a set of files (each with a file path and a parsed syntax tree)\n * to create a tree where:\n * - Directories become namespace modules.\n * - __init__.py files are interpreted as packages (using the parent folder as the package name).\n * - Other .py files become regular modules.\n *\n * The class also provides methods to resolve internal module import statements,\n * handling both relative and absolute imports within the project.\n * (Note: Imports to external libraries are not resolved.)\n *\n * For performance optimization, this class implements caching for module path resolution\n * to avoid redundant lookups when the same file path is repeatedly requested.\n */\nexport class PythonModuleResolver {\n  /**\n   * The root PythonModule representing the top-level namespace of the project.\n   * This module serves as the entry point for traversing the module tree.\n   *\n   * All project modules will be children or descendants of this root module.\n   */\n  public pythonModule: PythonModule;\n\n  /**\n   * The version of Python being used (only major).\n   */\n  public pythonVersion: string;\n\n  /**\n   * Set containing standard library module names for faster lookups.\n   *\n   * Used to quickly identify imports from the Python standard library versus\n   * project modules or third-party dependencies.\n   */\n  private stdModuleSet: Set<string>;\n\n  /**\n   * Cache that maps file paths to their corresponding resolved PythonModule objects.\n   * This improves performance by avoiding redundant resolution for frequently accessed modules.\n   *\n   * Key: filesystem path of the module\n   * Value: resolved PythonModule object\n   */\n  private modulePathCache: Map<string, PythonModule>;\n\n  /**\n   * Cache that maps import strings to their resolved module within a specific context.\n   * Format: `${currentModule.fullName}:${importString}` → resolvedModule\n   *\n   * This caches the results of import resolution to avoid redundant lookups,\n   * taking into account both the module being imported and the context from which\n   * it's being imported (for handling relative imports correctly).\n   */\n  private importResolutionCache: Map<string, PythonModule | undefined>;\n\n  /**\n   * Constructs a PythonModuleResolver.\n   *\n   * @param files - A mapping where each entry represents a file in the project,\n   *                containing its file system path and its parsed syntax tree.\n   * @param pythonVersion - The version of Python being used (only major).\n   */\n  constructor(filePaths: Set<string>, pythonVersion: string) {\n    this.pythonModule = this.buildModuleMap(filePaths);\n    this.pythonVersion = pythonVersion;\n    this.stdModuleSet = this.getPythonStdModules(pythonVersion);\n    // Initialize empty caches\n    this.modulePathCache = new Map();\n    this.importResolutionCache = new Map();\n  }\n\n  private getPythonStdModules(version: string) {\n    // Extract major.minor version\n    const versionMatch = version.match(/^(\\d+)(?:\\.(\\d+))?/);\n    if (!versionMatch) {\n      throw new Error(`Invalid Python version format: ${version}`);\n    }\n\n    const major = versionMatch[1];\n    const minor = versionMatch[2] || \"0\";\n    const pythonMajorVersion = `${major}.${minor}`;\n\n    const stdLib = pythonStdLib as Record<string, string[]>;\n\n    const stdModuleList = stdLib[pythonMajorVersion];\n\n    if (!stdModuleList) {\n      console.warn(\n        `No standard library modules found for Python version ${pythonMajorVersion}. Using standard library for Python 3.9 as a fallback`,\n      );\n      const fallbackStdLib = pythonStdLib[\"3.9\"];\n      if (!fallbackStdLib) {\n        throw new Error(\n          `No standard library modules found for Python version 3.9.`,\n        );\n      }\n      return new Set(fallbackStdLib);\n    }\n\n    return new Set(stdModuleList);\n  }\n\n  /**\n   * Constructs the hierarchical module tree for the project.\n   *\n   * The method iterates over each file provided and:\n   * - Splits the file path into directory and file name components.\n   * - Interprets __init__.py files as package indicators by removing the file name.\n   * - Strips the .py extension from regular module files.\n   * - Builds or reuses intermediate namespace modules corresponding to directories.\n   * - Sets the final module's type and full file path.\n   *\n   * @returns The root PythonModule representing the project's top-level namespace.\n   */\n  private buildModuleMap(filePaths: Set<string>): PythonModule {\n    const root: PythonModule = {\n      name: \"\",\n      fullName: \"\",\n      path: \"\",\n      type: PYTHON_NAMESPACE_MODULE_TYPE,\n      children: new Map(),\n      parent: undefined,\n    };\n\n    filePaths.forEach((filePath) => {\n      // Split the file path into directories and file name.\n      const parts = filePath.split(\"/\");\n\n      // Default to a normal module unless it is an __init__.py file.\n      let endModuleType: PythonModuleType = PYTHON_MODULE_TYPE;\n\n      if (parts[parts.length - 1] === \"__init__.py\") {\n        endModuleType = PYTHON_PACKAGE_MODULE_TYPE;\n        // Remove the \"__init__.py\" segment to represent the package as its directory.\n        parts.pop();\n      } else {\n        // Remove the .py extension from the module name.\n        parts[parts.length - 1] = parts[parts.length - 1].slice(0, -3);\n      }\n\n      let currentFullName = \"\";\n      let currentPath = \"\";\n      let currentModule = root;\n\n      // Traverse or create the module tree based on each part of the path.\n      parts.forEach((part) => {\n        currentFullName = currentFullName ? `${currentFullName}.${part}` : part;\n        currentPath = currentPath ? `${currentPath}/${part}` : part;\n\n        const existingModule = currentModule.children.get(part);\n        if (existingModule) {\n          currentModule = existingModule;\n        } else {\n          const newModule: PythonModule = {\n            name: part,\n            fullName: currentFullName,\n            path: currentPath,\n            // Initially mark as a namespace until further refined.\n            type: PYTHON_NAMESPACE_MODULE_TYPE,\n            children: new Map(),\n            parent: currentModule,\n          };\n          currentModule.children.set(part, newModule);\n          currentModule = newModule;\n        }\n      });\n\n      // Update the final module with the correct type and its original file path.\n      currentModule.type = endModuleType;\n      currentModule.path = filePath;\n    });\n\n    return root;\n  }\n\n  /**\n   * Retrieves the PythonModule associated with a given file path.\n   *\n   * This method uses a caching mechanism to improve performance when the same\n   * file path is requested multiple times. On the first request for a path,\n   * the module is resolved and cached; subsequent requests for the same path\n   * return the cached result without re-resolving.\n   *\n   * For file paths ending in __init__.py, the package directory is returned\n   * (by stripping off the __init__.py segment). For other .py files,\n   * the .py extension is removed before traversal.\n   *\n   * @param filePath - The file system path to resolve.\n   * @returns The matching PythonModule if found.\n   * @throws Error if the module could not be found in the project.\n   */\n  public getModuleFromFilePath(filePath: string): PythonModule {\n    // Check if this path has already been resolved and return from cache if available\n    if (this.modulePathCache.has(filePath)) {\n      return this.modulePathCache.get(filePath) as PythonModule;\n    }\n\n    // Treat __init__.py as indicating the package's directory.\n    if (filePath.endsWith(`/__init__.py`)) {\n      filePath = filePath.slice(0, -\"__init__.py\".length);\n      // Remove trailing separator if it exists.\n      if (filePath.endsWith(\"/\")) {\n        filePath = filePath.slice(0, -\"/\".length);\n      }\n    } else if (filePath.endsWith(\".py\")) {\n      // Remove the .py extension from module files.\n      filePath = filePath.slice(0, -3);\n    }\n\n    let currentNode = this.pythonModule;\n    for (const part of filePath.split(\"/\")) {\n      if (part === \"\") continue; // Skip empty segments.\n      const candidateNode = currentNode.children.get(part);\n      if (!candidateNode) {\n        throw new Error(\n          `Module not found for path: ${filePath}. Check if the module is part of the project.`,\n        );\n      }\n      currentNode = candidateNode;\n    }\n\n    // Cache the result before returning\n    this.modulePathCache.set(filePath, currentNode);\n    return currentNode;\n  }\n\n  /**\n   * Resolves an internal module import from a module file.\n   *\n   * This method determines whether the provided import string is relative\n   * (i.e. starts with \".\") or absolute, and then delegates the resolution\n   * to the corresponding helper method. This function exclusively handles\n   * imports internal to the project; external libraries are not processed.\n   *\n   * Implements caching to avoid redundant resolution of the same imports.\n   *\n   * @param currentModule - The module where the import occurs.\n   * @param moduleName - The import string (e.g. \".helper\" or \"project.module\").\n   * @returns The resolved PythonModule if found, or undefined otherwise.\n   */\n  public resolveModule(\n    currentModule: PythonModule,\n    moduleName: string,\n  ): PythonModule | undefined {\n    // Create a cache key using the current module's name and the import string\n    const cacheKey = `${currentModule.fullName}:${moduleName}`;\n\n    // Check if we've already resolved this import in this context\n    if (this.importResolutionCache.has(cacheKey)) {\n      return this.importResolutionCache.get(cacheKey);\n    }\n\n    const pythonModule = moduleName.startsWith(\".\")\n      ? this.resolveRelativeModule(currentModule, moduleName)\n      : this.resolveAbsoluteImport(currentModule, moduleName);\n\n    // Don't return self-references\n    const result = pythonModule === currentModule ? undefined : pythonModule;\n\n    // Cache the result\n    this.importResolutionCache.set(cacheKey, result);\n    return result;\n  }\n\n  /**\n   * Resolves a relative import for a given module.\n   *\n   * Relative import strings use leading dots to indicate how many levels up\n   * in the package hierarchy to traverse before locating the target module.\n   * The remaining dotted path is then followed from the base package.\n   *\n   * Examples:\n   *  - \".helper\" resolves to a sibling module named \"helper\".\n   *  - \"..module\" moves up one level and resolves to a module named \"module\".\n   *\n   * @param currentModule - The module performing the import.\n   * @param moduleName - The relative import string (e.g. \".helper\" or \"..module.sub\").\n   * @returns The corresponding PythonModule if found, or undefined otherwise.\n   */\n  private resolveRelativeModule(\n    currentModule: PythonModule,\n    moduleName: string,\n  ): PythonModule | undefined {\n    // Count the number of leading dots to determine the package level.\n    let level = 0;\n    while (moduleName[level] === \".\") {\n      level++;\n    }\n    const remainder = moduleName.slice(level);\n\n    // If the current module is a regular file (not a package), start from its parent.\n    let baseModule = currentModule;\n    if (currentModule.path && !currentModule.path.endsWith(\"__init__.py\")) {\n      if (currentModule.parent) {\n        baseModule = currentModule.parent;\n      }\n    }\n\n    // Traverse upward in the hierarchy based on the number of dots.\n    for (let i = 1; i < level; i++) {\n      if (!baseModule.parent) return undefined;\n      baseModule = baseModule.parent;\n    }\n\n    // If no additional module path is provided, return the base package.\n    if (!remainder) return baseModule;\n\n    // Traverse downward using the remaining dotted path.\n    const parts = remainder.split(\".\");\n    let resolved: PythonModule | undefined = baseModule;\n    for (const part of parts) {\n      resolved = resolved?.children.get(part);\n      if (!resolved) return undefined;\n    }\n    return resolved;\n  }\n\n  /**\n   * Resolves an absolute import starting from the current module's package context.\n   *\n   * The method splits the import string into its dotted components and then\n   * traverses upward from the current module's package, checking each ancestor's\n   * children for a matching module path.\n   *\n   * For example, an import like \"module.sub\" will be searched for in the current\n   * package and, if not found, in higher-level packages.\n   *\n   * Uses intermediate caching to improve performance when resolving deep import paths.\n   *\n   * @param currentModule - The module performing the import.\n   * @param moduleName - The absolute import string (e.g. \"module.sub\" or \"project.utils.helper\").\n   * @returns The resolved PythonModule if it exists, or undefined otherwise.\n   */\n  private resolveAbsoluteImport(\n    currentModule: PythonModule,\n    moduleName: string,\n  ): PythonModule | undefined {\n    // Check if it's a standard library module using the Set for faster lookup\n    if (this.stdModuleSet.has(moduleName)) {\n      return undefined;\n    }\n\n    // Split import into segments\n    const parts = moduleName.split(\".\");\n\n    // Cache for intermediate resolution results during this method call\n    // Maps partial paths to their resolved modules to avoid redundant lookups\n    const partialResolutionCache = new Map<string, PythonModule | undefined>();\n\n    if (currentModule.path && !currentModule.path.endsWith(\"__init__.py\")) {\n      if (currentModule.parent) {\n        currentModule = currentModule.parent;\n      }\n    }\n\n    // Walk upward in the module hierarchy to find a matching candidate\n    let ancestor: PythonModule | undefined = currentModule;\n    while (ancestor) {\n      let candidate: PythonModule | undefined = ancestor;\n      let partialName = \"\";\n\n      // Try to resolve each part of the import path\n      for (const part of parts) {\n        // Build the partial import path as we go\n        partialName = partialName ? `${partialName}.${part}` : part;\n\n        // Check if we already resolved this partial path from the current ancestor\n        const cacheKey = `${ancestor.fullName}:${partialName}`;\n        if (partialResolutionCache.has(cacheKey)) {\n          candidate = partialResolutionCache.get(cacheKey);\n          continue;\n        }\n\n        // If not in cache, resolve the next part\n        const nextCandidate = candidate?.children.get(part);\n        candidate = nextCandidate;\n\n        // Cache this partial resolution result\n        partialResolutionCache.set(cacheKey, candidate);\n\n        if (!candidate) {\n          // Cache negative result to avoid redundant lookups\n          partialResolutionCache.set(cacheKey, undefined);\n          break;\n        }\n      }\n\n      if (candidate) {\n        return candidate;\n      }\n\n      ancestor = ancestor.parent;\n    }\n\n    return undefined;\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/moduleResolver/types.ts",
    "content": "export const PYTHON_MODULE_TYPE = \"module\";\nexport const PYTHON_PACKAGE_MODULE_TYPE = \"package\";\nexport const PYTHON_NAMESPACE_MODULE_TYPE = \"namespace\";\n\n/**\n * Represents the different types of Python modules that can exist in a project.\n *\n * - module: A standard Python file (.py)\n * - package: A directory with an __init__.py file\n * - namespace: A directory containing Python modules/packages but without an __init__.py\n */\nexport type PythonModuleType =\n  | typeof PYTHON_MODULE_TYPE\n  | typeof PYTHON_PACKAGE_MODULE_TYPE\n  | typeof PYTHON_NAMESPACE_MODULE_TYPE;\n\n/**\n * Represents a Python module or package within a project's module tree.\n *\n * Each module has a simple name, a full dotted path name (fullName),\n * a file system path, a type (regular module, package, or namespace),\n * a collection of child modules (if any), and an optional reference to its parent module.\n *\n * Examples:\n * - Regular module: example.py (type: \"module\")\n * - Package: directory with __init__.py (type: \"package\")\n * - Namespace package: directory without __init__.py (type: \"namespace\")\n */\nexport interface PythonModule {\n  /** The simple name of the module (e.g., 'math' from 'mypackage.math') */\n  name: string;\n  /** The full dotted name of the module (e.g., 'mypackage.math') */\n  fullName: string;\n  /** The filesystem path to the module */\n  path: string;\n  /** The type of module (regular, package, or namespace) */\n  type: PythonModuleType;\n  /** Map of child modules (relevant for packages and namespace packages) */\n  children: Map<string, PythonModule>;\n  /** Reference to the parent module (undefined for top-level modules) */\n  parent?: PythonModule;\n}\n"
  },
  {
    "path": "src/languagePlugins/python/symbolExtractor/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport type Parser from \"tree-sitter\";\nimport { PythonSymbolExtractor } from \"./index.ts\";\nimport { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport { PythonModuleResolver } from \"../moduleResolver/index.ts\";\nimport { PythonItemResolver } from \"../itemResolver/index.ts\";\nimport { PythonImportExtractor } from \"../importExtractor/index.ts\";\nimport { PythonUsageResolver } from \"../usageResolver/index.ts\";\nimport {\n  pythonLanguage,\n  pythonParser,\n} from \"../../../helpers/treeSitter/parsers.ts\";\nimport type { localConfigSchema } from \"../../../cli/middlewares/napiConfig.ts\";\nimport type z from \"zod\";\nimport { generatePythonDependencyManifest } from \"../../../manifest/dependencyManifest/python/index.ts\";\nimport type { DependencyManifest } from \"../../../manifest/dependencyManifest/types.ts\";\n\ndescribe(\"PythonSymbolExtractor\", () => {\n  // Helper to create a map of parsed files\n  function createParsedFiles(\n    files: Map<string, { path: string; content: string }>,\n  ) {\n    const parsedFiles = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n\n    for (const { path, content } of files.values()) {\n      const rootNode = pythonParser.parse(content, undefined, {\n        bufferSize: content.length + 10,\n      }).rootNode;\n      parsedFiles.set(path, { path, rootNode });\n    }\n\n    return parsedFiles;\n  }\n\n  // Helper to create a dependency manifest for testing\n  function createDependencyManifest(\n    files: Map<string, { path: string; content: string }>,\n  ): DependencyManifest {\n    const dependencyManifest = generatePythonDependencyManifest(files, {\n      language: pythonLanguage,\n      python: {\n        version: \"3.10\",\n      },\n      outDir: \"napi_out\",\n      project: {\n        include: [],\n        exclude: [],\n      },\n    } as z.infer<typeof localConfigSchema>);\n\n    return dependencyManifest;\n  }\n\n  function createSymbolExtractor(\n    files: Map<string, { path: string; content: string }>,\n  ) {\n    const parsedFiles = createParsedFiles(files);\n    const dependencyManifest = createDependencyManifest(files);\n\n    const exportExtractor = new PythonExportExtractor(\n      pythonParser,\n      parsedFiles,\n    );\n    const importExtractor = new PythonImportExtractor(\n      pythonParser,\n      parsedFiles,\n    );\n    const moduleResolver = new PythonModuleResolver(\n      new Set(parsedFiles.keys()),\n      \"3.10\",\n    );\n    const itemResolver = new PythonItemResolver(\n      exportExtractor,\n      importExtractor,\n      moduleResolver,\n    );\n    const usageResolver = new PythonUsageResolver(\n      pythonParser,\n      exportExtractor,\n    );\n\n    const symbolExtractor = new PythonSymbolExtractor(\n      pythonParser,\n      parsedFiles,\n      exportExtractor,\n      importExtractor,\n      moduleResolver,\n      itemResolver,\n      usageResolver,\n      dependencyManifest,\n    );\n\n    return symbolExtractor;\n  }\n\n  // Basic test setup\n  test(\"should extract a class and its dependencies\", () => {\n    // Create test files with a class and its dependencies\n    const files = new Map<string, { path: string; content: string }>([\n      [\n        \"main.py\",\n        {\n          path: \"main.py\",\n          content: `\nfrom utils import Helper\n\nclass MyClass:\n    def __init__(self):\n        self.helper = Helper()\n\n    def do_something(self):\n        return self.helper.help()\n\ndef foo():\n    return \"foo\"\n`.trim(),\n        },\n      ],\n      [\n        \"utils.py\",\n        {\n          path: \"utils.py\",\n          content: `\nclass Helper:\n    def help(self):\n        return \"Helping...\"\n`.trim(),\n        },\n      ],\n    ]);\n\n    const symbolExtractor = createSymbolExtractor(files);\n\n    const symbolsToExtract = new Map([\n      [\n        \"main.py\",\n        {\n          filePath: \"main.py\",\n          symbols: new Set([\"MyClass\"]),\n        },\n      ],\n    ]);\n\n    const result = symbolExtractor.extractSymbol(symbolsToExtract);\n\n    expect(result).toBeDefined();\n    expect(result.size).toBe(2);\n    expect(result.get(\"main.py\")).toBeDefined();\n    expect(result.get(\"main.py\")?.content.trim()).toEqual(\n      `\nfrom utils import Helper\n\nclass MyClass:\n    def __init__(self):\n        self.helper = Helper()\n\n    def do_something(self):\n        return self.helper.help()\n`.trim(),\n    );\n    expect(result.get(\"utils.py\")).toBeDefined();\n    expect(result.get(\"utils.py\")?.content.trim()).toEqual(\n      `\nclass Helper:\n    def help(self):\n        return \"Helping...\"\n`.trim(),\n    );\n  });\n\n  test(\"should extract multiple symbols with nested dependencies\", () => {\n    const files = new Map([\n      [\n        \"app.py\",\n        {\n          path: \"app.py\",\n          content: `\nfrom services.user_service import UserService\nfrom services.auth_service import AuthService\nfrom models.user import User\n\nclass Application:\n    def __init__(self):\n        self.user_service = UserService()\n        self.auth_service = AuthService()\n\n    def register_user(self, username, password):\n        user = User(username)\n        self.auth_service.set_password(user, password)\n        return self.user_service.save_user(user)\n\n    def authenticate(self, username, password):\n        return self.auth_service.verify(username, password)\n\ndef foo():\n    return \"foo\"\n`.trim(),\n        },\n      ],\n      [\n        \"services/user_service.py\",\n        {\n          path: \"services/user_service.py\",\n          content: `\nfrom models.user import User\nfrom database.repository import Repository\n\nclass UserService:\n    def __init__(self):\n        self.repository = Repository(\"users\")\n\n    def save_user(self, user):\n        return self.repository.save(user)\n\n    def get_user(self, username):\n        return self.repository.find_one({\"username\": username})\n\ndef bar():\n    return \"bar\"\n`.trim(),\n        },\n      ],\n      [\n        \"services/auth_service.py\",\n        {\n          path: \"services/auth_service.py\",\n          content: `\nfrom models.user import User\nfrom services.user_service import UserService\nimport hashlib\n\nclass AuthService:\n    def __init__(self):\n        self.user_service = UserService()\n\n    def set_password(self, user, password):\n        user.password_hash = hashlib.sha256(password.encode()).hexdigest()\n\n    def verify(self, username, password):\n        user = self.user_service.get_user(username)\n        if not user:\n            return False\n        password_hash = hashlib.sha256(password.encode()).hexdigest()\n        return user.password_hash == password_hash\n`.trim(),\n        },\n      ],\n      [\n        \"models/user.py\",\n        {\n          path: \"models/user.py\",\n          content: `\nclass User:\n    def __init__(self, username):\n        self.username = username\n        self.password_hash = None\n        self.is_active = True\n\n    def deactivate(self):\n        self.is_active = False\n\n    def __str__(self):\n        return f\"User({self.username})\"\n`.trim(),\n        },\n      ],\n      [\n        \"database/repository.py\",\n        {\n          path: \"database/repository.py\",\n          content: `\nclass Repository:\n    def __init__(self, collection_name):\n        self.collection_name = collection_name\n        self.data = {}\n\n    def save(self, entity):\n        key = getattr(entity, \"username\", id(entity))\n        self.data[key] = entity\n        return entity\n\n    def find_one(self, query):\n        username = query.get(\"username\")\n        if username in self.data:\n            return self.data[username]\n        return None\n`.trim(),\n        },\n      ],\n    ]);\n\n    const symbolExtractor = createSymbolExtractor(files);\n\n    const symbolsToExtract = new Map([\n      [\n        \"app.py\",\n        {\n          filePath: \"app.py\",\n          symbols: new Set([\"Application\"]),\n        },\n      ],\n    ]);\n\n    const result = symbolExtractor.extractSymbol(symbolsToExtract);\n\n    expect(result).toBeDefined();\n    expect(result.size).toBe(5);\n\n    expect(result.get(\"app.py\")).toBeDefined();\n    expect(result.get(\"app.py\")?.content.trim()).toEqual(\n      `\nfrom services.user_service import UserService\nfrom services.auth_service import AuthService\nfrom models.user import User\n\nclass Application:\n    def __init__(self):\n        self.user_service = UserService()\n        self.auth_service = AuthService()\n\n    def register_user(self, username, password):\n        user = User(username)\n        self.auth_service.set_password(user, password)\n        return self.user_service.save_user(user)\n\n    def authenticate(self, username, password):\n        return self.auth_service.verify(username, password)\n`.trim(),\n    );\n    expect(result.get(\"services/user_service.py\")).toBeDefined();\n    expect(result.get(\"services/user_service.py\")?.content.trim()).toEqual(\n      `\nfrom models.user import User\nfrom database.repository import Repository\n\nclass UserService:\n    def __init__(self):\n        self.repository = Repository(\"users\")\n\n    def save_user(self, user):\n        return self.repository.save(user)\n\n    def get_user(self, username):\n        return self.repository.find_one({\"username\": username})\n    `.trim(),\n    );\n    expect(result.get(\"services/auth_service.py\")).toBeDefined();\n    expect(result.get(\"services/auth_service.py\")?.content.trim()).toEqual(\n      `\nfrom models.user import User\nfrom services.user_service import UserService\nimport hashlib\n\nclass AuthService:\n    def __init__(self):\n        self.user_service = UserService()\n\n    def set_password(self, user, password):\n        user.password_hash = hashlib.sha256(password.encode()).hexdigest()\n\n    def verify(self, username, password):\n        user = self.user_service.get_user(username)\n        if not user:\n            return False\n        password_hash = hashlib.sha256(password.encode()).hexdigest()\n        return user.password_hash == password_hash\n`.trim(),\n    );\n    expect(result.get(\"models/user.py\")).toBeDefined();\n    expect(result.get(\"models/user.py\")?.content.trim()).toEqual(\n      `\nclass User:\n    def __init__(self, username):\n        self.username = username\n        self.password_hash = None\n        self.is_active = True\n\n    def deactivate(self):\n        self.is_active = False\n\n    def __str__(self):\n        return f\"User({self.username})\"\n`.trim(),\n    );\n    expect(result.get(\"database/repository.py\")).toBeDefined();\n    expect(result.get(\"database/repository.py\")?.content.trim()).toEqual(\n      `\nclass Repository:\n    def __init__(self, collection_name):\n        self.collection_name = collection_name\n        self.data = {}\n\n    def save(self, entity):\n        key = getattr(entity, \"username\", id(entity))\n        self.data[key] = entity\n        return entity\n\n    def find_one(self, query):\n        username = query.get(\"username\")\n        if username in self.data:\n            return self.data[username]\n        return None\n`.trim(),\n    );\n  });\n\n  test(\"should remove invalid normal imports\", () => {\n    const files = new Map([\n      [\n        \"main.py\",\n        {\n          path: \"main.py\",\n          content: `\nimport valid_module\nimport invalid_module\n\nclass MyClass:\n    def __init__(self):\n        self.helper = valid_module.valid_function()\n\nclass AnotherClass:\n    def __init__(self):\n        self.invalid = invalid_module.something()\n  `.trim(),\n        },\n      ],\n      [\n        \"valid_module.py\",\n        {\n          path: \"valid_module.py\",\n          content: `\ndef valid_function():\n    return \"I'm valid\"\n  `.trim(),\n        },\n      ],\n      [\n        \"invalid_module.py\",\n        {\n          path: \"invalid_module.py\",\n          content: `\ndef something():\n    return \"I'll be removed\"\n  `.trim(),\n        },\n      ],\n    ]);\n\n    const symbolExtractor = createSymbolExtractor(files);\n\n    // Only extract MyClass which depends on Helper but not on invalid_module\n    const symbolsToExtract = new Map([\n      [\n        \"main.py\",\n        {\n          filePath: \"main.py\",\n          symbols: new Set([\"MyClass\"]),\n        },\n      ],\n    ]);\n\n    const result = symbolExtractor.extractSymbol(symbolsToExtract);\n\n    expect(result.size).toBe(2);\n    expect(result.get(\"main.py\")).toBeDefined();\n    expect(result.get(\"main.py\")?.content.trim()).toEqual(\n      `\nimport valid_module\n\n\nclass MyClass:\n    def __init__(self):\n        self.helper = valid_module.valid_function()\n  `.trim(),\n    );\n    expect(result.get(\"valid_module.py\")).toBeDefined();\n    expect(result.get(\"valid_module.py\")?.content.trim()).toEqual(\n      `def valid_function():\n    return \"I'm valid\"\n  `.trim(),\n    );\n  });\n\n  test(\"should remove invalid from imports\", () => {\n    const files = new Map([\n      [\n        \"main.py\",\n        {\n          path: \"main.py\",\n          content: `\nfrom valid_module import valid_function\nfrom invalid_module import something\n\nclass MyClass:\n    def __init__(self):\n        self.helper = valid_function()\n\nclass AnotherClass:\n    def __init__(self):\n        self.invalid = something()\n  `.trim(),\n        },\n      ],\n      [\n        \"valid_module.py\",\n        {\n          path: \"valid_module.py\",\n          content: `\ndef valid_function():\n    return \"I'm valid\"\n  `.trim(),\n        },\n      ],\n      [\n        \"invalid_module.py\",\n        {\n          path: \"invalid_module.py\",\n          content: `\ndef something():\n    return \"I'll be removed\"\n  `.trim(),\n        },\n      ],\n    ]);\n\n    const symbolExtractor = createSymbolExtractor(files);\n\n    const symbolsToExtract = new Map([\n      [\n        \"main.py\",\n        {\n          filePath: \"main.py\",\n          symbols: new Set([\"MyClass\"]),\n        },\n      ],\n    ]);\n\n    const result = symbolExtractor.extractSymbol(symbolsToExtract);\n\n    expect(result.size).toBe(2);\n    expect(result.get(\"main.py\")).toBeDefined();\n    expect(result.get(\"main.py\")?.content.trim()).toEqual(\n      `from valid_module import valid_function\n\n\nclass MyClass:\n    def __init__(self):\n        self.helper = valid_function()\n  `.trim(),\n    );\n    expect(result.get(\"valid_module.py\")).toBeDefined();\n    expect(result.get(\"valid_module.py\")?.content.trim()).toEqual(\n      `def valid_function():\n    return \"I'm valid\"\n  `.trim(),\n    );\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/symbolExtractor/index.ts",
    "content": "import Parser from \"tree-sitter\";\nimport { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport { PythonModuleResolver } from \"../moduleResolver/index.ts\";\nimport { PythonItemResolver } from \"../itemResolver/index.ts\";\nimport { PythonImportExtractor } from \"../importExtractor/index.ts\";\nimport { PythonUsageResolver } from \"../usageResolver/index.ts\";\nimport type { DependencyManifest } from \"../../../manifest/dependencyManifest/types.ts\";\nimport { removeIndexesFromSourceCode } from \"../../../helpers/sourceCode/index.ts\";\nimport {\n  FROM_IMPORT_STATEMENT_TYPE,\n  NORMAL_IMPORT_STATEMENT_TYPE,\n} from \"../importExtractor/types.ts\";\n\n/**\n * Python Symbol Extractor\n *\n * This class extracts a Python symbol and all its dependencies from a project\n * while preserving the original project structure.\n */\nexport class PythonSymbolExtractor {\n  private originalFiles: Map<\n    string,\n    { path: string; rootNode: Parser.SyntaxNode }\n  >;\n  private parser: Parser;\n  private exportExtractor: PythonExportExtractor;\n  public importExtractor: PythonImportExtractor;\n  public moduleResolver: PythonModuleResolver;\n  public itemResolver: PythonItemResolver;\n  public usageResolver: PythonUsageResolver;\n  private dependencyManifest: DependencyManifest;\n  private errorNodeQuery: Parser.Query;\n  private processedSymbols: Set<string> = new Set<string>();\n\n  /**\n   * Creates a new Python Symbol Extractor\n   */\n  constructor(\n    parser: Parser,\n    originalFiles: Map<string, { path: string; rootNode: Parser.SyntaxNode }>,\n    exportExtractor: PythonExportExtractor,\n    importExtractor: PythonImportExtractor,\n    moduleResolver: PythonModuleResolver,\n    itemResolver: PythonItemResolver,\n    usageResolver: PythonUsageResolver,\n    dependencyManifest: DependencyManifest,\n  ) {\n    this.parser = parser;\n    this.originalFiles = originalFiles;\n    this.exportExtractor = exportExtractor;\n    this.importExtractor = importExtractor;\n    this.moduleResolver = moduleResolver;\n    this.itemResolver = itemResolver;\n    this.usageResolver = usageResolver;\n    this.dependencyManifest = dependencyManifest;\n    this.errorNodeQuery = new Parser.Query(\n      this.parser.getLanguage(),\n      \"(ERROR) @error\",\n    );\n  }\n\n  /**\n   * Extracts a symbol and all its dependencies from the project\n   *\n   * @param symbolsToExtract A list of symbols to extract\n   * @returns Symbol extraction result\n   */\n  public extractSymbol(\n    symbolsMap: Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >,\n  ) {\n    // 1. Identify symbols to keep and their dependencies recursively\n    console.info(\"Finding dependencies for all symbols to extract...\");\n    const symbolsToKeep = this.identifySymbolsAndDependencies(symbolsMap);\n\n    let totalSymbols = 0;\n    for (const { symbols } of symbolsToKeep.values()) {\n      totalSymbols += symbols.size;\n    }\n    console.info(\n      `Found ${totalSymbols} symbols to keep across ${symbolsToKeep.size} files`,\n    );\n\n    // 2. Extract all the symbols\n    console.info(`Extracting files in-memory...`);\n    const extractedFiles = this.extractFilesInMemory(symbolsToKeep);\n\n    // 3. Clean error nodes\n    console.info(\"Cleaning error nodes from files...\");\n    this.cleanErrorNodes(extractedFiles);\n\n    // 4. Remove invalid imports.\n    console.info(\"Removing invalid imports...\");\n    this.cleanImports(extractedFiles);\n\n    // 5. Clean error nodes\n    console.info(\"Cleaning error nodes from files...\");\n    this.cleanErrorNodes(extractedFiles);\n\n    console.info(\"Successfully extracted all symbols!\");\n\n    // Return the extracted files\n    return extractedFiles;\n  }\n\n  /**\n   * Identifies symbols and their dependencies to keep\n   */\n  private identifySymbolsAndDependencies(\n    symbolsMap: Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >,\n  ) {\n    const symbolsToKeep = new Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >();\n\n    symbolsMap.values().forEach(({ filePath, symbols }) => {\n      symbols.forEach((symbol) => {\n        this.addSymbolAndDependencies(filePath, symbol, symbolsToKeep);\n      });\n    });\n\n    return symbolsToKeep;\n  }\n\n  private addSymbolAndDependencies(\n    filePath: string,\n    symbolName: string,\n    symbolsToKeep = new Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >(),\n  ) {\n    // Create a unique key for this symbol to track processing\n    const symbolKey = `${filePath}:${symbolName}`;\n\n    // If we've already processed this symbol, return to avoid circular dependency\n    if (this.processedSymbols.has(symbolKey)) {\n      return symbolsToKeep;\n    }\n\n    // Mark this symbol as being processed\n    this.processedSymbols.add(symbolKey);\n\n    // Add the symbol itself to the filesToKeep map\n    let fileToKeep = symbolsToKeep.get(filePath);\n    if (!fileToKeep) {\n      fileToKeep = {\n        filePath,\n        symbols: new Set(),\n      };\n    }\n    fileToKeep.symbols.add(symbolName);\n    symbolsToKeep.set(filePath, fileToKeep);\n\n    const fileManifest = this.dependencyManifest[filePath];\n    if (!fileManifest) {\n      throw new Error(`Could not find dependency file for ${filePath}`);\n    }\n\n    const symbolManifest = fileManifest.symbols[symbolName];\n    if (!symbolManifest) {\n      throw new Error(`Could not find symbol manifest for ${symbolName}`);\n    }\n\n    for (const dependency of Object.values(symbolManifest.dependencies)) {\n      // skip external dependencies\n      if (dependency.isExternal) {\n        continue;\n      }\n\n      // add the file to the filesToKeep map\n      if (!symbolsToKeep.has(dependency.id)) {\n        symbolsToKeep.set(dependency.id, {\n          filePath: dependency.id,\n          symbols: new Set(),\n        });\n      }\n      let fileToKeep = symbolsToKeep.get(dependency.id);\n      if (!fileToKeep) {\n        fileToKeep = {\n          filePath: dependency.id,\n          symbols: new Set(),\n        };\n      }\n      Object.values(dependency.symbols).forEach((depSymbol) => {\n        // add the symbol and its dependencies to the filesToKeep map\n        this.addSymbolAndDependencies(dependency.id, depSymbol, symbolsToKeep);\n      });\n    }\n\n    return symbolsToKeep;\n  }\n\n  /**\n   * Extracts files in memory\n   */\n  private extractFilesInMemory(\n    symbolsToKeep: Map<\n      string,\n      {\n        filePath: string;\n        symbols: Set<string>;\n      }\n    >,\n  ) {\n    const extractedFiles = new Map<string, { path: string; content: string }>();\n\n    // Process each file to keep\n    for (const { filePath, symbols } of symbolsToKeep.values()) {\n      // Get the file from originalFiles using the exact file path\n      const file = this.originalFiles.get(filePath);\n      if (!file) {\n        throw new Error(`Could not find file ${filePath} for extraction`);\n      }\n\n      // Get the full file content\n      const fileContent = file.rootNode.text;\n\n      // Get exported symbols\n      const exports = this.exportExtractor.getSymbols(filePath);\n\n      const indexesToRemove: { startIndex: number; endIndex: number }[] = [];\n\n      for (const symbol of exports.symbols) {\n        if (!symbols.has(symbol.id)) {\n          for (const node of symbol.nodes) {\n            indexesToRemove.push({\n              startIndex: node.startIndex,\n              endIndex: node.endIndex,\n            });\n          }\n        }\n      }\n\n      const cleanedContent = removeIndexesFromSourceCode(\n        fileContent,\n        indexesToRemove,\n      );\n\n      // Add the cleaned content to our extracted files\n      extractedFiles.set(filePath, {\n        path: filePath,\n        content: cleanedContent,\n      });\n    }\n\n    return extractedFiles;\n  }\n\n  /**\n   * Cleans error nodes from extracted files\n   */\n  private cleanErrorNodes(\n    extractedFiles: Map<string, { path: string; content: string }>,\n  ) {\n    for (const [filePath, fileData] of extractedFiles.entries()) {\n      let sourceCode = fileData.content;\n\n      // Need to remove error nodes one at a time, as fixing one error\n      // might make previously invalid code valid\n      while (true) {\n        // Parse the file content\n        const tree = this.parser.parse(sourceCode);\n\n        // Find error nodes\n        const captures = this.errorNodeQuery.captures(tree.rootNode);\n\n        if (captures.length === 0) {\n          break;\n        }\n\n        const firstCapture = captures[0];\n\n        sourceCode = sourceCode.substring(0, firstCapture.node.startIndex) +\n          sourceCode.substring(firstCapture.node.endIndex);\n      }\n\n      extractedFiles.set(filePath, {\n        path: filePath,\n        content: sourceCode,\n      });\n    }\n  }\n\n  /**\n   * Removes invalid imports from the extracted files\n   * Invalid imports are those that used to resolve to internal symbols or modules\n   * but no longer do after extraction.\n   */\n  private cleanImports(\n    extractedFiles: Map<string, { path: string; content: string }>,\n  ) {\n    const extractedParsedFiles = new Map<\n      string,\n      { path: string; rootNode: Parser.SyntaxNode }\n    >();\n    for (const { path, content } of extractedFiles.values()) {\n      const tree = this.parser.parse(content, undefined, {\n        bufferSize: content.length + 10,\n      });\n      extractedParsedFiles.set(path, { path, rootNode: tree.rootNode });\n    }\n\n    const extractedFilesExportExtractor = new PythonExportExtractor(\n      this.parser,\n      extractedParsedFiles,\n    );\n    const extractedFilesImportExtractor = new PythonImportExtractor(\n      this.parser,\n      extractedParsedFiles,\n    );\n    const extractedFilesModuleResolver = new PythonModuleResolver(\n      new Set(extractedParsedFiles.keys()),\n      this.moduleResolver.pythonVersion,\n    );\n    const extractedFilesItemResolver = new PythonItemResolver(\n      extractedFilesExportExtractor,\n      extractedFilesImportExtractor,\n      extractedFilesModuleResolver,\n    );\n    const extractedFilesUsageResolver = new PythonUsageResolver(\n      this.parser,\n      extractedFilesExportExtractor,\n    );\n\n    for (const { path, content } of extractedFiles.values()) {\n      const originalFile = this.originalFiles.get(path) as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n      const extractedFile = extractedParsedFiles.get(path) as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n\n      // Get all imports in the file\n      const originalImportStatements = this.importExtractor.getImportStatements(\n        originalFile.path,\n      );\n      const extractedFilesImportStatements = extractedFilesImportExtractor\n        .getImportStatements(\n          extractedFile.path,\n        );\n\n      const indexesToRemove: { startIndex: number; endIndex: number }[] = [];\n\n      // Check each import statement to see if it's still valid\n      for (\n        const extractedFilesImportStatement of extractedFilesImportStatements\n      ) {\n        if (\n          extractedFilesImportStatement.type === NORMAL_IMPORT_STATEMENT_TYPE\n        ) {\n          const indexesToRemoveForImport: {\n            startIndex: number;\n            endIndex: number;\n          }[] = [];\n          // Handle normal imports (import foo, import foo.bar)\n          for (const member of extractedFilesImportStatement.members) {\n            // resolve the module from the original file\n            const originalFileModule = this.moduleResolver\n              .getModuleFromFilePath(originalFile.path);\n            const resolvedOriginalModule = this.moduleResolver.resolveModule(\n              originalFileModule,\n              member.identifierNode.text,\n            );\n\n            if (!resolvedOriginalModule) {\n              // If the module doesn't resolve, it's an external module\n\n              // Check if the import is used in original code\n              const originalUsageNode = this.usageResolver.getUsageNode(\n                originalFile.rootNode,\n                originalImportStatements.map((s) => s.node),\n                member.aliasNode?.text || member.identifierNode.text,\n              );\n\n              // Check if the import is used in extracted code\n              const extractedUsageNode = extractedFilesUsageResolver\n                .getUsageNode(\n                  extractedFile.rootNode,\n                  extractedFilesImportStatements.map((s) => s.node),\n                  member.aliasNode?.text || member.identifierNode.text,\n                );\n\n              // If the import was used in original code but not in extracted code, remove it\n              if (\n                originalUsageNode.length > 0 &&\n                extractedUsageNode.length === 0\n              ) {\n                const startIndex = member.node.startIndex;\n                let endIndex = member.node.endIndex;\n                if (\n                  member.node.nextSibling &&\n                  member.node.nextSibling.type === \",\"\n                ) {\n                  endIndex = member.node.nextSibling.endIndex;\n                }\n                indexesToRemoveForImport.push({\n                  startIndex,\n                  endIndex,\n                });\n              }\n              continue;\n            }\n\n            // resolve the module from the extracted file\n            const extractedFileModule = extractedFilesModuleResolver\n              .getModuleFromFilePath(\n                extractedFile.path,\n              );\n            const resolvedExtractedFileModule = extractedFilesModuleResolver\n              .resolveModule(\n                extractedFileModule,\n                member.identifierNode.text,\n              );\n\n            if (!resolvedExtractedFileModule) {\n              // the module does not resolve anymore, we remove it\n              const startIndex = member.node.startIndex;\n              let endIndex = member.node.endIndex;\n              if (\n                member.node.nextSibling &&\n                member.node.nextSibling.type === \",\"\n              ) {\n                endIndex = member.node.nextSibling.endIndex;\n              }\n              indexesToRemoveForImport.push({\n                startIndex,\n                endIndex,\n              });\n              continue;\n            }\n\n            // check if used become unused. if so, we should remove it\n            const originalUsageNode = this.usageResolver.getUsageNode(\n              originalFile.rootNode,\n              originalImportStatements.map((s) => s.node),\n              member.aliasNode?.text || member.identifierNode.text,\n            );\n            const extractedUsageNode = extractedFilesUsageResolver.getUsageNode(\n              extractedFile.rootNode,\n              extractedFilesImportStatements.map((s) => s.node),\n              member.aliasNode?.text || member.identifierNode.text,\n            );\n\n            if (\n              originalUsageNode.length > 0 &&\n              extractedUsageNode.length === 0\n            ) {\n              // the import was used in original code but not in extracted code, we remove it\n              const startIndex = member.node.startIndex;\n              let endIndex = member.node.endIndex;\n              if (\n                member.node.nextSibling &&\n                member.node.nextSibling.type === \",\"\n              ) {\n                endIndex = member.node.nextSibling.endIndex;\n              }\n              indexesToRemoveForImport.push({\n                startIndex,\n                endIndex,\n              });\n            }\n          }\n\n          if (indexesToRemoveForImport.length > 0) {\n            if (\n              indexesToRemoveForImport.length ===\n                extractedFilesImportStatement.members.length\n            ) {\n              // remove the whole import\n              indexesToRemove.push({\n                startIndex: extractedFilesImportStatement.node.startIndex,\n                endIndex: extractedFilesImportStatement.node.endIndex,\n              });\n            } else {\n              // remove the members that are not kept\n              indexesToRemove.push(...indexesToRemoveForImport);\n            }\n          }\n        } else if (\n          extractedFilesImportStatement.type === FROM_IMPORT_STATEMENT_TYPE\n        ) {\n          // Handle from imports (from foo import bar)\n          const member = extractedFilesImportStatement.members[0];\n\n          const originalFileModule = this.moduleResolver.getModuleFromFilePath(\n            originalFile.path,\n          );\n          const resolvedOriginalModule = this.moduleResolver.resolveModule(\n            originalFileModule,\n            member.identifierNode.text,\n          );\n\n          // If the module doesn't resolve in the original files, it's external\n          if (!resolvedOriginalModule) {\n            if (member.isWildcardImport) {\n              // if wildcard, we skip it, no way to check if it's used or not\n              continue;\n            }\n\n            // Check each item in the import if used or not\n            if (!member.items) {\n              throw new Error(\n                `Could not find items for import ${member.identifierNode.text} in ${originalFile.path}`,\n              );\n            }\n\n            const indexesToRemoveForImport: {\n              startIndex: number;\n              endIndex: number;\n            }[] = [];\n\n            for (const item of member.items) {\n              // Check if the imported item is used in original code\n              const originalUsageNode = this.usageResolver.getUsageNode(\n                originalFile.rootNode,\n                originalImportStatements.map((s) => s.node),\n                item.aliasNode?.text || item.identifierNode.text,\n              );\n\n              // Check if the imported item is used in extracted code\n              const extractedUsageNode = extractedFilesUsageResolver\n                .getUsageNode(\n                  extractedFile.rootNode,\n                  extractedFilesImportStatements.map((s) => s.node),\n                  item.aliasNode?.text || item.identifierNode.text,\n                );\n\n              // If the item was used in original code but not in extracted code, remove it\n              if (\n                originalUsageNode.length > 0 &&\n                extractedUsageNode.length === 0\n              ) {\n                const startIndex = item.node.startIndex;\n                let endIndex = item.node.endIndex;\n                if (\n                  item.node.nextSibling &&\n                  item.node.nextSibling.type === \",\"\n                ) {\n                  endIndex = item.node.nextSibling.endIndex;\n                }\n                indexesToRemoveForImport.push({\n                  startIndex,\n                  endIndex,\n                });\n              }\n            }\n\n            if (indexesToRemoveForImport.length > 0) {\n              if (indexesToRemoveForImport.length === member.items.length) {\n                // remove the whole import\n                indexesToRemove.push({\n                  startIndex: extractedFilesImportStatement.node.startIndex,\n                  endIndex: extractedFilesImportStatement.node.endIndex,\n                });\n              } else {\n                // remove the items that are not kept\n                indexesToRemove.push(...indexesToRemoveForImport);\n              }\n            }\n            continue;\n          }\n\n          // Check if the module can be resolved in the extracted files\n          const extractedFileModule = extractedFilesModuleResolver\n            .getModuleFromFilePath(\n              extractedFile.path,\n            );\n          const resolvedExtractedModule = extractedFilesModuleResolver\n            .resolveModule(\n              extractedFileModule,\n              member.identifierNode.text,\n            );\n\n          // If the module doesn't resolve anymore, remove the entire import\n          if (!resolvedExtractedModule) {\n            indexesToRemove.push({\n              startIndex: extractedFilesImportStatement.node.startIndex,\n              endIndex: extractedFilesImportStatement.node.endIndex,\n            });\n            continue;\n          }\n\n          // if the module resolve we need to check each item in the import\n          if (member.isWildcardImport) {\n            // if wildcard, we skip it\n            continue;\n          }\n\n          // if the member is not a wildcard, we need to check if each items\n          const indexesToRemoveForImport: {\n            startIndex: number;\n            endIndex: number;\n          }[] = [];\n\n          if (!member.items) {\n            throw new Error(\n              `Could not find items for import ${member.identifierNode.text} in ${originalFile.path}`,\n            );\n          }\n\n          for (const item of member.items) {\n            // resolve the item from the original file\n            const originalItem = this.itemResolver.resolveItem(\n              resolvedOriginalModule,\n              item.identifierNode.text,\n            );\n\n            if (!originalItem) {\n              // if the item doesn't resolve in the original file, it's external - keep it\n\n              // Check if the imported item is used in original code\n              const originalUsageNode = this.usageResolver.getUsageNode(\n                originalFile.rootNode,\n                originalImportStatements.map((s) => s.node),\n                item.aliasNode?.text || item.identifierNode.text,\n              );\n\n              // Check if the imported item is used in extracted code\n              const extractedUsageNode = extractedFilesUsageResolver\n                .getUsageNode(\n                  extractedFile.rootNode,\n                  extractedFilesImportStatements.map((s) => s.node),\n                  item.aliasNode?.text || item.identifierNode.text,\n                );\n\n              // If the item was used in original code but not in extracted code, remove it\n              if (\n                originalUsageNode.length > 0 &&\n                extractedUsageNode.length === 0\n              ) {\n                const startIndex = item.node.startIndex;\n                let endIndex = item.node.endIndex;\n                if (\n                  item.node.nextSibling &&\n                  item.node.nextSibling.type === \",\"\n                ) {\n                  endIndex = item.node.nextSibling.endIndex;\n                }\n                indexesToRemoveForImport.push({\n                  startIndex,\n                  endIndex,\n                });\n              }\n              continue;\n            }\n\n            const extractedItem = extractedFilesItemResolver.resolveItem(\n              resolvedExtractedModule,\n              item.identifierNode.text,\n            );\n\n            if (!extractedItem) {\n              // if the item doesn't resolve we need to remove it\n              const startIndex = item.node.startIndex;\n              let endIndex = item.node.endIndex;\n              if (item.node.nextSibling && item.node.nextSibling.type === \",\") {\n                endIndex = item.node.nextSibling.endIndex;\n              }\n              indexesToRemoveForImport.push({\n                startIndex,\n                endIndex,\n              });\n              continue;\n            }\n\n            // Check if the imported item is used in original code\n            const originalUsageNode = this.usageResolver.getUsageNode(\n              originalFile.rootNode,\n              originalImportStatements.map((s) => s.node),\n              item.aliasNode?.text || item.identifierNode.text,\n            );\n\n            // Check if the imported item is used in extracted code\n            const extractedUsageNode = extractedFilesUsageResolver.getUsageNode(\n              extractedFile.rootNode,\n              extractedFilesImportStatements.map((s) => s.node),\n              item.aliasNode?.text || item.identifierNode.text,\n            );\n\n            // If the item was used in original code but not in extracted code, remove it\n            if (\n              originalUsageNode.length > 0 &&\n              extractedUsageNode.length === 0\n            ) {\n              const startIndex = item.node.startIndex;\n              let endIndex = item.node.endIndex;\n              if (item.node.nextSibling && item.node.nextSibling.type === \",\") {\n                endIndex = item.node.nextSibling.endIndex;\n              }\n              indexesToRemoveForImport.push({\n                startIndex,\n                endIndex,\n              });\n            }\n          }\n\n          if (indexesToRemoveForImport.length > 0) {\n            if (indexesToRemoveForImport.length === member.items.length) {\n              // remove the whole import\n              indexesToRemove.push({\n                startIndex: extractedFilesImportStatement.node.startIndex,\n                endIndex: extractedFilesImportStatement.node.endIndex,\n              });\n            } else {\n              // remove the items that are not kept\n              indexesToRemove.push(...indexesToRemoveForImport);\n            }\n          }\n        }\n      }\n\n      // Remove the invalid imports from the file content\n      if (indexesToRemove.length > 0) {\n        const newContent = removeIndexesFromSourceCode(\n          content,\n          indexesToRemove,\n        );\n        // Update the file in the map\n        extractedFiles.set(path, { path, content: newContent });\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/symbolExtractor/types.ts",
    "content": ""
  },
  {
    "path": "src/languagePlugins/python/usageResolver/index.test.ts",
    "content": "import { beforeEach, describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport type Parser from \"tree-sitter\";\nimport { pythonParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport { PythonModuleResolver } from \"../moduleResolver/index.ts\";\nimport { PythonUsageResolver } from \"./index.ts\";\nimport { PythonImportExtractor } from \"../importExtractor/index.ts\";\nimport type { PythonModule } from \"../moduleResolver/types.ts\";\nimport type { InternalUsage } from \"./types.ts\";\nimport type { PythonSymbol } from \"../exportExtractor/types.ts\";\n\ndescribe(\"USageResolver, getUsageNode\", () => {\n  let parser: Parser;\n  let exportExtractor: PythonExportExtractor;\n  let resolver: PythonUsageResolver;\n  let files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n\n  beforeEach(() => {\n    parser = pythonParser;\n    files = new Map();\n    exportExtractor = new PythonExportExtractor(parser, files);\n    resolver = new PythonUsageResolver(parser, exportExtractor);\n  });\n\n  test(\"should return the correct usage of a symbol that is a leaf and root node\", () => {\n    const targetNode = parser.parse(`\n        foo()\n      `).rootNode;\n\n    // as a leaf node\n    const usageNode = resolver.getUsageNode(targetNode, [], \"foo\");\n\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"foo\");\n  });\n\n  test(\"should return the correct usage of a chain attribute that is both a leaf and root node\", () => {\n    const targetNode = parser.parse(`\n        foo.bar()\n      `).rootNode;\n\n    const usageNode = resolver.getUsageNode(targetNode, [], \"foo.bar\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"foo.bar\");\n  });\n\n  test(\"should correctly identify parts of a complex attribute chain\", () => {\n    const targetNode = parser.parse(`\n      foo.f.o()\n    `).rootNode;\n\n    // Test 'foo' - should be root but not leaf\n    let usageNode = resolver.getUsageNode(targetNode, [], \"foo\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"foo\");\n\n    // Test 'foo.f' - should be root but not leaf\n    usageNode = resolver.getUsageNode(targetNode, [], \"foo.f\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"foo.f\");\n  });\n\n  test(\"should correctly identify parts of a complex attribute chain with brackets\", () => {\n    const targetNode = parser.parse(`\n      foo.bar.baz()[qux]\n    `).rootNode;\n\n    // Test 'foo' - should be root but not leaf\n    let usageNode = resolver.getUsageNode(targetNode, [], \"foo\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"foo\");\n\n    // Test 'bar' - should be neither root nor leaf\n    usageNode = resolver.getUsageNode(targetNode, [], \"bar\");\n    expect(usageNode.length).toBe(0);\n\n    // Test 'baz' - should be neither root nor leaf\n    usageNode = resolver.getUsageNode(targetNode, [], \"baz\");\n    expect(usageNode.length).toBe(0);\n\n    // Text 'foo.bar.baz' - should be root and leaf\n    usageNode = resolver.getUsageNode(targetNode, [], \"foo.bar.baz\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"foo.bar.baz\");\n\n    // Test 'qux' - should be leaf and a root\n    usageNode = resolver.getUsageNode(targetNode, [], \"qux\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"qux\");\n  });\n\n  test(\"should handle method calls with arguments\", () => {\n    const targetNode = parser.parse(`\n      foo.bar(1, x.y.z)\n    `).rootNode;\n\n    // Test 'foo' should be root but not leaf\n    let usageNode = resolver.getUsageNode(targetNode, [], \"foo\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"foo\");\n    // Test 'bar' should not resolve as a leaf node or root node\n    usageNode = resolver.getUsageNode(targetNode, [], \"bar\");\n    expect(usageNode.length).toBe(0);\n\n    // Test 'x' in x.y.z should be root but not leaf\n    usageNode = resolver.getUsageNode(targetNode, [], \"x\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"x\");\n\n    // Test 'z' in x.y.z should not resolve as a leaf node and root node\n    usageNode = resolver.getUsageNode(targetNode, [], \"z\");\n    expect(usageNode.length).toBe(0);\n\n    // Test 'x.y.z' should be root and leaf\n    usageNode = resolver.getUsageNode(targetNode, [], \"x.y.z\");\n    expect(usageNode.length).toBe(1);\n    expect(usageNode[0].text).toBe(\"x.y.z\");\n  });\n});\n\ndescribe(\"UsageResolver\", () => {\n  let parser: Parser;\n  let exportExtractor: PythonExportExtractor;\n  let resolver: PythonUsageResolver;\n  let files: Map<string, { path: string; rootNode: Parser.SyntaxNode }>;\n\n  beforeEach(() => {\n    parser = pythonParser;\n\n    // Create test files\n    files = new Map([\n      // Basic module with symbols\n      [\n        \"module_a.py\",\n        {\n          path: \"module_a.py\",\n          rootNode: parser.parse(`\ndef function_a():\n    return \"Hello from function A\"\n\ndef function_b():\n    return \"Hello from function B\"\n\nCONSTANT_A = 42\n          `).rootNode,\n        },\n      ],\n      // Module that imports and partially uses symbols\n      [\n        \"module_b.py\",\n        {\n          path: \"module_b.py\",\n          rootNode: parser.parse(`\nfrom module_a import function_a, function_b as renamed_b, CONSTANT_A\nimport external_module\n\ndef use_imports():\n    result = function_a()\n    another = renamed_b()\n    external_module.do_something()\n    return result\n          `).rootNode,\n        },\n      ],\n      // Module that imports symbols with ambiguous aliasing\n      [\n        \"module_c.py\",\n        {\n          path: \"module_c.py\",\n          rootNode: parser.parse(`\nfrom module_a import function_a as function_b, function_b as function_a\n\ndef use_ambiguous_imports():\n    result_a = function_a()\n    result_b = function_b()\n    return result_a, result_b\n          `).rootNode,\n        },\n      ],\n      // Module with direct module references\n      [\n        \"module_with_submodules.py\",\n        {\n          path: \"module_with_submodules.py\",\n          rootNode: parser.parse(`\nimport module_a\n\ndef use_module():\n    # Direct module reference\n    module_a.function_a()\n    module_a.function_b()\n        `).rootNode,\n        },\n      ],\n      // Package root\n      [\n        \"package/__init__.py\",\n        {\n          path: \"package/__init__.py\",\n          rootNode: parser.parse(`\nfrom .submod import submod_func\n\ndef init_func():\n    return \"Init function\"\n        `).rootNode,\n        },\n      ],\n      // Package submodule\n      [\n        \"package/submod.py\",\n        {\n          path: \"package/submod.py\",\n          rootNode: parser.parse(`\ndef submod_func():\n    return \"Submodule function\"\n        `).rootNode,\n        },\n      ],\n      // Module using a package\n      [\n        \"use_package.py\",\n        {\n          path: \"use_package.py\",\n          rootNode: parser.parse(`\nimport package\nfrom package import submod\n\ndef use_package_func():\n    # Use the package directly\n    package.init_func()\n    # Use a submodule\n    package.submod.submod_func()\n    # Use imported submodule\n    submod.submod_func()\n        `).rootNode,\n        },\n      ],\n      // Nested package root\n      [\n        \"nested_pkg/__init__.py\",\n        {\n          path: \"nested_pkg/__init__.py\",\n          rootNode: parser.parse(`\nfrom .sub1 import sub1_func\n        `).rootNode,\n        },\n      ],\n      // Nested package submodule\n      [\n        \"nested_pkg/sub1/__init__.py\",\n        {\n          path: \"nested_pkg/sub1/__init__.py\",\n          rootNode: parser.parse(`\nfrom .subsubmod import subsub_func\n\ndef sub1_func():\n    return \"Sub1 function\"\n        `).rootNode,\n        },\n      ],\n      // Nested package sub-submodule\n      [\n        \"nested_pkg/sub1/subsubmod.py\",\n        {\n          path: \"nested_pkg/sub1/subsubmod.py\",\n          rootNode: parser.parse(`\ndef subsub_func():\n    return \"Deep nested function\"\n        `).rootNode,\n        },\n      ],\n      // Module using nested package\n      [\n        \"use_nested_pkg.py\",\n        {\n          path: \"use_nested_pkg.py\",\n          rootNode: parser.parse(`\nimport nested_pkg\n\ndef use_nested_function():\n    # Use deeply nested module\n    result = nested_pkg.sub1.subsub_func()\n    return result\n        `).rootNode,\n        },\n      ],\n      // Module with unused imports\n      [\n        \"unused_module_import.py\",\n        {\n          path: \"unused_module_import.py\",\n          rootNode: parser.parse(`\nimport module_a\n\ndef no_usage():\n    # No module usage\n    return \"No module usage\"\n        `).rootNode,\n        },\n      ],\n      // Re-export test modules\n      [\n        \"original.py\",\n        {\n          path: \"original.py\",\n          rootNode: parser.parse(`\ndef original_func():\n    return \"Original function\"\n          `).rootNode,\n        },\n      ],\n      [\n        \"reexporter.py\",\n        {\n          path: \"reexporter.py\",\n          rootNode: parser.parse(`\nfrom original import original_func\n          `).rootNode,\n        },\n      ],\n      [\n        \"consumer.py\",\n        {\n          path: \"consumer.py\",\n          rootNode: parser.parse(`\nfrom reexporter import original_func\n\ndef consumer_func():\n    return original_func()\n          `).rootNode,\n        },\n      ],\n    ]);\n\n    exportExtractor = new PythonExportExtractor(parser, files);\n    resolver = new PythonUsageResolver(parser, exportExtractor);\n  });\n\n  describe(\"resolveInternalUsageForSymbol\", () => {\n    test(\"should resolve internal symbol usage\", () => {\n      const targetFile = files.get(\"module_b.py\") as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n      const importExtractor = new PythonImportExtractor(parser, files);\n      const importStatements = importExtractor.getImportStatements(\n        targetFile.path,\n      );\n      const exportExtractor = new PythonExportExtractor(parser, files);\n      const { symbols } = exportExtractor.getSymbols(\"module_a.py\");\n      const moduleResolver = new PythonModuleResolver(\n        new Set(files.keys()),\n        \"3.10\",\n      );\n\n      const moduleA = moduleResolver.getModuleFromFilePath(\n        \"module_a.py\",\n      ) as PythonModule;\n\n      // Setup a map to collect results\n      const internalUsageMap = new Map<string, InternalUsage>();\n\n      const symbol = symbols.find(\n        (s) => s.identifierNode.text === \"function_a\",\n      ) as PythonSymbol;\n\n      // Check for usage of module_a in module_b\n      resolver.resolveInternalUsageForSymbol(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        moduleA,\n        symbol,\n        symbol.identifierNode.text,\n        internalUsageMap,\n      );\n\n      // Verify results\n      expect(internalUsageMap.size).toBe(1);\n      expect(internalUsageMap.has(moduleA.path)).toBeTruthy();\n      expect(internalUsageMap.get(moduleA.path)?.symbols.size).toBe(1);\n      expect(internalUsageMap.get(moduleA.path)?.symbols.get(symbol.id)).toBe(\n        symbol,\n      );\n    });\n\n    test(\"should resolve internal symbol usage with alias\", () => {\n      const targetFile = files.get(\"module_b.py\") as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n      const importExtractor = new PythonImportExtractor(parser, files);\n      const importStatements = importExtractor.getImportStatements(\n        targetFile.path,\n      );\n      const exportExtractor = new PythonExportExtractor(parser, files);\n      const { symbols } = exportExtractor.getSymbols(\"module_a.py\");\n      const moduleResolver = new PythonModuleResolver(\n        new Set(files.keys()),\n        \"3.10\",\n      );\n\n      const moduleA = moduleResolver.getModuleFromFilePath(\n        \"module_a.py\",\n      ) as PythonModule;\n\n      // Setup a map to collect results\n      const internalUsageMap = new Map<string, InternalUsage>();\n\n      const symbol = symbols.find(\n        (s) => s.identifierNode.text === \"function_b\",\n      ) as PythonSymbol;\n\n      // Check for usage of module_a in module_b\n      resolver.resolveInternalUsageForSymbol(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        moduleA,\n        symbol,\n        \"renamed_b\",\n        internalUsageMap,\n      );\n\n      // Verify results\n      expect(internalUsageMap.size).toBe(1);\n      expect(internalUsageMap.has(moduleA.path)).toBeTruthy();\n      expect(internalUsageMap.get(moduleA.path)?.symbols.size).toBe(1);\n      expect(internalUsageMap.get(moduleA.path)?.symbols.get(symbol.id)).toBe(\n        symbol,\n      );\n    });\n\n    test(\"should not resolve internal symbol usage if unused (only within node to exclude)\", () => {\n      const targetFile = files.get(\"module_b.py\") as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n      const importExtractor = new PythonImportExtractor(parser, files);\n      const importStatements = importExtractor.getImportStatements(\n        targetFile.path,\n      );\n      const exportExtractor = new PythonExportExtractor(parser, files);\n      const { symbols } = exportExtractor.getSymbols(\"module_a.py\");\n      const moduleResolver = new PythonModuleResolver(\n        new Set(files.keys()),\n        \"3.10\",\n      );\n\n      const moduleA = moduleResolver.getModuleFromFilePath(\n        \"module_a.py\",\n      ) as PythonModule;\n\n      // Setup a map to collect results\n      const internalUsageMap = new Map<string, InternalUsage>();\n\n      const symbol = symbols.find(\n        (s) => s.identifierNode.text === \"CONSTANT_A\",\n      ) as PythonSymbol;\n\n      // Check for usage of module_a in module_b\n      resolver.resolveInternalUsageForSymbol(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        moduleA,\n        symbol,\n        \"CONSTANT_A\",\n        internalUsageMap,\n      );\n\n      // Verify results\n      expect(internalUsageMap.size).toBe(0);\n    });\n\n    test(\"should resolve internal symbol usage with ambiguous alias\", () => {\n      const targetFile = files.get(\"module_c.py\") as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n      const importExtractor = new PythonImportExtractor(parser, files);\n      const importStatements = importExtractor.getImportStatements(\n        targetFile.path,\n      );\n      const exportExtractor = new PythonExportExtractor(parser, files);\n      const { symbols } = exportExtractor.getSymbols(\"module_a.py\");\n      const moduleResolver = new PythonModuleResolver(\n        new Set(files.keys()),\n        \"3.10\",\n      );\n\n      const moduleA = moduleResolver.getModuleFromFilePath(\n        \"module_a.py\",\n      ) as PythonModule;\n\n      // Setup a map to collect results\n      const internalUsageMap = new Map<string, InternalUsage>();\n\n      const symbolFunctionA = symbols.find(\n        (s) => s.identifierNode.text === \"function_a\",\n      ) as PythonSymbol;\n      const symbolFunctionB = symbols.find(\n        (s) => s.identifierNode.text === \"function_b\",\n      ) as PythonSymbol;\n\n      // Check for usage of function_a in module_c\n      resolver.resolveInternalUsageForSymbol(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        moduleA,\n        symbolFunctionA,\n        \"function_b\", // alias for function_a\n        internalUsageMap,\n      );\n\n      expect(internalUsageMap.size).toBe(1);\n      expect(internalUsageMap.has(moduleA.path)).toBeTruthy();\n      expect(internalUsageMap.get(moduleA.path)?.symbols.size).toBe(1);\n      expect(\n        internalUsageMap.get(moduleA.path)?.symbols.get(symbolFunctionA.id),\n      ).toBe(symbolFunctionA);\n\n      internalUsageMap.clear();\n\n      // Check for usage of function_b in module_c\n      resolver.resolveInternalUsageForSymbol(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        moduleA,\n        symbolFunctionB,\n        \"function_a\", // alias for function_b\n        internalUsageMap,\n      );\n      expect(internalUsageMap.size).toBe(1);\n      expect(internalUsageMap.has(moduleA.path)).toBeTruthy();\n      expect(internalUsageMap.get(moduleA.path)?.symbols.size).toBe(1);\n      expect(\n        internalUsageMap.get(moduleA.path)?.symbols.get(symbolFunctionB.id),\n      ).toBe(symbolFunctionB);\n    });\n  });\n\n  describe(\"resolveInternalUsageForModule\", () => {\n    let moduleResolver: PythonModuleResolver;\n\n    beforeEach(() => {\n      moduleResolver = new PythonModuleResolver(new Set(files.keys()), \"3.10\");\n    });\n\n    test(\"should resolve usage of module and its symbols\", () => {\n      const targetFile = files.get(\"module_with_submodules.py\") as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n\n      const importExtractor = new PythonImportExtractor(parser, files);\n      const importStatements = importExtractor.getImportStatements(\n        targetFile.path,\n      );\n\n      const moduleA = moduleResolver.getModuleFromFilePath(\n        \"module_a.py\",\n      ) as PythonModule;\n\n      // Setup a map to collect results\n      const internalUsageMap = new Map<string, InternalUsage>();\n\n      // Check for usage of module_a and its symbols\n      resolver.resolveInternalUsageForModule(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        moduleA,\n        \"module_a\",\n        internalUsageMap,\n      );\n\n      // Verify results\n      expect(internalUsageMap.size).toBe(1);\n      expect(internalUsageMap.has(moduleA.path)).toBeTruthy();\n\n      // Get the symbols from module_a to verify\n      const exportResult = exportExtractor.getSymbols(\"module_a.py\");\n      const functionA = exportResult.symbols.find(\n        (s) => s.identifierNode.text === \"function_a\",\n      ) as PythonSymbol;\n      const functionB = exportResult.symbols.find(\n        (s) => s.identifierNode.text === \"function_b\",\n      ) as PythonSymbol;\n\n      // Should have found both function_a and function_b\n      expect(internalUsageMap.get(moduleA.path)?.symbols.size).toBe(2);\n      expect(\n        internalUsageMap.get(moduleA.path)?.symbols.get(functionA.id),\n      ).toBe(functionA);\n      expect(\n        internalUsageMap.get(moduleA.path)?.symbols.get(functionB.id),\n      ).toBe(functionB);\n    });\n\n    test(\"should resolve usage of package and its submodules\", () => {\n      const targetFile = files.get(\"use_package.py\") as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n\n      const importExtractor = new PythonImportExtractor(parser, files);\n      const importStatements = importExtractor.getImportStatements(\n        targetFile.path,\n      );\n\n      const packageModule = moduleResolver.getModuleFromFilePath(\n        \"package/__init__.py\",\n      ) as PythonModule;\n\n      // Setup a map to collect results\n      const internalUsageMap = new Map<string, InternalUsage>();\n\n      // Check for usage of package and its submodules\n      resolver.resolveInternalUsageForModule(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        packageModule,\n        \"package\",\n        internalUsageMap,\n      );\n\n      // Verify results\n      expect(internalUsageMap.size).toBeGreaterThan(0);\n      expect(internalUsageMap.has(packageModule.path)).toBeTruthy();\n\n      // Get the init module function\n      const initExportResult = exportExtractor.getSymbols(\n        \"package/__init__.py\",\n      );\n      const initFunc = initExportResult.symbols.find(\n        (s) => s.identifierNode.text === \"init_func\",\n      ) as PythonSymbol;\n\n      // Check that the init_func is found\n      expect(\n        internalUsageMap.get(packageModule.path)?.symbols.get(initFunc.id),\n      ).toBe(initFunc);\n\n      // Since submod is a submodule of package, we need to find it in the children\n      const submodPath = \"package/submod.py\";\n      const hasSubmod = Array.from(internalUsageMap.keys()).some(\n        (key) => key === submodPath,\n      );\n      expect(hasSubmod).toBeTruthy();\n\n      // Get the submodule function\n      const submodExportResult = exportExtractor.getSymbols(\n        \"package/submod.py\",\n      );\n      const submodFunc = submodExportResult.symbols.find(\n        (s) => s.identifierNode.text === \"submod_func\",\n      ) as PythonSymbol;\n\n      // Check that the submod_func is found\n      expect(internalUsageMap.get(submodPath)?.symbols.get(submodFunc.id)).toBe(\n        submodFunc,\n      );\n    });\n\n    test(\"should not detect usage of module that isn't used\", () => {\n      const targetFile = files.get(\"unused_module_import.py\") as {\n        path: string;\n        rootNode: Parser.SyntaxNode;\n      };\n\n      const importExtractor = new PythonImportExtractor(parser, files);\n      const importStatements = importExtractor.getImportStatements(\n        targetFile.path,\n      );\n\n      const moduleA = moduleResolver.getModuleFromFilePath(\n        \"module_a.py\",\n      ) as PythonModule;\n\n      // Setup a map to collect results\n      const internalUsageMap = new Map<string, InternalUsage>();\n\n      // Check for usage of module_a (which isn't actually used)\n      resolver.resolveInternalUsageForModule(\n        targetFile.rootNode,\n        importStatements.map((i) => i.node),\n        moduleA,\n        \"module_a\",\n        internalUsageMap,\n      );\n\n      // Verify the module is tracked but no symbols are used\n      expect(internalUsageMap.size).toBe(0);\n    });\n  });\n\n  test(\"should track re-exporting modules\", () => {\n    const targetFile = files.get(\"consumer.py\") as {\n      path: string;\n      rootNode: Parser.SyntaxNode;\n    };\n    const importExtractor = new PythonImportExtractor(parser, files);\n    const importStatements = importExtractor.getImportStatements(\n      targetFile.path,\n    );\n    const exportExtractor = new PythonExportExtractor(parser, files);\n    const { symbols: originalSymbols } = exportExtractor.getSymbols(\n      \"original.py\",\n    );\n    const moduleResolver = new PythonModuleResolver(\n      new Set(files.keys()),\n      \"3.10\",\n    );\n\n    const originalModule = moduleResolver.getModuleFromFilePath(\n      \"original.py\",\n    ) as PythonModule;\n    const reexporterModule = moduleResolver.getModuleFromFilePath(\n      \"reexporter.py\",\n    ) as PythonModule;\n\n    // Setup a map to collect results\n    const internalUsageMap = new Map<string, InternalUsage>();\n\n    const symbol = originalSymbols.find(\n      (s) => s.identifierNode.text === \"original_func\",\n    ) as PythonSymbol;\n\n    // Check for usage of original.py in consumer.py with reexporter.py as the re-exporting module\n    resolver.resolveInternalUsageForSymbol(\n      targetFile.rootNode,\n      importStatements.map((i) => i.node),\n      originalModule,\n      symbol,\n      symbol.identifierNode.text,\n      internalUsageMap,\n      reexporterModule,\n    );\n\n    // Verify results\n    expect(internalUsageMap.size).toBe(1);\n    expect(internalUsageMap.has(originalModule.path)).toBeTruthy();\n\n    // Check symbol tracking\n    const usage = internalUsageMap.get(originalModule.path);\n    expect(usage?.symbols.size).toBe(1);\n    expect(usage?.symbols.get(symbol.id)).toBe(symbol);\n\n    // Check re-exporting module tracking\n    expect(usage?.reExportingModules).toBeDefined();\n    expect(usage?.reExportingModules?.size).toBe(1);\n    expect(usage?.reExportingModules?.has(reexporterModule.path)).toBeTruthy();\n    expect(usage?.reExportingModules?.get(reexporterModule.path)).toBe(\n      reexporterModule,\n    );\n  });\n});\n"
  },
  {
    "path": "src/languagePlugins/python/usageResolver/index.ts",
    "content": "/**\n * Python Usage Resolver\n *\n * This module provides functionality to analyze and track how Python modules and symbols are used\n * within Python source code. It helps identify which imported modules and their exported symbols\n * are actually referenced in the code, which is essential for dependency analysis and dead code detection.\n *\n * The implementation uses Tree-sitter for parsing Python code into an Abstract Syntax Tree (AST),\n * then traverses the AST to find references to modules and symbols.\n *\n * Key features:\n * - Tracks usage of internal modules (within the project)\n * - Tracks usage of external modules (from standard library or third-party)\n * - Distinguishes between module imports and symbol imports\n * - Handles aliased imports correctly\n * - Detects re-exports of imported symbols\n */\nimport {\n  PYTHON_NAMESPACE_MODULE_TYPE,\n  type PythonModule,\n} from \"../moduleResolver/types.ts\";\nimport type { PythonExportExtractor } from \"../exportExtractor/index.ts\";\nimport Parser from \"tree-sitter\";\nimport type { PythonSymbol } from \"../exportExtractor/types.ts\";\nimport type { ExternalUsage, InternalUsage } from \"./types.ts\";\n\n/**\n * UsageResolver analyzes Python code to identify module and symbol references.\n * It tracks which imported modules and symbols are actually used within a file.\n *\n * This class provides essential information for:\n * - Dependency analysis (what modules depend on what)\n * - Dead code detection (which imports are unused)\n * - Refactoring safety (understanding the impact of changes)\n */\nexport class PythonUsageResolver {\n  /**\n   * Tree-sitter parser for Python code\n   * Used to parse and analyze Python ASTs\n   */\n  private parser: Parser;\n\n  /**\n   * Export extractor for retrieving symbols from modules\n   * Used to get the symbols defined in each module\n   */\n  private exportExtractor: PythonExportExtractor;\n\n  /**\n   * Tree-sitter query for finding identifiers and attributes in code\n   * Used to locate references to modules and symbols\n   */\n  private query: Parser.Query;\n\n  /**\n   * Creates a new UsageResolver instance\n   * @param parser - Tree-sitter parser for Python code analysis\n   * @param exportExtractor - Utility for extracting exported symbols from Python modules\n   */\n  constructor(parser: Parser, exportExtractor: PythonExportExtractor) {\n    this.parser = parser;\n    this.exportExtractor = exportExtractor;\n    this.query = new Parser.Query(\n      this.parser.getLanguage(),\n      `\n        ((identifier) @id)\n        ((attribute) @attr)\n      `,\n    );\n  }\n\n  /**\n   * Finds all nodes in the AST that match the specified reference name\n   *\n   * @param targetNode - Root node to search within\n   * @param nodesToExclude - Nodes to ignore during the search (like import statements)\n   * @param refToLookFor - The name/reference to search for in the code\n   * @returns Array of matching syntax nodes\n   */\n  public getUsageNode(\n    targetNode: Parser.SyntaxNode,\n    nodesToExclude: Parser.SyntaxNode[],\n    refToLookFor: string,\n  ) {\n    let captures = this.query.captures(targetNode);\n\n    // Filter out nodes that are inside excluded nodes\n    captures = captures.filter(({ node }) => {\n      // First filter by text match\n      if (node.text !== refToLookFor) {\n        return false;\n      }\n\n      const isNodeInsideAnyExclude = this.isNodeInsideAnyExclude(\n        node,\n        nodesToExclude,\n      );\n\n      if (isNodeInsideAnyExclude) {\n        return false;\n      }\n\n      let isRoot = true;\n      const parent = node.parent;\n      if (parent && parent.type === \"attribute\") {\n        const parentObjChild = parent.childForFieldName(\"object\");\n        if (!parentObjChild || parentObjChild.id !== node.id) {\n          isRoot = false;\n        }\n      }\n\n      if (isRoot) {\n        return true;\n      }\n\n      return false;\n    });\n\n    const nodes = captures.map(({ node }) => node);\n    return nodes;\n  }\n\n  /**\n   * Determines whether a node is contained within any of the excluded nodes\n   *\n   * @param node - Node to check\n   * @param nodesToExclude - List of nodes that should be excluded\n   * @returns True if the node is inside an excluded region, false otherwise\n   */\n  private isNodeInsideAnyExclude(\n    node: Parser.SyntaxNode,\n    nodesToExclude: Parser.SyntaxNode[],\n  ): boolean {\n    return nodesToExclude.some(\n      (excludeNode) =>\n        node.startIndex >= excludeNode.startIndex &&\n        node.endIndex <= excludeNode.endIndex,\n    );\n  }\n\n  /**\n   * Records usage of a specific symbol from a module\n   *\n   * @param targetNode - AST node to search within\n   * @param nodesToExclude - Nodes to exclude from search\n   * @param module - Module containing the symbol\n   * @param symbol - Symbol being checked for usage\n   * @param lookupRef - Reference name to search for (often includes module alias)\n   * @param internalUsageMap - Map to record usage information\n   * @param reExportingModule - Optional module that re-exports this symbol\n   */\n  public resolveInternalUsageForSymbol(\n    /* The node to search for usage. eg a function or class */\n    targetNode: Parser.SyntaxNode,\n    /* Nodes to exclude from the search. eg import statements */\n    nodesToExclude: Parser.SyntaxNode[],\n    /* Internal python module to resolve usage for */\n    module: PythonModule,\n    /* Internal python symbol to resolve usage for */\n    symbol: PythonSymbol,\n    /* potential alias. Or name of the module */\n    lookupRef: string,\n    /* Map of internal usage results, used for recursions */\n    internalUsageMap: Map<string, InternalUsage>,\n    /* Optional module that re-exports this symbol */\n    reExportingModule?: PythonModule,\n  ) {\n    const usageNodes = this.getUsageNode(targetNode, nodesToExclude, lookupRef);\n\n    if (usageNodes.length > 0) {\n      if (!internalUsageMap.has(module.path)) {\n        internalUsageMap.set(module.path, {\n          module,\n          symbols: new Map(),\n          reExportingModules: reExportingModule ? new Map() : undefined,\n        });\n      }\n      const internalUsage = internalUsageMap.get(module.path) as {\n        module: PythonModule;\n        symbols: Map<string, PythonSymbol>;\n        reExportingModules?: Map<string, PythonModule>;\n      };\n\n      if (!internalUsage.symbols.has(symbol.id)) {\n        internalUsage.symbols.set(symbol.id, symbol);\n      }\n\n      // Add re-exporting module as a dependency if provided\n      if (reExportingModule) {\n        if (!internalUsage.reExportingModules) {\n          internalUsage.reExportingModules = new Map();\n        }\n        if (!internalUsage.reExportingModules.has(reExportingModule.path)) {\n          internalUsage.reExportingModules.set(\n            reExportingModule.path,\n            reExportingModule,\n          );\n        }\n      }\n    }\n  }\n\n  /**\n   * Analyzes code for usage of a module and its symbols\n   *\n   * This method serves as the main entry point for analyzing how modules and their symbols are used in Python code.\n   * It performs several key tasks:\n   *\n   * 1. Checks for direct references to the module itself\n   * 2. Checks for references to symbols (functions, classes, variables) defined within the module\n   * 3. Recursively checks for usage of submodules and their symbols\n   *\n   * This multi-layered analysis ensures complete tracking of module dependencies,\n   * whether they're used directly or through nested references.\n   *\n   * @param targetNode - AST node to search within\n   * @param nodesToExclude - Nodes to exclude from search (e.g. import statements)\n   * @param module - Module to check for usage\n   * @param lookupRef - Reference name to look for (potentially an alias)\n   * @param internalUsageMap - Map to record module and symbol usage\n   */\n  public resolveInternalUsageForModule(\n    /* The node to search for usage. eg a function or class */\n    targetNode: Parser.SyntaxNode,\n    /* Nodes to exclude from the search. eg import statements */\n    nodesToExclude: Parser.SyntaxNode[],\n    /* Internal python module to resolve usage for */\n    module: PythonModule,\n    /* potential alias. Or name of the module */\n    lookupRef: string,\n    /* Map of internal usage results, used for recursions */\n    internalUsageMap: Map<string, InternalUsage>,\n  ) {\n    const symbols: PythonSymbol[] = [];\n\n    const moduleUsageNodes = this.getUsageNode(\n      targetNode,\n      nodesToExclude,\n      lookupRef,\n    );\n    if (moduleUsageNodes.length > 0) {\n      // Register module usage even without specific symbols\n      if (!internalUsageMap.has(module.path)) {\n        internalUsageMap.set(module.path, {\n          module,\n          symbols: new Map(),\n        });\n      }\n    }\n\n    // namespace module are not file, so they do not have symbols\n    if (module.type !== PYTHON_NAMESPACE_MODULE_TYPE) {\n      const exports = this.exportExtractor.getSymbols(module.path);\n      symbols.push(...exports.symbols);\n    }\n\n    // check for usage of module.symbol\n    symbols.forEach((symbol) => {\n      const symbolLookupRef = `${lookupRef}.${symbol.identifierNode.text}`;\n\n      this.resolveInternalUsageForSymbol(\n        targetNode,\n        nodesToExclude,\n        module,\n        symbol,\n        symbolLookupRef,\n        internalUsageMap,\n      );\n    });\n\n    // check for usage of submodules\n    module.children.forEach((subModule) => {\n      const subModuleLookupRef = `${lookupRef}.${subModule.name}`;\n\n      this.resolveInternalUsageForModule(\n        targetNode,\n        nodesToExclude,\n        subModule,\n        subModuleLookupRef,\n        internalUsageMap,\n      );\n    });\n  }\n\n  /**\n   * Resolves external usage for a module item, tracking both direct references\n   * and attribute access patterns\n   *\n   * This method analyzes how external Python modules (like standard library or third-party packages)\n   * are used in the code. It performs two key tasks:\n   *\n   * 1. Identifies direct references to the module itself\n   * 2. Tracks attribute access patterns (like numpy.array, requests.get, etc.)\n   *\n   * The method maintains a hierarchical structure of usage, which helps in understanding\n   * not just which modules are imported, but specifically which parts of those modules\n   * are actually being used in the code.\n   *\n   * @param targetNode - AST node to search within\n   * @param nodesToExclude - Nodes to exclude from search\n   * @param itemName - Name of the external module/item to check\n   * @param lookupRef - Reference name to look for (potentially an alias)\n   * @param externalUsageMap - Map to record external usage\n   */\n  public resolveExternalUsageForItem(\n    targetNode: Parser.SyntaxNode,\n    nodesToExclude: Parser.SyntaxNode[],\n    item: {\n      moduleName: string;\n      itemName?: string;\n    },\n    lookupRef: string,\n    externalUsageMap: Map<string, ExternalUsage>,\n  ) {\n    const usageNodes = this.getUsageNode(targetNode, nodesToExclude, lookupRef);\n\n    if (usageNodes.length > 0) {\n      // Initialize entry for base module\n      if (!externalUsageMap.has(item.moduleName)) {\n        externalUsageMap.set(item.moduleName, {\n          moduleName: item.moduleName,\n          itemNames: new Set(),\n        });\n      }\n\n      // Add the item name to the usage map\n      if (item.itemName) {\n        externalUsageMap.get(item.moduleName)?.itemNames.add(item.itemName);\n      } else {\n        // try resolving symbols fromt the usage node\n        usageNodes.forEach((usageNode) => {\n          const symbol = this.resolveExternalUsageSymbolFromUsage(usageNode);\n          if (symbol) {\n            externalUsageMap.get(item.moduleName)?.itemNames.add(symbol);\n          }\n        });\n      }\n    }\n  }\n\n  private resolveExternalUsageSymbolFromUsage(usageNode: Parser.SyntaxNode) {\n    if (usageNode.parent && usageNode.parent.type === \"attribute\") {\n      const attributeName = usageNode.parent.childForFieldName(\"attribute\");\n      if (attributeName) {\n        return attributeName.text;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/languagePlugins/python/usageResolver/types.ts",
    "content": "import type { PythonModule } from \"../moduleResolver/types.ts\";\nimport type { PythonSymbol } from \"../exportExtractor/types.ts\";\n\n/**\n * Represents usage information for a Python module, containing the module reference\n * and a map of symbols used from that module.\n *\n * This is used to track how internal project modules are used within the codebase,\n * including which specific symbols are referenced from each module.\n */\nexport interface InternalUsage {\n  /** The Python module being used */\n  module: PythonModule;\n  /** Map of symbol IDs to their corresponding PythonSymbol objects that are used in the code */\n  symbols: Map<string, PythonSymbol>;\n  /**\n   * Optional information about re-exporting modules (modules that re-export this module's symbols)\n   * This helps track indirect dependencies through re-exports\n   */\n  reExportingModules?: Map<string, PythonModule>;\n}\n\n/**\n * Represents usage information for an external Python module (e.g., from standard library or third-party packages).\n *\n * Unlike InternalUsage, this only tracks the module name and which symbols are used,\n * since we don't have access to the full AST information for external modules.\n */\nexport interface ExternalUsage {\n  /** The name of the external module (e.g., 'os', 'numpy', etc.) */\n  moduleName: string;\n  /** Set of specific items (functions, classes, variables) used from this module */\n  itemNames: Set<string>;\n}\n"
  },
  {
    "path": "src/manifest/auditManifest/index.ts",
    "content": "import type { DependencyManifest } from \"../dependencyManifest/types.ts\";\nimport {\n  metricCharacterCount,\n  metricCodeCharacterCount,\n  metricCodeLineCount,\n  metricCyclomaticComplexity,\n  metricDependencyCount,\n  metricDependentCount,\n  metricLinesCount,\n} from \"../dependencyManifest/types.ts\";\nimport type {\n  AuditConfig,\n  AuditManifest,\n  FileAuditManifest,\n  SymbolAuditManifest,\n} from \"./types.ts\";\n\nfunction getSeverityLevel(\n  value: number,\n  targetValue = 0,\n): 1 | 2 | 3 | 4 | 5 {\n  if (value > targetValue * 10) {\n    return 5;\n  } else if (value > targetValue * 5) {\n    return 4;\n  } else if (value > targetValue * 2) {\n    return 3;\n  } else if (value > targetValue * 1.5) {\n    return 2;\n  } else {\n    return 1;\n  }\n}\n\nexport function generateAuditManifest(\n  dependencyManifest: DependencyManifest,\n  config: AuditConfig,\n): AuditManifest {\n  const auditManifest: AuditManifest = {};\n\n  for (const fileDependencyManifest of Object.values(dependencyManifest)) {\n    const fileAudit: FileAuditManifest = {\n      id: fileDependencyManifest.id,\n      alerts: {},\n      symbols: {},\n    };\n\n    const fm = fileDependencyManifest.metrics;\n\n    if (fm.codeCharacterCount > config.file.maxCodeChar) {\n      fileAudit.alerts[metricCodeCharacterCount] = {\n        metric: metricCodeCharacterCount,\n        severity: getSeverityLevel(\n          fm.codeCharacterCount,\n          config.file.maxCodeChar,\n        ),\n        message: {\n          short: \"File too large\",\n          long:\n            `File exceeds maximum character limit (${fm.codeCharacterCount}/${config.file.maxCodeChar})`,\n        },\n      };\n    }\n\n    if (fm.characterCount > config.file.maxChar) {\n      fileAudit.alerts[metricCharacterCount] = {\n        metric: metricCharacterCount,\n        severity: getSeverityLevel(fm.characterCount, config.file.maxChar),\n        message: {\n          short: \"File too large\",\n          long:\n            `File exceeds maximum character limit (${fm.characterCount}/${config.file.maxChar})`,\n        },\n      };\n    }\n\n    if (fm.codeLineCount > config.file.maxCodeLine) {\n      fileAudit.alerts[metricCodeLineCount] = {\n        metric: metricCodeLineCount,\n        severity: getSeverityLevel(fm.codeLineCount, config.file.maxCodeLine),\n        message: {\n          short: \"Too many lines\",\n          long:\n            `File exceeds maximum line count (${fm.codeLineCount}/${config.file.maxCodeLine})`,\n        },\n      };\n    }\n\n    if (fm.linesCount > config.file.maxLine) {\n      fileAudit.alerts[metricLinesCount] = {\n        metric: metricLinesCount,\n        severity: getSeverityLevel(fm.linesCount, config.file.maxLine),\n        message: {\n          short: \"Too many lines\",\n          long:\n            `File exceeds maximum line count (${fm.linesCount}/${config.file.maxLine})`,\n        },\n      };\n    }\n\n    if (fm.dependencyCount > config.file.maxDependency) {\n      fileAudit.alerts[metricDependencyCount] = {\n        metric: metricDependencyCount,\n        severity: getSeverityLevel(\n          fm.dependencyCount,\n          config.file.maxDependency,\n        ),\n        message: {\n          short: \"Too many dependencies\",\n          long:\n            `File exceeds maximum dependency count (${fm.dependencyCount}/${config.file.maxDependency})`,\n        },\n      };\n    }\n\n    if (fm.dependentCount > config.file.maxDependent) {\n      fileAudit.alerts[metricDependentCount] = {\n        metric: metricDependentCount,\n        severity: getSeverityLevel(fm.dependentCount, config.file.maxDependent),\n        message: {\n          short: \"Too many dependents\",\n          long:\n            `File exceeds maximum dependent count (${fm.dependentCount}/${config.file.maxDependent})`,\n        },\n      };\n    }\n\n    if (fm.cyclomaticComplexity > config.file.maxCyclomaticComplexity) {\n      fileAudit.alerts[metricCyclomaticComplexity] = {\n        metric: metricCyclomaticComplexity,\n        severity: getSeverityLevel(\n          fm.cyclomaticComplexity,\n          config.file.maxCyclomaticComplexity,\n        ),\n        message: {\n          short: \"Too complex\",\n          long:\n            `File exceeds maximum cyclomatic complexity (${fm.cyclomaticComplexity}/${config.file.maxCyclomaticComplexity})`,\n        },\n      };\n    }\n\n    for (const symbol of Object.values(fileDependencyManifest.symbols)) {\n      const symbolAudit: SymbolAuditManifest = {\n        id: symbol.id,\n        alerts: {},\n      };\n\n      const sm = symbol.metrics;\n\n      if (sm.codeCharacterCount > config.symbol.maxCodeChar) {\n        symbolAudit.alerts[metricCodeCharacterCount] = {\n          metric: metricCodeCharacterCount,\n          severity: getSeverityLevel(\n            sm.codeCharacterCount,\n            config.symbol.maxCodeChar,\n          ),\n          message: {\n            short: \"Symbol too large\",\n            long:\n              `Symbol exceeds maximum character limit (${sm.codeCharacterCount}/${config.symbol.maxCodeChar})`,\n          },\n        };\n      }\n\n      if (sm.characterCount > config.symbol.maxChar) {\n        symbolAudit.alerts[metricCharacterCount] = {\n          metric: metricCharacterCount,\n          severity: getSeverityLevel(sm.characterCount, config.symbol.maxChar),\n          message: {\n            short: \"Symbol too large\",\n            long:\n              `Symbol exceeds maximum character limit (${sm.characterCount}/${config.symbol.maxChar})`,\n          },\n        };\n      }\n\n      if (sm.codeLineCount > config.symbol.maxCodeLine) {\n        symbolAudit.alerts[metricCodeLineCount] = {\n          metric: metricCodeLineCount,\n          severity: getSeverityLevel(\n            sm.codeLineCount,\n            config.symbol.maxCodeLine,\n          ),\n          message: {\n            short: \"Symbol too long\",\n            long:\n              `Symbol exceeds maximum line count (${sm.codeLineCount}/${config.symbol.maxCodeLine})`,\n          },\n        };\n      }\n\n      if (sm.linesCount > config.symbol.maxLine) {\n        symbolAudit.alerts[metricLinesCount] = {\n          metric: metricLinesCount,\n          severity: getSeverityLevel(sm.linesCount, config.symbol.maxLine),\n          message: {\n            short: \"Symbol too long\",\n            long:\n              `Symbol exceeds maximum line count (${sm.linesCount}/${config.symbol.maxLine})`,\n          },\n        };\n      }\n\n      if (sm.dependencyCount > config.symbol.maxDependency) {\n        symbolAudit.alerts[metricDependencyCount] = {\n          metric: metricDependencyCount,\n          severity: getSeverityLevel(\n            sm.dependencyCount,\n            config.symbol.maxDependency,\n          ),\n          message: {\n            short: \"Too many dependencies\",\n            long:\n              `Symbol exceeds maximum dependency count (${sm.dependencyCount}/${config.symbol.maxDependency})`,\n          },\n        };\n      }\n\n      if (sm.dependentCount > config.symbol.maxDependent) {\n        symbolAudit.alerts[metricDependentCount] = {\n          metric: metricDependentCount,\n          severity: getSeverityLevel(\n            sm.dependentCount,\n            config.symbol.maxDependent,\n          ),\n          message: {\n            short: \"Too many dependents\",\n            long:\n              `Symbol exceeds maximum dependent count (${sm.dependentCount}/${config.symbol.maxDependent})`,\n          },\n        };\n      }\n\n      if (sm.cyclomaticComplexity > config.symbol.maxCyclomaticComplexity) {\n        symbolAudit.alerts[metricCyclomaticComplexity] = {\n          metric: metricCyclomaticComplexity,\n          severity: getSeverityLevel(\n            sm.cyclomaticComplexity,\n            config.symbol.maxCyclomaticComplexity,\n          ),\n          message: {\n            short: \"Symbol too complex\",\n            long:\n              `Symbol exceeds maximum cyclomatic complexity (${sm.cyclomaticComplexity}/${config.symbol.maxCyclomaticComplexity})`,\n          },\n        };\n      }\n\n      fileAudit.symbols[symbol.id] = symbolAudit;\n    }\n\n    auditManifest[fileDependencyManifest.id] = fileAudit;\n  }\n\n  return auditManifest;\n}\n"
  },
  {
    "path": "src/manifest/auditManifest/types.ts",
    "content": "import type { Metric } from \"../dependencyManifest/types.ts\";\n\nexport type AuditAlert = {\n  metric: Metric;\n  severity: number;\n  message: {\n    short: string;\n    long: string;\n  };\n};\n\nexport type SymbolAuditManifest = {\n  id: string;\n  alerts: Record<string, AuditAlert>;\n};\n\nexport type FileAuditManifest = {\n  id: string;\n  alerts: Record<string, AuditAlert>;\n  symbols: Record<string, SymbolAuditManifest>;\n};\n\nexport type AuditManifest = Record<string, FileAuditManifest>;\n\nexport interface AuditConfig {\n  file: {\n    maxCodeChar: number;\n    maxChar: number;\n    maxCodeLine: number;\n    maxLine: number;\n    maxDependency: number;\n    maxDependent: number;\n    maxCyclomaticComplexity: number;\n  };\n  symbol: {\n    maxCodeChar: number;\n    maxChar: number;\n    maxCodeLine: number;\n    maxLine: number;\n    maxDependency: number;\n    maxDependent: number;\n    maxCyclomaticComplexity: number;\n  };\n}\n\nexport const defaultAuditConfig: AuditConfig = {\n  file: {\n    maxCodeChar: 1000,\n    maxChar: 1000,\n    maxCodeLine: 100,\n    maxLine: 100,\n    maxDependency: 100,\n    maxDependent: 100,\n    maxCyclomaticComplexity: 100,\n  },\n  symbol: {\n    maxCodeChar: 100,\n    maxChar: 100,\n    maxCodeLine: 10,\n    maxLine: 10,\n    maxDependency: 10,\n    maxDependent: 10,\n    maxCyclomaticComplexity: 10,\n  },\n};\n"
  },
  {
    "path": "src/manifest/dependencyManifest/c/index.ts",
    "content": "import {\n  type DependencyManifest,\n  metricCharacterCount,\n  metricCodeCharacterCount,\n  metricCodeLineCount,\n  metricCyclomaticComplexity,\n  metricDependencyCount,\n  metricDependentCount,\n  metricLinesCount,\n  type SymbolDependencyManifest,\n  type SymbolType,\n} from \"../types.ts\";\nimport { CDependencyFormatter } from \"../../../languagePlugins/c/dependencyFormatting/index.ts\";\nimport { CMetricsAnalyzer } from \"../../../languagePlugins/c/metrics/index.ts\";\nimport type Parser from \"tree-sitter\";\nimport { cLanguage, cParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { CWarningManager } from \"../../../languagePlugins/c/warnings/index.ts\";\nimport type { localConfigSchema } from \"../../../cli/middlewares/napiConfig.ts\";\nimport type z from \"zod\";\n\nexport function generateCDependencyManifest(\n  files: Map<string, { path: string; content: string }>,\n  napiConfig: z.infer<typeof localConfigSchema>,\n): DependencyManifest {\n  console.time(\"generateCDependencyManifest\");\n  console.info(\"Processing project...\");\n  const parsedFiles = new Map<\n    string,\n    { path: string; rootNode: Parser.SyntaxNode }\n  >();\n  for (const [filePath, { content: fileContent }] of files) {\n    try {\n      const rootNode = cParser.parse(fileContent, undefined, {\n        bufferSize: fileContent.length + 10,\n      }).rootNode;\n      parsedFiles.set(filePath, { path: filePath, rootNode });\n    } catch (e) {\n      console.error(`Failed to parse ${filePath}, skipping`);\n      console.error(e);\n    }\n  }\n\n  const warningManager = new CWarningManager(parsedFiles);\n  if (warningManager.diagnostics.length > 0) {\n    console.warn(`⚠️ ${warningManager.diagnostics.length} warnings found:`);\n    for (const warning of warningManager.diagnostics) {\n      console.warn(warning.message);\n    }\n  }\n  const includeDirs = napiConfig[cLanguage]?.includedirs ?? [];\n  const formatter = new CDependencyFormatter(parsedFiles, includeDirs);\n  const metricsAnalyzer = new CMetricsAnalyzer();\n  const manifest: DependencyManifest = {};\n  const filecount = parsedFiles.size;\n  let i = 0;\n  for (const [, { path }] of parsedFiles) {\n    console.info(`Processing ${path} (${++i}/${filecount})`);\n    const fm = formatter.formatFile(path);\n    const cSyms = fm.symbols;\n    const symbols: Record<string, SymbolDependencyManifest> = {};\n    for (const [symName, symbol] of Object.entries(cSyms)) {\n      const symType = symbol.type;\n      const dependencies = symbol.dependencies;\n      const metrics = metricsAnalyzer.analyzeNode(symbol.node);\n      symbols[symName] = {\n        id: symName,\n        type: symType as SymbolType,\n        positions: [{\n          start: {\n            index: symbol.node.startIndex,\n            row: symbol.node.startPosition.row,\n            column: symbol.node.startPosition.column,\n          },\n          end: {\n            index: symbol.node.endIndex,\n            row: symbol.node.endPosition.row,\n            column: symbol.node.endPosition.column,\n          },\n        }],\n        description: \"\",\n        metrics: {\n          [metricCharacterCount]: metrics.characterCount,\n          [metricCodeCharacterCount]: metrics.codeCharacterCount,\n          [metricLinesCount]: metrics.linesCount,\n          [metricCodeLineCount]: metrics.codeLinesCount,\n          [metricDependencyCount]: Object.keys(dependencies).length,\n          [metricDependentCount]: 0,\n          [metricCyclomaticComplexity]: metrics.cyclomaticComplexity,\n        },\n        dependencies: dependencies,\n        dependents: {},\n      };\n    }\n    const metrics = metricsAnalyzer.analyzeNode(fm.rootNode);\n    manifest[path] = {\n      id: fm.id,\n      filePath: fm.filePath,\n      language: cLanguage,\n      metrics: {\n        [metricCharacterCount]: metrics.characterCount,\n        [metricCodeCharacterCount]: metrics.codeCharacterCount,\n        [metricLinesCount]: metrics.linesCount,\n        [metricCodeLineCount]: metrics.codeLinesCount,\n        [metricDependencyCount]: Object.keys(fm.dependencies).length,\n        [metricDependentCount]: 0,\n        [metricCyclomaticComplexity]: metrics.cyclomaticComplexity,\n      },\n      dependencies: fm.dependencies,\n      symbols: symbols,\n      dependents: {},\n    };\n  }\n  console.info(\"Populating dependents...\");\n  i = 0;\n  for (const fm of Object.values(manifest)) {\n    const path = fm.filePath;\n    console.info(`Populating dependents for ${path} (${++i}/${filecount})`);\n    for (const symbol of Object.values(fm.symbols)) {\n      for (const dpncy of Object.values(symbol.dependencies)) {\n        for (const depsymbol of Object.values(dpncy.symbols)) {\n          const otherFile = manifest[dpncy.id];\n          const otherSymbol = otherFile.symbols[depsymbol];\n          if (!otherSymbol.dependents[fm.id]) {\n            otherSymbol.dependents[fm.id] = {\n              id: fm.id,\n              symbols: {},\n            };\n          }\n          otherSymbol.dependents[fm.id].symbols[symbol.id] = symbol.id;\n          fm.metrics[metricDependentCount]++;\n          symbol.metrics[metricDependentCount]++;\n        }\n      }\n    }\n  }\n  console.timeEnd(\"generateCDependencyManifest\");\n  return manifest;\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/csharp/index.test.ts",
    "content": "import { describe, test } from \"@std/testing/bdd\";\nimport { expect } from \"@std/expect\";\nimport { generateCSharpDependencyManifest } from \"./index.ts\";\nimport { join } from \"@std/path\";\nimport {\n  csharpFilesFolder,\n  getCSharpFilesMap,\n  getCsprojFilesMap,\n} from \"../../../languagePlugins/csharp/testFiles/index.ts\";\n\ndescribe(\"generateCSharpDependencymanifest\", () => {\n  const parsedfiles = getCSharpFilesMap();\n  const csprojFiles = getCsprojFilesMap();\n  const files = new Map<string, { path: string; content: string }>();\n  for (const [filePath, { path, rootNode }] of parsedfiles) {\n    files.set(filePath, { path, content: rootNode.text });\n  }\n  for (const [filePath, { path, content }] of csprojFiles) {\n    files.set(filePath, { path, content });\n  }\n  const manifest = generateCSharpDependencyManifest(files);\n  const burgers = join(csharpFilesFolder, \"2Namespaces1File.cs\");\n  const models = join(csharpFilesFolder, \"Models.cs\");\n  const namespaced = join(csharpFilesFolder, \"Namespaced.cs\");\n  const nested = join(csharpFilesFolder, \"Nested.cs\");\n  const program = join(csharpFilesFolder, \"Program.cs\");\n  const semiNamespaced = join(csharpFilesFolder, \"SemiNamespaced.cs\");\n  const usage = join(csharpFilesFolder, \"Usage.cs\");\n\n  test(\"Correctly identifies files\", () => {\n    expect(Object.keys(manifest).length).toBe(9);\n  });\n\n  test(\"Resolves exports\", () => {\n    expect(Object.keys(manifest[burgers].symbols).length).toBe(6);\n    expect(Object.keys(manifest[models].symbols).length).toBe(5);\n    expect(Object.keys(manifest[namespaced].symbols).length).toBe(1);\n    expect(Object.keys(manifest[nested].symbols).length).toBe(3);\n    expect(Object.keys(manifest[program].symbols).length).toBe(1);\n    expect(Object.keys(manifest[semiNamespaced].symbols).length).toBe(3);\n    expect(Object.keys(manifest[usage].symbols).length).toBe(1);\n  });\n\n  test(\"Resolves dependencies\", () => {\n    expect(Object.keys(manifest[burgers].dependencies).length).toBe(3);\n    expect(Object.keys(manifest[models].dependencies).length).toBe(1);\n    expect(Object.keys(manifest[namespaced].dependencies).length).toBe(1);\n    expect(Object.keys(manifest[nested].dependencies).length).toBe(1);\n    expect(Object.keys(manifest[program].dependencies).length).toBe(6);\n    expect(Object.keys(manifest[semiNamespaced].dependencies).length).toBe(2);\n    expect(Object.keys(manifest[usage].dependencies).length).toBe(4);\n  });\n\n  test(\"Resolves dependents\", () => {\n    expect(\n      Object.keys(manifest[burgers].symbols[\"MyApp.BeefBurger.Bun\"].dependents)\n        .length,\n    ).toBe(1);\n    expect(\n      Object.keys(manifest[burgers].symbols[\"ChickenBurger.Bun\"].dependents)\n        .length,\n    ).toBe(1);\n    expect(\n      Object.keys(\n        manifest[namespaced].symbols[\"MyNamespace.MyClass\"].dependents,\n      ).length,\n    ).toBe(1);\n    expect(\n      Object.keys(\n        manifest[semiNamespaced].symbols[\"HalfNamespace.Gordon\"].dependents,\n      ).length,\n    ).toBe(2);\n    expect(\n      Object.keys(manifest[semiNamespaced].symbols[\"Freeman\"].dependents)\n        .length,\n    ).toBe(2);\n    expect(\n      Object.keys(\n        manifest[nested].symbols[\"OuterNamespace.OuterInnerClass\"].dependents,\n      ).length,\n    ).toBe(1);\n    expect(\n      Object.keys(\n        manifest[nested].symbols[\"OuterNamespace.InnerNamespace.InnerClass\"]\n          .dependents,\n      ).length,\n    ).toBe(1);\n    expect(\n      Object.keys(\n        manifest[models].symbols[\"MyApp.Models.OrderStatus\"].dependents,\n      ).length,\n    ).toBe(1);\n  });\n});\n"
  },
  {
    "path": "src/manifest/dependencyManifest/csharp/index.ts",
    "content": "import {\n  type DependencyManifest,\n  metricCharacterCount,\n  metricCodeCharacterCount,\n  metricCodeLineCount,\n  metricCyclomaticComplexity,\n  metricDependencyCount,\n  metricDependentCount,\n  metricLinesCount,\n  type SymbolDependencyManifest,\n  type SymbolType,\n} from \"../types.ts\";\nimport {\n  CSharpDependencyFormatter,\n  type CSharpFile,\n} from \"../../../languagePlugins/csharp/dependencyFormatting/index.ts\";\nimport type Parser from \"tree-sitter\";\nimport {\n  csharpLanguage,\n  csharpParser,\n} from \"../../../helpers/treeSitter/parsers.ts\";\nimport { CSharpMetricsAnalyzer } from \"../../../languagePlugins/csharp/metricsAnalyzer/index.ts\";\n\n/**\n * Generates a dependency manifest for C# files.\n * @param files - A map of file paths to their corresponding syntax nodes.\n * @returns A dependency manifest for the C# files.\n */\nexport function generateCSharpDependencyManifest(\n  files: Map<string, { path: string; content: string }>,\n): DependencyManifest {\n  console.time(\"generateCSharpDependencyManifest\");\n  console.info(\"Processing project...\");\n  const parsedFiles = new Map<\n    string,\n    { path: string; rootNode: Parser.SyntaxNode }\n  >();\n  const csprojFiles = new Map<string, { path: string; content: string }>();\n\n  // Filter out csproj files and parse C# files\n  for (const [filePath, { content: fileContent }] of files) {\n    if (filePath.endsWith(\".csproj\")) {\n      csprojFiles.set(filePath, { path: filePath, content: fileContent });\n    } else {\n      try {\n        const rootNode = csharpParser.parse(fileContent, undefined, {\n          bufferSize: fileContent.length + 10,\n        }).rootNode;\n        parsedFiles.set(filePath, { path: filePath, rootNode });\n      } catch (e) {\n        console.error(`Failed to parse ${filePath}, skipping`);\n        console.error(e);\n      }\n    }\n  }\n\n  const formatter = new CSharpDependencyFormatter(parsedFiles, csprojFiles);\n  const analyzer = new CSharpMetricsAnalyzer();\n  const manifest: DependencyManifest = {};\n  const filecount = parsedFiles.size;\n  let i = 0;\n  for (const [, { path }] of parsedFiles) {\n    console.info(`Processing ${path} (${++i}/${filecount})`);\n    const fm = formatter.formatFile(path) as CSharpFile;\n    const csharpSyms = fm.symbols;\n    const symbols: Record<string, SymbolDependencyManifest> = {};\n    for (const [symbolName, symbol] of Object.entries(csharpSyms)) {\n      const metrics = analyzer.analyzeNode(symbol.node);\n      symbols[symbolName] = {\n        id: symbolName,\n        type: symbol.type as SymbolType,\n        positions: [{\n          start: {\n            index: symbol.node.startIndex,\n            row: symbol.node.startPosition.row,\n            column: symbol.node.startPosition.column,\n          },\n          end: {\n            index: symbol.node.endIndex,\n            row: symbol.node.endPosition.row,\n            column: symbol.node.endPosition.column,\n          },\n        }],\n        description: \"\",\n        metrics: {\n          [metricCharacterCount]: symbol.characterCount,\n          [metricCodeCharacterCount]: metrics.codeCharacterCount,\n          [metricLinesCount]: symbol.lineCount,\n          [metricCodeLineCount]: metrics.codeLinesCount,\n          [metricDependencyCount]: Object.keys(symbol.dependencies).length,\n          [metricDependentCount]: 0, // TODO: fix this\n          [metricCyclomaticComplexity]: metrics.cyclomaticComplexity,\n        },\n        dependencies: symbol.dependencies,\n        dependents: {},\n      };\n    }\n    const filemetrics = analyzer.analyzeNode(fm.rootNode);\n    manifest[path] = {\n      id: fm.id,\n      filePath: fm.filepath,\n      language: csharpLanguage,\n      metrics: {\n        [metricCharacterCount]: fm.characterCount,\n        [metricCodeCharacterCount]: filemetrics.codeCharacterCount,\n        [metricLinesCount]: fm.lineCount,\n        [metricCodeLineCount]: filemetrics.codeLinesCount,\n        [metricDependencyCount]: Object.keys(fm.dependencies).length,\n        [metricDependentCount]: 0, // TODO: fix this\n        [metricCyclomaticComplexity]: filemetrics.cyclomaticComplexity,\n      },\n      dependencies: fm.dependencies,\n      symbols,\n      dependents: {}, //TODO fix this\n    };\n    // Delete isNamespace from dependencies\n    for (const dep of Object.values(fm.dependencies)) {\n      delete dep.isNamespace;\n    }\n  }\n  console.info(\"Populating dependents...\");\n  i = 0;\n  // Populate dependents\n  for (const fm of Object.values(manifest)) {\n    const path = fm.filePath;\n    console.info(`Populating dependents for ${path} (${++i}/${filecount})`);\n    for (const symbol of Object.values(fm.symbols)) {\n      for (const dpncy of Object.values(symbol.dependencies)) {\n        for (const depsymbol of Object.values(dpncy.symbols)) {\n          const otherFile = manifest[dpncy.id];\n          const otherSymbol = otherFile.symbols[depsymbol];\n          if (!otherSymbol.dependents[fm.id]) {\n            otherSymbol.dependents[fm.id] = {\n              id: fm.id,\n              symbols: {},\n            };\n          }\n          otherSymbol.dependents[fm.id].symbols[symbol.id] = symbol.id;\n          fm.metrics[metricDependentCount]++;\n          symbol.metrics[metricDependentCount]++;\n        }\n      }\n    }\n  }\n  console.timeEnd(\"generateCSharpDependencyManifest\");\n  return manifest;\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/index.ts",
    "content": "import { generatePythonDependencyManifest } from \"./python/index.ts\";\nimport { generateCSharpDependencyManifest } from \"./csharp/index.ts\";\nimport { generateCDependencyManifest } from \"./c/index.ts\";\nimport type { localConfigSchema } from \"../../cli/middlewares/napiConfig.ts\";\nimport type z from \"zod\";\nimport type { DependencyManifest, SymbolDependencyManifest } from \"./types.ts\";\nimport {\n  cLanguage,\n  csharpLanguage,\n  javaLanguage,\n  pythonLanguage,\n} from \"../../helpers/treeSitter/parsers.ts\";\nimport { generateJavaDependencyManifest } from \"./java/index.ts\";\nimport { generateSymbolDescriptions } from \"./labeling/index.ts\";\nimport type { globalConfigSchema } from \"../../cli/middlewares/globalConfig.ts\";\nimport {\n  ANTHROPIC_PROVIDER,\n  GOOGLE_PROVIDER,\n  OPENAI_PROVIDER,\n} from \"./labeling/model.ts\";\n\nconst handlerMap: Record<\n  string,\n  (\n    files: Map<string, { path: string; content: string }>,\n    napiConfig: z.infer<typeof localConfigSchema>,\n  ) => DependencyManifest\n> = {\n  [pythonLanguage]: generatePythonDependencyManifest,\n  [csharpLanguage]: generateCSharpDependencyManifest,\n  [cLanguage]: generateCDependencyManifest,\n  [javaLanguage]: generateJavaDependencyManifest,\n};\n\nexport class UnsupportedLanguageError extends Error {\n  constructor(language: string) {\n    const supportedLanguages = Object.keys(handlerMap).join(\", \");\n    super(\n      `Unsupported language: ${language}. Supported languages: ${supportedLanguages}`,\n    );\n  }\n}\n\nexport async function generateDependencyManifest(\n  files: Map<string, { path: string; content: string }>,\n  napiConfig: z.infer<typeof localConfigSchema>,\n  globalConfig: z.infer<typeof globalConfigSchema>,\n  labelingApiKey: string | undefined,\n): Promise<DependencyManifest> {\n  const languageName = napiConfig.language;\n\n  const handler = handlerMap[languageName];\n  if (!handler) {\n    throw new UnsupportedLanguageError(languageName);\n  }\n\n  const depMap = handler(files, napiConfig);\n\n  // Sort the keys of the dependency map and consider them all as lowercase\n  const sortedKeys = Object.keys(depMap).sort((a, b) =>\n    a.localeCompare(b, undefined, { sensitivity: \"base\" })\n  );\n  // Create a new object with sorted keys\n  const sortedDepMap: DependencyManifest = {};\n  for (const key of sortedKeys) {\n    sortedDepMap[key] = depMap[key];\n\n    // Sort the symbols within each file manifest and consider them all as lowercase\n    const sortedSymbols = Object.keys(depMap[key].symbols).sort((a, b) =>\n      a.localeCompare(b, undefined, { sensitivity: \"base\" })\n    );\n\n    // Then put the symbols in a new object with sorted keys\n    // in their original case\n    const sortedSymbolsMap: Record<string, SymbolDependencyManifest> = {};\n    for (const symbolKey of sortedSymbols) {\n      sortedSymbolsMap[symbolKey] = depMap[key].symbols[symbolKey];\n    }\n    // Assign the sorted symbols back to the file manifest\n    sortedDepMap[key].symbols = sortedSymbolsMap;\n  }\n\n  if (napiConfig.labeling) {\n    let apiKey: string | undefined;\n    if (labelingApiKey) {\n      apiKey = labelingApiKey;\n    } else {\n      if (napiConfig.labeling.modelProvider === GOOGLE_PROVIDER) {\n        apiKey = globalConfig.labeling?.apiKeys.google;\n      }\n      if (napiConfig.labeling.modelProvider === OPENAI_PROVIDER) {\n        apiKey = globalConfig.labeling?.apiKeys.openai;\n      }\n      if (napiConfig.labeling.modelProvider === ANTHROPIC_PROVIDER) {\n        apiKey = globalConfig.labeling?.apiKeys.anthropic;\n      }\n    }\n\n    if (!apiKey) {\n      console.warn(\n        \"No API key found for the selected model provider. Please run `napi set apiKey` to set an API key.\",\n      );\n      return sortedDepMap;\n    }\n\n    const labeledDependencyManifest = await generateSymbolDescriptions(\n      files,\n      sortedDepMap,\n      apiKey,\n      napiConfig.labeling.modelProvider,\n      napiConfig.labeling.maxConcurrency,\n    );\n\n    return labeledDependencyManifest;\n  } else {\n    return sortedDepMap;\n  }\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/java/index.ts",
    "content": "import {\n  type DependencyManifest,\n  metricCharacterCount,\n  metricCodeCharacterCount,\n  metricCodeLineCount,\n  metricCyclomaticComplexity,\n  metricDependencyCount,\n  metricDependentCount,\n  metricLinesCount,\n  type SymbolDependencyManifest,\n  type SymbolType,\n} from \"../types.ts\";\nimport { JavaDependencyFormatter } from \"../../../languagePlugins/java/dependencyFormatting/index.ts\";\n// import { JavaMetricsAnalyzer } from \"../../../languagePlugins/java/metrics/index.ts\";\nimport { javaLanguage } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { JavaMetricsAnalyzer } from \"../../../languagePlugins/java/metrics/index.ts\";\n\nexport function generateJavaDependencyManifest(\n  files: Map<string, { path: string; content: string }>,\n): DependencyManifest {\n  console.time(\"generateJavaDependencyManifest\");\n  console.info(\"Processing project...\");\n  const formatter = new JavaDependencyFormatter(files);\n  const metricsAnalyzer = new JavaMetricsAnalyzer();\n  const manifest: DependencyManifest = {};\n  const newfiles = formatter.mapper.files;\n  const filecount = newfiles.size;\n  let i = 0;\n  for (const [, { path }] of newfiles) {\n    console.info(`Processing ${path} (${++i}/${filecount})`);\n    const fm = formatter.formatFile(path);\n    const cSyms = fm.symbols;\n    const symbols: Record<string, SymbolDependencyManifest> = {};\n    for (const [symName, symbol] of Object.entries(cSyms)) {\n      const symType = symbol.type;\n      const dependencies = symbol.dependencies;\n      const metrics = metricsAnalyzer.analyzeNode(symbol.node);\n      symbols[symName] = {\n        id: symName,\n        type: symType as SymbolType,\n        positions: [{\n          start: {\n            index: symbol.node.startIndex,\n            row: symbol.node.startPosition.row,\n            column: symbol.node.startPosition.column,\n          },\n          end: {\n            index: symbol.node.endIndex,\n            row: symbol.node.endPosition.row,\n            column: symbol.node.endPosition.column,\n          },\n        }],\n        description: \"\",\n        metrics: {\n          [metricCharacterCount]: metrics.characterCount,\n          [metricCodeCharacterCount]: metrics.codeCharacterCount,\n          [metricLinesCount]: metrics.linesCount,\n          [metricCodeLineCount]: metrics.codeLinesCount,\n          [metricDependencyCount]: Object.keys(dependencies).length,\n          [metricDependentCount]: 0,\n          [metricCyclomaticComplexity]: metrics.cyclomaticComplexity,\n        },\n        dependencies: dependencies,\n        dependents: {},\n      };\n    }\n    const metrics = metricsAnalyzer.analyzeNode(fm.rootNode);\n    manifest[path] = {\n      id: fm.id,\n      filePath: fm.filePath,\n      language: javaLanguage,\n      metrics: {\n        [metricCharacterCount]: metrics.characterCount,\n        [metricCodeCharacterCount]: metrics.codeCharacterCount,\n        [metricLinesCount]: metrics.linesCount,\n        [metricCodeLineCount]: metrics.codeLinesCount,\n        [metricDependencyCount]: Object.keys(fm.dependencies).length,\n        [metricDependentCount]: 0,\n        [metricCyclomaticComplexity]: metrics.cyclomaticComplexity,\n      },\n      dependencies: fm.dependencies,\n      symbols: symbols,\n      dependents: {},\n    };\n  }\n  console.info(\"Populating dependents...\");\n  i = 0;\n  for (const fm of Object.values(manifest)) {\n    const path = fm.filePath;\n    console.info(`Populating dependents for ${path} (${++i}/${filecount})`);\n    for (const symbol of Object.values(fm.symbols)) {\n      for (const dpncy of Object.values(symbol.dependencies)) {\n        for (const depsymbol of Object.values(dpncy.symbols)) {\n          const otherFile = manifest[dpncy.id];\n          const otherSymbol = otherFile.symbols[depsymbol];\n          if (!otherSymbol.dependents[fm.id]) {\n            otherSymbol.dependents[fm.id] = {\n              id: fm.id,\n              symbols: {},\n            };\n          }\n          otherSymbol.dependents[fm.id].symbols[symbol.id] = symbol.id;\n          fm.metrics[metricDependentCount]++;\n          symbol.metrics[metricDependentCount]++;\n        }\n      }\n    }\n  }\n  console.timeEnd(\"generateJavaDependencyManifest\");\n  return manifest;\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/labeling/graph.ts",
    "content": "import { Annotation, StateGraph } from \"@langchain/langgraph\";\nimport type { DependencyManifest } from \"../types.ts\";\nimport type { GroupLayer, SymbolRef } from \"./types.ts\";\nimport { symbolRefToKey } from \"./grouping.ts\";\nimport {\n  type AIMessage,\n  HumanMessage,\n  SystemMessage,\n} from \"@langchain/core/messages\";\nimport { z } from \"zod\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\n\nfunction getSymbolDependencyContextMessages(\n  state: typeof workflowState.State,\n  symbolRef: SymbolRef,\n) {\n  const symbolDependencyManifest =\n    state.dependencyManifest[symbolRef.fileId].symbols[symbolRef.symbolId];\n\n  const messages: HumanMessage[] = [];\n\n  for (\n    const fileDependency of Object.values(\n      symbolDependencyManifest.dependencies,\n    )\n  ) {\n    // First check if external\n    if (fileDependency.isExternal) {\n      const symbols = Object.values(fileDependency.symbols);\n      if (symbols.length === 0) {\n        messages.push(\n          new HumanMessage(\n            `External dependency from the following source: ${fileDependency.id}`,\n          ),\n        );\n      } else {\n        messages.push(\n          new HumanMessage(\n            `External dependency from the following source: ${fileDependency.id}\nwith the following symbols: ${symbols.join(\", \")}`,\n          ),\n        );\n      }\n      continue;\n    }\n\n    const filedependencyManifest = state.dependencyManifest[fileDependency.id];\n    for (const symbol of Object.values(fileDependency.symbols)) {\n      const symbolDependencyManifest =\n        state.dependencyManifest[fileDependency.id].symbols[symbol];\n      // Then check if we have the symbol in the labelManifest\n      if (filedependencyManifest) {\n        const symbolDependencyManifest = filedependencyManifest.symbols[symbol];\n        if (symbolDependencyManifest.description) {\n          messages.push(\n            new HumanMessage(\n              `Dependency from the following source: ${fileDependency.id} with the following symbol: ${symbolDependencyManifest.id} (${symbolDependencyManifest.type})\nHere is a brief description of this symbol: ${symbolDependencyManifest.description}`,\n            ),\n          );\n          continue;\n        }\n      }\n\n      // Last, check if we have the symbol in the notYetProcessedSymbolDescriptionMap\n      const key = symbolRefToKey({\n        fileId: fileDependency.id,\n        symbolId: symbol,\n      });\n      const description = state.notYetProcessedSymbolDescriptionMap.get(key);\n      if (description) {\n        messages.push(\n          new HumanMessage(\n            `Dependency from the following source: ${fileDependency.id} with the following symbol: ${symbolDependencyManifest.id} (${symbolDependencyManifest.type})\nHere is a brief description of this symbol: ${description}`,\n          ),\n        );\n      }\n    }\n  }\n\n  if (messages.length >= 1) {\n    messages.unshift(\n      new HumanMessage(\n        \"Next messages is a list of dependencies to the symbol you need to process as well as some information about each of them:\",\n      ),\n    );\n  }\n\n  return messages;\n}\n\nfunction generateContentsMessages(\n  state: typeof workflowState.State,\n  symbolRef: SymbolRef,\n) {\n  const file = state.files.get(symbolRef.fileId);\n  if (!file) {\n    throw new Error(`File not found: ${symbolRef.fileId}`);\n  }\n\n  const symbolManifest =\n    state.dependencyManifest[symbolRef.fileId].symbols[symbolRef.symbolId];\n\n  const contents: string[] = [];\n  for (const position of symbolManifest.positions) {\n    const lines = file.content.split(\"\\n\");\n\n    const symbolLines: string[] = [];\n    // get content between startLine and endLine\n    for (let i = position.start.row; i <= position.end.row; i++) {\n      symbolLines.push(lines[i]);\n    }\n\n    const content = symbolLines.join(\"\\n\");\n\n    contents.push(content);\n  }\n\n  const messages: HumanMessage[] = [];\n\n  messages.push(\n    new HumanMessage(\n      `Here is the symbolRef of the symbol that you need to process: { fileId: ${symbolRef.fileId}, symbolId: ${symbolRef.symbolId} }`,\n    ),\n  );\n\n  if (contents.length === 0) {\n    messages.push(\n      new HumanMessage(\n        \"The symbol that you need to process has no content.\",\n      ),\n    );\n  } else {\n    messages.push(\n      new HumanMessage(\n        `The symbol that you need to process has content. Here is the content (${contents.length} parts):`,\n      ),\n    );\n    for (const [index, content] of contents.entries()) {\n      messages.push(\n        new HumanMessage(\n          `Part ${index + 1} of ${contents.length}: ${content}`,\n        ),\n      );\n    }\n  }\n\n  return messages;\n}\n\nconst workflowState = Annotation.Root({\n  files: Annotation<Map<string, { path: string; content: string }>>,\n  dependencyManifest: Annotation<DependencyManifest>,\n  groupLayer: Annotation<GroupLayer>,\n  notYetProcessedSymbolDescriptionMap: Annotation<Map<string, string>>,\n  model: Annotation<BaseChatModel>,\n  results: Annotation<{ symbolRef: SymbolRef; description: string }[]>,\n});\n\nexport function createGroupSymbolLabelingWorkflow(\n  files: Map<string, { path: string; content: string }>,\n  dependencyManifest: DependencyManifest,\n  groupLayer: GroupLayer,\n  model: BaseChatModel,\n) {\n  function initNode(_state: typeof workflowState.State) {\n    return {\n      files,\n      dependencyManifest,\n      groupLayer,\n      notYetProcessedSymbolDescriptionMap: new Map<string, string>(),\n      model,\n      results: [],\n    };\n  }\n\n  async function generateDescriptionsForYetToBeProcessedSymbols(\n    state: typeof workflowState.State,\n  ) {\n    if (state.groupLayer.symbolRefsToProcess.length === 0) {\n      // no symbol yet to process\n      // we have all the context needed in the labelManifest\n      return state;\n    }\n\n    const messagesBatch: AIMessage[][] = [];\n\n    for (const symbolRef of state.groupLayer.symbolRefsToProcess) {\n      const messages = generateDescriptionMessagesForSymbol(\n        state,\n        symbolRef,\n      );\n      messagesBatch.push(messages);\n    }\n\n    const schema = z.object({\n      symbolRef: z.object({\n        fileId: z.string().describe(\"The id of the file.\"),\n        symbolId: z.string().describe(\"The id of the symbol.\"),\n      }),\n      description: z.string().max(500).describe(\n        \"A short description of what the symbol is doing.\",\n      ),\n    });\n\n    const results = await (model as BaseChatModel).withStructuredOutput(schema)\n      .batch(messagesBatch) as z.infer<typeof schema>[];\n\n    for (const result of results) {\n      const key = symbolRefToKey(result.symbolRef);\n      const description = result.description;\n      state.notYetProcessedSymbolDescriptionMap.set(key, description);\n    }\n\n    return state;\n  }\n\n  function generateDescriptionMessagesForSymbol(\n    state: typeof workflowState.State,\n    symbolRef: SymbolRef,\n  ) {\n    const messages: AIMessage[] = [];\n    messages.push(\n      new SystemMessage(\n        \"You are a helpful assistant that generates a short description of what the symbol (class, function, etc.) is doing.\",\n      ),\n    );\n\n    const symbolDependencyMessages = getSymbolDependencyContextMessages(\n      state,\n      symbolRef,\n    );\n    for (const message of symbolDependencyMessages) {\n      messages.push(message);\n    }\n\n    const symbolContentsMessages = generateContentsMessages(\n      state,\n      symbolRef,\n    );\n    for (const message of symbolContentsMessages) {\n      messages.push(message);\n    }\n\n    return messages;\n  }\n\n  async function generateLabels(\n    state: typeof workflowState.State,\n  ) {\n    const messagesBatch: AIMessage[][] = [];\n\n    for (const symbolRef of state.groupLayer.symbolRefsToProcess) {\n      const messages = generateMessagesForLabelingSymbol(state, symbolRef);\n      messagesBatch.push(messages);\n    }\n\n    const schema = z.object({\n      symbolRef: z.object({\n        fileId: z.string().describe(\"The id of the file.\"),\n        symbolId: z.string().describe(\"The id of the symbol.\"),\n      }),\n      description: z.string().describe(\n        \"A business focused description of what the symbol is doing (max 500 char).\",\n      ),\n    });\n\n    const results = await (model as BaseChatModel).withStructuredOutput(schema)\n      .batch(messagesBatch) as z.infer<typeof schema>[];\n\n    for (const result of results) {\n      state.results.push(result);\n    }\n\n    return state;\n  }\n\n  function generateMessagesForLabelingSymbol(\n    state: typeof workflowState.State,\n    symbolRef: SymbolRef,\n  ) {\n    const messages: AIMessage[] = [];\n    messages.push(\n      new SystemMessage(\n        \"You are a helpful assistant that generates labels for a symbol (class, function, etc.).\",\n      ),\n    );\n\n    const symbolDependencyMessages = getSymbolDependencyContextMessages(\n      state,\n      symbolRef,\n    );\n    for (const message of symbolDependencyMessages) {\n      messages.push(message);\n    }\n\n    const symbolContentsMessages = generateContentsMessages(\n      state,\n      symbolRef,\n    );\n    for (const message of symbolContentsMessages) {\n      messages.push(message);\n    }\n\n    return messages;\n  }\n\n  const workflow = new StateGraph(workflowState).addNode(\"init\", initNode)\n    // nodes\n    .addNode(\n      \"generateDescriptionsForYetToBeProcessedSymbols\",\n      generateDescriptionsForYetToBeProcessedSymbols,\n    )\n    .addNode(\"generateLabels\", generateLabels)\n    // edges\n    .addEdge(\"__start__\", \"init\")\n    .addEdge(\"init\", \"generateDescriptionsForYetToBeProcessedSymbols\")\n    .addEdge(\n      \"generateDescriptionsForYetToBeProcessedSymbols\",\n      \"generateLabels\",\n    )\n    .addEdge(\"generateLabels\", \"__end__\");\n\n  return workflow.compile();\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/labeling/grouping.ts",
    "content": "import type { DependencyManifest } from \"../types.ts\";\nimport type { GroupLayer, SymbolRef } from \"./types.ts\";\n\n// =============================================================================\n// CONSTANTS AND CONFIGURATION\n// =============================================================================\n\n/**\n * The separator used to join the fileId and symbolId in the key.\n * This is a URL-safe separator that won't conflict with file paths.\n */\nconst JOINT_SYMBOL_SEPARATOR = \"::\";\n\n// =============================================================================\n// SYMBOL KEY MANAGEMENT UTILITIES\n// =============================================================================\n\n/**\n * Converts a SymbolRef into a unique string key for efficient lookups.\n * Uses URL encoding to handle special characters in file paths and symbol names.\n *\n * @param ref - The symbol reference to convert\n * @returns A unique string key for the symbol\n */\nexport function symbolRefToKey(ref: SymbolRef): string {\n  const urlEncodedFileId = encodeURIComponent(ref.fileId);\n  const urlEncodedSymbolId = encodeURIComponent(ref.symbolId);\n  return `${urlEncodedFileId}${JOINT_SYMBOL_SEPARATOR}${urlEncodedSymbolId}`;\n}\n\n/**\n * Converts a symbol key back into a SymbolRef.\n * This is the inverse operation of symbolRefToKey.\n *\n * @param key - The string key to convert back\n * @returns The original SymbolRef\n */\nexport function keyToSymbolRef(key: string): SymbolRef {\n  const [urlEncodedFileId, urlEncodedSymbolId] = key.split(\n    JOINT_SYMBOL_SEPARATOR,\n  );\n  const fileId = decodeURIComponent(urlEncodedFileId);\n  const symbolId = decodeURIComponent(urlEncodedSymbolId);\n  return { fileId, symbolId };\n}\n\n// =============================================================================\n// DEPENDENCY ANALYSIS FUNCTIONS\n// =============================================================================\n\n/**\n * Gets all unprocessed internal dependencies for a given symbol.\n * This function filters out:\n * - External dependencies (outside the codebase)\n * - Self-dependencies (symbol depending on itself)\n * - Already processed dependencies\n *\n * @param symbolRef - The symbol to analyze\n * @param manifest - The complete dependency manifest\n * @param processedSymbols - Set of already processed symbol keys\n * @returns Array of dependency symbol keys that haven't been processed yet\n */\nfunction getUnprocessedDependencies(\n  symbolRef: SymbolRef,\n  manifest: DependencyManifest,\n  processedSymbols: Set<string>,\n): string[] {\n  const currentSymbolKey = symbolRefToKey(symbolRef);\n  const symbolManifest = manifest[symbolRef.fileId]\n    ?.symbols[symbolRef.symbolId];\n  if (!symbolManifest) return [];\n\n  const dependencies: string[] = [];\n\n  // Iterate through all files this symbol depends on\n  for (\n    const [depFileId, depInfo] of Object.entries(symbolManifest.dependencies)\n  ) {\n    // Skip external dependencies - we only care about internal code dependencies\n    if (depInfo.isExternal) continue;\n\n    // Check each symbol within the dependency file\n    for (const depSymbolId of Object.keys(depInfo.symbols)) {\n      // Verify the dependency symbol actually exists in the manifest\n      if (manifest[depFileId]?.symbols[depSymbolId]) {\n        const depKey = symbolRefToKey({\n          fileId: depFileId,\n          symbolId: depSymbolId,\n        });\n\n        // Skip self-dependencies and already processed symbols\n        if (currentSymbolKey !== depKey && !processedSymbols.has(depKey)) {\n          dependencies.push(depKey);\n        }\n      }\n    }\n  }\n\n  return dependencies;\n}\n\n// =============================================================================\n// STRONGLY CONNECTED COMPONENTS (SCC) ALGORITHM\n// =============================================================================\n\n/**\n * Tarjan's strongly connected components algorithm.\n * This finds groups of symbols that form dependency cycles.\n *\n * A strongly connected component is a maximal set of vertices such that\n * for every pair of vertices u and v, there is a directed path from u to v\n * and a directed path from v to u.\n *\n * Time complexity: O(V + E) where V is vertices and E is edges\n *\n * https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm#The_algorithm_in_pseudocode\n *\n * @param graph - Adjacency list representation of the dependency graph\n * @returns Array of sets, each containing symbol keys that form an SCC\n */\nfunction stronglyConnectedComponents(\n  graph: Map<string, Set<string>>,\n): Array<Set<string>> {\n  // Tarjan's algorithm state\n  const indices = new Map<string, number>(); // Discovery time of each vertex\n  const lowlinks = new Map<string, number>(); // Lowest reachable discovery time\n  const onStack = new Set<string>(); // Vertices currently on the stack\n  const stack: string[] = []; // Stack for the algorithm\n  const components: Array<Set<string>> = []; // Resulting SCCs\n  let index = 0; // Global discovery time counter\n\n  /**\n   * Recursive function that performs the depth-first search for Tarjan's algorithm.\n   * This is the core of the SCC detection logic.\n   */\n  function strongConnect(v: string): void {\n    // Initialize the vertex\n    indices.set(v, index);\n    lowlinks.set(v, index);\n    index++;\n    stack.push(v);\n    onStack.add(v);\n\n    // Check all neighbors (dependencies)\n    const neighbors = graph.get(v) || new Set();\n    for (const w of neighbors) {\n      if (!indices.has(w)) {\n        // Neighbor w has not yet been visited; recurse on it\n        strongConnect(w);\n        lowlinks.set(v, Math.min(lowlinks.get(v)!, lowlinks.get(w)!));\n      } else if (onStack.has(w)) {\n        // Neighbor w is in the stack and hence in the current SCC\n        lowlinks.set(v, Math.min(lowlinks.get(v)!, indices.get(w)!));\n      }\n    }\n\n    // If v is a root node, pop the stack and create an SCC\n    if (lowlinks.get(v) === indices.get(v)) {\n      const component = new Set<string>();\n      let w: string;\n      do {\n        w = stack.pop()!;\n        onStack.delete(w);\n        component.add(w);\n      } while (w !== v);\n      components.push(component);\n    }\n  }\n\n  // Start DFS from each unvisited vertex\n  for (const v of graph.keys()) {\n    if (!indices.has(v)) {\n      strongConnect(v);\n    }\n  }\n\n  return components;\n}\n\n/**\n * Builds a dependency graph from remaining symbols and finds SCCs.\n * This creates the graph representation needed for SCC analysis.\n *\n * @param remainingSymbols - Map of symbol keys to SymbolRefs that haven't been processed\n * @param manifest - The complete dependency manifest\n * @param processedSymbols - Set of already processed symbol keys\n * @returns Array of SCCs (sets of symbol keys that form cycles)\n */\nfunction findStronglyConnectedComponents(\n  remainingSymbols: Map<string, SymbolRef>,\n  manifest: DependencyManifest,\n  processedSymbols: Set<string>,\n): Array<Set<string>> {\n  const graph = new Map<string, Set<string>>();\n\n  // Build the dependency graph for remaining symbols only\n  for (const [symbolKey, symbolRef] of remainingSymbols) {\n    const dependencies = getUnprocessedDependencies(\n      symbolRef,\n      manifest,\n      processedSymbols,\n    );\n\n    // Only include dependencies that are also in remainingSymbols\n    // This ensures we only analyze cycles among unprocessed symbols\n    const filteredDeps = dependencies.filter((dep) =>\n      remainingSymbols.has(dep)\n    );\n    graph.set(symbolKey, new Set(filteredDeps));\n  }\n\n  return stronglyConnectedComponents(graph);\n}\n\n// =============================================================================\n// CYCLE BREAKING STRATEGIES\n// =============================================================================\n\n/**\n * Selects the best symbol from a set based on dependency count.\n * \"Best\" means the symbol with the fewest unprocessed dependencies,\n * which makes it a good candidate for breaking cycles with minimal impact.\n *\n * @param symbolKeys - Set of symbol keys to choose from\n * @param remainingSymbols - Map of remaining symbols\n * @param manifest - The dependency manifest\n * @param processedSymbols - Set of processed symbols\n * @returns The best SymbolRef or null if none found\n */\nfunction selectBestSymbol(\n  symbolKeys: Set<string>,\n  remainingSymbols: Map<string, SymbolRef>,\n  manifest: DependencyManifest,\n  processedSymbols: Set<string>,\n): SymbolRef | null {\n  let bestSymbol: SymbolRef | null = null;\n  let minDeps = Infinity;\n\n  for (const symbolKey of symbolKeys) {\n    const symbolRef = remainingSymbols.get(symbolKey);\n    if (!symbolRef) continue;\n\n    const depCount =\n      getUnprocessedDependencies(symbolRef, manifest, processedSymbols).length;\n    if (depCount < minDeps) {\n      minDeps = depCount;\n      bestSymbol = symbolRef;\n    }\n  }\n\n  return bestSymbol;\n}\n\n/**\n * Selects optimal symbols to break dependency cycles using SCC analysis.\n *\n * This function implements a two-phase strategy:\n * 1. Break major cycles by selecting one representative from each large SCC\n * 2. Add all remaining independent symbols that don't depend on selected ones\n *\n * The goal is to maximize the number of symbols that can be processed\n * while minimizing the total notYetProcessedDependencySymbolRefs across all layers.\n *\n * @param remainingSymbols - Map of unprocessed symbols\n * @param manifest - The dependency manifest\n * @param processedSymbols - Set of processed symbols\n * @returns Object containing selected symbols and their dependencies\n */\nfunction selectCycleBreakers(\n  remainingSymbols: Map<string, SymbolRef>,\n  manifest: DependencyManifest,\n  processedSymbols: Set<string>,\n): { symbols: SymbolRef[]; dependencies: SymbolRef[] } {\n  const sccs = findStronglyConnectedComponents(\n    remainingSymbols,\n    manifest,\n    processedSymbols,\n  );\n  const selectedSymbols: SymbolRef[] = [];\n  const selectedKeys = new Set<string>();\n\n  // Phase 1: Break major cycles (large SCCs with 3+ symbols)\n  // These represent significant circular dependencies that need to be broken\n  const largeSCCs = sccs.filter((scc) => scc.size >= 3);\n  for (const scc of largeSCCs) {\n    // Select the symbol with minimum dependencies to minimize impact\n    const bestSymbol = selectBestSymbol(\n      scc,\n      remainingSymbols,\n      manifest,\n      processedSymbols,\n    );\n    if (bestSymbol) {\n      selectedSymbols.push(bestSymbol);\n      selectedKeys.add(symbolRefToKey(bestSymbol));\n    }\n  }\n\n  // Phase 2: Add all qualifying independent symbols (small SCCs)\n  // Small SCCs (size < 3) are typically independent symbols or simple mutual dependencies\n  const smallSCCs = sccs.filter((scc) => scc.size < 3);\n  const candidates = smallSCCs\n    .flatMap((scc) => Array.from(scc))\n    .map((symbolKey) => {\n      const symbolRef = remainingSymbols.get(symbolKey);\n      if (!symbolRef) return null;\n      return {\n        key: symbolKey,\n        ref: symbolRef,\n        deps: getUnprocessedDependencies(symbolRef, manifest, processedSymbols)\n          .length,\n      };\n    })\n    .filter(Boolean)\n    .sort((a, b) => a!.deps - b!.deps); // Sort by dependency count (prefer fewer dependencies)\n\n  // Greedily add independent symbols that don't depend on already selected ones\n  for (const candidate of candidates) {\n    // Only add if it doesn't depend on any already selected symbols\n    // This ensures symbols in the same batch can be processed in parallel\n    const dependencies = getUnprocessedDependencies(\n      candidate!.ref,\n      manifest,\n      processedSymbols,\n    );\n    const dependsOnSelected = dependencies.some((depKey) =>\n      selectedKeys.has(depKey)\n    );\n\n    if (!dependsOnSelected) {\n      selectedSymbols.push(candidate!.ref);\n      selectedKeys.add(candidate!.key);\n    }\n  }\n\n  // Phase 3: Calculate all dependencies for the selected batch\n  // These will become the notYetProcessedDependencySymbolRefs for this layer\n  const allDependencies = new Set<string>();\n  for (const symbolRef of selectedSymbols) {\n    const deps = getUnprocessedDependencies(\n      symbolRef,\n      manifest,\n      processedSymbols,\n    );\n    deps.forEach((dep) => allDependencies.add(dep));\n  }\n\n  return {\n    symbols: selectedSymbols,\n    dependencies: Array.from(allDependencies).map(keyToSymbolRef),\n  };\n}\n\n// =============================================================================\n// INDEPENDENT SYMBOL DETECTION\n// =============================================================================\n\n/**\n * Finds symbols that have no unprocessed dependencies.\n * These symbols can be processed immediately without waiting for other symbols.\n * This is the optimal case - no cycle breaking needed.\n *\n * @param remainingSymbols - Map of unprocessed symbols\n * @param manifest - The dependency manifest\n * @param processedSymbols - Set of processed symbols\n * @returns Array of symbols that can be processed independently\n */\nfunction findIndependentSymbols(\n  remainingSymbols: Map<string, SymbolRef>,\n  manifest: DependencyManifest,\n  processedSymbols: Set<string>,\n): SymbolRef[] {\n  return Array.from(remainingSymbols.values()).filter(\n    (symbolRef) =>\n      getUnprocessedDependencies(symbolRef, manifest, processedSymbols)\n        .length === 0,\n  );\n}\n\n// =============================================================================\n// BATCH PROCESSING UTILITIES\n// =============================================================================\n\n/**\n * Marks a batch of symbols as processed by updating the tracking sets.\n * This is a utility function to keep the main algorithm clean.\n *\n * @param symbols - Array of symbols to mark as processed\n * @param remainingSymbols - Map to remove symbols from\n * @param processedSymbols - Set to add symbols to\n */\nfunction processBatch(\n  symbols: SymbolRef[],\n  remainingSymbols: Map<string, SymbolRef>,\n  processedSymbols: Set<string>,\n): void {\n  for (const symbolRef of symbols) {\n    const key = symbolRefToKey(symbolRef);\n    remainingSymbols.delete(key);\n    processedSymbols.add(key);\n  }\n}\n\n// =============================================================================\n// MAIN ALGORITHM\n// =============================================================================\n\n/**\n * Generates group layers for dependency-aware parallel processing.\n *\n * This is the main algorithm that creates a series of layers where:\n * - Symbols in each layer can be processed in parallel\n * - Symbols in layer N only depend on symbols from layers 0 to N-1\n * - The algorithm handles circular dependencies using SCC analysis\n *\n * Algorithm Overview:\n * 1. Start with all symbols as \"remaining\"\n * 2. While there are remaining symbols:\n *    a. Find symbols with no dependencies → process them (optimal case)\n *    b. If no independent symbols exist → use SCC-based cycle breaking\n * 3. Each iteration creates a new layer\n *\n * The algorithm prioritizes processing independent symbols first (no dependencies),\n * and only resorts to cycle breaking when necessary. This minimizes the number\n * of layers and maximizes parallelism.\n *\n * @param manifest - The complete dependency manifest for the codebase\n * @returns Array of GroupLayers representing the processing order\n */\nexport function generateGroupLayers(\n  manifest: DependencyManifest,\n): GroupLayer[] {\n  const groupLayers: GroupLayer[] = [];\n  const processedSymbols = new Set<string>(); // Symbols we've already processed\n  const remainingSymbols = new Map<string, SymbolRef>(); // Symbols still to process\n\n  // Initialize: all symbols start as \"remaining\"\n  for (const [fileId, fileManifest] of Object.entries(manifest)) {\n    for (const symbolId of Object.keys(fileManifest.symbols)) {\n      const ref = { fileId, symbolId };\n      remainingSymbols.set(symbolRefToKey(ref), ref);\n    }\n  }\n\n  let level = 0;\n\n  // Main processing loop: continue until all symbols are processed\n  while (remainingSymbols.size > 0) {\n    // Strategy 1: Look for symbols with no dependencies (optimal case)\n    const independentSymbols = findIndependentSymbols(\n      remainingSymbols,\n      manifest,\n      processedSymbols,\n    );\n\n    if (independentSymbols.length > 0) {\n      // Found independent symbols - process them all in this layer\n      groupLayers.push({\n        level,\n        symbolRefsToProcess: independentSymbols,\n        notYetProcessedDependencySymbolRefs: [], // No dependencies since they're independent\n      });\n      processBatch(independentSymbols, remainingSymbols, processedSymbols);\n    } else {\n      // Strategy 2: No independent symbols - must break cycles using SCC analysis\n      const result = selectCycleBreakers(\n        remainingSymbols,\n        manifest,\n        processedSymbols,\n      );\n\n      groupLayers.push({\n        level,\n        symbolRefsToProcess: result.symbols,\n        notYetProcessedDependencySymbolRefs: result.dependencies,\n      });\n      processBatch(result.symbols, remainingSymbols, processedSymbols);\n    }\n\n    level++;\n  }\n\n  return groupLayers;\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/labeling/index.ts",
    "content": "import type { DependencyManifest } from \"../types.ts\";\nimport { getModel, type ModelProvider } from \"./model.ts\";\nimport { generateGroupLayers } from \"./grouping.ts\";\nimport { createGroupSymbolLabelingWorkflow } from \"./graph.ts\";\n\nexport async function generateSymbolDescriptions(\n  files: Map<string, { path: string; content: string }>,\n  dependencyManifest: DependencyManifest,\n  apiKey: string,\n  modelProvider: ModelProvider,\n  maxConcurrency?: number,\n): Promise<DependencyManifest> {\n  console.info(\"Generating descriptions for symbols...\");\n\n  const groups = generateGroupLayers(dependencyManifest);\n\n  console.info(`✅ Successfully generated ${groups.length} independent groups`);\n\n  const model = getModel(modelProvider, apiKey, maxConcurrency);\n\n  console.info(\"Starting symbol labeling...\");\n  for (const [index, group] of groups.entries()) {\n    const workflow = createGroupSymbolLabelingWorkflow(\n      files,\n      dependencyManifest,\n      group,\n      model,\n    );\n\n    const state = await workflow.invoke({});\n    for (const result of state.results) {\n      dependencyManifest[result.symbolRef.fileId].symbols[\n        result.symbolRef.symbolId\n      ].description = result.description;\n    }\n\n    console.info(\n      `✅ Successfully processed group ${\n        index + 1\n      } of ${groups.length} groups. ${group.symbolRefsToProcess.length} symbols processed`,\n    );\n  }\n\n  return dependencyManifest;\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/labeling/model.ts",
    "content": "import { ChatGoogleGenerativeAI } from \"@langchain/google-genai\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { ChatAnthropic } from \"@langchain/anthropic\";\n\nexport const GOOGLE_PROVIDER = \"google\";\nexport const OPENAI_PROVIDER = \"openai\";\nexport const ANTHROPIC_PROVIDER = \"anthropic\";\n\nexport type ModelProvider =\n  | typeof GOOGLE_PROVIDER\n  | typeof OPENAI_PROVIDER\n  | typeof ANTHROPIC_PROVIDER;\n\nexport function getModel(\n  provider: ModelProvider,\n  apiKey: string,\n  maxConcurrency?: number,\n): BaseChatModel {\n  // // Later we will support multiple model and will get them from the config\n  if (provider === OPENAI_PROVIDER) {\n    return new ChatOpenAI({\n      apiKey,\n      model: \"o3-mini\",\n      maxConcurrency: maxConcurrency ? maxConcurrency : Infinity,\n    }) as unknown as BaseChatModel;\n  }\n\n  if (provider === GOOGLE_PROVIDER) {\n    return new ChatGoogleGenerativeAI({\n      apiKey,\n      model: \"gemini-2.5-flash-lite-preview-06-17\",\n      maxConcurrency: maxConcurrency ? maxConcurrency : Infinity,\n    }) as BaseChatModel;\n  }\n\n  if (provider === ANTHROPIC_PROVIDER) {\n    return new ChatAnthropic({\n      apiKey,\n      model: \"claude-3-5-sonnet-latest\",\n      maxConcurrency: maxConcurrency ? maxConcurrency : Infinity,\n    }) as unknown as BaseChatModel;\n  }\n\n  throw new Error(`Unsupported model provider: ${provider}`);\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/labeling/types.ts",
    "content": "/**\n * Reference to a specific symbol within a file.\n * Used to uniquely identify symbols across the entire codebase.\n */\nexport type SymbolRef = {\n  /** The unique identifier of the file containing the symbol */\n  fileId: string;\n  /** The unique identifier of the symbol within the file */\n  symbolId: string;\n};\n\n/**\n * Represents a layer of symbols that can be processed in parallel.\n * The key insight is that symbols in layer N only depend on symbols from layers 0 to N-1.\n * This allows for efficient parallel processing while respecting dependency order.\n */\nexport type GroupLayer = {\n  /**\n   * The dependency level (0 to n).\n   * Symbols at level n depend on symbols of groups from levels 0 through n-1.\n   */\n  level: number;\n  /**\n   * The symbolRefs to process in this layer.\n   * These symbolRefs can be processed in parallel. They do not depend on each other.\n   */\n  symbolRefsToProcess: SymbolRef[];\n  /**\n   * Dependency symbolRefs that some symbolRefsToProcess depend on.\n   * These symbolRefs will be process in a later layer.\n   * We have no information about them.\n   * This should be as little as possible. Best effort is made to create the groups\n   * that have the least notYetProcessedDependencySymbolRefs as possible. Ideally none.\n   */\n  notYetProcessedDependencySymbolRefs: SymbolRef[];\n};\n"
  },
  {
    "path": "src/manifest/dependencyManifest/python/index.ts",
    "content": "import type Parser from \"tree-sitter\";\nimport {\n  type DependencyInfo,\n  type DependencyManifest,\n  type DependentInfo,\n  type FileDependencyManifest,\n  metricCharacterCount,\n  metricCodeCharacterCount,\n  metricCodeLineCount,\n  metricCyclomaticComplexity,\n  metricDependencyCount,\n  metricDependentCount,\n  metricLinesCount,\n  type SymbolDependencyManifest,\n} from \"../types.ts\";\nimport { PythonExportExtractor } from \"../../../languagePlugins/python/exportExtractor/index.ts\";\nimport { pythonParser } from \"../../../helpers/treeSitter/parsers.ts\";\nimport { PythonModuleResolver } from \"../../../languagePlugins/python/moduleResolver/index.ts\";\nimport { PythonUsageResolver } from \"../../../languagePlugins/python/usageResolver/index.ts\";\nimport { PythonDependencyResolver } from \"../../../languagePlugins/python/dependencyResolver/index.ts\";\nimport { PythonItemResolver } from \"../../../languagePlugins/python/itemResolver/index.ts\";\nimport { PythonImportExtractor } from \"../../../languagePlugins/python/importExtractor/index.ts\";\nimport type { localConfigSchema } from \"../../../cli/middlewares/napiConfig.ts\";\nimport type z from \"zod\";\nimport { PythonMetricsAnalyzer } from \"../../../languagePlugins/python/metricAnalyzer/index.ts\";\n\n/**\n * Builds dependent relationships in the manifest by traversing all dependencies\n */\nfunction generateDependentsForManifest(\n  manifest: DependencyManifest,\n): DependencyManifest {\n  // Go through each file in the manifest\n  for (const [fileId, fileManifest] of Object.entries(manifest)) {\n    // Process file-level dependencies first\n    for (\n      const [depFileId, depInfo] of Object.entries(\n        fileManifest.dependencies,\n      )\n    ) {\n      // Only proceed if it's an internal dependency and the target file actually exists in our manifest\n      if (!depInfo.isExternal && manifest[depFileId]) {\n        const depFile = manifest[depFileId];\n\n        // Add file-level dependent relationship\n        if (!depFile.dependents[fileId]) {\n          const dependent: DependentInfo = {\n            id: fileId,\n            symbols: {},\n          };\n          depFile.dependents[fileId] = dependent;\n        }\n\n        // For each symbol name that we reference from the dependency at file level\n        for (const usedSymbolName of Object.keys(depInfo.symbols)) {\n          // Check if that symbol actually exists in the target file\n          if (depFile.symbols[usedSymbolName]) {\n            const targetSymbol = depFile.symbols[usedSymbolName];\n\n            // Ensure there's a record for the dependent file\n            if (!targetSymbol.dependents[fileId]) {\n              targetSymbol.dependents[fileId] = {\n                id: fileId,\n                symbols: {},\n              };\n            }\n\n            // Record that the file as a whole depends on 'usedSymbolName' in depFile\n            depFile.dependents[fileId].symbols[usedSymbolName] = usedSymbolName;\n          }\n        }\n      }\n    }\n\n    // For each symbol in the file\n    for (const [symbolId, symbolData] of Object.entries(fileManifest.symbols)) {\n      // For each dependency that this symbol uses\n      for (\n        const [depFileId, depInfo] of Object.entries(\n          symbolData.dependencies,\n        )\n      ) {\n        // Only proceed if it's an internal dependency and the target file actually exists in our manifest\n        if (!depInfo.isExternal && manifest[depFileId]) {\n          const depFile = manifest[depFileId];\n\n          // Add file-level dependent relationship\n          if (!depFile.dependents[fileId]) {\n            depFile.dependents[fileId] = {\n              id: fileId,\n              symbols: {},\n            };\n          }\n\n          // For each symbol name that we reference from the dependency\n          for (const usedSymbolName of Object.keys(depInfo.symbols)) {\n            // Check if that symbol actually exists in the target file\n            if (depFile.symbols[usedSymbolName]) {\n              const targetSymbol = depFile.symbols[usedSymbolName];\n\n              // Ensure there's a record for the dependent file\n              if (!targetSymbol.dependents[fileId]) {\n                const dependent: DependentInfo = {\n                  id: fileId,\n                  symbols: {},\n                };\n                targetSymbol.dependents[fileId] = dependent;\n              }\n\n              // Record that 'symbolId' in this file depends on 'usedSymbolName' in depFile\n              targetSymbol.dependents[fileId].symbols[symbolId] = symbolId;\n\n              // Update the file-level dependents with this symbol relationship\n              depFile.dependents[fileId].symbols[symbolId] = symbolId;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  // Update dependency and dependent counts in metrics\n  for (const fileManifest of Object.values(manifest)) {\n    // Update file-level metrics\n    fileManifest.metrics[metricDependencyCount] = Object.keys(\n      fileManifest.dependencies,\n    ).length;\n    fileManifest.metrics[metricDependentCount] = Object.keys(\n      fileManifest.dependents,\n    ).length;\n\n    // Update symbol-level metrics\n    for (const symbolData of Object.values(fileManifest.symbols)) {\n      symbolData.metrics[metricDependencyCount] = Object.keys(\n        symbolData.dependencies,\n      ).length;\n      symbolData.metrics[metricDependentCount] = Object.keys(\n        symbolData.dependents,\n      ).length;\n    }\n  }\n\n  return manifest;\n}\n\n/**\n * Generates a dependency manifest for Python files\n */\nexport function generatePythonDependencyManifest(\n  files: Map<string, { path: string; content: string }>,\n  napiConfig: z.infer<typeof localConfigSchema>,\n): DependencyManifest {\n  const parsedFiles = new Map<\n    string,\n    { path: string; rootNode: Parser.SyntaxNode }\n  >();\n\n  for (const [filePath, { content: fileContent }] of files) {\n    try {\n      const rootNode = pythonParser.parse(fileContent, undefined, {\n        bufferSize: fileContent.length + 10,\n      }).rootNode;\n      parsedFiles.set(filePath, { path: filePath, rootNode });\n    } catch (e) {\n      console.error(`Failed to parse ${filePath}, skipping`);\n      console.error(e);\n    }\n  }\n\n  console.time(\"generatePythonDependencyManifest\");\n\n  const parser = pythonParser;\n\n  const pythonVersion = napiConfig.python?.version;\n  if (!pythonVersion) {\n    throw new Error(\n      \"Python version is required in the .napirc file (audit.pythonVersion).\",\n    );\n  }\n\n  console.time(\"generatePythonDependencyManifest:initialization\");\n  console.info(\"Initializing Python export resolver...\");\n  const exportExtractor = new PythonExportExtractor(parser, parsedFiles);\n  console.info(\"Initializing Python import resolver...\");\n  const importExtractor = new PythonImportExtractor(parser, parsedFiles);\n  console.info(\"Initializing Python module resolver...\");\n\n  const moduleResolver = new PythonModuleResolver(\n    new Set(parsedFiles.keys()),\n    pythonVersion,\n  );\n  console.info(\"Initializing Python item resolver...\");\n  const itemResolver = new PythonItemResolver(\n    exportExtractor,\n    importExtractor,\n    moduleResolver,\n  );\n  console.info(\"Initializing Python usage resolver...\");\n  const usageResolver = new PythonUsageResolver(parser, exportExtractor);\n  console.info(\"Initializing Python dependency resolver...\");\n  const complexityAnalyzer = new PythonMetricsAnalyzer(parser);\n  const dependencyResolver = new PythonDependencyResolver(\n    parsedFiles,\n    exportExtractor,\n    importExtractor,\n    itemResolver,\n    usageResolver,\n    moduleResolver,\n    complexityAnalyzer,\n  );\n\n  console.timeEnd(\"generatePythonDependencyManifest:initialization\");\n\n  console.time(\"generatePythonDependencyManifest:processing\");\n  console.info(\"Generating Python dependency manifest...\");\n  let manifest: DependencyManifest = {};\n\n  let i = 0;\n  for (const file of files.values()) {\n    const fileDependencies = dependencyResolver.getFileDependencies(file.path);\n\n    const dependencies: Record<string, DependencyInfo> = {};\n    for (const dep of fileDependencies.dependencies.values()) {\n      const symbols: Record<string, string> = {};\n      dep.symbols.forEach((symbol) => {\n        symbols[symbol] = symbol;\n      });\n\n      dependencies[dep.id] = {\n        id: dep.id,\n        isExternal: dep.isExternal,\n        symbols,\n      };\n    }\n\n    const symbols: Record<string, SymbolDependencyManifest> = {};\n    for (const symbol of fileDependencies.symbols.values()) {\n      const dependencies: Record<string, DependencyInfo> = {};\n      // Process symbol dependencies from all sources at once\n      for (const dep of symbol.dependencies.values()) {\n        const symbols: Record<string, string> = {};\n        dep.symbols.forEach((symbol) => {\n          symbols[symbol] = symbol;\n        });\n\n        dependencies[dep.id] = {\n          id: dep.id,\n          isExternal: dep.isExternal,\n          symbols,\n        };\n      }\n\n      symbols[symbol.id] = {\n        id: symbol.id,\n        type: symbol.type,\n        positions: symbol.positions,\n        description: \"\",\n        metrics: {\n          [metricLinesCount]: symbol.metrics.linesCount,\n          [metricCodeLineCount]: symbol.metrics.codeLineCount,\n          [metricCharacterCount]: symbol.metrics.characterCount,\n          [metricCodeCharacterCount]: symbol.metrics.codeCharacterCount,\n          [metricDependencyCount]: symbol.dependencies.size,\n          [metricDependentCount]: 0, // Will be computed later\n          [metricCyclomaticComplexity]: symbol.metrics.cyclomaticComplexity,\n        },\n        dependencies,\n        dependents: {}, // Will be computed later\n      };\n    }\n\n    const fileManifest: FileDependencyManifest = {\n      id: file.path,\n      filePath: file.path,\n      metrics: {\n        [metricLinesCount]: fileDependencies.metrics.codeLineCount,\n        [metricCodeLineCount]: fileDependencies.metrics.codeLineCount,\n        [metricCharacterCount]: fileDependencies.metrics.characterCount,\n        [metricCodeCharacterCount]: fileDependencies.metrics.codeCharacterCount,\n        [metricDependencyCount]: fileDependencies.dependencies.size,\n        [metricDependentCount]: 0, // Will be computed later\n        [metricCyclomaticComplexity]:\n          fileDependencies.metrics.cyclomaticComplexity,\n      },\n      language: parser.getLanguage().name,\n      dependencies,\n      dependents: {}, // Will be computed later\n      symbols,\n    };\n\n    manifest[file.path] = fileManifest;\n\n    console.info(`✅ Processed ${i + 1}/${files.size}: ${file.path}`);\n    i++;\n  }\n\n  console.info(\n    `Generated Python dependency manifest for ${parsedFiles.size} files`,\n  );\n\n  console.timeEnd(\"generatePythonDependencyManifest:processing\");\n\n  console.time(\"generatePythonDependencyManifest:dependents\");\n  console.info(\"Generating Python dependents...\");\n  manifest = generateDependentsForManifest(manifest);\n  console.info(\"Generated Python dependents\");\n  console.timeEnd(\"generatePythonDependencyManifest:dependents\");\n\n  console.info(\"Python dependency manifest generated\");\n  console.timeEnd(\"generatePythonDependencyManifest\");\n\n  return manifest;\n}\n"
  },
  {
    "path": "src/manifest/dependencyManifest/types.ts",
    "content": "/** Identifies the \"class\" instance type. */\nexport const classSymbolType = \"class\";\n\n/** Identifies the \"struct\" instance type. */\nexport const structSymbolType = \"struct\";\n\n/** Identifies the \"enum\" instance type. */\nexport const enumSymbolType = \"enum\";\n\n/** Identifies the \"union\" instance type. */\nexport const unionSymbolType = \"union\";\n\n/** Identifies the \"typedef\" instance type. */\nexport const typedefSymbolType = \"typedef\";\n\n/** Identifies the \"interface\" instance type. */\nexport const interfaceSymbolType = \"interface\";\n\n/** Identifies the \"record\" instance type. */\nexport const recordSymbolType = \"record\";\n\n/** Identifies the \"delegate\" instance type. */\nexport const delegateSymbolType = \"delegate\";\n\n/** Identifies the \"function\" instance type. */\nexport const functionSymbolType = \"function\";\n\n/** Identifies the \"variable\" instance type.  */\nexport const variableSymbolType = \"variable\";\n\n/** Possible categories of an instance (symbol) within a file:\n * - class\n * - struct\n * - enum\n * - function\n * - variable\n * - interface\n * - record\n * - delegate */\nexport type SymbolType =\n  | typeof classSymbolType\n  | typeof functionSymbolType\n  | typeof variableSymbolType\n  | typeof structSymbolType\n  | typeof enumSymbolType\n  | typeof unionSymbolType\n  | typeof typedefSymbolType\n  | typeof interfaceSymbolType\n  | typeof recordSymbolType\n  | typeof delegateSymbolType;\n\nexport const metricLinesCount = \"linesCount\";\nexport const metricCodeLineCount = \"codeLineCount\";\nexport const metricCharacterCount = \"characterCount\";\nexport const metricCodeCharacterCount = \"codeCharacterCount\";\nexport const metricDependencyCount = \"dependencyCount\";\nexport const metricDependentCount = \"dependentCount\";\nexport const metricCyclomaticComplexity = \"cyclomaticComplexity\";\n\nexport type Metric =\n  | typeof metricLinesCount\n  | typeof metricCodeLineCount\n  | typeof metricCharacterCount\n  | typeof metricCodeCharacterCount\n  | typeof metricDependencyCount\n  | typeof metricDependentCount\n  | typeof metricCyclomaticComplexity;\n\n/** Represents a single dependency. For example, if File A depends on\nFile B, `DependencyInfo` captures how A uses B's symbols. */\nexport interface DependencyInfo {\n  /** A unique identifier for the dependency, often matching the file path if internal,\n   * or a package/module name if external. */\n  id: string;\n  /** Whether this dependency is an external library/package (true) vs. an internal file (false). */\n  isExternal: boolean;\n  /** A map of symbol names used from this dependency (key → value).\n   * Example:\n   * {\n   *   \"foo\": \"foo\",\n   *   \"bar\": \"bar\"\n   * }\n   * For Python-only (current scope), both key and value can be the same string,\n   * indicating the file references a symbol by that name. */\n  symbols: Record<string, string>;\n}\n\n/** Represents a single \"dependent\": something that depends on the file or symbol in question.\n * For instance, if File X depends on File Y, in Y's \"dependents\" you'd have an\n * entry for X describing the symbols X uses from Y. */\nexport interface DependentInfo {\n  /** A unique identifier for the dependent, typically the file path or module name. */\n  id: string;\n  /** A map of symbol names that this dependent references from the file or symbol.\n   * Example:\n   * {\n   *   \"myClass\": \"myClass\",\n   *   \"someFunc\": \"someFunc\"\n   * } */\n  symbols: Record<string, string>;\n}\n\n/** Represents a single named symbol (class, function, or variable) declared in a file.\n * Symbols can have their own dependencies and also be depended upon by others. */\nexport interface SymbolDependencyManifest {\n  /** Unique name for this symbol (e.g., the class name or function name). */\n  id: string;\n  /** The type of this symbol: \"class\", \"function\", or \"variable\". */\n  type: SymbolType;\n  /** The start position of the symbol. */\n  positions: {\n    start: {\n      index: number;\n      row: number;\n      column: number;\n    };\n    end: {\n      index: number;\n      row: number;\n      column: number;\n    };\n  }[];\n  /** Metrics for the symbol. */\n  metrics: {\n    /** The number of lines in the symbol. */\n    [metricLinesCount]: number;\n    /** The number of lines in the symbol. */\n    [metricCodeLineCount]: number;\n    /** The number of characters in the symbol. */\n    [metricCharacterCount]: number;\n    /** The number of characters in the symbol. */\n    [metricCodeCharacterCount]: number;\n    /** The number of dependencies on the symbol. */\n    [metricDependencyCount]: number;\n    /** The number of dependents on the symbol. */\n    [metricDependentCount]: number;\n    /** The cyclomatic complexity of the symbol. */\n    [metricCyclomaticComplexity]: number;\n  };\n  /** A short description of the symbol. */\n  description: string;\n  /** Other modules/files on which this symbol depends.\n   * Keyed by the dependency's unique ID (often a file path). */\n  dependencies: Record<string, DependencyInfo>;\n  /** A reverse map of modules/files (or symbols) that depend on this symbol. */\n  dependents: Record<string, DependentInfo>;\n}\n\n/** Represents a single file in the project, including:\n * - file-level dependencies\n * - file-level dependents\n * - symbols declared in the file */\nexport interface FileDependencyManifest {\n  /** A unique identifier for the file, often the file path. */\n  id: string;\n  /** The path or name of the file (e.g. \"src/app/main.py\"). */\n  filePath: string;\n  /** The programming language of the file. It may be extended in the future to handle other languages. */\n  language: string;\n  /** The metrics for the file. */\n  metrics: {\n    /** The number of lines in the symbol. */\n    [metricLinesCount]: number;\n    /** The number of lines in the symbol. */\n    [metricCodeLineCount]: number;\n    /** The number of characters in the symbol. */\n    [metricCharacterCount]: number;\n    /** The number of characters in the symbol. */\n    [metricCodeCharacterCount]: number;\n    /** The number of dependencies on the symbol. */\n    [metricDependencyCount]: number;\n    /** The number of dependents on the symbol. */\n    [metricDependentCount]: number;\n    /** The cyclomatic complexity of the symbol. */\n    [metricCyclomaticComplexity]: number;\n  };\n  /** Dependencies at the file level. If this file imports other files/packages,\n   * each one appears here (with the associated symbols used). */\n  dependencies: Record<string, DependencyInfo>;\n  /** Dependents at the file level. If this file is used by other files/packages,\n   * each one appears here (with the associated symbols used). */\n  dependents: Record<string, DependentInfo>;\n  /** Symbols (classes, functions, variables) declared in this file, keyed by symbol name (ID).\n   * Each symbol may also have dependencies and dependents of its own. */\n  symbols: Record<string, SymbolDependencyManifest>;\n}\n\n/** A global structure mapping each file's unique ID (often its file path)\n * to its `FileManifest`. This collectively represents the project's\n * dependency graph or manifest. */\nexport type DependencyManifest = Record<string, FileDependencyManifest>;\n"
  },
  {
    "path": "src/scripts/generate_python_stdlib_list/output.json",
    "content": "{\n  \"2.7\": [\n    \"AL\",\n    \"BaseHTTPServer\",\n    \"Bastion\",\n    \"CGIHTTPServer\",\n    \"Canvas\",\n    \"Carbon.AE\",\n    \"Carbon.AH\",\n    \"Carbon.App\",\n    \"Carbon.Appearance\",\n    \"Carbon.CF\",\n    \"Carbon.CG\",\n    \"Carbon.CarbonEvents\",\n    \"Carbon.CarbonEvt\",\n    \"Carbon.Cm\",\n    \"Carbon.Components\",\n    \"Carbon.ControlAccessor\",\n    \"Carbon.Controls\",\n    \"Carbon.CoreFounation\",\n    \"Carbon.CoreGraphics\",\n    \"Carbon.Ctl\",\n    \"Carbon.Dialogs\",\n    \"Carbon.Dlg\",\n    \"Carbon.Drag\",\n    \"Carbon.Dragconst\",\n    \"Carbon.Events\",\n    \"Carbon.Evt\",\n    \"Carbon.File\",\n    \"Carbon.Files\",\n    \"Carbon.Fm\",\n    \"Carbon.Folder\",\n    \"Carbon.Folders\",\n    \"Carbon.Fonts\",\n    \"Carbon.Help\",\n    \"Carbon.IBCarbon\",\n    \"Carbon.IBCarbonRuntime\",\n    \"Carbon.Icns\",\n    \"Carbon.Icons\",\n    \"Carbon.Launch\",\n    \"Carbon.LaunchServices\",\n    \"Carbon.List\",\n    \"Carbon.Lists\",\n    \"Carbon.MacHelp\",\n    \"Carbon.MediaDescr\",\n    \"Carbon.Menu\",\n    \"Carbon.Menus\",\n    \"Carbon.Mlte\",\n    \"Carbon.OSA\",\n    \"Carbon.OSAconst\",\n    \"Carbon.QDOffscreen\",\n    \"Carbon.Qd\",\n    \"Carbon.Qdoffs\",\n    \"Carbon.Qt\",\n    \"Carbon.QuickDraw\",\n    \"Carbon.QuickTime\",\n    \"Carbon.Res\",\n    \"Carbon.Resources\",\n    \"Carbon.Scrap\",\n    \"Carbon.Snd\",\n    \"Carbon.Sound\",\n    \"Carbon.TE\",\n    \"Carbon.TextEdit\",\n    \"Carbon.Win\",\n    \"Carbon.Windows\",\n    \"ColorPicker\",\n    \"ConfigParser\",\n    \"Cookie\",\n    \"DEVICE\",\n    \"Dialog\",\n    \"DocXMLRPCServer\",\n    \"EasyDialogs\",\n    \"FL\",\n    \"FileDialog\",\n    \"FixTk\",\n    \"FrameWork\",\n    \"GL\",\n    \"HTMLParser\",\n    \"MacOS\",\n    \"MimeWriter\",\n    \"MiniAEFrame\",\n    \"Nav\",\n    \"PixMapWrapper\",\n    \"Queue\",\n    \"SUNAUDIODEV\",\n    \"ScrolledText\",\n    \"SimpleDialog\",\n    \"SimpleHTTPServer\",\n    \"SimpleXMLRPCServer\",\n    \"SocketServer\",\n    \"StringIO\",\n    \"Tix\",\n    \"Tkconstants\",\n    \"Tkdnd\",\n    \"Tkinter\",\n    \"UserDict\",\n    \"UserList\",\n    \"UserString\",\n    \"W\",\n    \"_LWPCookieJar\",\n    \"_MozillaCookieJar\",\n    \"__builtin__\",\n    \"__future__\",\n    \"__main__\",\n    \"_abcoll\",\n    \"_ast\",\n    \"_bisect\",\n    \"_bsddb\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_ctypes_test\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_elementtree\",\n    \"_functools\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_hotshot\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_md5\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_osx_support\",\n    \"_pyio\",\n    \"_random\",\n    \"_sha\",\n    \"_sha256\",\n    \"_sha512\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_sysconfigdata\",\n    \"_testcapi\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"_winreg\",\n    \"abc\",\n    \"aepack\",\n    \"aetools\",\n    \"aetypes\",\n    \"aifc\",\n    \"al\",\n    \"antigravity\",\n    \"anydbm\",\n    \"applesingle\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncore\",\n    \"atexit\",\n    \"audiodev\",\n    \"audioop\",\n    \"autoGIL\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"bsddb\",\n    \"bsddb.db\",\n    \"bsddb.dbobj\",\n    \"bsddb.dbrecio\",\n    \"bsddb.dbshelve\",\n    \"bsddb.dbtables\",\n    \"bsddb.dbutils\",\n    \"bsddb.test\",\n    \"bsddb.test.test_all\",\n    \"bsddb.test.test_associate\",\n    \"bsddb.test.test_basics\",\n    \"bsddb.test.test_compare\",\n    \"bsddb.test.test_compat\",\n    \"bsddb.test.test_cursor_pget_bug\",\n    \"bsddb.test.test_db\",\n    \"bsddb.test.test_dbenv\",\n    \"bsddb.test.test_dbobj\",\n    \"bsddb.test.test_dbshelve\",\n    \"bsddb.test.test_dbtables\",\n    \"bsddb.test.test_distributed_transactions\",\n    \"bsddb.test.test_early_close\",\n    \"bsddb.test.test_fileid\",\n    \"bsddb.test.test_get_none\",\n    \"bsddb.test.test_join\",\n    \"bsddb.test.test_lock\",\n    \"bsddb.test.test_misc\",\n    \"bsddb.test.test_pickle\",\n    \"bsddb.test.test_queue\",\n    \"bsddb.test.test_recno\",\n    \"bsddb.test.test_replication\",\n    \"bsddb.test.test_sequence\",\n    \"bsddb.test.test_thread\",\n    \"buildtools\",\n    \"bz2\",\n    \"cPickle\",\n    \"cProfile\",\n    \"cStringIO\",\n    \"calendar\",\n    \"cd\",\n    \"cfmfile\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"colorsys\",\n    \"commands\",\n    \"compileall\",\n    \"compiler\",\n    \"compiler.ast\",\n    \"compiler.consts\",\n    \"compiler.future\",\n    \"compiler.misc\",\n    \"compiler.pyassem\",\n    \"compiler.pycodegen\",\n    \"compiler.symbols\",\n    \"compiler.syntax\",\n    \"compiler.transformer\",\n    \"compiler.visitor\",\n    \"contextlib\",\n    \"cookielib\",\n    \"copy\",\n    \"copy_reg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.runtests\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"curses.wrapper\",\n    \"datetime\",\n    \"dbhash\",\n    \"dbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dircache\",\n    \"dis\",\n    \"distutils\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.emxccompiler\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.tests\",\n    \"distutils.tests.setuptools_build_ext\",\n    \"distutils.tests.setuptools_extension\",\n    \"distutils.tests.support\",\n    \"distutils.tests.test_archive_util\",\n    \"distutils.tests.test_bdist\",\n    \"distutils.tests.test_bdist_dumb\",\n    \"distutils.tests.test_bdist_msi\",\n    \"distutils.tests.test_bdist_rpm\",\n    \"distutils.tests.test_bdist_wininst\",\n    \"distutils.tests.test_build\",\n    \"distutils.tests.test_build_clib\",\n    \"distutils.tests.test_build_ext\",\n    \"distutils.tests.test_build_py\",\n    \"distutils.tests.test_build_scripts\",\n    \"distutils.tests.test_ccompiler\",\n    \"distutils.tests.test_check\",\n    \"distutils.tests.test_clean\",\n    \"distutils.tests.test_cmd\",\n    \"distutils.tests.test_config\",\n    \"distutils.tests.test_config_cmd\",\n    \"distutils.tests.test_core\",\n    \"distutils.tests.test_dep_util\",\n    \"distutils.tests.test_dir_util\",\n    \"distutils.tests.test_dist\",\n    \"distutils.tests.test_file_util\",\n    \"distutils.tests.test_filelist\",\n    \"distutils.tests.test_install\",\n    \"distutils.tests.test_install_data\",\n    \"distutils.tests.test_install_headers\",\n    \"distutils.tests.test_install_lib\",\n    \"distutils.tests.test_install_scripts\",\n    \"distutils.tests.test_msvc9compiler\",\n    \"distutils.tests.test_register\",\n    \"distutils.tests.test_sdist\",\n    \"distutils.tests.test_spawn\",\n    \"distutils.tests.test_sysconfig\",\n    \"distutils.tests.test_text_file\",\n    \"distutils.tests.test_unixccompiler\",\n    \"distutils.tests.test_upload\",\n    \"distutils.tests.test_util\",\n    \"distutils.tests.test_version\",\n    \"distutils.tests.test_versionpredicate\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"dl\",\n    \"doctest\",\n    \"dumbdbm\",\n    \"dummy_thread\",\n    \"dummy_threading\",\n    \"email\",\n    \"email._parseaddr\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.quoprimime\",\n    \"email.test\",\n    \"email.test.test_email\",\n    \"email.test.test_email_codecs\",\n    \"email.test.test_email_codecs_renamed\",\n    \"email.test.test_email_renamed\",\n    \"email.test.test_email_torture\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.__builtin__\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.codecs\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.encodings\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_u\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_centeuro\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.string_escape\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.unicode_internal\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"errno\",\n    \"exceptions\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"findertools\",\n    \"fl\",\n    \"flp\",\n    \"fm\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fpectl\",\n    \"fpformat\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"future_builtins\",\n    \"gc\",\n    \"gdbm\",\n    \"genericpath\",\n    \"gensuitemodule\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"gl\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"hotshot\",\n    \"hotshot.log\",\n    \"hotshot.stats\",\n    \"hotshot.stones\",\n    \"htmlentitydefs\",\n    \"htmllib\",\n    \"httplib\",\n    \"ic\",\n    \"icopen\",\n    \"idlelib\",\n    \"idlelib.AutoComplete\",\n    \"idlelib.AutoCompleteWindow\",\n    \"idlelib.AutoExpand\",\n    \"idlelib.Bindings\",\n    \"idlelib.CallTipWindow\",\n    \"idlelib.CallTips\",\n    \"idlelib.ClassBrowser\",\n    \"idlelib.CodeContext\",\n    \"idlelib.ColorDelegator\",\n    \"idlelib.Debugger\",\n    \"idlelib.Delegator\",\n    \"idlelib.EditorWindow\",\n    \"idlelib.FileList\",\n    \"idlelib.FormatParagraph\",\n    \"idlelib.GrepDialog\",\n    \"idlelib.HyperParser\",\n    \"idlelib.IOBinding\",\n    \"idlelib.IdleHistory\",\n    \"idlelib.MultiCall\",\n    \"idlelib.MultiStatusBar\",\n    \"idlelib.ObjectBrowser\",\n    \"idlelib.OutputWindow\",\n    \"idlelib.ParenMatch\",\n    \"idlelib.PathBrowser\",\n    \"idlelib.Percolator\",\n    \"idlelib.PyParse\",\n    \"idlelib.PyShell\",\n    \"idlelib.RemoteDebugger\",\n    \"idlelib.RemoteObjectBrowser\",\n    \"idlelib.ReplaceDialog\",\n    \"idlelib.RstripExtension\",\n    \"idlelib.ScriptBinding\",\n    \"idlelib.ScrolledList\",\n    \"idlelib.SearchDialog\",\n    \"idlelib.SearchDialogBase\",\n    \"idlelib.SearchEngine\",\n    \"idlelib.StackViewer\",\n    \"idlelib.ToolTip\",\n    \"idlelib.TreeWidget\",\n    \"idlelib.UndoDelegator\",\n    \"idlelib.WidgetRedirector\",\n    \"idlelib.WindowList\",\n    \"idlelib.ZoomHeight\",\n    \"idlelib.aboutDialog\",\n    \"idlelib.configDialog\",\n    \"idlelib.configHandler\",\n    \"idlelib.configHelpSourceEdit\",\n    \"idlelib.configSectionNameDialog\",\n    \"idlelib.dynOptionMenuWidget\",\n    \"idlelib.help\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_calltips\",\n    \"idlelib.idle_test.test_config_name\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_formatparagraph\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_helpabout\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_idlehistory\",\n    \"idlelib.idle_test.test_io\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_rstrip\",\n    \"idlelib.idle_test.test_searchdialogbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_widgetredir\",\n    \"idlelib.idlever\",\n    \"idlelib.keybindingDialog\",\n    \"idlelib.macosxSupport\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.tabbedpages\",\n    \"idlelib.textView\",\n    \"ihooks\",\n    \"imageop\",\n    \"imaplib\",\n    \"imgfile\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"imputil\",\n    \"inspect\",\n    \"io\",\n    \"itertools\",\n    \"jpeg\",\n    \"json\",\n    \"json._json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.json\",\n    \"json.re\",\n    \"json.scanner\",\n    \"json.struct\",\n    \"json.sys\",\n    \"json.tests\",\n    \"json.tests.test_check_circular\",\n    \"json.tests.test_decode\",\n    \"json.tests.test_default\",\n    \"json.tests.test_dump\",\n    \"json.tests.test_encode_basestring_ascii\",\n    \"json.tests.test_fail\",\n    \"json.tests.test_float\",\n    \"json.tests.test_indent\",\n    \"json.tests.test_pass1\",\n    \"json.tests.test_pass2\",\n    \"json.tests.test_pass3\",\n    \"json.tests.test_recursion\",\n    \"json.tests.test_scanstring\",\n    \"json.tests.test_separators\",\n    \"json.tests.test_speedups\",\n    \"json.tests.test_tool\",\n    \"json.tests.test_unicode\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.data.bom\",\n    \"lib2to3.tests.data.crlf\",\n    \"lib2to3.tests.data.different_encoding\",\n    \"lib2to3.tests.data.false_encoding\",\n    \"lib2to3.tests.data.fixers.bad_order\",\n    \"lib2to3.tests.data.fixers.myfixes\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_explicit\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_first\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_last\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_parrot\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_preorder\",\n    \"lib2to3.tests.data.fixers.no_fixer_cls\",\n    \"lib2to3.tests.data.fixers.parrot_example\",\n    \"lib2to3.tests.data.infinite_recursion\",\n    \"lib2to3.tests.data.py2_test_grammar\",\n    \"lib2to3.tests.data.py3_test_grammar\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"linuxaudiodev\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"macerrors\",\n    \"macostools\",\n    \"macpath\",\n    \"macresource\",\n    \"macurl2path\",\n    \"mailbox\",\n    \"mailcap\",\n    \"markupbase\",\n    \"marshal\",\n    \"math\",\n    \"md5\",\n    \"mhlib\",\n    \"mimetools\",\n    \"mimetypes\",\n    \"mimify\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multifile\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forking\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"mutex\",\n    \"netrc\",\n    \"new\",\n    \"nis\",\n    \"nntplib\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"os2emxpath\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"popen2\",\n    \"poplib\",\n    \"posix\",\n    \"posixfile\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"repr\",\n    \"resource\",\n    \"rexec\",\n    \"rfc822\",\n    \"rlcompleter\",\n    \"robotparser\",\n    \"runpy\",\n    \"sched\",\n    \"select\",\n    \"sets\",\n    \"sgmllib\",\n    \"sha\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.py25tests\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statvfs\",\n    \"string\",\n    \"stringold\",\n    \"stringprep\",\n    \"strop\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"sunaudio\",\n    \"sunaudiodev\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.__main__\",\n    \"test._mock_backport\",\n    \"test.audiotests\",\n    \"test.autotest\",\n    \"test.bad_coding\",\n    \"test.bad_coding2\",\n    \"test.bad_coding3\",\n    \"test.badsyntax_future3\",\n    \"test.badsyntax_future4\",\n    \"test.badsyntax_future5\",\n    \"test.badsyntax_future6\",\n    \"test.badsyntax_future7\",\n    \"test.badsyntax_future8\",\n    \"test.badsyntax_future9\",\n    \"test.badsyntax_nocaret\",\n    \"test.bisect\",\n    \"test.bisect_cmd\",\n    \"test.curses_tests\",\n    \"test.doctest_aliases\",\n    \"test.double_const\",\n    \"test.fork_wait\",\n    \"test.gdb_sample\",\n    \"test.infinite_reload\",\n    \"test.inspect_fodder\",\n    \"test.inspect_fodder2\",\n    \"test.list_tests\",\n    \"test.lock_tests\",\n    \"test.make_ssl_certs\",\n    \"test.mapping_tests\",\n    \"test.mp_fork_bomb\",\n    \"test.multibytecodec_support\",\n    \"test.outstanding_bugs\",\n    \"test.pickletester\",\n    \"test.profilee\",\n    \"test.pyclbr_input\",\n    \"test.pydoc_mod\",\n    \"test.pydocfodder\",\n    \"test.pystone\",\n    \"test.pythoninfo\",\n    \"test.re_tests\",\n    \"test.regrtest\",\n    \"test.relimport\",\n    \"test.reperf\",\n    \"test.sample_doctest\",\n    \"test.sample_doctest_no_docstrings\",\n    \"test.sample_doctest_no_doctests\",\n    \"test.script_helper\",\n    \"test.seq_tests\",\n    \"test.sortperf\",\n    \"test.ssl_servers\",\n    \"test.ssltests\",\n    \"test.string_tests\",\n    \"test.subprocessdata.sigchild_ignore\",\n    \"test.support\",\n    \"test.support.script_helper\",\n    \"test.symlink_support\",\n    \"test.test_MimeWriter\",\n    \"test.test_SimpleHTTPServer\",\n    \"test.test_StringIO\",\n    \"test.test___all__\",\n    \"test.test___future__\",\n    \"test.test__locale\",\n    \"test.test__osx_support\",\n    \"test.test_abc\",\n    \"test.test_abstract_numbers\",\n    \"test.test_aepack\",\n    \"test.test_aifc\",\n    \"test.test_al\",\n    \"test.test_anydbm\",\n    \"test.test_applesingle\",\n    \"test.test_argparse\",\n    \"test.test_array\",\n    \"test.test_ascii_formatd\",\n    \"test.test_ast\",\n    \"test.test_asynchat\",\n    \"test.test_asyncore\",\n    \"test.test_atexit\",\n    \"test.test_audioop\",\n    \"test.test_augassign\",\n    \"test.test_base64\",\n    \"test.test_bastion\",\n    \"test.test_bdb\",\n    \"test.test_bigaddrspace\",\n    \"test.test_bigmem\",\n    \"test.test_binascii\",\n    \"test.test_binhex\",\n    \"test.test_binop\",\n    \"test.test_bisect\",\n    \"test.test_bool\",\n    \"test.test_bsddb\",\n    \"test.test_bsddb185\",\n    \"test.test_bsddb3\",\n    \"test.test_buffer\",\n    \"test.test_bufio\",\n    \"test.test_builtin\",\n    \"test.test_bytes\",\n    \"test.test_bz2\",\n    \"test.test_calendar\",\n    \"test.test_call\",\n    \"test.test_capi\",\n    \"test.test_cd\",\n    \"test.test_cfgparser\",\n    \"test.test_cgi\",\n    \"test.test_charmapcodec\",\n    \"test.test_cl\",\n    \"test.test_class\",\n    \"test.test_cmath\",\n    \"test.test_cmd\",\n    \"test.test_cmd_line\",\n    \"test.test_cmd_line_script\",\n    \"test.test_code\",\n    \"test.test_codeccallbacks\",\n    \"test.test_codecencodings_cn\",\n    \"test.test_codecencodings_hk\",\n    \"test.test_codecencodings_iso2022\",\n    \"test.test_codecencodings_jp\",\n    \"test.test_codecencodings_kr\",\n    \"test.test_codecencodings_tw\",\n    \"test.test_codecmaps_cn\",\n    \"test.test_codecmaps_hk\",\n    \"test.test_codecmaps_jp\",\n    \"test.test_codecmaps_kr\",\n    \"test.test_codecmaps_tw\",\n    \"test.test_codecs\",\n    \"test.test_codeop\",\n    \"test.test_coercion\",\n    \"test.test_collections\",\n    \"test.test_colorsys\",\n    \"test.test_commands\",\n    \"test.test_compare\",\n    \"test.test_compile\",\n    \"test.test_compileall\",\n    \"test.test_compiler\",\n    \"test.test_complex\",\n    \"test.test_complex_args\",\n    \"test.test_contains\",\n    \"test.test_contextlib\",\n    \"test.test_cookie\",\n    \"test.test_cookielib\",\n    \"test.test_copy\",\n    \"test.test_copy_reg\",\n    \"test.test_cpickle\",\n    \"test.test_cprofile\",\n    \"test.test_crypt\",\n    \"test.test_csv\",\n    \"test.test_ctypes\",\n    \"test.test_curses\",\n    \"test.test_datetime\",\n    \"test.test_dbm\",\n    \"test.test_decimal\",\n    \"test.test_decorators\",\n    \"test.test_defaultdict\",\n    \"test.test_deque\",\n    \"test.test_descr\",\n    \"test.test_descrtut\",\n    \"test.test_dict\",\n    \"test.test_dictcomps\",\n    \"test.test_dictviews\",\n    \"test.test_difflib\",\n    \"test.test_dircache\",\n    \"test.test_dis\",\n    \"test.test_distutils\",\n    \"test.test_dl\",\n    \"test.test_doctest\",\n    \"test.test_doctest2\",\n    \"test.test_docxmlrpc\",\n    \"test.test_dumbdbm\",\n    \"test.test_dummy_thread\",\n    \"test.test_dummy_threading\",\n    \"test.test_email\",\n    \"test.test_email_codecs\",\n    \"test.test_email_renamed\",\n    \"test.test_ensurepip\",\n    \"test.test_enumerate\",\n    \"test.test_eof\",\n    \"test.test_epoll\",\n    \"test.test_errno\",\n    \"test.test_exception_variations\",\n    \"test.test_exceptions\",\n    \"test.test_extcall\",\n    \"test.test_fcntl\",\n    \"test.test_file\",\n    \"test.test_file2k\",\n    \"test.test_file_eintr\",\n    \"test.test_filecmp\",\n    \"test.test_fileinput\",\n    \"test.test_fileio\",\n    \"test.test_float\",\n    \"test.test_fnmatch\",\n    \"test.test_fork1\",\n    \"test.test_format\",\n    \"test.test_fpformat\",\n    \"test.test_fractions\",\n    \"test.test_frozen\",\n    \"test.test_ftplib\",\n    \"test.test_funcattrs\",\n    \"test.test_functools\",\n    \"test.test_future\",\n    \"test.test_future1\",\n    \"test.test_future2\",\n    \"test.test_future3\",\n    \"test.test_future4\",\n    \"test.test_future5\",\n    \"test.test_future_builtins\",\n    \"test.test_gc\",\n    \"test.test_gdb\",\n    \"test.test_gdbm\",\n    \"test.test_generators\",\n    \"test.test_genericpath\",\n    \"test.test_genexps\",\n    \"test.test_getargs\",\n    \"test.test_getargs2\",\n    \"test.test_getopt\",\n    \"test.test_gettext\",\n    \"test.test_gl\",\n    \"test.test_glob\",\n    \"test.test_global\",\n    \"test.test_grammar\",\n    \"test.test_grp\",\n    \"test.test_gzip\",\n    \"test.test_hash\",\n    \"test.test_hashlib\",\n    \"test.test_heapq\",\n    \"test.test_hmac\",\n    \"test.test_hotshot\",\n    \"test.test_htmllib\",\n    \"test.test_htmlparser\",\n    \"test.test_httplib\",\n    \"test.test_httpservers\",\n    \"test.test_idle\",\n    \"test.test_imageop\",\n    \"test.test_imaplib\",\n    \"test.test_imgfile\",\n    \"test.test_imghdr\",\n    \"test.test_imp\",\n    \"test.test_import\",\n    \"test.test_import_magic\",\n    \"test.test_importhooks\",\n    \"test.test_importlib\",\n    \"test.test_index\",\n    \"test.test_inspect\",\n    \"test.test_int\",\n    \"test.test_int_literal\",\n    \"test.test_io\",\n    \"test.test_ioctl\",\n    \"test.test_isinstance\",\n    \"test.test_iter\",\n    \"test.test_iterlen\",\n    \"test.test_itertools\",\n    \"test.test_json\",\n    \"test.test_kqueue\",\n    \"test.test_largefile\",\n    \"test.test_lib2to3\",\n    \"test.test_linecache\",\n    \"test.test_linuxaudiodev\",\n    \"test.test_list\",\n    \"test.test_locale\",\n    \"test.test_logging\",\n    \"test.test_long\",\n    \"test.test_long_future\",\n    \"test.test_longexp\",\n    \"test.test_macos\",\n    \"test.test_macostools\",\n    \"test.test_macpath\",\n    \"test.test_macurl2path\",\n    \"test.test_mailbox\",\n    \"test.test_marshal\",\n    \"test.test_math\",\n    \"test.test_md5\",\n    \"test.test_memoryio\",\n    \"test.test_memoryview\",\n    \"test.test_mhlib\",\n    \"test.test_mimetools\",\n    \"test.test_mimetypes\",\n    \"test.test_minidom\",\n    \"test.test_mmap\",\n    \"test.test_module\",\n    \"test.test_modulefinder\",\n    \"test.test_msilib\",\n    \"test.test_multibytecodec\",\n    \"test.test_multifile\",\n    \"test.test_multiprocessing\",\n    \"test.test_mutants\",\n    \"test.test_mutex\",\n    \"test.test_netrc\",\n    \"test.test_new\",\n    \"test.test_nis\",\n    \"test.test_nntplib\",\n    \"test.test_normalization\",\n    \"test.test_ntpath\",\n    \"test.test_old_mailbox\",\n    \"test.test_opcodes\",\n    \"test.test_openpty\",\n    \"test.test_operator\",\n    \"test.test_optparse\",\n    \"test.test_ordered_dict\",\n    \"test.test_os\",\n    \"test.test_ossaudiodev\",\n    \"test.test_parser\",\n    \"test.test_pdb\",\n    \"test.test_peepholer\",\n    \"test.test_pep247\",\n    \"test.test_pep277\",\n    \"test.test_pep352\",\n    \"test.test_pickle\",\n    \"test.test_pickletools\",\n    \"test.test_pipes\",\n    \"test.test_pkg\",\n    \"test.test_pkgimport\",\n    \"test.test_pkgutil\",\n    \"test.test_platform\",\n    \"test.test_plistlib\",\n    \"test.test_poll\",\n    \"test.test_popen\",\n    \"test.test_popen2\",\n    \"test.test_poplib\",\n    \"test.test_posix\",\n    \"test.test_posixpath\",\n    \"test.test_pow\",\n    \"test.test_pprint\",\n    \"test.test_print\",\n    \"test.test_profile\",\n    \"test.test_property\",\n    \"test.test_pstats\",\n    \"test.test_pty\",\n    \"test.test_pwd\",\n    \"test.test_py3kwarn\",\n    \"test.test_py_compile\",\n    \"test.test_pyclbr\",\n    \"test.test_pydoc\",\n    \"test.test_pyexpat\",\n    \"test.test_queue\",\n    \"test.test_quopri\",\n    \"test.test_random\",\n    \"test.test_re\",\n    \"test.test_readline\",\n    \"test.test_regrtest\",\n    \"test.test_repr\",\n    \"test.test_resource\",\n    \"test.test_rfc822\",\n    \"test.test_richcmp\",\n    \"test.test_rlcompleter\",\n    \"test.test_robotparser\",\n    \"test.test_runpy\",\n    \"test.test_sax\",\n    \"test.test_scope\",\n    \"test.test_scriptpackages\",\n    \"test.test_select\",\n    \"test.test_set\",\n    \"test.test_setcomps\",\n    \"test.test_sets\",\n    \"test.test_sgmllib\",\n    \"test.test_sha\",\n    \"test.test_shelve\",\n    \"test.test_shlex\",\n    \"test.test_shutil\",\n    \"test.test_signal\",\n    \"test.test_site\",\n    \"test.test_slice\",\n    \"test.test_smtplib\",\n    \"test.test_smtpnet\",\n    \"test.test_socket\",\n    \"test.test_socketserver\",\n    \"test.test_softspace\",\n    \"test.test_sort\",\n    \"test.test_source_encoding\",\n    \"test.test_spwd\",\n    \"test.test_sqlite\",\n    \"test.test_ssl\",\n    \"test.test_startfile\",\n    \"test.test_stat\",\n    \"test.test_str\",\n    \"test.test_strftime\",\n    \"test.test_string\",\n    \"test.test_stringprep\",\n    \"test.test_strop\",\n    \"test.test_strptime\",\n    \"test.test_strtod\",\n    \"test.test_struct\",\n    \"test.test_structmembers\",\n    \"test.test_structseq\",\n    \"test.test_subprocess\",\n    \"test.test_sunau\",\n    \"test.test_sunaudiodev\",\n    \"test.test_sundry\",\n    \"test.test_support\",\n    \"test.test_symtable\",\n    \"test.test_syntax\",\n    \"test.test_sys\",\n    \"test.test_sys_setprofile\",\n    \"test.test_sys_settrace\",\n    \"test.test_sysconfig\",\n    \"test.test_tarfile\",\n    \"test.test_tcl\",\n    \"test.test_telnetlib\",\n    \"test.test_tempfile\",\n    \"test.test_test_support\",\n    \"test.test_textwrap\",\n    \"test.test_thread\",\n    \"test.test_threaded_import\",\n    \"test.test_threadedtempfile\",\n    \"test.test_threading\",\n    \"test.test_threading_local\",\n    \"test.test_threadsignals\",\n    \"test.test_time\",\n    \"test.test_timeit\",\n    \"test.test_timeout\",\n    \"test.test_tk\",\n    \"test.test_tokenize\",\n    \"test.test_tools\",\n    \"test.test_trace\",\n    \"test.test_traceback\",\n    \"test.test_transformer\",\n    \"test.test_ttk_guionly\",\n    \"test.test_ttk_textonly\",\n    \"test.test_tuple\",\n    \"test.test_turtle\",\n    \"test.test_typechecks\",\n    \"test.test_types\",\n    \"test.test_ucn\",\n    \"test.test_unary\",\n    \"test.test_undocumented_details\",\n    \"test.test_unicode\",\n    \"test.test_unicode_file\",\n    \"test.test_unicodedata\",\n    \"test.test_unittest\",\n    \"test.test_univnewlines\",\n    \"test.test_univnewlines2k\",\n    \"test.test_unpack\",\n    \"test.test_urllib\",\n    \"test.test_urllib2\",\n    \"test.test_urllib2_localnet\",\n    \"test.test_urllib2net\",\n    \"test.test_urllibnet\",\n    \"test.test_urlparse\",\n    \"test.test_userdict\",\n    \"test.test_userlist\",\n    \"test.test_userstring\",\n    \"test.test_uu\",\n    \"test.test_uuid\",\n    \"test.test_wait3\",\n    \"test.test_wait4\",\n    \"test.test_warnings\",\n    \"test.test_wave\",\n    \"test.test_weakref\",\n    \"test.test_weakset\",\n    \"test.test_whichdb\",\n    \"test.test_winreg\",\n    \"test.test_winsound\",\n    \"test.test_with\",\n    \"test.test_wsgiref\",\n    \"test.test_xdrlib\",\n    \"test.test_xml_etree\",\n    \"test.test_xml_etree_c\",\n    \"test.test_xmllib\",\n    \"test.test_xmlrpc\",\n    \"test.test_xpickle\",\n    \"test.test_xrange\",\n    \"test.test_zipfile\",\n    \"test.test_zipfile64\",\n    \"test.test_zipimport\",\n    \"test.test_zipimport_support\",\n    \"test.test_zlib\",\n    \"test.testall\",\n    \"test.testcodec\",\n    \"test.tf_inherit_check\",\n    \"test.threaded_import_hangers\",\n    \"test.time_hashlib\",\n    \"test.tracedmodules\",\n    \"test.tracedmodules.testmod\",\n    \"test.warning_tests\",\n    \"test.win_console_handler\",\n    \"test.xmltests\",\n    \"textwrap\",\n    \"this\",\n    \"thread\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkColorChooser\",\n    \"tkCommonDialog\",\n    \"tkFileDialog\",\n    \"tkFont\",\n    \"tkMessageBox\",\n    \"tkSimpleDialog\",\n    \"toaiff\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"ttk\",\n    \"tty\",\n    \"turtle\",\n    \"types\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib2\",\n    \"urlparse\",\n    \"user\",\n    \"uu\",\n    \"uuid\",\n    \"videoreader\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"whichdb\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmllib\",\n    \"xmlrpclib\",\n    \"xxsubtype\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.2\": [\n    \"__future__\",\n    \"__main__\",\n    \"_dummy_thread\",\n    \"_struct\",\n    \"_thread\",\n    \"abc\",\n    \"aifc\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent.futures\",\n    \"configparser\",\n    \"contextlib\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.emxccompiler\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"doctest\",\n    \"dummy_threading\",\n    \"email\",\n    \"email.charset\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.parser\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.idna\",\n    \"encodings.mbcs\",\n    \"encodings.utf_8_sig\",\n    \"errno\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fpectl\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"itertools\",\n    \"json\",\n    \"keyword\",\n    \"lib2to3\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"macpath\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.sharedctypes\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"numbers\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"select\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"ssl\",\n    \"stat\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.support\",\n    \"textwrap\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tty\",\n    \"turtle\",\n    \"types\",\n    \"unicodedata\",\n    \"unittest\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.etree.ElementTree\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.model\",\n    \"xml.sax\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.3\": [\n    \"__future__\",\n    \"__main__\",\n    \"_dummy_thread\",\n    \"_struct\",\n    \"_thread\",\n    \"abc\",\n    \"aifc\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent.futures\",\n    \"configparser\",\n    \"contextlib\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.emxccompiler\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"doctest\",\n    \"dummy_threading\",\n    \"email\",\n    \"email.charset\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.idna\",\n    \"encodings.mbcs\",\n    \"encodings.utf_8_sig\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fpectl\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"keyword\",\n    \"lib2to3\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"macpath\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.sharedctypes\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"numbers\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"select\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"ssl\",\n    \"stat\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.support\",\n    \"textwrap\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tty\",\n    \"turtle\",\n    \"types\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.mock\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.etree.ElementTree\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.model\",\n    \"xml.sax\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.4\": [\n    \"__future__\",\n    \"__main__\",\n    \"_ast\",\n    \"_bisect\",\n    \"_bootlocale\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_ctypes_test\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_dummy_thread\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_pickle\",\n    \"_posixsubprocess\",\n    \"_pyio\",\n    \"_random\",\n    \"_sha1\",\n    \"_sha256\",\n    \"_sha512\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_sysconfigdata\",\n    \"_testbuffer\",\n    \"_testcapi\",\n    \"_testimportmultiple\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tracemalloc\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.base_events\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.compat\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.tasks\",\n    \"asyncio.test_utils\",\n    \"asyncio.transports\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.__main__\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.tests\",\n    \"distutils.tests.support\",\n    \"distutils.tests.test_archive_util\",\n    \"distutils.tests.test_bdist\",\n    \"distutils.tests.test_bdist_dumb\",\n    \"distutils.tests.test_bdist_msi\",\n    \"distutils.tests.test_bdist_rpm\",\n    \"distutils.tests.test_bdist_wininst\",\n    \"distutils.tests.test_build\",\n    \"distutils.tests.test_build_clib\",\n    \"distutils.tests.test_build_ext\",\n    \"distutils.tests.test_build_py\",\n    \"distutils.tests.test_build_scripts\",\n    \"distutils.tests.test_check\",\n    \"distutils.tests.test_clean\",\n    \"distutils.tests.test_cmd\",\n    \"distutils.tests.test_config\",\n    \"distutils.tests.test_config_cmd\",\n    \"distutils.tests.test_core\",\n    \"distutils.tests.test_cygwinccompiler\",\n    \"distutils.tests.test_dep_util\",\n    \"distutils.tests.test_dir_util\",\n    \"distutils.tests.test_dist\",\n    \"distutils.tests.test_extension\",\n    \"distutils.tests.test_file_util\",\n    \"distutils.tests.test_filelist\",\n    \"distutils.tests.test_install\",\n    \"distutils.tests.test_install_data\",\n    \"distutils.tests.test_install_headers\",\n    \"distutils.tests.test_install_lib\",\n    \"distutils.tests.test_install_scripts\",\n    \"distutils.tests.test_log\",\n    \"distutils.tests.test_msvc9compiler\",\n    \"distutils.tests.test_register\",\n    \"distutils.tests.test_sdist\",\n    \"distutils.tests.test_spawn\",\n    \"distutils.tests.test_sysconfig\",\n    \"distutils.tests.test_text_file\",\n    \"distutils.tests.test_unixccompiler\",\n    \"distutils.tests.test_upload\",\n    \"distutils.tests.test_util\",\n    \"distutils.tests.test_version\",\n    \"distutils.tests.test_versionpredicate\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"dummy_threading\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp65001\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_u\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_centeuro\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.unicode_internal\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fpectl\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.AutoComplete\",\n    \"idlelib.AutoCompleteWindow\",\n    \"idlelib.AutoExpand\",\n    \"idlelib.Bindings\",\n    \"idlelib.CallTipWindow\",\n    \"idlelib.CallTips\",\n    \"idlelib.ClassBrowser\",\n    \"idlelib.CodeContext\",\n    \"idlelib.ColorDelegator\",\n    \"idlelib.Debugger\",\n    \"idlelib.Delegator\",\n    \"idlelib.EditorWindow\",\n    \"idlelib.FileList\",\n    \"idlelib.FormatParagraph\",\n    \"idlelib.GrepDialog\",\n    \"idlelib.HyperParser\",\n    \"idlelib.IOBinding\",\n    \"idlelib.IdleHistory\",\n    \"idlelib.MultiCall\",\n    \"idlelib.MultiStatusBar\",\n    \"idlelib.ObjectBrowser\",\n    \"idlelib.OutputWindow\",\n    \"idlelib.ParenMatch\",\n    \"idlelib.PathBrowser\",\n    \"idlelib.Percolator\",\n    \"idlelib.PyParse\",\n    \"idlelib.PyShell\",\n    \"idlelib.RemoteDebugger\",\n    \"idlelib.RemoteObjectBrowser\",\n    \"idlelib.ReplaceDialog\",\n    \"idlelib.RstripExtension\",\n    \"idlelib.ScriptBinding\",\n    \"idlelib.ScrolledList\",\n    \"idlelib.SearchDialog\",\n    \"idlelib.SearchDialogBase\",\n    \"idlelib.SearchEngine\",\n    \"idlelib.StackViewer\",\n    \"idlelib.ToolTip\",\n    \"idlelib.TreeWidget\",\n    \"idlelib.UndoDelegator\",\n    \"idlelib.WidgetRedirector\",\n    \"idlelib.WindowList\",\n    \"idlelib.ZoomHeight\",\n    \"idlelib.__main__\",\n    \"idlelib.aboutDialog\",\n    \"idlelib.configDialog\",\n    \"idlelib.configHandler\",\n    \"idlelib.configHelpSourceEdit\",\n    \"idlelib.configSectionNameDialog\",\n    \"idlelib.dynOptionMenuWidget\",\n    \"idlelib.help\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_calltips\",\n    \"idlelib.idle_test.test_config_name\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_formatparagraph\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_idlehistory\",\n    \"idlelib.idle_test.test_io\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_rstrip\",\n    \"idlelib.idle_test.test_searchdialogbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_widgetredir\",\n    \"idlelib.idlever\",\n    \"idlelib.keybindingDialog\",\n    \"idlelib.macosxSupport\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.tabbedpages\",\n    \"idlelib.textView\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._bootstrap\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_callable\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.data.bom\",\n    \"lib2to3.tests.data.crlf\",\n    \"lib2to3.tests.data.different_encoding\",\n    \"lib2to3.tests.data.false_encoding\",\n    \"lib2to3.tests.data.fixers.bad_order\",\n    \"lib2to3.tests.data.fixers.myfixes\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_explicit\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_first\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_last\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_parrot\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_preorder\",\n    \"lib2to3.tests.data.fixers.no_fixer_cls\",\n    \"lib2to3.tests.data.fixers.parrot_example\",\n    \"lib2to3.tests.data.infinite_recursion\",\n    \"lib2to3.tests.data.py2_test_grammar\",\n    \"lib2to3.tests.data.py3_test_grammar\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"macpath\",\n    \"macurl2path\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.semaphore_tracker\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.__main__\",\n    \"test._test_multiprocessing\",\n    \"test.audiotests\",\n    \"test.autotest\",\n    \"test.bad_coding\",\n    \"test.bad_coding2\",\n    \"test.badsyntax_3131\",\n    \"test.badsyntax_future10\",\n    \"test.badsyntax_future3\",\n    \"test.badsyntax_future4\",\n    \"test.badsyntax_future5\",\n    \"test.badsyntax_future6\",\n    \"test.badsyntax_future7\",\n    \"test.badsyntax_future8\",\n    \"test.badsyntax_future9\",\n    \"test.badsyntax_pep3120\",\n    \"test.buffer_tests\",\n    \"test.bytecode_helper\",\n    \"test.coding20731\",\n    \"test.curses_tests\",\n    \"test.datetimetester\",\n    \"test.dis_module\",\n    \"test.doctest_aliases\",\n    \"test.double_const\",\n    \"test.encoded_modules\",\n    \"test.encoded_modules.module_iso_8859_1\",\n    \"test.encoded_modules.module_koi8_r\",\n    \"test.final_a\",\n    \"test.final_b\",\n    \"test.fork_wait\",\n    \"test.future_test1\",\n    \"test.future_test2\",\n    \"test.gdb_sample\",\n    \"test.inspect_fodder\",\n    \"test.inspect_fodder2\",\n    \"test.list_tests\",\n    \"test.lock_tests\",\n    \"test.make_ssl_certs\",\n    \"test.mapping_tests\",\n    \"test.memory_watchdog\",\n    \"test.mock_socket\",\n    \"test.mp_fork_bomb\",\n    \"test.multibytecodec_support\",\n    \"test.outstanding_bugs\",\n    \"test.pickletester\",\n    \"test.profilee\",\n    \"test.pyclbr_input\",\n    \"test.pydoc_mod\",\n    \"test.pydocfodder\",\n    \"test.pystone\",\n    \"test.re_tests\",\n    \"test.regrtest\",\n    \"test.relimport\",\n    \"test.reperf\",\n    \"test.sample_doctest\",\n    \"test.sample_doctest_no_docstrings\",\n    \"test.sample_doctest_no_doctests\",\n    \"test.script_helper\",\n    \"test.seq_tests\",\n    \"test.sortperf\",\n    \"test.ssl_servers\",\n    \"test.ssltests\",\n    \"test.string_tests\",\n    \"test.subprocessdata.fd_status\",\n    \"test.subprocessdata.input_reader\",\n    \"test.subprocessdata.qcat\",\n    \"test.subprocessdata.qgrep\",\n    \"test.subprocessdata.sigchild_ignore\",\n    \"test.support\",\n    \"test.test___all__\",\n    \"test.test___future__\",\n    \"test.test__locale\",\n    \"test.test__opcode\",\n    \"test.test__osx_support\",\n    \"test.test_abc\",\n    \"test.test_abstract_numbers\",\n    \"test.test_aifc\",\n    \"test.test_argparse\",\n    \"test.test_array\",\n    \"test.test_ast\",\n    \"test.test_asynchat\",\n    \"test.test_asyncio\",\n    \"test.test_asyncio.__main__\",\n    \"test.test_asyncio.echo\",\n    \"test.test_asyncio.echo2\",\n    \"test.test_asyncio.echo3\",\n    \"test.test_asyncio.test_base_events\",\n    \"test.test_asyncio.test_events\",\n    \"test.test_asyncio.test_futures\",\n    \"test.test_asyncio.test_locks\",\n    \"test.test_asyncio.test_proactor_events\",\n    \"test.test_asyncio.test_queues\",\n    \"test.test_asyncio.test_selector_events\",\n    \"test.test_asyncio.test_sslproto\",\n    \"test.test_asyncio.test_streams\",\n    \"test.test_asyncio.test_subprocess\",\n    \"test.test_asyncio.test_tasks\",\n    \"test.test_asyncio.test_transports\",\n    \"test.test_asyncio.test_unix_events\",\n    \"test.test_asyncio.test_windows_events\",\n    \"test.test_asyncio.test_windows_utils\",\n    \"test.test_asyncore\",\n    \"test.test_atexit\",\n    \"test.test_audioop\",\n    \"test.test_augassign\",\n    \"test.test_base64\",\n    \"test.test_bigaddrspace\",\n    \"test.test_bigmem\",\n    \"test.test_binascii\",\n    \"test.test_binhex\",\n    \"test.test_binop\",\n    \"test.test_bisect\",\n    \"test.test_bool\",\n    \"test.test_buffer\",\n    \"test.test_bufio\",\n    \"test.test_builtin\",\n    \"test.test_bytes\",\n    \"test.test_bz2\",\n    \"test.test_calendar\",\n    \"test.test_call\",\n    \"test.test_capi\",\n    \"test.test_cgi\",\n    \"test.test_cgitb\",\n    \"test.test_charmapcodec\",\n    \"test.test_class\",\n    \"test.test_cmath\",\n    \"test.test_cmd\",\n    \"test.test_cmd_line\",\n    \"test.test_cmd_line_script\",\n    \"test.test_code\",\n    \"test.test_code_module\",\n    \"test.test_codeccallbacks\",\n    \"test.test_codecencodings_cn\",\n    \"test.test_codecencodings_hk\",\n    \"test.test_codecencodings_iso2022\",\n    \"test.test_codecencodings_jp\",\n    \"test.test_codecencodings_kr\",\n    \"test.test_codecencodings_tw\",\n    \"test.test_codecmaps_cn\",\n    \"test.test_codecmaps_hk\",\n    \"test.test_codecmaps_jp\",\n    \"test.test_codecmaps_kr\",\n    \"test.test_codecmaps_tw\",\n    \"test.test_codecs\",\n    \"test.test_codeop\",\n    \"test.test_collections\",\n    \"test.test_colorsys\",\n    \"test.test_compare\",\n    \"test.test_compile\",\n    \"test.test_compileall\",\n    \"test.test_complex\",\n    \"test.test_concurrent_futures\",\n    \"test.test_configparser\",\n    \"test.test_contains\",\n    \"test.test_contextlib\",\n    \"test.test_copy\",\n    \"test.test_copyreg\",\n    \"test.test_cprofile\",\n    \"test.test_crashers\",\n    \"test.test_crypt\",\n    \"test.test_csv\",\n    \"test.test_ctypes\",\n    \"test.test_curses\",\n    \"test.test_datetime\",\n    \"test.test_dbm\",\n    \"test.test_dbm_dumb\",\n    \"test.test_dbm_gnu\",\n    \"test.test_dbm_ndbm\",\n    \"test.test_decimal\",\n    \"test.test_decorators\",\n    \"test.test_defaultdict\",\n    \"test.test_deque\",\n    \"test.test_descr\",\n    \"test.test_descrtut\",\n    \"test.test_devpoll\",\n    \"test.test_dict\",\n    \"test.test_dictcomps\",\n    \"test.test_dictviews\",\n    \"test.test_difflib\",\n    \"test.test_dis\",\n    \"test.test_distutils\",\n    \"test.test_doctest\",\n    \"test.test_doctest2\",\n    \"test.test_docxmlrpc\",\n    \"test.test_dummy_thread\",\n    \"test.test_dummy_threading\",\n    \"test.test_dynamic\",\n    \"test.test_dynamicclassattribute\",\n    \"test.test_email\",\n    \"test.test_email.__main__\",\n    \"test.test_email.test__encoded_words\",\n    \"test.test_email.test__header_value_parser\",\n    \"test.test_email.test_asian_codecs\",\n    \"test.test_email.test_contentmanager\",\n    \"test.test_email.test_defect_handling\",\n    \"test.test_email.test_email\",\n    \"test.test_email.test_generator\",\n    \"test.test_email.test_headerregistry\",\n    \"test.test_email.test_inversion\",\n    \"test.test_email.test_message\",\n    \"test.test_email.test_parser\",\n    \"test.test_email.test_pickleable\",\n    \"test.test_email.test_policy\",\n    \"test.test_email.test_utils\",\n    \"test.test_email.torture_test\",\n    \"test.test_ensurepip\",\n    \"test.test_enum\",\n    \"test.test_enumerate\",\n    \"test.test_eof\",\n    \"test.test_epoll\",\n    \"test.test_errno\",\n    \"test.test_exception_variations\",\n    \"test.test_exceptions\",\n    \"test.test_extcall\",\n    \"test.test_faulthandler\",\n    \"test.test_fcntl\",\n    \"test.test_file\",\n    \"test.test_file_eintr\",\n    \"test.test_filecmp\",\n    \"test.test_fileinput\",\n    \"test.test_fileio\",\n    \"test.test_finalization\",\n    \"test.test_float\",\n    \"test.test_flufl\",\n    \"test.test_fnmatch\",\n    \"test.test_fork1\",\n    \"test.test_format\",\n    \"test.test_fractions\",\n    \"test.test_frame\",\n    \"test.test_ftplib\",\n    \"test.test_funcattrs\",\n    \"test.test_functools\",\n    \"test.test_future\",\n    \"test.test_future3\",\n    \"test.test_future4\",\n    \"test.test_future5\",\n    \"test.test_gc\",\n    \"test.test_gdb\",\n    \"test.test_generators\",\n    \"test.test_genericpath\",\n    \"test.test_genexps\",\n    \"test.test_getargs2\",\n    \"test.test_getopt\",\n    \"test.test_getpass\",\n    \"test.test_gettext\",\n    \"test.test_glob\",\n    \"test.test_global\",\n    \"test.test_grammar\",\n    \"test.test_grp\",\n    \"test.test_gzip\",\n    \"test.test_hash\",\n    \"test.test_hashlib\",\n    \"test.test_heapq\",\n    \"test.test_hmac\",\n    \"test.test_html\",\n    \"test.test_htmlparser\",\n    \"test.test_http_cookiejar\",\n    \"test.test_http_cookies\",\n    \"test.test_httplib\",\n    \"test.test_httpservers\",\n    \"test.test_idle\",\n    \"test.test_imaplib\",\n    \"test.test_imghdr\",\n    \"test.test_imp\",\n    \"test.test_import\",\n    \"test.test_importlib\",\n    \"test.test_importlib.__main__\",\n    \"test.test_importlib.abc\",\n    \"test.test_importlib.builtin\",\n    \"test.test_importlib.builtin.__main__\",\n    \"test.test_importlib.builtin.test_finder\",\n    \"test.test_importlib.builtin.test_loader\",\n    \"test.test_importlib.builtin.util\",\n    \"test.test_importlib.extension\",\n    \"test.test_importlib.extension.__main__\",\n    \"test.test_importlib.extension.test_case_sensitivity\",\n    \"test.test_importlib.extension.test_finder\",\n    \"test.test_importlib.extension.test_loader\",\n    \"test.test_importlib.extension.test_path_hook\",\n    \"test.test_importlib.extension.util\",\n    \"test.test_importlib.frozen\",\n    \"test.test_importlib.frozen.__main__\",\n    \"test.test_importlib.frozen.test_finder\",\n    \"test.test_importlib.frozen.test_loader\",\n    \"test.test_importlib.import_\",\n    \"test.test_importlib.import_.__main__\",\n    \"test.test_importlib.import_.test___loader__\",\n    \"test.test_importlib.import_.test___package__\",\n    \"test.test_importlib.import_.test_api\",\n    \"test.test_importlib.import_.test_caching\",\n    \"test.test_importlib.import_.test_fromlist\",\n    \"test.test_importlib.import_.test_meta_path\",\n    \"test.test_importlib.import_.test_packages\",\n    \"test.test_importlib.import_.test_path\",\n    \"test.test_importlib.import_.test_relative_imports\",\n    \"test.test_importlib.import_.util\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.one\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.two\",\n    \"test.test_importlib.namespace_pkgs.module_and_namespace_package.a_test\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion1.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion2.foo.two\",\n    \"test.test_importlib.namespace_pkgs.project1.parent.child.one\",\n    \"test.test_importlib.namespace_pkgs.project2.parent.child.two\",\n    \"test.test_importlib.namespace_pkgs.project3.parent.child.three\",\n    \"test.test_importlib.regrtest\",\n    \"test.test_importlib.source\",\n    \"test.test_importlib.source.__main__\",\n    \"test.test_importlib.source.test_case_sensitivity\",\n    \"test.test_importlib.source.test_file_loader\",\n    \"test.test_importlib.source.test_finder\",\n    \"test.test_importlib.source.test_path_hook\",\n    \"test.test_importlib.source.test_source_encoding\",\n    \"test.test_importlib.source.util\",\n    \"test.test_importlib.test_abc\",\n    \"test.test_importlib.test_api\",\n    \"test.test_importlib.test_locks\",\n    \"test.test_importlib.test_namespace_pkgs\",\n    \"test.test_importlib.test_spec\",\n    \"test.test_importlib.test_util\",\n    \"test.test_importlib.test_windows\",\n    \"test.test_importlib.util\",\n    \"test.test_index\",\n    \"test.test_inspect\",\n    \"test.test_int\",\n    \"test.test_int_literal\",\n    \"test.test_io\",\n    \"test.test_ioctl\",\n    \"test.test_ipaddress\",\n    \"test.test_isinstance\",\n    \"test.test_iter\",\n    \"test.test_iterlen\",\n    \"test.test_itertools\",\n    \"test.test_json\",\n    \"test.test_json.__main__\",\n    \"test.test_json.test_decode\",\n    \"test.test_json.test_default\",\n    \"test.test_json.test_dump\",\n    \"test.test_json.test_encode_basestring_ascii\",\n    \"test.test_json.test_enum\",\n    \"test.test_json.test_fail\",\n    \"test.test_json.test_float\",\n    \"test.test_json.test_indent\",\n    \"test.test_json.test_pass1\",\n    \"test.test_json.test_pass2\",\n    \"test.test_json.test_pass3\",\n    \"test.test_json.test_recursion\",\n    \"test.test_json.test_scanstring\",\n    \"test.test_json.test_separators\",\n    \"test.test_json.test_speedups\",\n    \"test.test_json.test_tool\",\n    \"test.test_json.test_unicode\",\n    \"test.test_keyword\",\n    \"test.test_keywordonlyarg\",\n    \"test.test_kqueue\",\n    \"test.test_largefile\",\n    \"test.test_lib2to3\",\n    \"test.test_linecache\",\n    \"test.test_list\",\n    \"test.test_listcomps\",\n    \"test.test_locale\",\n    \"test.test_logging\",\n    \"test.test_long\",\n    \"test.test_longexp\",\n    \"test.test_lzma\",\n    \"test.test_macpath\",\n    \"test.test_macurl2path\",\n    \"test.test_mailbox\",\n    \"test.test_mailcap\",\n    \"test.test_marshal\",\n    \"test.test_math\",\n    \"test.test_memoryio\",\n    \"test.test_memoryview\",\n    \"test.test_metaclass\",\n    \"test.test_mimetypes\",\n    \"test.test_minidom\",\n    \"test.test_mmap\",\n    \"test.test_module\",\n    \"test.test_modulefinder\",\n    \"test.test_msilib\",\n    \"test.test_multibytecodec\",\n    \"test.test_multiprocessing_fork\",\n    \"test.test_multiprocessing_forkserver\",\n    \"test.test_multiprocessing_main_handling\",\n    \"test.test_multiprocessing_spawn\",\n    \"test.test_netrc\",\n    \"test.test_nis\",\n    \"test.test_nntplib\",\n    \"test.test_normalization\",\n    \"test.test_ntpath\",\n    \"test.test_numeric_tower\",\n    \"test.test_opcodes\",\n    \"test.test_openpty\",\n    \"test.test_operator\",\n    \"test.test_optparse\",\n    \"test.test_ordered_dict\",\n    \"test.test_os\",\n    \"test.test_ossaudiodev\",\n    \"test.test_osx_env\",\n    \"test.test_parser\",\n    \"test.test_pathlib\",\n    \"test.test_pdb\",\n    \"test.test_peepholer\",\n    \"test.test_pep247\",\n    \"test.test_pep277\",\n    \"test.test_pep292\",\n    \"test.test_pep3120\",\n    \"test.test_pep3131\",\n    \"test.test_pep3151\",\n    \"test.test_pep352\",\n    \"test.test_pep380\",\n    \"test.test_pickle\",\n    \"test.test_pickletools\",\n    \"test.test_pipes\",\n    \"test.test_pkg\",\n    \"test.test_pkgimport\",\n    \"test.test_pkgutil\",\n    \"test.test_platform\",\n    \"test.test_plistlib\",\n    \"test.test_poll\",\n    \"test.test_popen\",\n    \"test.test_poplib\",\n    \"test.test_posix\",\n    \"test.test_posixpath\",\n    \"test.test_pow\",\n    \"test.test_pprint\",\n    \"test.test_print\",\n    \"test.test_profile\",\n    \"test.test_property\",\n    \"test.test_pstats\",\n    \"test.test_pty\",\n    \"test.test_pulldom\",\n    \"test.test_pwd\",\n    \"test.test_py_compile\",\n    \"test.test_pyclbr\",\n    \"test.test_pydoc\",\n    \"test.test_pyexpat\",\n    \"test.test_queue\",\n    \"test.test_quopri\",\n    \"test.test_raise\",\n    \"test.test_random\",\n    \"test.test_range\",\n    \"test.test_re\",\n    \"test.test_readline\",\n    \"test.test_regrtest\",\n    \"test.test_reprlib\",\n    \"test.test_resource\",\n    \"test.test_richcmp\",\n    \"test.test_rlcompleter\",\n    \"test.test_robotparser\",\n    \"test.test_runpy\",\n    \"test.test_sax\",\n    \"test.test_sched\",\n    \"test.test_scope\",\n    \"test.test_script_helper\",\n    \"test.test_select\",\n    \"test.test_selectors\",\n    \"test.test_set\",\n    \"test.test_setcomps\",\n    \"test.test_shelve\",\n    \"test.test_shlex\",\n    \"test.test_shutil\",\n    \"test.test_signal\",\n    \"test.test_site\",\n    \"test.test_slice\",\n    \"test.test_smtpd\",\n    \"test.test_smtplib\",\n    \"test.test_smtpnet\",\n    \"test.test_sndhdr\",\n    \"test.test_socket\",\n    \"test.test_socketserver\",\n    \"test.test_sort\",\n    \"test.test_source_encoding\",\n    \"test.test_spwd\",\n    \"test.test_sqlite\",\n    \"test.test_ssl\",\n    \"test.test_startfile\",\n    \"test.test_stat\",\n    \"test.test_statistics\",\n    \"test.test_strftime\",\n    \"test.test_string\",\n    \"test.test_stringprep\",\n    \"test.test_strlit\",\n    \"test.test_strptime\",\n    \"test.test_strtod\",\n    \"test.test_struct\",\n    \"test.test_structmembers\",\n    \"test.test_structseq\",\n    \"test.test_subprocess\",\n    \"test.test_sunau\",\n    \"test.test_sundry\",\n    \"test.test_super\",\n    \"test.test_support\",\n    \"test.test_symtable\",\n    \"test.test_syntax\",\n    \"test.test_sys\",\n    \"test.test_sys_setprofile\",\n    \"test.test_sys_settrace\",\n    \"test.test_sysconfig\",\n    \"test.test_syslog\",\n    \"test.test_tarfile\",\n    \"test.test_tcl\",\n    \"test.test_telnetlib\",\n    \"test.test_tempfile\",\n    \"test.test_textwrap\",\n    \"test.test_thread\",\n    \"test.test_threaded_import\",\n    \"test.test_threadedtempfile\",\n    \"test.test_threading\",\n    \"test.test_threading_local\",\n    \"test.test_threadsignals\",\n    \"test.test_time\",\n    \"test.test_timeit\",\n    \"test.test_timeout\",\n    \"test.test_tk\",\n    \"test.test_tokenize\",\n    \"test.test_trace\",\n    \"test.test_traceback\",\n    \"test.test_tracemalloc\",\n    \"test.test_ttk_guionly\",\n    \"test.test_ttk_textonly\",\n    \"test.test_tuple\",\n    \"test.test_typechecks\",\n    \"test.test_types\",\n    \"test.test_ucn\",\n    \"test.test_unary\",\n    \"test.test_unicode\",\n    \"test.test_unicode_file\",\n    \"test.test_unicodedata\",\n    \"test.test_unittest\",\n    \"test.test_univnewlines\",\n    \"test.test_unpack\",\n    \"test.test_unpack_ex\",\n    \"test.test_urllib\",\n    \"test.test_urllib2\",\n    \"test.test_urllib2_localnet\",\n    \"test.test_urllib2net\",\n    \"test.test_urllib_response\",\n    \"test.test_urllibnet\",\n    \"test.test_urlparse\",\n    \"test.test_userdict\",\n    \"test.test_userlist\",\n    \"test.test_userstring\",\n    \"test.test_uu\",\n    \"test.test_uuid\",\n    \"test.test_venv\",\n    \"test.test_wait3\",\n    \"test.test_wait4\",\n    \"test.test_warnings\",\n    \"test.test_wave\",\n    \"test.test_weakref\",\n    \"test.test_weakset\",\n    \"test.test_webbrowser\",\n    \"test.test_winreg\",\n    \"test.test_winsound\",\n    \"test.test_with\",\n    \"test.test_wsgiref\",\n    \"test.test_xdrlib\",\n    \"test.test_xml_dom_minicompat\",\n    \"test.test_xml_etree\",\n    \"test.test_xml_etree_c\",\n    \"test.test_xmlrpc\",\n    \"test.test_xmlrpc_net\",\n    \"test.test_zipfile\",\n    \"test.test_zipfile64\",\n    \"test.test_zipimport\",\n    \"test.test_zipimport_support\",\n    \"test.test_zlib\",\n    \"test.testcodec\",\n    \"test.tf_inherit_check\",\n    \"test.threaded_import_hangers\",\n    \"test.time_hashlib\",\n    \"test.tracedmodules\",\n    \"test.tracedmodules.testmod\",\n    \"test.warning_tests\",\n    \"test.win_console_handler\",\n    \"test.xmltests\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter._fix\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.runtktests\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_functions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.wikipedia\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.model\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxlimited\",\n    \"xxsubtype\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.5\": [\n    \"__future__\",\n    \"__main__\",\n    \"_ast\",\n    \"_bisect\",\n    \"_bootlocale\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_ctypes_test\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_dummy_thread\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_pickle\",\n    \"_posixsubprocess\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_random\",\n    \"_sha1\",\n    \"_sha256\",\n    \"_sha512\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_sysconfigdata\",\n    \"_testbuffer\",\n    \"_testcapi\",\n    \"_testimportmultiple\",\n    \"_testmultiphase\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tracemalloc\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.base_events\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.compat\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.tasks\",\n    \"asyncio.test_utils\",\n    \"asyncio.transports\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.__main__\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils._msvccompiler\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.tests\",\n    \"distutils.tests.support\",\n    \"distutils.tests.test_archive_util\",\n    \"distutils.tests.test_bdist\",\n    \"distutils.tests.test_bdist_dumb\",\n    \"distutils.tests.test_bdist_msi\",\n    \"distutils.tests.test_bdist_rpm\",\n    \"distutils.tests.test_bdist_wininst\",\n    \"distutils.tests.test_build\",\n    \"distutils.tests.test_build_clib\",\n    \"distutils.tests.test_build_ext\",\n    \"distutils.tests.test_build_py\",\n    \"distutils.tests.test_build_scripts\",\n    \"distutils.tests.test_check\",\n    \"distutils.tests.test_clean\",\n    \"distutils.tests.test_cmd\",\n    \"distutils.tests.test_config\",\n    \"distutils.tests.test_config_cmd\",\n    \"distutils.tests.test_core\",\n    \"distutils.tests.test_cygwinccompiler\",\n    \"distutils.tests.test_dep_util\",\n    \"distutils.tests.test_dir_util\",\n    \"distutils.tests.test_dist\",\n    \"distutils.tests.test_extension\",\n    \"distutils.tests.test_file_util\",\n    \"distutils.tests.test_filelist\",\n    \"distutils.tests.test_install\",\n    \"distutils.tests.test_install_data\",\n    \"distutils.tests.test_install_headers\",\n    \"distutils.tests.test_install_lib\",\n    \"distutils.tests.test_install_scripts\",\n    \"distutils.tests.test_log\",\n    \"distutils.tests.test_msvc9compiler\",\n    \"distutils.tests.test_msvccompiler\",\n    \"distutils.tests.test_register\",\n    \"distutils.tests.test_sdist\",\n    \"distutils.tests.test_spawn\",\n    \"distutils.tests.test_sysconfig\",\n    \"distutils.tests.test_text_file\",\n    \"distutils.tests.test_unixccompiler\",\n    \"distutils.tests.test_upload\",\n    \"distutils.tests.test_util\",\n    \"distutils.tests.test_version\",\n    \"distutils.tests.test_versionpredicate\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"dummy_threading\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp65001\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_centeuro\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.unicode_internal\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fpectl\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.AutoComplete\",\n    \"idlelib.AutoCompleteWindow\",\n    \"idlelib.AutoExpand\",\n    \"idlelib.Bindings\",\n    \"idlelib.CallTipWindow\",\n    \"idlelib.CallTips\",\n    \"idlelib.ClassBrowser\",\n    \"idlelib.CodeContext\",\n    \"idlelib.ColorDelegator\",\n    \"idlelib.Debugger\",\n    \"idlelib.Delegator\",\n    \"idlelib.EditorWindow\",\n    \"idlelib.FileList\",\n    \"idlelib.FormatParagraph\",\n    \"idlelib.GrepDialog\",\n    \"idlelib.HyperParser\",\n    \"idlelib.IOBinding\",\n    \"idlelib.IdleHistory\",\n    \"idlelib.MultiCall\",\n    \"idlelib.MultiStatusBar\",\n    \"idlelib.ObjectBrowser\",\n    \"idlelib.OutputWindow\",\n    \"idlelib.ParenMatch\",\n    \"idlelib.PathBrowser\",\n    \"idlelib.Percolator\",\n    \"idlelib.PyParse\",\n    \"idlelib.PyShell\",\n    \"idlelib.RemoteDebugger\",\n    \"idlelib.RemoteObjectBrowser\",\n    \"idlelib.ReplaceDialog\",\n    \"idlelib.RstripExtension\",\n    \"idlelib.ScriptBinding\",\n    \"idlelib.ScrolledList\",\n    \"idlelib.SearchDialog\",\n    \"idlelib.SearchDialogBase\",\n    \"idlelib.SearchEngine\",\n    \"idlelib.StackViewer\",\n    \"idlelib.ToolTip\",\n    \"idlelib.TreeWidget\",\n    \"idlelib.UndoDelegator\",\n    \"idlelib.WidgetRedirector\",\n    \"idlelib.WindowList\",\n    \"idlelib.ZoomHeight\",\n    \"idlelib.__main__\",\n    \"idlelib.aboutDialog\",\n    \"idlelib.configDialog\",\n    \"idlelib.configHandler\",\n    \"idlelib.configHelpSourceEdit\",\n    \"idlelib.configSectionNameDialog\",\n    \"idlelib.dynOptionMenuWidget\",\n    \"idlelib.help\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_calltips\",\n    \"idlelib.idle_test.test_config_help\",\n    \"idlelib.idle_test.test_config_name\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_formatparagraph\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_idlehistory\",\n    \"idlelib.idle_test.test_io\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_replacedialog\",\n    \"idlelib.idle_test.test_rstrip\",\n    \"idlelib.idle_test.test_searchdialog\",\n    \"idlelib.idle_test.test_searchdialogbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_undodelegator\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_widgetredir\",\n    \"idlelib.idlever\",\n    \"idlelib.keybindingDialog\",\n    \"idlelib.macosxSupport\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.tabbedpages\",\n    \"idlelib.textView\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.data.bom\",\n    \"lib2to3.tests.data.crlf\",\n    \"lib2to3.tests.data.different_encoding\",\n    \"lib2to3.tests.data.false_encoding\",\n    \"lib2to3.tests.data.fixers.bad_order\",\n    \"lib2to3.tests.data.fixers.myfixes\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_explicit\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_first\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_last\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_parrot\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_preorder\",\n    \"lib2to3.tests.data.fixers.no_fixer_cls\",\n    \"lib2to3.tests.data.fixers.parrot_example\",\n    \"lib2to3.tests.data.infinite_recursion\",\n    \"lib2to3.tests.data.py2_test_grammar\",\n    \"lib2to3.tests.data.py3_test_grammar\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"macpath\",\n    \"macurl2path\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.semaphore_tracker\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.__main__\",\n    \"test._test_multiprocessing\",\n    \"test.audiotests\",\n    \"test.autotest\",\n    \"test.bad_coding\",\n    \"test.bad_coding2\",\n    \"test.badsyntax_3131\",\n    \"test.badsyntax_async1\",\n    \"test.badsyntax_async2\",\n    \"test.badsyntax_async3\",\n    \"test.badsyntax_async4\",\n    \"test.badsyntax_async5\",\n    \"test.badsyntax_async6\",\n    \"test.badsyntax_async7\",\n    \"test.badsyntax_async8\",\n    \"test.badsyntax_future10\",\n    \"test.badsyntax_future3\",\n    \"test.badsyntax_future4\",\n    \"test.badsyntax_future5\",\n    \"test.badsyntax_future6\",\n    \"test.badsyntax_future7\",\n    \"test.badsyntax_future8\",\n    \"test.badsyntax_future9\",\n    \"test.badsyntax_pep3120\",\n    \"test.bisect\",\n    \"test.bytecode_helper\",\n    \"test.coding20731\",\n    \"test.curses_tests\",\n    \"test.datetimetester\",\n    \"test.dis_module\",\n    \"test.doctest_aliases\",\n    \"test.double_const\",\n    \"test.eintrdata.eintr_tester\",\n    \"test.encoded_modules\",\n    \"test.encoded_modules.module_iso_8859_1\",\n    \"test.encoded_modules.module_koi8_r\",\n    \"test.final_a\",\n    \"test.final_b\",\n    \"test.fork_wait\",\n    \"test.future_test1\",\n    \"test.future_test2\",\n    \"test.gdb_sample\",\n    \"test.imp_dummy\",\n    \"test.inspect_fodder\",\n    \"test.inspect_fodder2\",\n    \"test.list_tests\",\n    \"test.lock_tests\",\n    \"test.make_ssl_certs\",\n    \"test.mapping_tests\",\n    \"test.memory_watchdog\",\n    \"test.mock_socket\",\n    \"test.mod_generics_cache\",\n    \"test.mp_fork_bomb\",\n    \"test.mp_preload\",\n    \"test.multibytecodec_support\",\n    \"test.outstanding_bugs\",\n    \"test.pickletester\",\n    \"test.profilee\",\n    \"test.pyclbr_input\",\n    \"test.pydoc_mod\",\n    \"test.pydocfodder\",\n    \"test.pystone\",\n    \"test.re_tests\",\n    \"test.regrtest\",\n    \"test.relimport\",\n    \"test.reperf\",\n    \"test.sample_doctest\",\n    \"test.sample_doctest_no_docstrings\",\n    \"test.sample_doctest_no_doctests\",\n    \"test.seq_tests\",\n    \"test.sortperf\",\n    \"test.ssl_servers\",\n    \"test.ssltests\",\n    \"test.string_tests\",\n    \"test.subprocessdata.fd_status\",\n    \"test.subprocessdata.input_reader\",\n    \"test.subprocessdata.qcat\",\n    \"test.subprocessdata.qgrep\",\n    \"test.subprocessdata.sigchild_ignore\",\n    \"test.support\",\n    \"test.support.script_helper\",\n    \"test.test___all__\",\n    \"test.test___future__\",\n    \"test.test__locale\",\n    \"test.test__opcode\",\n    \"test.test__osx_support\",\n    \"test.test_abc\",\n    \"test.test_abstract_numbers\",\n    \"test.test_aifc\",\n    \"test.test_argparse\",\n    \"test.test_array\",\n    \"test.test_asdl_parser\",\n    \"test.test_ast\",\n    \"test.test_asynchat\",\n    \"test.test_asyncio\",\n    \"test.test_asyncio.__main__\",\n    \"test.test_asyncio.echo\",\n    \"test.test_asyncio.echo2\",\n    \"test.test_asyncio.echo3\",\n    \"test.test_asyncio.test_base_events\",\n    \"test.test_asyncio.test_events\",\n    \"test.test_asyncio.test_futures\",\n    \"test.test_asyncio.test_locks\",\n    \"test.test_asyncio.test_pep492\",\n    \"test.test_asyncio.test_proactor_events\",\n    \"test.test_asyncio.test_queues\",\n    \"test.test_asyncio.test_selector_events\",\n    \"test.test_asyncio.test_sslproto\",\n    \"test.test_asyncio.test_streams\",\n    \"test.test_asyncio.test_subprocess\",\n    \"test.test_asyncio.test_tasks\",\n    \"test.test_asyncio.test_transports\",\n    \"test.test_asyncio.test_unix_events\",\n    \"test.test_asyncio.test_windows_events\",\n    \"test.test_asyncio.test_windows_utils\",\n    \"test.test_asyncore\",\n    \"test.test_atexit\",\n    \"test.test_audioop\",\n    \"test.test_augassign\",\n    \"test.test_base64\",\n    \"test.test_bigaddrspace\",\n    \"test.test_bigmem\",\n    \"test.test_binascii\",\n    \"test.test_binhex\",\n    \"test.test_binop\",\n    \"test.test_bisect\",\n    \"test.test_bool\",\n    \"test.test_buffer\",\n    \"test.test_bufio\",\n    \"test.test_builtin\",\n    \"test.test_bytes\",\n    \"test.test_bz2\",\n    \"test.test_calendar\",\n    \"test.test_call\",\n    \"test.test_capi\",\n    \"test.test_cgi\",\n    \"test.test_cgitb\",\n    \"test.test_charmapcodec\",\n    \"test.test_class\",\n    \"test.test_cmath\",\n    \"test.test_cmd\",\n    \"test.test_cmd_line\",\n    \"test.test_cmd_line_script\",\n    \"test.test_code\",\n    \"test.test_code_module\",\n    \"test.test_codeccallbacks\",\n    \"test.test_codecencodings_cn\",\n    \"test.test_codecencodings_hk\",\n    \"test.test_codecencodings_iso2022\",\n    \"test.test_codecencodings_jp\",\n    \"test.test_codecencodings_kr\",\n    \"test.test_codecencodings_tw\",\n    \"test.test_codecmaps_cn\",\n    \"test.test_codecmaps_hk\",\n    \"test.test_codecmaps_jp\",\n    \"test.test_codecmaps_kr\",\n    \"test.test_codecmaps_tw\",\n    \"test.test_codecs\",\n    \"test.test_codeop\",\n    \"test.test_collections\",\n    \"test.test_colorsys\",\n    \"test.test_compare\",\n    \"test.test_compile\",\n    \"test.test_compileall\",\n    \"test.test_complex\",\n    \"test.test_concurrent_futures\",\n    \"test.test_configparser\",\n    \"test.test_contains\",\n    \"test.test_contextlib\",\n    \"test.test_copy\",\n    \"test.test_copyreg\",\n    \"test.test_coroutines\",\n    \"test.test_cprofile\",\n    \"test.test_crashers\",\n    \"test.test_crypt\",\n    \"test.test_csv\",\n    \"test.test_ctypes\",\n    \"test.test_curses\",\n    \"test.test_datetime\",\n    \"test.test_dbm\",\n    \"test.test_dbm_dumb\",\n    \"test.test_dbm_gnu\",\n    \"test.test_dbm_ndbm\",\n    \"test.test_decimal\",\n    \"test.test_decorators\",\n    \"test.test_defaultdict\",\n    \"test.test_deque\",\n    \"test.test_descr\",\n    \"test.test_descrtut\",\n    \"test.test_devpoll\",\n    \"test.test_dict\",\n    \"test.test_dictcomps\",\n    \"test.test_dictviews\",\n    \"test.test_difflib\",\n    \"test.test_dis\",\n    \"test.test_distutils\",\n    \"test.test_doctest\",\n    \"test.test_doctest2\",\n    \"test.test_docxmlrpc\",\n    \"test.test_dummy_thread\",\n    \"test.test_dummy_threading\",\n    \"test.test_dynamic\",\n    \"test.test_dynamicclassattribute\",\n    \"test.test_eintr\",\n    \"test.test_email\",\n    \"test.test_email.__main__\",\n    \"test.test_email.test__encoded_words\",\n    \"test.test_email.test__header_value_parser\",\n    \"test.test_email.test_asian_codecs\",\n    \"test.test_email.test_contentmanager\",\n    \"test.test_email.test_defect_handling\",\n    \"test.test_email.test_email\",\n    \"test.test_email.test_generator\",\n    \"test.test_email.test_headerregistry\",\n    \"test.test_email.test_inversion\",\n    \"test.test_email.test_message\",\n    \"test.test_email.test_parser\",\n    \"test.test_email.test_pickleable\",\n    \"test.test_email.test_policy\",\n    \"test.test_email.test_utils\",\n    \"test.test_email.torture_test\",\n    \"test.test_ensurepip\",\n    \"test.test_enum\",\n    \"test.test_enumerate\",\n    \"test.test_eof\",\n    \"test.test_epoll\",\n    \"test.test_errno\",\n    \"test.test_exception_variations\",\n    \"test.test_exceptions\",\n    \"test.test_extcall\",\n    \"test.test_faulthandler\",\n    \"test.test_fcntl\",\n    \"test.test_file\",\n    \"test.test_file_eintr\",\n    \"test.test_filecmp\",\n    \"test.test_fileinput\",\n    \"test.test_fileio\",\n    \"test.test_finalization\",\n    \"test.test_float\",\n    \"test.test_flufl\",\n    \"test.test_fnmatch\",\n    \"test.test_fork1\",\n    \"test.test_format\",\n    \"test.test_fractions\",\n    \"test.test_frame\",\n    \"test.test_ftplib\",\n    \"test.test_funcattrs\",\n    \"test.test_functools\",\n    \"test.test_future\",\n    \"test.test_future3\",\n    \"test.test_future4\",\n    \"test.test_future5\",\n    \"test.test_gc\",\n    \"test.test_gdb\",\n    \"test.test_generators\",\n    \"test.test_genericpath\",\n    \"test.test_genexps\",\n    \"test.test_getargs2\",\n    \"test.test_getopt\",\n    \"test.test_getpass\",\n    \"test.test_gettext\",\n    \"test.test_glob\",\n    \"test.test_global\",\n    \"test.test_grammar\",\n    \"test.test_grp\",\n    \"test.test_gzip\",\n    \"test.test_hash\",\n    \"test.test_hashlib\",\n    \"test.test_heapq\",\n    \"test.test_hmac\",\n    \"test.test_html\",\n    \"test.test_htmlparser\",\n    \"test.test_http_cookiejar\",\n    \"test.test_http_cookies\",\n    \"test.test_httplib\",\n    \"test.test_httpservers\",\n    \"test.test_idle\",\n    \"test.test_imaplib\",\n    \"test.test_imghdr\",\n    \"test.test_imp\",\n    \"test.test_import\",\n    \"test.test_import.__main__\",\n    \"test.test_import.data.circular_imports.basic\",\n    \"test.test_import.data.circular_imports.basic2\",\n    \"test.test_import.data.circular_imports.indirect\",\n    \"test.test_import.data.circular_imports.rebinding\",\n    \"test.test_import.data.circular_imports.rebinding2\",\n    \"test.test_import.data.circular_imports.subpackage\",\n    \"test.test_import.data.circular_imports.subpkg.subpackage2\",\n    \"test.test_import.data.circular_imports.subpkg.util\",\n    \"test.test_import.data.circular_imports.util\",\n    \"test.test_import.data.package2.submodule1\",\n    \"test.test_import.data.package2.submodule2\",\n    \"test.test_importlib\",\n    \"test.test_importlib.__main__\",\n    \"test.test_importlib.abc\",\n    \"test.test_importlib.builtin\",\n    \"test.test_importlib.builtin.__main__\",\n    \"test.test_importlib.builtin.test_finder\",\n    \"test.test_importlib.builtin.test_loader\",\n    \"test.test_importlib.extension\",\n    \"test.test_importlib.extension.__main__\",\n    \"test.test_importlib.extension.test_case_sensitivity\",\n    \"test.test_importlib.extension.test_finder\",\n    \"test.test_importlib.extension.test_loader\",\n    \"test.test_importlib.extension.test_path_hook\",\n    \"test.test_importlib.frozen\",\n    \"test.test_importlib.frozen.__main__\",\n    \"test.test_importlib.frozen.test_finder\",\n    \"test.test_importlib.frozen.test_loader\",\n    \"test.test_importlib.import_\",\n    \"test.test_importlib.import_.__main__\",\n    \"test.test_importlib.import_.test___loader__\",\n    \"test.test_importlib.import_.test___package__\",\n    \"test.test_importlib.import_.test_api\",\n    \"test.test_importlib.import_.test_caching\",\n    \"test.test_importlib.import_.test_fromlist\",\n    \"test.test_importlib.import_.test_meta_path\",\n    \"test.test_importlib.import_.test_packages\",\n    \"test.test_importlib.import_.test_path\",\n    \"test.test_importlib.import_.test_relative_imports\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.one\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.two\",\n    \"test.test_importlib.namespace_pkgs.module_and_namespace_package.a_test\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion1.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion2.foo.two\",\n    \"test.test_importlib.namespace_pkgs.project1.parent.child.one\",\n    \"test.test_importlib.namespace_pkgs.project2.parent.child.two\",\n    \"test.test_importlib.namespace_pkgs.project3.parent.child.three\",\n    \"test.test_importlib.regrtest\",\n    \"test.test_importlib.source\",\n    \"test.test_importlib.source.__main__\",\n    \"test.test_importlib.source.test_case_sensitivity\",\n    \"test.test_importlib.source.test_file_loader\",\n    \"test.test_importlib.source.test_finder\",\n    \"test.test_importlib.source.test_path_hook\",\n    \"test.test_importlib.source.test_source_encoding\",\n    \"test.test_importlib.test_abc\",\n    \"test.test_importlib.test_api\",\n    \"test.test_importlib.test_lazy\",\n    \"test.test_importlib.test_locks\",\n    \"test.test_importlib.test_namespace_pkgs\",\n    \"test.test_importlib.test_spec\",\n    \"test.test_importlib.test_util\",\n    \"test.test_importlib.test_windows\",\n    \"test.test_importlib.util\",\n    \"test.test_index\",\n    \"test.test_inspect\",\n    \"test.test_int\",\n    \"test.test_int_literal\",\n    \"test.test_io\",\n    \"test.test_ioctl\",\n    \"test.test_ipaddress\",\n    \"test.test_isinstance\",\n    \"test.test_iter\",\n    \"test.test_iterlen\",\n    \"test.test_itertools\",\n    \"test.test_json\",\n    \"test.test_json.__main__\",\n    \"test.test_json.test_decode\",\n    \"test.test_json.test_default\",\n    \"test.test_json.test_dump\",\n    \"test.test_json.test_encode_basestring_ascii\",\n    \"test.test_json.test_enum\",\n    \"test.test_json.test_fail\",\n    \"test.test_json.test_float\",\n    \"test.test_json.test_indent\",\n    \"test.test_json.test_pass1\",\n    \"test.test_json.test_pass2\",\n    \"test.test_json.test_pass3\",\n    \"test.test_json.test_recursion\",\n    \"test.test_json.test_scanstring\",\n    \"test.test_json.test_separators\",\n    \"test.test_json.test_speedups\",\n    \"test.test_json.test_tool\",\n    \"test.test_json.test_unicode\",\n    \"test.test_keyword\",\n    \"test.test_keywordonlyarg\",\n    \"test.test_kqueue\",\n    \"test.test_largefile\",\n    \"test.test_lib2to3\",\n    \"test.test_linecache\",\n    \"test.test_list\",\n    \"test.test_listcomps\",\n    \"test.test_locale\",\n    \"test.test_logging\",\n    \"test.test_long\",\n    \"test.test_longexp\",\n    \"test.test_lzma\",\n    \"test.test_macpath\",\n    \"test.test_macurl2path\",\n    \"test.test_mailbox\",\n    \"test.test_mailcap\",\n    \"test.test_marshal\",\n    \"test.test_math\",\n    \"test.test_memoryio\",\n    \"test.test_memoryview\",\n    \"test.test_metaclass\",\n    \"test.test_mimetypes\",\n    \"test.test_minidom\",\n    \"test.test_mmap\",\n    \"test.test_module\",\n    \"test.test_modulefinder\",\n    \"test.test_msilib\",\n    \"test.test_multibytecodec\",\n    \"test.test_multiprocessing_fork\",\n    \"test.test_multiprocessing_forkserver\",\n    \"test.test_multiprocessing_main_handling\",\n    \"test.test_multiprocessing_spawn\",\n    \"test.test_netrc\",\n    \"test.test_nis\",\n    \"test.test_nntplib\",\n    \"test.test_normalization\",\n    \"test.test_ntpath\",\n    \"test.test_numeric_tower\",\n    \"test.test_opcodes\",\n    \"test.test_openpty\",\n    \"test.test_operator\",\n    \"test.test_optparse\",\n    \"test.test_ordered_dict\",\n    \"test.test_os\",\n    \"test.test_ossaudiodev\",\n    \"test.test_osx_env\",\n    \"test.test_parser\",\n    \"test.test_pathlib\",\n    \"test.test_pdb\",\n    \"test.test_peepholer\",\n    \"test.test_pep247\",\n    \"test.test_pep277\",\n    \"test.test_pep3120\",\n    \"test.test_pep3131\",\n    \"test.test_pep3151\",\n    \"test.test_pep352\",\n    \"test.test_pep380\",\n    \"test.test_pep479\",\n    \"test.test_pickle\",\n    \"test.test_pickletools\",\n    \"test.test_pipes\",\n    \"test.test_pkg\",\n    \"test.test_pkgimport\",\n    \"test.test_pkgutil\",\n    \"test.test_platform\",\n    \"test.test_plistlib\",\n    \"test.test_poll\",\n    \"test.test_popen\",\n    \"test.test_poplib\",\n    \"test.test_posix\",\n    \"test.test_posixpath\",\n    \"test.test_pow\",\n    \"test.test_pprint\",\n    \"test.test_print\",\n    \"test.test_profile\",\n    \"test.test_property\",\n    \"test.test_pstats\",\n    \"test.test_pty\",\n    \"test.test_pulldom\",\n    \"test.test_pwd\",\n    \"test.test_py_compile\",\n    \"test.test_pyclbr\",\n    \"test.test_pydoc\",\n    \"test.test_pyexpat\",\n    \"test.test_queue\",\n    \"test.test_quopri\",\n    \"test.test_raise\",\n    \"test.test_random\",\n    \"test.test_range\",\n    \"test.test_re\",\n    \"test.test_readline\",\n    \"test.test_regrtest\",\n    \"test.test_reprlib\",\n    \"test.test_resource\",\n    \"test.test_richcmp\",\n    \"test.test_rlcompleter\",\n    \"test.test_robotparser\",\n    \"test.test_runpy\",\n    \"test.test_sax\",\n    \"test.test_sched\",\n    \"test.test_scope\",\n    \"test.test_script_helper\",\n    \"test.test_select\",\n    \"test.test_selectors\",\n    \"test.test_set\",\n    \"test.test_setcomps\",\n    \"test.test_shelve\",\n    \"test.test_shlex\",\n    \"test.test_shutil\",\n    \"test.test_signal\",\n    \"test.test_site\",\n    \"test.test_slice\",\n    \"test.test_smtpd\",\n    \"test.test_smtplib\",\n    \"test.test_smtpnet\",\n    \"test.test_sndhdr\",\n    \"test.test_socket\",\n    \"test.test_socketserver\",\n    \"test.test_sort\",\n    \"test.test_source_encoding\",\n    \"test.test_spwd\",\n    \"test.test_sqlite\",\n    \"test.test_ssl\",\n    \"test.test_startfile\",\n    \"test.test_stat\",\n    \"test.test_statistics\",\n    \"test.test_strftime\",\n    \"test.test_string\",\n    \"test.test_stringprep\",\n    \"test.test_strlit\",\n    \"test.test_strptime\",\n    \"test.test_strtod\",\n    \"test.test_struct\",\n    \"test.test_structmembers\",\n    \"test.test_structseq\",\n    \"test.test_subprocess\",\n    \"test.test_sunau\",\n    \"test.test_sundry\",\n    \"test.test_super\",\n    \"test.test_support\",\n    \"test.test_symtable\",\n    \"test.test_syntax\",\n    \"test.test_sys\",\n    \"test.test_sys_setprofile\",\n    \"test.test_sys_settrace\",\n    \"test.test_sysconfig\",\n    \"test.test_syslog\",\n    \"test.test_tarfile\",\n    \"test.test_tcl\",\n    \"test.test_telnetlib\",\n    \"test.test_tempfile\",\n    \"test.test_textwrap\",\n    \"test.test_thread\",\n    \"test.test_threaded_import\",\n    \"test.test_threadedtempfile\",\n    \"test.test_threading\",\n    \"test.test_threading_local\",\n    \"test.test_threadsignals\",\n    \"test.test_time\",\n    \"test.test_timeit\",\n    \"test.test_timeout\",\n    \"test.test_tix\",\n    \"test.test_tk\",\n    \"test.test_tokenize\",\n    \"test.test_tools\",\n    \"test.test_tools.__main__\",\n    \"test.test_tools.test_fixcid\",\n    \"test.test_tools.test_gprof2html\",\n    \"test.test_tools.test_i18n\",\n    \"test.test_tools.test_md5sum\",\n    \"test.test_tools.test_pdeps\",\n    \"test.test_tools.test_pindent\",\n    \"test.test_tools.test_reindent\",\n    \"test.test_tools.test_sundry\",\n    \"test.test_tools.test_unparse\",\n    \"test.test_trace\",\n    \"test.test_traceback\",\n    \"test.test_tracemalloc\",\n    \"test.test_ttk_guionly\",\n    \"test.test_ttk_textonly\",\n    \"test.test_tuple\",\n    \"test.test_turtle\",\n    \"test.test_typechecks\",\n    \"test.test_types\",\n    \"test.test_typing\",\n    \"test.test_ucn\",\n    \"test.test_unary\",\n    \"test.test_unicode\",\n    \"test.test_unicode_file\",\n    \"test.test_unicodedata\",\n    \"test.test_unittest\",\n    \"test.test_univnewlines\",\n    \"test.test_unpack\",\n    \"test.test_unpack_ex\",\n    \"test.test_urllib\",\n    \"test.test_urllib2\",\n    \"test.test_urllib2_localnet\",\n    \"test.test_urllib2net\",\n    \"test.test_urllib_response\",\n    \"test.test_urllibnet\",\n    \"test.test_urlparse\",\n    \"test.test_userdict\",\n    \"test.test_userlist\",\n    \"test.test_userstring\",\n    \"test.test_uu\",\n    \"test.test_uuid\",\n    \"test.test_venv\",\n    \"test.test_wait3\",\n    \"test.test_wait4\",\n    \"test.test_warnings\",\n    \"test.test_warnings.__main__\",\n    \"test.test_warnings.data.import_warning\",\n    \"test.test_warnings.data.stacklevel\",\n    \"test.test_wave\",\n    \"test.test_weakref\",\n    \"test.test_weakset\",\n    \"test.test_webbrowser\",\n    \"test.test_winreg\",\n    \"test.test_winsound\",\n    \"test.test_with\",\n    \"test.test_wsgiref\",\n    \"test.test_xdrlib\",\n    \"test.test_xml_dom_minicompat\",\n    \"test.test_xml_etree\",\n    \"test.test_xml_etree_c\",\n    \"test.test_xmlrpc\",\n    \"test.test_xmlrpc_net\",\n    \"test.test_zipapp\",\n    \"test.test_zipfile\",\n    \"test.test_zipfile64\",\n    \"test.test_zipimport\",\n    \"test.test_zipimport_support\",\n    \"test.test_zlib\",\n    \"test.testcodec\",\n    \"test.tf_inherit_check\",\n    \"test.threaded_import_hangers\",\n    \"test.time_hashlib\",\n    \"test.tracedmodules\",\n    \"test.tracedmodules.testmod\",\n    \"test.win_console_handler\",\n    \"test.xmltests\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.runtktests\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_functions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.wikipedia\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.model\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxlimited\",\n    \"xxsubtype\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.6\": [\n    \"__future__\",\n    \"__main__\",\n    \"_ast\",\n    \"_asyncio\",\n    \"_bisect\",\n    \"_blake2\",\n    \"_bootlocale\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_ctypes_test\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_dummy_thread\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_pickle\",\n    \"_posixsubprocess\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_random\",\n    \"_sha1\",\n    \"_sha256\",\n    \"_sha3\",\n    \"_sha512\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_testbuffer\",\n    \"_testcapi\",\n    \"_testimportmultiple\",\n    \"_testmultiphase\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tracemalloc\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.compat\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.tasks\",\n    \"asyncio.test_utils\",\n    \"asyncio.transports\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils._msvccompiler\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.tests\",\n    \"distutils.tests.support\",\n    \"distutils.tests.test_archive_util\",\n    \"distutils.tests.test_bdist\",\n    \"distutils.tests.test_bdist_dumb\",\n    \"distutils.tests.test_bdist_msi\",\n    \"distutils.tests.test_bdist_rpm\",\n    \"distutils.tests.test_bdist_wininst\",\n    \"distutils.tests.test_build\",\n    \"distutils.tests.test_build_clib\",\n    \"distutils.tests.test_build_ext\",\n    \"distutils.tests.test_build_py\",\n    \"distutils.tests.test_build_scripts\",\n    \"distutils.tests.test_check\",\n    \"distutils.tests.test_clean\",\n    \"distutils.tests.test_cmd\",\n    \"distutils.tests.test_config\",\n    \"distutils.tests.test_config_cmd\",\n    \"distutils.tests.test_core\",\n    \"distutils.tests.test_cygwinccompiler\",\n    \"distutils.tests.test_dep_util\",\n    \"distutils.tests.test_dir_util\",\n    \"distutils.tests.test_dist\",\n    \"distutils.tests.test_extension\",\n    \"distutils.tests.test_file_util\",\n    \"distutils.tests.test_filelist\",\n    \"distutils.tests.test_install\",\n    \"distutils.tests.test_install_data\",\n    \"distutils.tests.test_install_headers\",\n    \"distutils.tests.test_install_lib\",\n    \"distutils.tests.test_install_scripts\",\n    \"distutils.tests.test_log\",\n    \"distutils.tests.test_msvc9compiler\",\n    \"distutils.tests.test_msvccompiler\",\n    \"distutils.tests.test_register\",\n    \"distutils.tests.test_sdist\",\n    \"distutils.tests.test_spawn\",\n    \"distutils.tests.test_sysconfig\",\n    \"distutils.tests.test_text_file\",\n    \"distutils.tests.test_unixccompiler\",\n    \"distutils.tests.test_upload\",\n    \"distutils.tests.test_util\",\n    \"distutils.tests.test_version\",\n    \"distutils.tests.test_versionpredicate\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"dummy_threading\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp65001\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_centeuro\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.unicode_internal\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fpectl\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.__main__\",\n    \"idlelib._pyclbr\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_paragraph\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_rstrip\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.paragraph\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.rstrip\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.data.bom\",\n    \"lib2to3.tests.data.crlf\",\n    \"lib2to3.tests.data.different_encoding\",\n    \"lib2to3.tests.data.false_encoding\",\n    \"lib2to3.tests.data.fixers.bad_order\",\n    \"lib2to3.tests.data.fixers.myfixes\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_explicit\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_first\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_last\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_parrot\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_preorder\",\n    \"lib2to3.tests.data.fixers.no_fixer_cls\",\n    \"lib2to3.tests.data.fixers.parrot_example\",\n    \"lib2to3.tests.data.infinite_recursion\",\n    \"lib2to3.tests.data.py2_test_grammar\",\n    \"lib2to3.tests.data.py3_test_grammar\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"macpath\",\n    \"macurl2path\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.semaphore_tracker\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.__main__\",\n    \"test._test_multiprocessing\",\n    \"test.ann_module\",\n    \"test.ann_module2\",\n    \"test.ann_module3\",\n    \"test.audiotests\",\n    \"test.autotest\",\n    \"test.bad_coding\",\n    \"test.bad_coding2\",\n    \"test.badsyntax_3131\",\n    \"test.badsyntax_future10\",\n    \"test.badsyntax_future3\",\n    \"test.badsyntax_future4\",\n    \"test.badsyntax_future5\",\n    \"test.badsyntax_future6\",\n    \"test.badsyntax_future7\",\n    \"test.badsyntax_future8\",\n    \"test.badsyntax_future9\",\n    \"test.badsyntax_pep3120\",\n    \"test.bisect\",\n    \"test.bytecode_helper\",\n    \"test.coding20731\",\n    \"test.curses_tests\",\n    \"test.datetimetester\",\n    \"test.dis_module\",\n    \"test.doctest_aliases\",\n    \"test.double_const\",\n    \"test.dtracedata.call_stack\",\n    \"test.dtracedata.gc\",\n    \"test.dtracedata.instance\",\n    \"test.dtracedata.line\",\n    \"test.eintrdata.eintr_tester\",\n    \"test.encoded_modules\",\n    \"test.encoded_modules.module_iso_8859_1\",\n    \"test.encoded_modules.module_koi8_r\",\n    \"test.final_a\",\n    \"test.final_b\",\n    \"test.fork_wait\",\n    \"test.future_test1\",\n    \"test.future_test2\",\n    \"test.gdb_sample\",\n    \"test.imp_dummy\",\n    \"test.inspect_fodder\",\n    \"test.inspect_fodder2\",\n    \"test.libregrtest\",\n    \"test.libregrtest.cmdline\",\n    \"test.libregrtest.main\",\n    \"test.libregrtest.refleak\",\n    \"test.libregrtest.runtest\",\n    \"test.libregrtest.runtest_mp\",\n    \"test.libregrtest.save_env\",\n    \"test.libregrtest.setup\",\n    \"test.libregrtest.utils\",\n    \"test.list_tests\",\n    \"test.lock_tests\",\n    \"test.make_ssl_certs\",\n    \"test.mapping_tests\",\n    \"test.memory_watchdog\",\n    \"test.mock_socket\",\n    \"test.mod_generics_cache\",\n    \"test.mp_fork_bomb\",\n    \"test.mp_preload\",\n    \"test.multibytecodec_support\",\n    \"test.outstanding_bugs\",\n    \"test.pickletester\",\n    \"test.profilee\",\n    \"test.pyclbr_input\",\n    \"test.pydoc_mod\",\n    \"test.pydocfodder\",\n    \"test.pystone\",\n    \"test.pythoninfo\",\n    \"test.re_tests\",\n    \"test.regrtest\",\n    \"test.relimport\",\n    \"test.reperf\",\n    \"test.sample_doctest\",\n    \"test.sample_doctest_no_docstrings\",\n    \"test.sample_doctest_no_doctests\",\n    \"test.seq_tests\",\n    \"test.signalinterproctester\",\n    \"test.sortperf\",\n    \"test.ssl_servers\",\n    \"test.ssltests\",\n    \"test.string_tests\",\n    \"test.subprocessdata.fd_status\",\n    \"test.subprocessdata.input_reader\",\n    \"test.subprocessdata.qcat\",\n    \"test.subprocessdata.qgrep\",\n    \"test.subprocessdata.sigchild_ignore\",\n    \"test.support\",\n    \"test.support.script_helper\",\n    \"test.support.testresult\",\n    \"test.test___all__\",\n    \"test.test___future__\",\n    \"test.test__locale\",\n    \"test.test__opcode\",\n    \"test.test__osx_support\",\n    \"test.test_abc\",\n    \"test.test_abstract_numbers\",\n    \"test.test_aifc\",\n    \"test.test_argparse\",\n    \"test.test_array\",\n    \"test.test_asdl_parser\",\n    \"test.test_ast\",\n    \"test.test_asyncgen\",\n    \"test.test_asynchat\",\n    \"test.test_asyncio\",\n    \"test.test_asyncio.__main__\",\n    \"test.test_asyncio.echo\",\n    \"test.test_asyncio.echo2\",\n    \"test.test_asyncio.echo3\",\n    \"test.test_asyncio.test_base_events\",\n    \"test.test_asyncio.test_events\",\n    \"test.test_asyncio.test_futures\",\n    \"test.test_asyncio.test_locks\",\n    \"test.test_asyncio.test_pep492\",\n    \"test.test_asyncio.test_proactor_events\",\n    \"test.test_asyncio.test_queues\",\n    \"test.test_asyncio.test_selector_events\",\n    \"test.test_asyncio.test_sslproto\",\n    \"test.test_asyncio.test_streams\",\n    \"test.test_asyncio.test_subprocess\",\n    \"test.test_asyncio.test_tasks\",\n    \"test.test_asyncio.test_transports\",\n    \"test.test_asyncio.test_unix_events\",\n    \"test.test_asyncio.test_windows_events\",\n    \"test.test_asyncio.test_windows_utils\",\n    \"test.test_asyncore\",\n    \"test.test_atexit\",\n    \"test.test_audioop\",\n    \"test.test_augassign\",\n    \"test.test_base64\",\n    \"test.test_baseexception\",\n    \"test.test_bdb\",\n    \"test.test_bigaddrspace\",\n    \"test.test_bigmem\",\n    \"test.test_binascii\",\n    \"test.test_binhex\",\n    \"test.test_binop\",\n    \"test.test_bisect\",\n    \"test.test_bool\",\n    \"test.test_buffer\",\n    \"test.test_bufio\",\n    \"test.test_builtin\",\n    \"test.test_bytes\",\n    \"test.test_bz2\",\n    \"test.test_calendar\",\n    \"test.test_call\",\n    \"test.test_capi\",\n    \"test.test_cgi\",\n    \"test.test_cgitb\",\n    \"test.test_charmapcodec\",\n    \"test.test_class\",\n    \"test.test_cmath\",\n    \"test.test_cmd\",\n    \"test.test_cmd_line\",\n    \"test.test_cmd_line_script\",\n    \"test.test_code\",\n    \"test.test_code_module\",\n    \"test.test_codeccallbacks\",\n    \"test.test_codecencodings_cn\",\n    \"test.test_codecencodings_hk\",\n    \"test.test_codecencodings_iso2022\",\n    \"test.test_codecencodings_jp\",\n    \"test.test_codecencodings_kr\",\n    \"test.test_codecencodings_tw\",\n    \"test.test_codecmaps_cn\",\n    \"test.test_codecmaps_hk\",\n    \"test.test_codecmaps_jp\",\n    \"test.test_codecmaps_kr\",\n    \"test.test_codecmaps_tw\",\n    \"test.test_codecs\",\n    \"test.test_codeop\",\n    \"test.test_collections\",\n    \"test.test_colorsys\",\n    \"test.test_compare\",\n    \"test.test_compile\",\n    \"test.test_compileall\",\n    \"test.test_complex\",\n    \"test.test_concurrent_futures\",\n    \"test.test_configparser\",\n    \"test.test_contains\",\n    \"test.test_contextlib\",\n    \"test.test_copy\",\n    \"test.test_copyreg\",\n    \"test.test_coroutines\",\n    \"test.test_cprofile\",\n    \"test.test_crashers\",\n    \"test.test_crypt\",\n    \"test.test_csv\",\n    \"test.test_ctypes\",\n    \"test.test_curses\",\n    \"test.test_datetime\",\n    \"test.test_dbm\",\n    \"test.test_dbm_dumb\",\n    \"test.test_dbm_gnu\",\n    \"test.test_dbm_ndbm\",\n    \"test.test_decimal\",\n    \"test.test_decorators\",\n    \"test.test_defaultdict\",\n    \"test.test_deque\",\n    \"test.test_descr\",\n    \"test.test_descrtut\",\n    \"test.test_devpoll\",\n    \"test.test_dict\",\n    \"test.test_dict_version\",\n    \"test.test_dictcomps\",\n    \"test.test_dictviews\",\n    \"test.test_difflib\",\n    \"test.test_dis\",\n    \"test.test_distutils\",\n    \"test.test_doctest\",\n    \"test.test_doctest2\",\n    \"test.test_docxmlrpc\",\n    \"test.test_dtrace\",\n    \"test.test_dummy_thread\",\n    \"test.test_dummy_threading\",\n    \"test.test_dynamic\",\n    \"test.test_dynamicclassattribute\",\n    \"test.test_eintr\",\n    \"test.test_email\",\n    \"test.test_email.__main__\",\n    \"test.test_email.test__encoded_words\",\n    \"test.test_email.test__header_value_parser\",\n    \"test.test_email.test_asian_codecs\",\n    \"test.test_email.test_contentmanager\",\n    \"test.test_email.test_defect_handling\",\n    \"test.test_email.test_email\",\n    \"test.test_email.test_generator\",\n    \"test.test_email.test_headerregistry\",\n    \"test.test_email.test_inversion\",\n    \"test.test_email.test_message\",\n    \"test.test_email.test_parser\",\n    \"test.test_email.test_pickleable\",\n    \"test.test_email.test_policy\",\n    \"test.test_email.test_utils\",\n    \"test.test_email.torture_test\",\n    \"test.test_ensurepip\",\n    \"test.test_enum\",\n    \"test.test_enumerate\",\n    \"test.test_eof\",\n    \"test.test_epoll\",\n    \"test.test_errno\",\n    \"test.test_exception_hierarchy\",\n    \"test.test_exception_variations\",\n    \"test.test_exceptions\",\n    \"test.test_extcall\",\n    \"test.test_faulthandler\",\n    \"test.test_fcntl\",\n    \"test.test_file\",\n    \"test.test_file_eintr\",\n    \"test.test_filecmp\",\n    \"test.test_fileinput\",\n    \"test.test_fileio\",\n    \"test.test_finalization\",\n    \"test.test_float\",\n    \"test.test_flufl\",\n    \"test.test_fnmatch\",\n    \"test.test_fork1\",\n    \"test.test_format\",\n    \"test.test_fractions\",\n    \"test.test_frame\",\n    \"test.test_fstring\",\n    \"test.test_ftplib\",\n    \"test.test_funcattrs\",\n    \"test.test_functools\",\n    \"test.test_future\",\n    \"test.test_future3\",\n    \"test.test_future4\",\n    \"test.test_future5\",\n    \"test.test_gc\",\n    \"test.test_gdb\",\n    \"test.test_generator_stop\",\n    \"test.test_generators\",\n    \"test.test_genericpath\",\n    \"test.test_genexps\",\n    \"test.test_getargs2\",\n    \"test.test_getopt\",\n    \"test.test_getpass\",\n    \"test.test_gettext\",\n    \"test.test_glob\",\n    \"test.test_global\",\n    \"test.test_grammar\",\n    \"test.test_grp\",\n    \"test.test_gzip\",\n    \"test.test_hash\",\n    \"test.test_hashlib\",\n    \"test.test_heapq\",\n    \"test.test_hmac\",\n    \"test.test_html\",\n    \"test.test_htmlparser\",\n    \"test.test_http_cookiejar\",\n    \"test.test_http_cookies\",\n    \"test.test_httplib\",\n    \"test.test_httpservers\",\n    \"test.test_idle\",\n    \"test.test_imaplib\",\n    \"test.test_imghdr\",\n    \"test.test_imp\",\n    \"test.test_import\",\n    \"test.test_import.__main__\",\n    \"test.test_import.data.circular_imports.basic\",\n    \"test.test_import.data.circular_imports.basic2\",\n    \"test.test_import.data.circular_imports.indirect\",\n    \"test.test_import.data.circular_imports.rebinding\",\n    \"test.test_import.data.circular_imports.rebinding2\",\n    \"test.test_import.data.circular_imports.subpackage\",\n    \"test.test_import.data.circular_imports.subpkg.subpackage2\",\n    \"test.test_import.data.circular_imports.subpkg.util\",\n    \"test.test_import.data.circular_imports.util\",\n    \"test.test_import.data.package\",\n    \"test.test_import.data.package.submodule\",\n    \"test.test_import.data.package2.submodule1\",\n    \"test.test_import.data.package2.submodule2\",\n    \"test.test_importlib\",\n    \"test.test_importlib.__main__\",\n    \"test.test_importlib.abc\",\n    \"test.test_importlib.builtin\",\n    \"test.test_importlib.builtin.__main__\",\n    \"test.test_importlib.builtin.test_finder\",\n    \"test.test_importlib.builtin.test_loader\",\n    \"test.test_importlib.extension\",\n    \"test.test_importlib.extension.__main__\",\n    \"test.test_importlib.extension.test_case_sensitivity\",\n    \"test.test_importlib.extension.test_finder\",\n    \"test.test_importlib.extension.test_loader\",\n    \"test.test_importlib.extension.test_path_hook\",\n    \"test.test_importlib.frozen\",\n    \"test.test_importlib.frozen.__main__\",\n    \"test.test_importlib.frozen.test_finder\",\n    \"test.test_importlib.frozen.test_loader\",\n    \"test.test_importlib.import_\",\n    \"test.test_importlib.import_.__main__\",\n    \"test.test_importlib.import_.test___loader__\",\n    \"test.test_importlib.import_.test___package__\",\n    \"test.test_importlib.import_.test_api\",\n    \"test.test_importlib.import_.test_caching\",\n    \"test.test_importlib.import_.test_fromlist\",\n    \"test.test_importlib.import_.test_meta_path\",\n    \"test.test_importlib.import_.test_packages\",\n    \"test.test_importlib.import_.test_path\",\n    \"test.test_importlib.import_.test_relative_imports\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.one\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.two\",\n    \"test.test_importlib.namespace_pkgs.module_and_namespace_package.a_test\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion1.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion2.foo.two\",\n    \"test.test_importlib.namespace_pkgs.project1.parent.child.one\",\n    \"test.test_importlib.namespace_pkgs.project2.parent.child.two\",\n    \"test.test_importlib.namespace_pkgs.project3.parent.child.three\",\n    \"test.test_importlib.source\",\n    \"test.test_importlib.source.__main__\",\n    \"test.test_importlib.source.test_case_sensitivity\",\n    \"test.test_importlib.source.test_file_loader\",\n    \"test.test_importlib.source.test_finder\",\n    \"test.test_importlib.source.test_path_hook\",\n    \"test.test_importlib.source.test_source_encoding\",\n    \"test.test_importlib.test_abc\",\n    \"test.test_importlib.test_api\",\n    \"test.test_importlib.test_lazy\",\n    \"test.test_importlib.test_locks\",\n    \"test.test_importlib.test_namespace_pkgs\",\n    \"test.test_importlib.test_spec\",\n    \"test.test_importlib.test_util\",\n    \"test.test_importlib.test_windows\",\n    \"test.test_importlib.util\",\n    \"test.test_index\",\n    \"test.test_inspect\",\n    \"test.test_int\",\n    \"test.test_int_literal\",\n    \"test.test_io\",\n    \"test.test_ioctl\",\n    \"test.test_ipaddress\",\n    \"test.test_isinstance\",\n    \"test.test_iter\",\n    \"test.test_iterlen\",\n    \"test.test_itertools\",\n    \"test.test_json\",\n    \"test.test_json.__main__\",\n    \"test.test_json.test_decode\",\n    \"test.test_json.test_default\",\n    \"test.test_json.test_dump\",\n    \"test.test_json.test_encode_basestring_ascii\",\n    \"test.test_json.test_enum\",\n    \"test.test_json.test_fail\",\n    \"test.test_json.test_float\",\n    \"test.test_json.test_indent\",\n    \"test.test_json.test_pass1\",\n    \"test.test_json.test_pass2\",\n    \"test.test_json.test_pass3\",\n    \"test.test_json.test_recursion\",\n    \"test.test_json.test_scanstring\",\n    \"test.test_json.test_separators\",\n    \"test.test_json.test_speedups\",\n    \"test.test_json.test_tool\",\n    \"test.test_json.test_unicode\",\n    \"test.test_keyword\",\n    \"test.test_keywordonlyarg\",\n    \"test.test_kqueue\",\n    \"test.test_largefile\",\n    \"test.test_lib2to3\",\n    \"test.test_linecache\",\n    \"test.test_list\",\n    \"test.test_listcomps\",\n    \"test.test_locale\",\n    \"test.test_logging\",\n    \"test.test_long\",\n    \"test.test_longexp\",\n    \"test.test_lzma\",\n    \"test.test_macpath\",\n    \"test.test_macurl2path\",\n    \"test.test_mailbox\",\n    \"test.test_mailcap\",\n    \"test.test_marshal\",\n    \"test.test_math\",\n    \"test.test_memoryio\",\n    \"test.test_memoryview\",\n    \"test.test_metaclass\",\n    \"test.test_mimetypes\",\n    \"test.test_minidom\",\n    \"test.test_mmap\",\n    \"test.test_module\",\n    \"test.test_modulefinder\",\n    \"test.test_msilib\",\n    \"test.test_multibytecodec\",\n    \"test.test_multiprocessing_fork\",\n    \"test.test_multiprocessing_forkserver\",\n    \"test.test_multiprocessing_main_handling\",\n    \"test.test_multiprocessing_spawn\",\n    \"test.test_netrc\",\n    \"test.test_nis\",\n    \"test.test_nntplib\",\n    \"test.test_normalization\",\n    \"test.test_ntpath\",\n    \"test.test_numeric_tower\",\n    \"test.test_opcodes\",\n    \"test.test_openpty\",\n    \"test.test_operator\",\n    \"test.test_optparse\",\n    \"test.test_ordered_dict\",\n    \"test.test_os\",\n    \"test.test_ossaudiodev\",\n    \"test.test_osx_env\",\n    \"test.test_parser\",\n    \"test.test_pathlib\",\n    \"test.test_pdb\",\n    \"test.test_peepholer\",\n    \"test.test_pickle\",\n    \"test.test_pickletools\",\n    \"test.test_pipes\",\n    \"test.test_pkg\",\n    \"test.test_pkgimport\",\n    \"test.test_pkgutil\",\n    \"test.test_platform\",\n    \"test.test_plistlib\",\n    \"test.test_poll\",\n    \"test.test_popen\",\n    \"test.test_poplib\",\n    \"test.test_posix\",\n    \"test.test_posixpath\",\n    \"test.test_pow\",\n    \"test.test_pprint\",\n    \"test.test_print\",\n    \"test.test_profile\",\n    \"test.test_property\",\n    \"test.test_pstats\",\n    \"test.test_pty\",\n    \"test.test_pulldom\",\n    \"test.test_pwd\",\n    \"test.test_py_compile\",\n    \"test.test_pyclbr\",\n    \"test.test_pydoc\",\n    \"test.test_pyexpat\",\n    \"test.test_queue\",\n    \"test.test_quopri\",\n    \"test.test_raise\",\n    \"test.test_random\",\n    \"test.test_range\",\n    \"test.test_re\",\n    \"test.test_readline\",\n    \"test.test_regrtest\",\n    \"test.test_repl\",\n    \"test.test_reprlib\",\n    \"test.test_resource\",\n    \"test.test_richcmp\",\n    \"test.test_rlcompleter\",\n    \"test.test_robotparser\",\n    \"test.test_runpy\",\n    \"test.test_sax\",\n    \"test.test_sched\",\n    \"test.test_scope\",\n    \"test.test_script_helper\",\n    \"test.test_secrets\",\n    \"test.test_select\",\n    \"test.test_selectors\",\n    \"test.test_set\",\n    \"test.test_setcomps\",\n    \"test.test_shelve\",\n    \"test.test_shlex\",\n    \"test.test_shutil\",\n    \"test.test_signal\",\n    \"test.test_site\",\n    \"test.test_slice\",\n    \"test.test_smtpd\",\n    \"test.test_smtplib\",\n    \"test.test_smtpnet\",\n    \"test.test_sndhdr\",\n    \"test.test_socket\",\n    \"test.test_socketserver\",\n    \"test.test_sort\",\n    \"test.test_source_encoding\",\n    \"test.test_spwd\",\n    \"test.test_sqlite\",\n    \"test.test_ssl\",\n    \"test.test_startfile\",\n    \"test.test_stat\",\n    \"test.test_statistics\",\n    \"test.test_strftime\",\n    \"test.test_string\",\n    \"test.test_string_literals\",\n    \"test.test_stringprep\",\n    \"test.test_strptime\",\n    \"test.test_strtod\",\n    \"test.test_struct\",\n    \"test.test_structmembers\",\n    \"test.test_structseq\",\n    \"test.test_subclassinit\",\n    \"test.test_subprocess\",\n    \"test.test_sunau\",\n    \"test.test_sundry\",\n    \"test.test_super\",\n    \"test.test_support\",\n    \"test.test_symbol\",\n    \"test.test_symtable\",\n    \"test.test_syntax\",\n    \"test.test_sys\",\n    \"test.test_sys_setprofile\",\n    \"test.test_sys_settrace\",\n    \"test.test_sysconfig\",\n    \"test.test_syslog\",\n    \"test.test_tarfile\",\n    \"test.test_tcl\",\n    \"test.test_telnetlib\",\n    \"test.test_tempfile\",\n    \"test.test_textwrap\",\n    \"test.test_thread\",\n    \"test.test_threaded_import\",\n    \"test.test_threadedtempfile\",\n    \"test.test_threading\",\n    \"test.test_threading_local\",\n    \"test.test_threadsignals\",\n    \"test.test_time\",\n    \"test.test_timeit\",\n    \"test.test_timeout\",\n    \"test.test_tix\",\n    \"test.test_tk\",\n    \"test.test_tokenize\",\n    \"test.test_tools\",\n    \"test.test_tools.__main__\",\n    \"test.test_tools.test_fixcid\",\n    \"test.test_tools.test_gprof2html\",\n    \"test.test_tools.test_i18n\",\n    \"test.test_tools.test_md5sum\",\n    \"test.test_tools.test_pdeps\",\n    \"test.test_tools.test_pindent\",\n    \"test.test_tools.test_reindent\",\n    \"test.test_tools.test_sundry\",\n    \"test.test_tools.test_unparse\",\n    \"test.test_trace\",\n    \"test.test_traceback\",\n    \"test.test_tracemalloc\",\n    \"test.test_ttk_guionly\",\n    \"test.test_ttk_textonly\",\n    \"test.test_tuple\",\n    \"test.test_turtle\",\n    \"test.test_typechecks\",\n    \"test.test_types\",\n    \"test.test_typing\",\n    \"test.test_ucn\",\n    \"test.test_unary\",\n    \"test.test_unicode\",\n    \"test.test_unicode_file\",\n    \"test.test_unicode_file_functions\",\n    \"test.test_unicode_identifiers\",\n    \"test.test_unicodedata\",\n    \"test.test_unittest\",\n    \"test.test_univnewlines\",\n    \"test.test_unpack\",\n    \"test.test_unpack_ex\",\n    \"test.test_urllib\",\n    \"test.test_urllib2\",\n    \"test.test_urllib2_localnet\",\n    \"test.test_urllib2net\",\n    \"test.test_urllib_response\",\n    \"test.test_urllibnet\",\n    \"test.test_urlparse\",\n    \"test.test_userdict\",\n    \"test.test_userlist\",\n    \"test.test_userstring\",\n    \"test.test_utf8source\",\n    \"test.test_uu\",\n    \"test.test_uuid\",\n    \"test.test_venv\",\n    \"test.test_wait3\",\n    \"test.test_wait4\",\n    \"test.test_warnings\",\n    \"test.test_warnings.__main__\",\n    \"test.test_warnings.data.import_warning\",\n    \"test.test_warnings.data.stacklevel\",\n    \"test.test_wave\",\n    \"test.test_weakref\",\n    \"test.test_weakset\",\n    \"test.test_webbrowser\",\n    \"test.test_winconsoleio\",\n    \"test.test_winreg\",\n    \"test.test_winsound\",\n    \"test.test_with\",\n    \"test.test_wsgiref\",\n    \"test.test_xdrlib\",\n    \"test.test_xml_dom_minicompat\",\n    \"test.test_xml_etree\",\n    \"test.test_xml_etree_c\",\n    \"test.test_xmlrpc\",\n    \"test.test_xmlrpc_net\",\n    \"test.test_yield_from\",\n    \"test.test_zipapp\",\n    \"test.test_zipfile\",\n    \"test.test_zipfile64\",\n    \"test.test_zipimport\",\n    \"test.test_zipimport_support\",\n    \"test.test_zlib\",\n    \"test.testcodec\",\n    \"test.tf_inherit_check\",\n    \"test.threaded_import_hangers\",\n    \"test.time_hashlib\",\n    \"test.tracedmodules\",\n    \"test.tracedmodules.testmod\",\n    \"test.win_console_handler\",\n    \"test.xmltests\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.runtktests\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_functions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.model\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxlimited\",\n    \"xxsubtype\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.7\": [\n    \"__future__\",\n    \"__main__\",\n    \"_abc\",\n    \"_ast\",\n    \"_asyncio\",\n    \"_bisect\",\n    \"_blake2\",\n    \"_bootlocale\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_contextvars\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_ctypes_test\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_dummy_thread\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_pickle\",\n    \"_posixsubprocess\",\n    \"_py_abc\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_queue\",\n    \"_random\",\n    \"_sha1\",\n    \"_sha256\",\n    \"_sha3\",\n    \"_sha512\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_testbuffer\",\n    \"_testcapi\",\n    \"_testimportmultiple\",\n    \"_testmultiphase\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tracemalloc\",\n    \"_uuid\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"_xxtestfuzz\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.format_helpers\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.runners\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.tasks\",\n    \"asyncio.transports\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"contextvars\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._aix\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"dataclasses\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils._msvccompiler\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.tests\",\n    \"distutils.tests.support\",\n    \"distutils.tests.test_archive_util\",\n    \"distutils.tests.test_bdist\",\n    \"distutils.tests.test_bdist_dumb\",\n    \"distutils.tests.test_bdist_msi\",\n    \"distutils.tests.test_bdist_rpm\",\n    \"distutils.tests.test_bdist_wininst\",\n    \"distutils.tests.test_build\",\n    \"distutils.tests.test_build_clib\",\n    \"distutils.tests.test_build_ext\",\n    \"distutils.tests.test_build_py\",\n    \"distutils.tests.test_build_scripts\",\n    \"distutils.tests.test_check\",\n    \"distutils.tests.test_clean\",\n    \"distutils.tests.test_cmd\",\n    \"distutils.tests.test_config\",\n    \"distutils.tests.test_config_cmd\",\n    \"distutils.tests.test_core\",\n    \"distutils.tests.test_cygwinccompiler\",\n    \"distutils.tests.test_dep_util\",\n    \"distutils.tests.test_dir_util\",\n    \"distutils.tests.test_dist\",\n    \"distutils.tests.test_extension\",\n    \"distutils.tests.test_file_util\",\n    \"distutils.tests.test_filelist\",\n    \"distutils.tests.test_install\",\n    \"distutils.tests.test_install_data\",\n    \"distutils.tests.test_install_headers\",\n    \"distutils.tests.test_install_lib\",\n    \"distutils.tests.test_install_scripts\",\n    \"distutils.tests.test_log\",\n    \"distutils.tests.test_msvc9compiler\",\n    \"distutils.tests.test_msvccompiler\",\n    \"distutils.tests.test_register\",\n    \"distutils.tests.test_sdist\",\n    \"distutils.tests.test_spawn\",\n    \"distutils.tests.test_sysconfig\",\n    \"distutils.tests.test_text_file\",\n    \"distutils.tests.test_unixccompiler\",\n    \"distutils.tests.test_upload\",\n    \"distutils.tests.test_util\",\n    \"distutils.tests.test_version\",\n    \"distutils.tests.test_versionpredicate\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"dummy_threading\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp65001\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_centeuro\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.unicode_internal\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.__main__\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_paragraph\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_rstrip\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.paragraph\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.rstrip\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.resources\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.data.bom\",\n    \"lib2to3.tests.data.crlf\",\n    \"lib2to3.tests.data.different_encoding\",\n    \"lib2to3.tests.data.false_encoding\",\n    \"lib2to3.tests.data.fixers.bad_order\",\n    \"lib2to3.tests.data.fixers.myfixes\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_explicit\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_first\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_last\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_parrot\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_preorder\",\n    \"lib2to3.tests.data.fixers.no_fixer_cls\",\n    \"lib2to3.tests.data.fixers.parrot_example\",\n    \"lib2to3.tests.data.infinite_recursion\",\n    \"lib2to3.tests.data.py2_test_grammar\",\n    \"lib2to3.tests.data.py3_test_grammar\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"macpath\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.semaphore_tracker\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.backup\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.__main__\",\n    \"test._test_multiprocessing\",\n    \"test.ann_module\",\n    \"test.ann_module2\",\n    \"test.ann_module3\",\n    \"test.audiotests\",\n    \"test.autotest\",\n    \"test.bad_coding\",\n    \"test.bad_coding2\",\n    \"test.bad_getattr\",\n    \"test.bad_getattr2\",\n    \"test.bad_getattr3\",\n    \"test.badsyntax_3131\",\n    \"test.badsyntax_future10\",\n    \"test.badsyntax_future3\",\n    \"test.badsyntax_future4\",\n    \"test.badsyntax_future5\",\n    \"test.badsyntax_future6\",\n    \"test.badsyntax_future7\",\n    \"test.badsyntax_future8\",\n    \"test.badsyntax_future9\",\n    \"test.badsyntax_pep3120\",\n    \"test.bisect\",\n    \"test.bisect_cmd\",\n    \"test.bytecode_helper\",\n    \"test.coding20731\",\n    \"test.curses_tests\",\n    \"test.dataclass_module_1\",\n    \"test.dataclass_module_1_str\",\n    \"test.dataclass_module_2\",\n    \"test.dataclass_module_2_str\",\n    \"test.dataclass_textanno\",\n    \"test.datetimetester\",\n    \"test.dis_module\",\n    \"test.doctest_aliases\",\n    \"test.double_const\",\n    \"test.dtracedata.call_stack\",\n    \"test.dtracedata.gc\",\n    \"test.dtracedata.instance\",\n    \"test.dtracedata.line\",\n    \"test.eintrdata.eintr_tester\",\n    \"test.encoded_modules\",\n    \"test.encoded_modules.module_iso_8859_1\",\n    \"test.encoded_modules.module_koi8_r\",\n    \"test.final_a\",\n    \"test.final_b\",\n    \"test.fork_wait\",\n    \"test.future_test1\",\n    \"test.future_test2\",\n    \"test.gdb_sample\",\n    \"test.good_getattr\",\n    \"test.imp_dummy\",\n    \"test.inspect_fodder\",\n    \"test.inspect_fodder2\",\n    \"test.libregrtest\",\n    \"test.libregrtest.cmdline\",\n    \"test.libregrtest.main\",\n    \"test.libregrtest.refleak\",\n    \"test.libregrtest.runtest\",\n    \"test.libregrtest.runtest_mp\",\n    \"test.libregrtest.save_env\",\n    \"test.libregrtest.setup\",\n    \"test.libregrtest.utils\",\n    \"test.libregrtest.win_utils\",\n    \"test.list_tests\",\n    \"test.lock_tests\",\n    \"test.make_ssl_certs\",\n    \"test.mapping_tests\",\n    \"test.memory_watchdog\",\n    \"test.mock_socket\",\n    \"test.mod_generics_cache\",\n    \"test.mp_fork_bomb\",\n    \"test.mp_preload\",\n    \"test.multibytecodec_support\",\n    \"test.outstanding_bugs\",\n    \"test.pickletester\",\n    \"test.profilee\",\n    \"test.pyclbr_input\",\n    \"test.pydoc_mod\",\n    \"test.pydocfodder\",\n    \"test.pythoninfo\",\n    \"test.re_tests\",\n    \"test.regrtest\",\n    \"test.relimport\",\n    \"test.reperf\",\n    \"test.sample_doctest\",\n    \"test.sample_doctest_no_docstrings\",\n    \"test.sample_doctest_no_doctests\",\n    \"test.seq_tests\",\n    \"test.signalinterproctester\",\n    \"test.sortperf\",\n    \"test.ssl_servers\",\n    \"test.ssltests\",\n    \"test.string_tests\",\n    \"test.subprocessdata.fd_status\",\n    \"test.subprocessdata.input_reader\",\n    \"test.subprocessdata.qcat\",\n    \"test.subprocessdata.qgrep\",\n    \"test.subprocessdata.sigchild_ignore\",\n    \"test.support\",\n    \"test.support.script_helper\",\n    \"test.support.testresult\",\n    \"test.test___all__\",\n    \"test.test___future__\",\n    \"test.test__locale\",\n    \"test.test__opcode\",\n    \"test.test__osx_support\",\n    \"test.test_abc\",\n    \"test.test_abstract_numbers\",\n    \"test.test_aifc\",\n    \"test.test_argparse\",\n    \"test.test_array\",\n    \"test.test_asdl_parser\",\n    \"test.test_ast\",\n    \"test.test_asyncgen\",\n    \"test.test_asynchat\",\n    \"test.test_asyncio\",\n    \"test.test_asyncio.__main__\",\n    \"test.test_asyncio.echo\",\n    \"test.test_asyncio.echo2\",\n    \"test.test_asyncio.echo3\",\n    \"test.test_asyncio.functional\",\n    \"test.test_asyncio.test_base_events\",\n    \"test.test_asyncio.test_buffered_proto\",\n    \"test.test_asyncio.test_context\",\n    \"test.test_asyncio.test_events\",\n    \"test.test_asyncio.test_futures\",\n    \"test.test_asyncio.test_locks\",\n    \"test.test_asyncio.test_pep492\",\n    \"test.test_asyncio.test_proactor_events\",\n    \"test.test_asyncio.test_queues\",\n    \"test.test_asyncio.test_runners\",\n    \"test.test_asyncio.test_selector_events\",\n    \"test.test_asyncio.test_server\",\n    \"test.test_asyncio.test_sslproto\",\n    \"test.test_asyncio.test_streams\",\n    \"test.test_asyncio.test_subprocess\",\n    \"test.test_asyncio.test_tasks\",\n    \"test.test_asyncio.test_transports\",\n    \"test.test_asyncio.test_unix_events\",\n    \"test.test_asyncio.test_windows_events\",\n    \"test.test_asyncio.test_windows_utils\",\n    \"test.test_asyncio.utils\",\n    \"test.test_asyncore\",\n    \"test.test_atexit\",\n    \"test.test_audioop\",\n    \"test.test_augassign\",\n    \"test.test_base64\",\n    \"test.test_baseexception\",\n    \"test.test_bdb\",\n    \"test.test_bigaddrspace\",\n    \"test.test_bigmem\",\n    \"test.test_binascii\",\n    \"test.test_binhex\",\n    \"test.test_binop\",\n    \"test.test_bisect\",\n    \"test.test_bool\",\n    \"test.test_buffer\",\n    \"test.test_bufio\",\n    \"test.test_builtin\",\n    \"test.test_bytes\",\n    \"test.test_bz2\",\n    \"test.test_c_locale_coercion\",\n    \"test.test_calendar\",\n    \"test.test_call\",\n    \"test.test_capi\",\n    \"test.test_cgi\",\n    \"test.test_cgitb\",\n    \"test.test_charmapcodec\",\n    \"test.test_class\",\n    \"test.test_clinic\",\n    \"test.test_cmath\",\n    \"test.test_cmd\",\n    \"test.test_cmd_line\",\n    \"test.test_cmd_line_script\",\n    \"test.test_code\",\n    \"test.test_code_module\",\n    \"test.test_codeccallbacks\",\n    \"test.test_codecencodings_cn\",\n    \"test.test_codecencodings_hk\",\n    \"test.test_codecencodings_iso2022\",\n    \"test.test_codecencodings_jp\",\n    \"test.test_codecencodings_kr\",\n    \"test.test_codecencodings_tw\",\n    \"test.test_codecmaps_cn\",\n    \"test.test_codecmaps_hk\",\n    \"test.test_codecmaps_jp\",\n    \"test.test_codecmaps_kr\",\n    \"test.test_codecmaps_tw\",\n    \"test.test_codecs\",\n    \"test.test_codeop\",\n    \"test.test_collections\",\n    \"test.test_colorsys\",\n    \"test.test_compare\",\n    \"test.test_compile\",\n    \"test.test_compileall\",\n    \"test.test_complex\",\n    \"test.test_concurrent_futures\",\n    \"test.test_configparser\",\n    \"test.test_contains\",\n    \"test.test_context\",\n    \"test.test_contextlib\",\n    \"test.test_contextlib_async\",\n    \"test.test_copy\",\n    \"test.test_copyreg\",\n    \"test.test_coroutines\",\n    \"test.test_cprofile\",\n    \"test.test_crashers\",\n    \"test.test_crypt\",\n    \"test.test_csv\",\n    \"test.test_ctypes\",\n    \"test.test_curses\",\n    \"test.test_dataclasses\",\n    \"test.test_datetime\",\n    \"test.test_dbm\",\n    \"test.test_dbm_dumb\",\n    \"test.test_dbm_gnu\",\n    \"test.test_dbm_ndbm\",\n    \"test.test_decimal\",\n    \"test.test_decorators\",\n    \"test.test_defaultdict\",\n    \"test.test_deque\",\n    \"test.test_descr\",\n    \"test.test_descrtut\",\n    \"test.test_devpoll\",\n    \"test.test_dict\",\n    \"test.test_dict_version\",\n    \"test.test_dictcomps\",\n    \"test.test_dictviews\",\n    \"test.test_difflib\",\n    \"test.test_dis\",\n    \"test.test_distutils\",\n    \"test.test_doctest\",\n    \"test.test_doctest2\",\n    \"test.test_docxmlrpc\",\n    \"test.test_dtrace\",\n    \"test.test_dummy_thread\",\n    \"test.test_dummy_threading\",\n    \"test.test_dynamic\",\n    \"test.test_dynamicclassattribute\",\n    \"test.test_eintr\",\n    \"test.test_email\",\n    \"test.test_email.__main__\",\n    \"test.test_email.test__encoded_words\",\n    \"test.test_email.test__header_value_parser\",\n    \"test.test_email.test_asian_codecs\",\n    \"test.test_email.test_contentmanager\",\n    \"test.test_email.test_defect_handling\",\n    \"test.test_email.test_email\",\n    \"test.test_email.test_generator\",\n    \"test.test_email.test_headerregistry\",\n    \"test.test_email.test_inversion\",\n    \"test.test_email.test_message\",\n    \"test.test_email.test_parser\",\n    \"test.test_email.test_pickleable\",\n    \"test.test_email.test_policy\",\n    \"test.test_email.test_utils\",\n    \"test.test_email.torture_test\",\n    \"test.test_embed\",\n    \"test.test_ensurepip\",\n    \"test.test_enum\",\n    \"test.test_enumerate\",\n    \"test.test_eof\",\n    \"test.test_epoll\",\n    \"test.test_errno\",\n    \"test.test_exception_hierarchy\",\n    \"test.test_exception_variations\",\n    \"test.test_exceptions\",\n    \"test.test_extcall\",\n    \"test.test_faulthandler\",\n    \"test.test_fcntl\",\n    \"test.test_file\",\n    \"test.test_file_eintr\",\n    \"test.test_filecmp\",\n    \"test.test_fileinput\",\n    \"test.test_fileio\",\n    \"test.test_finalization\",\n    \"test.test_float\",\n    \"test.test_flufl\",\n    \"test.test_fnmatch\",\n    \"test.test_fork1\",\n    \"test.test_format\",\n    \"test.test_fractions\",\n    \"test.test_frame\",\n    \"test.test_frozen\",\n    \"test.test_fstring\",\n    \"test.test_ftplib\",\n    \"test.test_funcattrs\",\n    \"test.test_functools\",\n    \"test.test_future\",\n    \"test.test_future3\",\n    \"test.test_future4\",\n    \"test.test_future5\",\n    \"test.test_gc\",\n    \"test.test_gdb\",\n    \"test.test_generator_stop\",\n    \"test.test_generators\",\n    \"test.test_genericclass\",\n    \"test.test_genericpath\",\n    \"test.test_genexps\",\n    \"test.test_getargs2\",\n    \"test.test_getopt\",\n    \"test.test_getpass\",\n    \"test.test_gettext\",\n    \"test.test_glob\",\n    \"test.test_global\",\n    \"test.test_grammar\",\n    \"test.test_grp\",\n    \"test.test_gzip\",\n    \"test.test_hash\",\n    \"test.test_hashlib\",\n    \"test.test_heapq\",\n    \"test.test_hmac\",\n    \"test.test_html\",\n    \"test.test_htmlparser\",\n    \"test.test_http_cookiejar\",\n    \"test.test_http_cookies\",\n    \"test.test_httplib\",\n    \"test.test_httpservers\",\n    \"test.test_idle\",\n    \"test.test_imaplib\",\n    \"test.test_imghdr\",\n    \"test.test_imp\",\n    \"test.test_import\",\n    \"test.test_import.__main__\",\n    \"test.test_import.data.circular_imports.basic\",\n    \"test.test_import.data.circular_imports.basic2\",\n    \"test.test_import.data.circular_imports.binding\",\n    \"test.test_import.data.circular_imports.binding2\",\n    \"test.test_import.data.circular_imports.indirect\",\n    \"test.test_import.data.circular_imports.rebinding\",\n    \"test.test_import.data.circular_imports.rebinding2\",\n    \"test.test_import.data.circular_imports.subpackage\",\n    \"test.test_import.data.circular_imports.subpkg.subpackage2\",\n    \"test.test_import.data.circular_imports.subpkg.util\",\n    \"test.test_import.data.circular_imports.util\",\n    \"test.test_import.data.package\",\n    \"test.test_import.data.package.submodule\",\n    \"test.test_import.data.package2.submodule1\",\n    \"test.test_import.data.package2.submodule2\",\n    \"test.test_importlib\",\n    \"test.test_importlib.__main__\",\n    \"test.test_importlib.abc\",\n    \"test.test_importlib.builtin\",\n    \"test.test_importlib.builtin.__main__\",\n    \"test.test_importlib.builtin.test_finder\",\n    \"test.test_importlib.builtin.test_loader\",\n    \"test.test_importlib.data01\",\n    \"test.test_importlib.data01.subdirectory\",\n    \"test.test_importlib.data02\",\n    \"test.test_importlib.data02.one\",\n    \"test.test_importlib.data02.two\",\n    \"test.test_importlib.data03\",\n    \"test.test_importlib.data03.namespace.portion1\",\n    \"test.test_importlib.data03.namespace.portion2\",\n    \"test.test_importlib.extension\",\n    \"test.test_importlib.extension.__main__\",\n    \"test.test_importlib.extension.test_case_sensitivity\",\n    \"test.test_importlib.extension.test_finder\",\n    \"test.test_importlib.extension.test_loader\",\n    \"test.test_importlib.extension.test_path_hook\",\n    \"test.test_importlib.frozen\",\n    \"test.test_importlib.frozen.__main__\",\n    \"test.test_importlib.frozen.test_finder\",\n    \"test.test_importlib.frozen.test_loader\",\n    \"test.test_importlib.import_\",\n    \"test.test_importlib.import_.__main__\",\n    \"test.test_importlib.import_.test___loader__\",\n    \"test.test_importlib.import_.test___package__\",\n    \"test.test_importlib.import_.test_api\",\n    \"test.test_importlib.import_.test_caching\",\n    \"test.test_importlib.import_.test_fromlist\",\n    \"test.test_importlib.import_.test_meta_path\",\n    \"test.test_importlib.import_.test_packages\",\n    \"test.test_importlib.import_.test_path\",\n    \"test.test_importlib.import_.test_relative_imports\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.one\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.two\",\n    \"test.test_importlib.namespace_pkgs.module_and_namespace_package.a_test\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion1.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion2.foo.two\",\n    \"test.test_importlib.namespace_pkgs.project1.parent.child.one\",\n    \"test.test_importlib.namespace_pkgs.project2.parent.child.two\",\n    \"test.test_importlib.namespace_pkgs.project3.parent.child.three\",\n    \"test.test_importlib.source\",\n    \"test.test_importlib.source.__main__\",\n    \"test.test_importlib.source.test_case_sensitivity\",\n    \"test.test_importlib.source.test_file_loader\",\n    \"test.test_importlib.source.test_finder\",\n    \"test.test_importlib.source.test_path_hook\",\n    \"test.test_importlib.source.test_source_encoding\",\n    \"test.test_importlib.test_abc\",\n    \"test.test_importlib.test_api\",\n    \"test.test_importlib.test_lazy\",\n    \"test.test_importlib.test_locks\",\n    \"test.test_importlib.test_namespace_pkgs\",\n    \"test.test_importlib.test_open\",\n    \"test.test_importlib.test_path\",\n    \"test.test_importlib.test_read\",\n    \"test.test_importlib.test_resource\",\n    \"test.test_importlib.test_spec\",\n    \"test.test_importlib.test_util\",\n    \"test.test_importlib.test_windows\",\n    \"test.test_importlib.util\",\n    \"test.test_importlib.zipdata01\",\n    \"test.test_importlib.zipdata02\",\n    \"test.test_index\",\n    \"test.test_inspect\",\n    \"test.test_int\",\n    \"test.test_int_literal\",\n    \"test.test_io\",\n    \"test.test_ioctl\",\n    \"test.test_ipaddress\",\n    \"test.test_isinstance\",\n    \"test.test_iter\",\n    \"test.test_iterlen\",\n    \"test.test_itertools\",\n    \"test.test_json\",\n    \"test.test_json.__main__\",\n    \"test.test_json.test_decode\",\n    \"test.test_json.test_default\",\n    \"test.test_json.test_dump\",\n    \"test.test_json.test_encode_basestring_ascii\",\n    \"test.test_json.test_enum\",\n    \"test.test_json.test_fail\",\n    \"test.test_json.test_float\",\n    \"test.test_json.test_indent\",\n    \"test.test_json.test_pass1\",\n    \"test.test_json.test_pass2\",\n    \"test.test_json.test_pass3\",\n    \"test.test_json.test_recursion\",\n    \"test.test_json.test_scanstring\",\n    \"test.test_json.test_separators\",\n    \"test.test_json.test_speedups\",\n    \"test.test_json.test_tool\",\n    \"test.test_json.test_unicode\",\n    \"test.test_keyword\",\n    \"test.test_keywordonlyarg\",\n    \"test.test_kqueue\",\n    \"test.test_largefile\",\n    \"test.test_lib2to3\",\n    \"test.test_linecache\",\n    \"test.test_list\",\n    \"test.test_listcomps\",\n    \"test.test_locale\",\n    \"test.test_logging\",\n    \"test.test_long\",\n    \"test.test_longexp\",\n    \"test.test_lzma\",\n    \"test.test_macpath\",\n    \"test.test_mailbox\",\n    \"test.test_mailcap\",\n    \"test.test_marshal\",\n    \"test.test_math\",\n    \"test.test_memoryio\",\n    \"test.test_memoryview\",\n    \"test.test_metaclass\",\n    \"test.test_mimetypes\",\n    \"test.test_minidom\",\n    \"test.test_mmap\",\n    \"test.test_module\",\n    \"test.test_modulefinder\",\n    \"test.test_msilib\",\n    \"test.test_multibytecodec\",\n    \"test.test_multiprocessing_fork\",\n    \"test.test_multiprocessing_forkserver\",\n    \"test.test_multiprocessing_main_handling\",\n    \"test.test_multiprocessing_spawn\",\n    \"test.test_netrc\",\n    \"test.test_nis\",\n    \"test.test_nntplib\",\n    \"test.test_normalization\",\n    \"test.test_ntpath\",\n    \"test.test_numeric_tower\",\n    \"test.test_opcodes\",\n    \"test.test_openpty\",\n    \"test.test_operator\",\n    \"test.test_optparse\",\n    \"test.test_ordered_dict\",\n    \"test.test_os\",\n    \"test.test_ossaudiodev\",\n    \"test.test_osx_env\",\n    \"test.test_parser\",\n    \"test.test_pathlib\",\n    \"test.test_pdb\",\n    \"test.test_peepholer\",\n    \"test.test_pickle\",\n    \"test.test_pickletools\",\n    \"test.test_pipes\",\n    \"test.test_pkg\",\n    \"test.test_pkgimport\",\n    \"test.test_pkgutil\",\n    \"test.test_platform\",\n    \"test.test_plistlib\",\n    \"test.test_poll\",\n    \"test.test_popen\",\n    \"test.test_poplib\",\n    \"test.test_posix\",\n    \"test.test_posixpath\",\n    \"test.test_pow\",\n    \"test.test_pprint\",\n    \"test.test_print\",\n    \"test.test_profile\",\n    \"test.test_property\",\n    \"test.test_pstats\",\n    \"test.test_pty\",\n    \"test.test_pulldom\",\n    \"test.test_pwd\",\n    \"test.test_py_compile\",\n    \"test.test_pyclbr\",\n    \"test.test_pydoc\",\n    \"test.test_pyexpat\",\n    \"test.test_queue\",\n    \"test.test_quopri\",\n    \"test.test_raise\",\n    \"test.test_random\",\n    \"test.test_range\",\n    \"test.test_re\",\n    \"test.test_readline\",\n    \"test.test_regrtest\",\n    \"test.test_repl\",\n    \"test.test_reprlib\",\n    \"test.test_resource\",\n    \"test.test_richcmp\",\n    \"test.test_rlcompleter\",\n    \"test.test_robotparser\",\n    \"test.test_runpy\",\n    \"test.test_sax\",\n    \"test.test_sched\",\n    \"test.test_scope\",\n    \"test.test_script_helper\",\n    \"test.test_secrets\",\n    \"test.test_select\",\n    \"test.test_selectors\",\n    \"test.test_set\",\n    \"test.test_setcomps\",\n    \"test.test_shelve\",\n    \"test.test_shlex\",\n    \"test.test_shutil\",\n    \"test.test_signal\",\n    \"test.test_site\",\n    \"test.test_slice\",\n    \"test.test_smtpd\",\n    \"test.test_smtplib\",\n    \"test.test_smtpnet\",\n    \"test.test_sndhdr\",\n    \"test.test_socket\",\n    \"test.test_socketserver\",\n    \"test.test_sort\",\n    \"test.test_source_encoding\",\n    \"test.test_spwd\",\n    \"test.test_sqlite\",\n    \"test.test_ssl\",\n    \"test.test_startfile\",\n    \"test.test_stat\",\n    \"test.test_statistics\",\n    \"test.test_strftime\",\n    \"test.test_string\",\n    \"test.test_string_literals\",\n    \"test.test_stringprep\",\n    \"test.test_strptime\",\n    \"test.test_strtod\",\n    \"test.test_struct\",\n    \"test.test_structmembers\",\n    \"test.test_structseq\",\n    \"test.test_subclassinit\",\n    \"test.test_subprocess\",\n    \"test.test_sunau\",\n    \"test.test_sundry\",\n    \"test.test_super\",\n    \"test.test_support\",\n    \"test.test_symbol\",\n    \"test.test_symtable\",\n    \"test.test_syntax\",\n    \"test.test_sys\",\n    \"test.test_sys_setprofile\",\n    \"test.test_sys_settrace\",\n    \"test.test_sysconfig\",\n    \"test.test_syslog\",\n    \"test.test_tarfile\",\n    \"test.test_tcl\",\n    \"test.test_telnetlib\",\n    \"test.test_tempfile\",\n    \"test.test_textwrap\",\n    \"test.test_thread\",\n    \"test.test_threaded_import\",\n    \"test.test_threadedtempfile\",\n    \"test.test_threading\",\n    \"test.test_threading_local\",\n    \"test.test_threadsignals\",\n    \"test.test_time\",\n    \"test.test_timeit\",\n    \"test.test_timeout\",\n    \"test.test_tix\",\n    \"test.test_tk\",\n    \"test.test_tokenize\",\n    \"test.test_tools\",\n    \"test.test_tools.__main__\",\n    \"test.test_tools.test_fixcid\",\n    \"test.test_tools.test_gprof2html\",\n    \"test.test_tools.test_i18n\",\n    \"test.test_tools.test_lll\",\n    \"test.test_tools.test_md5sum\",\n    \"test.test_tools.test_pdeps\",\n    \"test.test_tools.test_pindent\",\n    \"test.test_tools.test_reindent\",\n    \"test.test_tools.test_sundry\",\n    \"test.test_tools.test_unparse\",\n    \"test.test_trace\",\n    \"test.test_traceback\",\n    \"test.test_tracemalloc\",\n    \"test.test_ttk_guionly\",\n    \"test.test_ttk_textonly\",\n    \"test.test_tuple\",\n    \"test.test_turtle\",\n    \"test.test_typechecks\",\n    \"test.test_types\",\n    \"test.test_typing\",\n    \"test.test_ucn\",\n    \"test.test_unary\",\n    \"test.test_unicode\",\n    \"test.test_unicode_file\",\n    \"test.test_unicode_file_functions\",\n    \"test.test_unicode_identifiers\",\n    \"test.test_unicodedata\",\n    \"test.test_unittest\",\n    \"test.test_univnewlines\",\n    \"test.test_unpack\",\n    \"test.test_unpack_ex\",\n    \"test.test_urllib\",\n    \"test.test_urllib2\",\n    \"test.test_urllib2_localnet\",\n    \"test.test_urllib2net\",\n    \"test.test_urllib_response\",\n    \"test.test_urllibnet\",\n    \"test.test_urlparse\",\n    \"test.test_userdict\",\n    \"test.test_userlist\",\n    \"test.test_userstring\",\n    \"test.test_utf8_mode\",\n    \"test.test_utf8source\",\n    \"test.test_uu\",\n    \"test.test_uuid\",\n    \"test.test_venv\",\n    \"test.test_wait3\",\n    \"test.test_wait4\",\n    \"test.test_warnings\",\n    \"test.test_warnings.__main__\",\n    \"test.test_warnings.data.import_warning\",\n    \"test.test_warnings.data.stacklevel\",\n    \"test.test_wave\",\n    \"test.test_weakref\",\n    \"test.test_weakset\",\n    \"test.test_webbrowser\",\n    \"test.test_winconsoleio\",\n    \"test.test_winreg\",\n    \"test.test_winsound\",\n    \"test.test_with\",\n    \"test.test_wsgiref\",\n    \"test.test_xdrlib\",\n    \"test.test_xml_dom_minicompat\",\n    \"test.test_xml_etree\",\n    \"test.test_xml_etree_c\",\n    \"test.test_xmlrpc\",\n    \"test.test_xmlrpc_net\",\n    \"test.test_xxtestfuzz\",\n    \"test.test_yield_from\",\n    \"test.test_zipapp\",\n    \"test.test_zipfile\",\n    \"test.test_zipfile64\",\n    \"test.test_zipimport\",\n    \"test.test_zipimport_support\",\n    \"test.test_zlib\",\n    \"test.testcodec\",\n    \"test.tf_inherit_check\",\n    \"test.threaded_import_hangers\",\n    \"test.time_hashlib\",\n    \"test.tracedmodules\",\n    \"test.tracedmodules.testmod\",\n    \"test.win_console_handler\",\n    \"test.xmltests\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.runtktests\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_functions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsealable\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.errors.dom\",\n    \"xml.parsers.expat.errors.etree\",\n    \"xml.parsers.expat.errors.parsers\",\n    \"xml.parsers.expat.errors.sax\",\n    \"xml.parsers.expat.model\",\n    \"xml.parsers.expat.model.dom\",\n    \"xml.parsers.expat.model.etree\",\n    \"xml.parsers.expat.model.parsers\",\n    \"xml.parsers.expat.model.sax\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxlimited\",\n    \"xxsubtype\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.8\": [\n    \"__future__\",\n    \"__main__\",\n    \"_abc\",\n    \"_ast\",\n    \"_asyncio\",\n    \"_bisect\",\n    \"_blake2\",\n    \"_bootlocale\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_contextvars\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_ctypes_test\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_dummy_thread\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_pickle\",\n    \"_posixshmem\",\n    \"_posixsubprocess\",\n    \"_py_abc\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_queue\",\n    \"_random\",\n    \"_sha1\",\n    \"_sha256\",\n    \"_sha3\",\n    \"_sha512\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_statistics\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_testbuffer\",\n    \"_testcapi\",\n    \"_testimportmultiple\",\n    \"_testinternalcapi\",\n    \"_testmultiphase\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tracemalloc\",\n    \"_uuid\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"_xxsubinterpreters\",\n    \"_xxtestfuzz\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.__main__\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.exceptions\",\n    \"asyncio.format_helpers\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.runners\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.staggered\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.tasks\",\n    \"asyncio.transports\",\n    \"asyncio.trsock\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"contextvars\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._aix\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"dataclasses\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils._msvccompiler\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.tests\",\n    \"distutils.tests.support\",\n    \"distutils.tests.test_archive_util\",\n    \"distutils.tests.test_bdist\",\n    \"distutils.tests.test_bdist_dumb\",\n    \"distutils.tests.test_bdist_msi\",\n    \"distutils.tests.test_bdist_rpm\",\n    \"distutils.tests.test_bdist_wininst\",\n    \"distutils.tests.test_build\",\n    \"distutils.tests.test_build_clib\",\n    \"distutils.tests.test_build_ext\",\n    \"distutils.tests.test_build_py\",\n    \"distutils.tests.test_build_scripts\",\n    \"distutils.tests.test_check\",\n    \"distutils.tests.test_clean\",\n    \"distutils.tests.test_cmd\",\n    \"distutils.tests.test_config\",\n    \"distutils.tests.test_config_cmd\",\n    \"distutils.tests.test_core\",\n    \"distutils.tests.test_cygwinccompiler\",\n    \"distutils.tests.test_dep_util\",\n    \"distutils.tests.test_dir_util\",\n    \"distutils.tests.test_dist\",\n    \"distutils.tests.test_extension\",\n    \"distutils.tests.test_file_util\",\n    \"distutils.tests.test_filelist\",\n    \"distutils.tests.test_install\",\n    \"distutils.tests.test_install_data\",\n    \"distutils.tests.test_install_headers\",\n    \"distutils.tests.test_install_lib\",\n    \"distutils.tests.test_install_scripts\",\n    \"distutils.tests.test_log\",\n    \"distutils.tests.test_msvc9compiler\",\n    \"distutils.tests.test_msvccompiler\",\n    \"distutils.tests.test_register\",\n    \"distutils.tests.test_sdist\",\n    \"distutils.tests.test_spawn\",\n    \"distutils.tests.test_sysconfig\",\n    \"distutils.tests.test_text_file\",\n    \"distutils.tests.test_unixccompiler\",\n    \"distutils.tests.test_upload\",\n    \"distutils.tests.test_util\",\n    \"distutils.tests.test_version\",\n    \"distutils.tests.test_versionpredicate\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"dummy_threading\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_centeuro\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.__main__\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.format\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_format\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_sidebar\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.sidebar\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.metadata\",\n    \"importlib.resources\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.data.bom\",\n    \"lib2to3.tests.data.crlf\",\n    \"lib2to3.tests.data.different_encoding\",\n    \"lib2to3.tests.data.false_encoding\",\n    \"lib2to3.tests.data.fixers.bad_order\",\n    \"lib2to3.tests.data.fixers.myfixes\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_explicit\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_first\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_last\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_parrot\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_preorder\",\n    \"lib2to3.tests.data.fixers.no_fixer_cls\",\n    \"lib2to3.tests.data.fixers.parrot_example\",\n    \"lib2to3.tests.data.infinite_recursion\",\n    \"lib2to3.tests.data.py2_test_grammar\",\n    \"lib2to3.tests.data.py3_test_grammar\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.resource_tracker\",\n    \"multiprocessing.shared_memory\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.backup\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.__main__\",\n    \"test._test_multiprocessing\",\n    \"test.ann_module\",\n    \"test.ann_module2\",\n    \"test.ann_module3\",\n    \"test.audiotests\",\n    \"test.audit-tests\",\n    \"test.autotest\",\n    \"test.bad_coding\",\n    \"test.bad_coding2\",\n    \"test.bad_getattr\",\n    \"test.bad_getattr2\",\n    \"test.bad_getattr3\",\n    \"test.badsyntax_3131\",\n    \"test.badsyntax_future10\",\n    \"test.badsyntax_future3\",\n    \"test.badsyntax_future4\",\n    \"test.badsyntax_future5\",\n    \"test.badsyntax_future6\",\n    \"test.badsyntax_future7\",\n    \"test.badsyntax_future8\",\n    \"test.badsyntax_future9\",\n    \"test.badsyntax_pep3120\",\n    \"test.bisect_cmd\",\n    \"test.bytecode_helper\",\n    \"test.coding20731\",\n    \"test.curses_tests\",\n    \"test.dataclass_module_1\",\n    \"test.dataclass_module_1_str\",\n    \"test.dataclass_module_2\",\n    \"test.dataclass_module_2_str\",\n    \"test.dataclass_textanno\",\n    \"test.datetimetester\",\n    \"test.dis_module\",\n    \"test.doctest_aliases\",\n    \"test.double_const\",\n    \"test.dtracedata.call_stack\",\n    \"test.dtracedata.gc\",\n    \"test.dtracedata.instance\",\n    \"test.dtracedata.line\",\n    \"test.eintrdata.eintr_tester\",\n    \"test.encoded_modules\",\n    \"test.encoded_modules.module_iso_8859_1\",\n    \"test.encoded_modules.module_koi8_r\",\n    \"test.final_a\",\n    \"test.final_b\",\n    \"test.fork_wait\",\n    \"test.future_test1\",\n    \"test.future_test2\",\n    \"test.gdb_sample\",\n    \"test.good_getattr\",\n    \"test.imp_dummy\",\n    \"test.inspect_fodder\",\n    \"test.inspect_fodder2\",\n    \"test.libregrtest\",\n    \"test.libregrtest.cmdline\",\n    \"test.libregrtest.main\",\n    \"test.libregrtest.pgo\",\n    \"test.libregrtest.refleak\",\n    \"test.libregrtest.runtest\",\n    \"test.libregrtest.runtest_mp\",\n    \"test.libregrtest.save_env\",\n    \"test.libregrtest.setup\",\n    \"test.libregrtest.utils\",\n    \"test.libregrtest.win_utils\",\n    \"test.list_tests\",\n    \"test.lock_tests\",\n    \"test.make_ssl_certs\",\n    \"test.mapping_tests\",\n    \"test.memory_watchdog\",\n    \"test.mock_socket\",\n    \"test.mod_generics_cache\",\n    \"test.mp_fork_bomb\",\n    \"test.mp_preload\",\n    \"test.multibytecodec_support\",\n    \"test.outstanding_bugs\",\n    \"test.pickletester\",\n    \"test.profilee\",\n    \"test.pyclbr_input\",\n    \"test.pydoc_mod\",\n    \"test.pydocfodder\",\n    \"test.pythoninfo\",\n    \"test.re_tests\",\n    \"test.regrtest\",\n    \"test.relimport\",\n    \"test.reperf\",\n    \"test.sample_doctest\",\n    \"test.sample_doctest_no_docstrings\",\n    \"test.sample_doctest_no_doctests\",\n    \"test.seq_tests\",\n    \"test.signalinterproctester\",\n    \"test.sortperf\",\n    \"test.ssl_servers\",\n    \"test.ssltests\",\n    \"test.string_tests\",\n    \"test.subprocessdata.fd_status\",\n    \"test.subprocessdata.input_reader\",\n    \"test.subprocessdata.qcat\",\n    \"test.subprocessdata.qgrep\",\n    \"test.subprocessdata.sigchild_ignore\",\n    \"test.support\",\n    \"test.support.script_helper\",\n    \"test.support.testresult\",\n    \"test.test___all__\",\n    \"test.test___future__\",\n    \"test.test__locale\",\n    \"test.test__opcode\",\n    \"test.test__osx_support\",\n    \"test.test__xxsubinterpreters\",\n    \"test.test_abc\",\n    \"test.test_abstract_numbers\",\n    \"test.test_aifc\",\n    \"test.test_argparse\",\n    \"test.test_array\",\n    \"test.test_asdl_parser\",\n    \"test.test_ast\",\n    \"test.test_asyncgen\",\n    \"test.test_asynchat\",\n    \"test.test_asyncio\",\n    \"test.test_asyncio.__main__\",\n    \"test.test_asyncio.echo\",\n    \"test.test_asyncio.echo2\",\n    \"test.test_asyncio.echo3\",\n    \"test.test_asyncio.functional\",\n    \"test.test_asyncio.test_asyncio_waitfor\",\n    \"test.test_asyncio.test_base_events\",\n    \"test.test_asyncio.test_buffered_proto\",\n    \"test.test_asyncio.test_context\",\n    \"test.test_asyncio.test_events\",\n    \"test.test_asyncio.test_futures\",\n    \"test.test_asyncio.test_futures2\",\n    \"test.test_asyncio.test_locks\",\n    \"test.test_asyncio.test_pep492\",\n    \"test.test_asyncio.test_proactor_events\",\n    \"test.test_asyncio.test_protocols\",\n    \"test.test_asyncio.test_queues\",\n    \"test.test_asyncio.test_runners\",\n    \"test.test_asyncio.test_selector_events\",\n    \"test.test_asyncio.test_sendfile\",\n    \"test.test_asyncio.test_server\",\n    \"test.test_asyncio.test_sock_lowlevel\",\n    \"test.test_asyncio.test_sslproto\",\n    \"test.test_asyncio.test_streams\",\n    \"test.test_asyncio.test_subprocess\",\n    \"test.test_asyncio.test_tasks\",\n    \"test.test_asyncio.test_transports\",\n    \"test.test_asyncio.test_unix_events\",\n    \"test.test_asyncio.test_windows_events\",\n    \"test.test_asyncio.test_windows_utils\",\n    \"test.test_asyncio.utils\",\n    \"test.test_asyncore\",\n    \"test.test_atexit\",\n    \"test.test_audioop\",\n    \"test.test_audit\",\n    \"test.test_augassign\",\n    \"test.test_base64\",\n    \"test.test_baseexception\",\n    \"test.test_bdb\",\n    \"test.test_bigaddrspace\",\n    \"test.test_bigmem\",\n    \"test.test_binascii\",\n    \"test.test_binhex\",\n    \"test.test_binop\",\n    \"test.test_bisect\",\n    \"test.test_bool\",\n    \"test.test_buffer\",\n    \"test.test_bufio\",\n    \"test.test_builtin\",\n    \"test.test_bytes\",\n    \"test.test_bz2\",\n    \"test.test_c_locale_coercion\",\n    \"test.test_calendar\",\n    \"test.test_call\",\n    \"test.test_capi\",\n    \"test.test_cgi\",\n    \"test.test_cgitb\",\n    \"test.test_charmapcodec\",\n    \"test.test_class\",\n    \"test.test_clinic\",\n    \"test.test_cmath\",\n    \"test.test_cmd\",\n    \"test.test_cmd_line\",\n    \"test.test_cmd_line_script\",\n    \"test.test_code\",\n    \"test.test_code_module\",\n    \"test.test_codeccallbacks\",\n    \"test.test_codecencodings_cn\",\n    \"test.test_codecencodings_hk\",\n    \"test.test_codecencodings_iso2022\",\n    \"test.test_codecencodings_jp\",\n    \"test.test_codecencodings_kr\",\n    \"test.test_codecencodings_tw\",\n    \"test.test_codecmaps_cn\",\n    \"test.test_codecmaps_hk\",\n    \"test.test_codecmaps_jp\",\n    \"test.test_codecmaps_kr\",\n    \"test.test_codecmaps_tw\",\n    \"test.test_codecs\",\n    \"test.test_codeop\",\n    \"test.test_collections\",\n    \"test.test_colorsys\",\n    \"test.test_compare\",\n    \"test.test_compile\",\n    \"test.test_compileall\",\n    \"test.test_complex\",\n    \"test.test_concurrent_futures\",\n    \"test.test_configparser\",\n    \"test.test_contains\",\n    \"test.test_context\",\n    \"test.test_contextlib\",\n    \"test.test_contextlib_async\",\n    \"test.test_copy\",\n    \"test.test_copyreg\",\n    \"test.test_coroutines\",\n    \"test.test_cprofile\",\n    \"test.test_crashers\",\n    \"test.test_crypt\",\n    \"test.test_csv\",\n    \"test.test_ctypes\",\n    \"test.test_curses\",\n    \"test.test_dataclasses\",\n    \"test.test_datetime\",\n    \"test.test_dbm\",\n    \"test.test_dbm_dumb\",\n    \"test.test_dbm_gnu\",\n    \"test.test_dbm_ndbm\",\n    \"test.test_decimal\",\n    \"test.test_decorators\",\n    \"test.test_defaultdict\",\n    \"test.test_deque\",\n    \"test.test_descr\",\n    \"test.test_descrtut\",\n    \"test.test_devpoll\",\n    \"test.test_dict\",\n    \"test.test_dict_version\",\n    \"test.test_dictcomps\",\n    \"test.test_dictviews\",\n    \"test.test_difflib\",\n    \"test.test_dis\",\n    \"test.test_distutils\",\n    \"test.test_doctest\",\n    \"test.test_doctest2\",\n    \"test.test_docxmlrpc\",\n    \"test.test_dtrace\",\n    \"test.test_dummy_thread\",\n    \"test.test_dummy_threading\",\n    \"test.test_dynamic\",\n    \"test.test_dynamicclassattribute\",\n    \"test.test_eintr\",\n    \"test.test_email\",\n    \"test.test_email.__main__\",\n    \"test.test_email.test__encoded_words\",\n    \"test.test_email.test__header_value_parser\",\n    \"test.test_email.test_asian_codecs\",\n    \"test.test_email.test_contentmanager\",\n    \"test.test_email.test_defect_handling\",\n    \"test.test_email.test_email\",\n    \"test.test_email.test_generator\",\n    \"test.test_email.test_headerregistry\",\n    \"test.test_email.test_inversion\",\n    \"test.test_email.test_message\",\n    \"test.test_email.test_parser\",\n    \"test.test_email.test_pickleable\",\n    \"test.test_email.test_policy\",\n    \"test.test_email.test_utils\",\n    \"test.test_email.torture_test\",\n    \"test.test_embed\",\n    \"test.test_ensurepip\",\n    \"test.test_enum\",\n    \"test.test_enumerate\",\n    \"test.test_eof\",\n    \"test.test_epoll\",\n    \"test.test_errno\",\n    \"test.test_exception_hierarchy\",\n    \"test.test_exception_variations\",\n    \"test.test_exceptions\",\n    \"test.test_extcall\",\n    \"test.test_faulthandler\",\n    \"test.test_fcntl\",\n    \"test.test_file\",\n    \"test.test_file_eintr\",\n    \"test.test_filecmp\",\n    \"test.test_fileinput\",\n    \"test.test_fileio\",\n    \"test.test_finalization\",\n    \"test.test_float\",\n    \"test.test_flufl\",\n    \"test.test_fnmatch\",\n    \"test.test_fork1\",\n    \"test.test_format\",\n    \"test.test_fractions\",\n    \"test.test_frame\",\n    \"test.test_frozen\",\n    \"test.test_fstring\",\n    \"test.test_ftplib\",\n    \"test.test_funcattrs\",\n    \"test.test_functools\",\n    \"test.test_future\",\n    \"test.test_future3\",\n    \"test.test_future4\",\n    \"test.test_future5\",\n    \"test.test_gc\",\n    \"test.test_gdb\",\n    \"test.test_generator_stop\",\n    \"test.test_generators\",\n    \"test.test_genericclass\",\n    \"test.test_genericpath\",\n    \"test.test_genexps\",\n    \"test.test_getargs2\",\n    \"test.test_getopt\",\n    \"test.test_getpass\",\n    \"test.test_gettext\",\n    \"test.test_glob\",\n    \"test.test_global\",\n    \"test.test_grammar\",\n    \"test.test_grp\",\n    \"test.test_gzip\",\n    \"test.test_hash\",\n    \"test.test_hashlib\",\n    \"test.test_heapq\",\n    \"test.test_hmac\",\n    \"test.test_html\",\n    \"test.test_htmlparser\",\n    \"test.test_http_cookiejar\",\n    \"test.test_http_cookies\",\n    \"test.test_httplib\",\n    \"test.test_httpservers\",\n    \"test.test_idle\",\n    \"test.test_imaplib\",\n    \"test.test_imghdr\",\n    \"test.test_imp\",\n    \"test.test_import\",\n    \"test.test_import.__main__\",\n    \"test.test_import.data.circular_imports.basic\",\n    \"test.test_import.data.circular_imports.basic2\",\n    \"test.test_import.data.circular_imports.binding\",\n    \"test.test_import.data.circular_imports.binding2\",\n    \"test.test_import.data.circular_imports.from_cycle1\",\n    \"test.test_import.data.circular_imports.from_cycle2\",\n    \"test.test_import.data.circular_imports.indirect\",\n    \"test.test_import.data.circular_imports.rebinding\",\n    \"test.test_import.data.circular_imports.rebinding2\",\n    \"test.test_import.data.circular_imports.source\",\n    \"test.test_import.data.circular_imports.subpackage\",\n    \"test.test_import.data.circular_imports.subpkg.subpackage2\",\n    \"test.test_import.data.circular_imports.subpkg.util\",\n    \"test.test_import.data.circular_imports.use\",\n    \"test.test_import.data.circular_imports.util\",\n    \"test.test_import.data.package\",\n    \"test.test_import.data.package.submodule\",\n    \"test.test_import.data.package2.submodule1\",\n    \"test.test_import.data.package2.submodule2\",\n    \"test.test_importlib\",\n    \"test.test_importlib.__main__\",\n    \"test.test_importlib.abc\",\n    \"test.test_importlib.builtin\",\n    \"test.test_importlib.builtin.__main__\",\n    \"test.test_importlib.builtin.test_finder\",\n    \"test.test_importlib.builtin.test_loader\",\n    \"test.test_importlib.data\",\n    \"test.test_importlib.data01\",\n    \"test.test_importlib.data01.subdirectory\",\n    \"test.test_importlib.data02\",\n    \"test.test_importlib.data02.one\",\n    \"test.test_importlib.data02.two\",\n    \"test.test_importlib.data03\",\n    \"test.test_importlib.data03.namespace.portion1\",\n    \"test.test_importlib.data03.namespace.portion2\",\n    \"test.test_importlib.extension\",\n    \"test.test_importlib.extension.__main__\",\n    \"test.test_importlib.extension.test_case_sensitivity\",\n    \"test.test_importlib.extension.test_finder\",\n    \"test.test_importlib.extension.test_loader\",\n    \"test.test_importlib.extension.test_path_hook\",\n    \"test.test_importlib.fixtures\",\n    \"test.test_importlib.frozen\",\n    \"test.test_importlib.frozen.__main__\",\n    \"test.test_importlib.frozen.test_finder\",\n    \"test.test_importlib.frozen.test_loader\",\n    \"test.test_importlib.import_\",\n    \"test.test_importlib.import_.__main__\",\n    \"test.test_importlib.import_.test___loader__\",\n    \"test.test_importlib.import_.test___package__\",\n    \"test.test_importlib.import_.test_api\",\n    \"test.test_importlib.import_.test_caching\",\n    \"test.test_importlib.import_.test_fromlist\",\n    \"test.test_importlib.import_.test_meta_path\",\n    \"test.test_importlib.import_.test_packages\",\n    \"test.test_importlib.import_.test_path\",\n    \"test.test_importlib.import_.test_relative_imports\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.one\",\n    \"test.test_importlib.namespace_pkgs.both_portions.foo.two\",\n    \"test.test_importlib.namespace_pkgs.module_and_namespace_package.a_test\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo\",\n    \"test.test_importlib.namespace_pkgs.not_a_namespace_pkg.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion1.foo.one\",\n    \"test.test_importlib.namespace_pkgs.portion2.foo.two\",\n    \"test.test_importlib.namespace_pkgs.project1.parent.child.one\",\n    \"test.test_importlib.namespace_pkgs.project2.parent.child.two\",\n    \"test.test_importlib.namespace_pkgs.project3.parent.child.three\",\n    \"test.test_importlib.source\",\n    \"test.test_importlib.source.__main__\",\n    \"test.test_importlib.source.test_case_sensitivity\",\n    \"test.test_importlib.source.test_file_loader\",\n    \"test.test_importlib.source.test_finder\",\n    \"test.test_importlib.source.test_path_hook\",\n    \"test.test_importlib.source.test_source_encoding\",\n    \"test.test_importlib.stubs\",\n    \"test.test_importlib.test_abc\",\n    \"test.test_importlib.test_api\",\n    \"test.test_importlib.test_lazy\",\n    \"test.test_importlib.test_locks\",\n    \"test.test_importlib.test_main\",\n    \"test.test_importlib.test_metadata_api\",\n    \"test.test_importlib.test_namespace_pkgs\",\n    \"test.test_importlib.test_open\",\n    \"test.test_importlib.test_path\",\n    \"test.test_importlib.test_read\",\n    \"test.test_importlib.test_resource\",\n    \"test.test_importlib.test_spec\",\n    \"test.test_importlib.test_util\",\n    \"test.test_importlib.test_windows\",\n    \"test.test_importlib.test_zip\",\n    \"test.test_importlib.util\",\n    \"test.test_importlib.zipdata01\",\n    \"test.test_importlib.zipdata02\",\n    \"test.test_index\",\n    \"test.test_inspect\",\n    \"test.test_int\",\n    \"test.test_int_literal\",\n    \"test.test_io\",\n    \"test.test_ioctl\",\n    \"test.test_ipaddress\",\n    \"test.test_isinstance\",\n    \"test.test_iter\",\n    \"test.test_iterlen\",\n    \"test.test_itertools\",\n    \"test.test_json\",\n    \"test.test_json.__main__\",\n    \"test.test_json.test_decode\",\n    \"test.test_json.test_default\",\n    \"test.test_json.test_dump\",\n    \"test.test_json.test_encode_basestring_ascii\",\n    \"test.test_json.test_enum\",\n    \"test.test_json.test_fail\",\n    \"test.test_json.test_float\",\n    \"test.test_json.test_indent\",\n    \"test.test_json.test_pass1\",\n    \"test.test_json.test_pass2\",\n    \"test.test_json.test_pass3\",\n    \"test.test_json.test_recursion\",\n    \"test.test_json.test_scanstring\",\n    \"test.test_json.test_separators\",\n    \"test.test_json.test_speedups\",\n    \"test.test_json.test_tool\",\n    \"test.test_json.test_unicode\",\n    \"test.test_keyword\",\n    \"test.test_keywordonlyarg\",\n    \"test.test_kqueue\",\n    \"test.test_largefile\",\n    \"test.test_lib2to3\",\n    \"test.test_linecache\",\n    \"test.test_list\",\n    \"test.test_listcomps\",\n    \"test.test_lltrace\",\n    \"test.test_locale\",\n    \"test.test_logging\",\n    \"test.test_long\",\n    \"test.test_longexp\",\n    \"test.test_lzma\",\n    \"test.test_mailbox\",\n    \"test.test_mailcap\",\n    \"test.test_marshal\",\n    \"test.test_math\",\n    \"test.test_memoryio\",\n    \"test.test_memoryview\",\n    \"test.test_metaclass\",\n    \"test.test_mimetypes\",\n    \"test.test_minidom\",\n    \"test.test_mmap\",\n    \"test.test_module\",\n    \"test.test_modulefinder\",\n    \"test.test_msilib\",\n    \"test.test_multibytecodec\",\n    \"test.test_multiprocessing_fork\",\n    \"test.test_multiprocessing_forkserver\",\n    \"test.test_multiprocessing_main_handling\",\n    \"test.test_multiprocessing_spawn\",\n    \"test.test_named_expressions\",\n    \"test.test_netrc\",\n    \"test.test_nis\",\n    \"test.test_nntplib\",\n    \"test.test_normalization\",\n    \"test.test_ntpath\",\n    \"test.test_numeric_tower\",\n    \"test.test_opcodes\",\n    \"test.test_openpty\",\n    \"test.test_operator\",\n    \"test.test_optparse\",\n    \"test.test_ordered_dict\",\n    \"test.test_os\",\n    \"test.test_ossaudiodev\",\n    \"test.test_osx_env\",\n    \"test.test_parser\",\n    \"test.test_pathlib\",\n    \"test.test_pdb\",\n    \"test.test_peepholer\",\n    \"test.test_pickle\",\n    \"test.test_picklebuffer\",\n    \"test.test_pickletools\",\n    \"test.test_pipes\",\n    \"test.test_pkg\",\n    \"test.test_pkgimport\",\n    \"test.test_pkgutil\",\n    \"test.test_platform\",\n    \"test.test_plistlib\",\n    \"test.test_poll\",\n    \"test.test_popen\",\n    \"test.test_poplib\",\n    \"test.test_positional_only_arg\",\n    \"test.test_posix\",\n    \"test.test_posixpath\",\n    \"test.test_pow\",\n    \"test.test_pprint\",\n    \"test.test_print\",\n    \"test.test_profile\",\n    \"test.test_property\",\n    \"test.test_pstats\",\n    \"test.test_pty\",\n    \"test.test_pulldom\",\n    \"test.test_pwd\",\n    \"test.test_py_compile\",\n    \"test.test_pyclbr\",\n    \"test.test_pydoc\",\n    \"test.test_pyexpat\",\n    \"test.test_queue\",\n    \"test.test_quopri\",\n    \"test.test_raise\",\n    \"test.test_random\",\n    \"test.test_range\",\n    \"test.test_re\",\n    \"test.test_readline\",\n    \"test.test_regrtest\",\n    \"test.test_repl\",\n    \"test.test_reprlib\",\n    \"test.test_resource\",\n    \"test.test_richcmp\",\n    \"test.test_rlcompleter\",\n    \"test.test_robotparser\",\n    \"test.test_runpy\",\n    \"test.test_sax\",\n    \"test.test_sched\",\n    \"test.test_scope\",\n    \"test.test_script_helper\",\n    \"test.test_secrets\",\n    \"test.test_select\",\n    \"test.test_selectors\",\n    \"test.test_set\",\n    \"test.test_setcomps\",\n    \"test.test_shelve\",\n    \"test.test_shlex\",\n    \"test.test_shutil\",\n    \"test.test_signal\",\n    \"test.test_site\",\n    \"test.test_slice\",\n    \"test.test_smtpd\",\n    \"test.test_smtplib\",\n    \"test.test_smtpnet\",\n    \"test.test_sndhdr\",\n    \"test.test_socket\",\n    \"test.test_socketserver\",\n    \"test.test_sort\",\n    \"test.test_source_encoding\",\n    \"test.test_spwd\",\n    \"test.test_sqlite\",\n    \"test.test_ssl\",\n    \"test.test_startfile\",\n    \"test.test_stat\",\n    \"test.test_statistics\",\n    \"test.test_strftime\",\n    \"test.test_string\",\n    \"test.test_string_literals\",\n    \"test.test_stringprep\",\n    \"test.test_strptime\",\n    \"test.test_strtod\",\n    \"test.test_struct\",\n    \"test.test_structmembers\",\n    \"test.test_structseq\",\n    \"test.test_subclassinit\",\n    \"test.test_subprocess\",\n    \"test.test_sunau\",\n    \"test.test_sundry\",\n    \"test.test_super\",\n    \"test.test_support\",\n    \"test.test_symbol\",\n    \"test.test_symtable\",\n    \"test.test_syntax\",\n    \"test.test_sys\",\n    \"test.test_sys_setprofile\",\n    \"test.test_sys_settrace\",\n    \"test.test_sysconfig\",\n    \"test.test_syslog\",\n    \"test.test_tabnanny\",\n    \"test.test_tarfile\",\n    \"test.test_tcl\",\n    \"test.test_telnetlib\",\n    \"test.test_tempfile\",\n    \"test.test_textwrap\",\n    \"test.test_thread\",\n    \"test.test_threaded_import\",\n    \"test.test_threadedtempfile\",\n    \"test.test_threading\",\n    \"test.test_threading_local\",\n    \"test.test_threadsignals\",\n    \"test.test_time\",\n    \"test.test_timeit\",\n    \"test.test_timeout\",\n    \"test.test_tix\",\n    \"test.test_tk\",\n    \"test.test_tokenize\",\n    \"test.test_tools\",\n    \"test.test_tools.__main__\",\n    \"test.test_tools.test_fixcid\",\n    \"test.test_tools.test_gprof2html\",\n    \"test.test_tools.test_i18n\",\n    \"test.test_tools.test_lll\",\n    \"test.test_tools.test_md5sum\",\n    \"test.test_tools.test_pathfix\",\n    \"test.test_tools.test_pdeps\",\n    \"test.test_tools.test_pindent\",\n    \"test.test_tools.test_reindent\",\n    \"test.test_tools.test_sundry\",\n    \"test.test_tools.test_unparse\",\n    \"test.test_trace\",\n    \"test.test_traceback\",\n    \"test.test_tracemalloc\",\n    \"test.test_ttk_guionly\",\n    \"test.test_ttk_textonly\",\n    \"test.test_tuple\",\n    \"test.test_turtle\",\n    \"test.test_type_comments\",\n    \"test.test_typechecks\",\n    \"test.test_types\",\n    \"test.test_typing\",\n    \"test.test_ucn\",\n    \"test.test_unary\",\n    \"test.test_unicode\",\n    \"test.test_unicode_file\",\n    \"test.test_unicode_file_functions\",\n    \"test.test_unicode_identifiers\",\n    \"test.test_unicodedata\",\n    \"test.test_unittest\",\n    \"test.test_univnewlines\",\n    \"test.test_unpack\",\n    \"test.test_unpack_ex\",\n    \"test.test_urllib\",\n    \"test.test_urllib2\",\n    \"test.test_urllib2_localnet\",\n    \"test.test_urllib2net\",\n    \"test.test_urllib_response\",\n    \"test.test_urllibnet\",\n    \"test.test_urlparse\",\n    \"test.test_userdict\",\n    \"test.test_userlist\",\n    \"test.test_userstring\",\n    \"test.test_utf8_mode\",\n    \"test.test_utf8source\",\n    \"test.test_uu\",\n    \"test.test_uuid\",\n    \"test.test_venv\",\n    \"test.test_wait3\",\n    \"test.test_wait4\",\n    \"test.test_warnings\",\n    \"test.test_warnings.__main__\",\n    \"test.test_warnings.data.import_warning\",\n    \"test.test_warnings.data.stacklevel\",\n    \"test.test_wave\",\n    \"test.test_weakref\",\n    \"test.test_weakset\",\n    \"test.test_webbrowser\",\n    \"test.test_winconsoleio\",\n    \"test.test_winreg\",\n    \"test.test_winsound\",\n    \"test.test_with\",\n    \"test.test_wsgiref\",\n    \"test.test_xdrlib\",\n    \"test.test_xml_dom_minicompat\",\n    \"test.test_xml_etree\",\n    \"test.test_xml_etree_c\",\n    \"test.test_xmlrpc\",\n    \"test.test_xmlrpc_net\",\n    \"test.test_xxtestfuzz\",\n    \"test.test_yield_from\",\n    \"test.test_zipapp\",\n    \"test.test_zipfile\",\n    \"test.test_zipfile64\",\n    \"test.test_zipimport\",\n    \"test.test_zipimport_support\",\n    \"test.test_zlib\",\n    \"test.testcodec\",\n    \"test.tf_inherit_check\",\n    \"test.threaded_import_hangers\",\n    \"test.time_hashlib\",\n    \"test.tracedmodules\",\n    \"test.tracedmodules.testmod\",\n    \"test.win_console_handler\",\n    \"test.xmltests\",\n    \"test.ziptestdata.testdata_module_inside_zip\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.runtktests\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_colorchooser\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_simpledialog\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_functions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"typing.io\",\n    \"typing.re\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest.async_case\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_async_case\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testasync\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsealable\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.errors.dom\",\n    \"xml.parsers.expat.errors.etree\",\n    \"xml.parsers.expat.errors.parsers\",\n    \"xml.parsers.expat.errors.sax\",\n    \"xml.parsers.expat.model\",\n    \"xml.parsers.expat.model.dom\",\n    \"xml.parsers.expat.model.etree\",\n    \"xml.parsers.expat.model.parsers\",\n    \"xml.parsers.expat.model.sax\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxlimited\",\n    \"xxsubtype\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\"\n  ],\n  \"3.9\": [\n    \"__future__\",\n    \"__main__\",\n    \"__phello__.foo\",\n    \"_abc\",\n    \"_aix_support\",\n    \"_ast\",\n    \"_bootlocale\",\n    \"_bootsubprocess\",\n    \"_codecs\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_crypt\",\n    \"_functools\",\n    \"_hashlib\",\n    \"_imp\",\n    \"_io\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_markupbase\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_peg_parser\",\n    \"_posixsubprocess\",\n    \"_py_abc\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_random\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_string\",\n    \"_strptime\",\n    \"_symtable\",\n    \"_sysconfigdata_x86_64_conda_cos6_linux_gnu\",\n    \"_sysconfigdata_x86_64_conda_linux_gnu\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tracemalloc\",\n    \"_uuid\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.__main__\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.exceptions\",\n    \"asyncio.format_helpers\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.runners\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.staggered\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.tasks\",\n    \"asyncio.threads\",\n    \"asyncio.transports\",\n    \"asyncio.trsock\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"contextvars\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._aix\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"dataclasses\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils._msvccompiler\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_msi\",\n    \"distutils.command.bdist_packager\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.bdist_wininst\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.tests\",\n    \"distutils.tests.support\",\n    \"distutils.tests.test_archive_util\",\n    \"distutils.tests.test_bdist\",\n    \"distutils.tests.test_bdist_dumb\",\n    \"distutils.tests.test_bdist_msi\",\n    \"distutils.tests.test_bdist_rpm\",\n    \"distutils.tests.test_bdist_wininst\",\n    \"distutils.tests.test_build\",\n    \"distutils.tests.test_build_clib\",\n    \"distutils.tests.test_build_ext\",\n    \"distutils.tests.test_build_py\",\n    \"distutils.tests.test_build_scripts\",\n    \"distutils.tests.test_check\",\n    \"distutils.tests.test_clean\",\n    \"distutils.tests.test_cmd\",\n    \"distutils.tests.test_config\",\n    \"distutils.tests.test_config_cmd\",\n    \"distutils.tests.test_core\",\n    \"distutils.tests.test_cygwinccompiler\",\n    \"distutils.tests.test_dep_util\",\n    \"distutils.tests.test_dir_util\",\n    \"distutils.tests.test_dist\",\n    \"distutils.tests.test_extension\",\n    \"distutils.tests.test_file_util\",\n    \"distutils.tests.test_filelist\",\n    \"distutils.tests.test_install\",\n    \"distutils.tests.test_install_data\",\n    \"distutils.tests.test_install_headers\",\n    \"distutils.tests.test_install_lib\",\n    \"distutils.tests.test_install_scripts\",\n    \"distutils.tests.test_log\",\n    \"distutils.tests.test_msvc9compiler\",\n    \"distutils.tests.test_msvccompiler\",\n    \"distutils.tests.test_register\",\n    \"distutils.tests.test_sdist\",\n    \"distutils.tests.test_spawn\",\n    \"distutils.tests.test_sysconfig\",\n    \"distutils.tests.test_text_file\",\n    \"distutils.tests.test_unixccompiler\",\n    \"distutils.tests.test_upload\",\n    \"distutils.tests.test_util\",\n    \"distutils.tests.test_version\",\n    \"distutils.tests.test_versionpredicate\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._bundled\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"formatter\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"graphlib\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.format\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_format\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_sidebar\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.sidebar\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib._common\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.metadata\",\n    \"importlib.resources\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib.libpython3\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.data.bom\",\n    \"lib2to3.tests.data.crlf\",\n    \"lib2to3.tests.data.different_encoding\",\n    \"lib2to3.tests.data.false_encoding\",\n    \"lib2to3.tests.data.fixers.bad_order\",\n    \"lib2to3.tests.data.fixers.myfixes\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_explicit\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_first\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_last\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_parrot\",\n    \"lib2to3.tests.data.fixers.myfixes.fix_preorder\",\n    \"lib2to3.tests.data.fixers.no_fixer_cls\",\n    \"lib2to3.tests.data.fixers.parrot_example\",\n    \"lib2to3.tests.data.infinite_recursion\",\n    \"lib2to3.tests.data.py2_test_grammar\",\n    \"lib2to3.tests.data.py3_test_grammar\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.resource_tracker\",\n    \"multiprocessing.shared_memory\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"parser\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.backup\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symbol\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"test\",\n    \"test.__main__\",\n    \"test._test_multiprocessing\",\n    \"test._typed_dict_helper\",\n    \"test.ann_module\",\n    \"test.ann_module2\",\n    \"test.ann_module3\",\n    \"test.ann_module5\",\n    \"test.ann_module6\",\n    \"test.ann_module7\",\n    \"test.audiotests\",\n    \"test.audit-tests\",\n    \"test.autotest\",\n    \"test.bad_coding\",\n    \"test.bad_coding2\",\n    \"test.bad_getattr\",\n    \"test.bad_getattr2\",\n    \"test.bad_getattr3\",\n    \"test.badsyntax_3131\",\n    \"test.badsyntax_future10\",\n    \"test.badsyntax_future3\",\n    \"test.badsyntax_future4\",\n    \"test.badsyntax_future5\",\n    \"test.badsyntax_future6\",\n    \"test.badsyntax_future7\",\n    \"test.badsyntax_future8\",\n    \"test.badsyntax_future9\",\n    \"test.badsyntax_pep3120\",\n    \"test.bisect_cmd\",\n    \"test.coding20731\",\n    \"test.curses_tests\",\n    \"test.dataclass_module_1\",\n    \"test.dataclass_module_1_str\",\n    \"test.dataclass_module_2\",\n    \"test.dataclass_module_2_str\",\n    \"test.dataclass_textanno\",\n    \"test.datetimetester\",\n    \"test.dis_module\",\n    \"test.doctest_aliases\",\n    \"test.double_const\",\n    \"test.encoded_modules\",\n    \"test.encoded_modules.module_iso_8859_1\",\n    \"test.encoded_modules.module_koi8_r\",\n    \"test.final_a\",\n    \"test.final_b\",\n    \"test.fork_wait\",\n    \"test.future_test1\",\n    \"test.future_test2\",\n    \"test.gdb_sample\",\n    \"test.good_getattr\",\n    \"test.imp_dummy\",\n    \"test.inspect_fodder\",\n    \"test.inspect_fodder2\",\n    \"test.libregrtest\",\n    \"test.libregrtest.cmdline\",\n    \"test.libregrtest.main\",\n    \"test.libregrtest.pgo\",\n    \"test.libregrtest.refleak\",\n    \"test.libregrtest.runtest\",\n    \"test.libregrtest.runtest_mp\",\n    \"test.libregrtest.save_env\",\n    \"test.libregrtest.setup\",\n    \"test.libregrtest.utils\",\n    \"test.libregrtest.win_utils\",\n    \"test.list_tests\",\n    \"test.lock_tests\",\n    \"test.make_ssl_certs\",\n    \"test.mapping_tests\",\n    \"test.memory_watchdog\",\n    \"test.mock_socket\",\n    \"test.mod_generics_cache\",\n    \"test.mp_fork_bomb\",\n    \"test.mp_preload\",\n    \"test.multibytecodec_support\",\n    \"test.pickletester\",\n    \"test.profilee\",\n    \"test.pyclbr_input\",\n    \"test.pydoc_mod\",\n    \"test.pydocfodder\",\n    \"test.pythoninfo\",\n    \"test.re_tests\",\n    \"test.regrtest\",\n    \"test.relimport\",\n    \"test.reperf\",\n    \"test.sample_doctest\",\n    \"test.sample_doctest_no_docstrings\",\n    \"test.sample_doctest_no_doctests\",\n    \"test.seq_tests\",\n    \"test.signalinterproctester\",\n    \"test.sortperf\",\n    \"test.ssl_servers\",\n    \"test.ssltests\",\n    \"test.string_tests\",\n    \"test.support\",\n    \"test.support.bytecode_helper\",\n    \"test.support.hashlib_helper\",\n    \"test.support.logging_helper\",\n    \"test.support.script_helper\",\n    \"test.support.socket_helper\",\n    \"test.support.testresult\",\n    \"test.support.warnings_helper\",\n    \"test.test___all__\",\n    \"test.test___future__\",\n    \"test.test__locale\",\n    \"test.test__opcode\",\n    \"test.test__osx_support\",\n    \"test.test__xxsubinterpreters\",\n    \"test.test_abc\",\n    \"test.test_abstract_numbers\",\n    \"test.test_aifc\",\n    \"test.test_argparse\",\n    \"test.test_array\",\n    \"test.test_asdl_parser\",\n    \"test.test_ast\",\n    \"test.test_asyncgen\",\n    \"test.test_asynchat\",\n    \"test.test_asyncio\",\n    \"test.test_asyncio.__main__\",\n    \"test.test_asyncio.echo\",\n    \"test.test_asyncio.echo2\",\n    \"test.test_asyncio.echo3\",\n    \"test.test_asyncio.functional\",\n    \"test.test_asyncio.test_base_events\",\n    \"test.test_asyncio.test_buffered_proto\",\n    \"test.test_asyncio.test_context\",\n    \"test.test_asyncio.test_events\",\n    \"test.test_asyncio.test_futures\",\n    \"test.test_asyncio.test_futures2\",\n    \"test.test_asyncio.test_locks\",\n    \"test.test_asyncio.test_pep492\",\n    \"test.test_asyncio.test_proactor_events\",\n    \"test.test_asyncio.test_protocols\",\n    \"test.test_asyncio.test_queues\",\n    \"test.test_asyncio.test_runners\",\n    \"test.test_asyncio.test_selector_events\",\n    \"test.test_asyncio.test_sendfile\",\n    \"test.test_asyncio.test_server\",\n    \"test.test_asyncio.test_sock_lowlevel\",\n    \"test.test_asyncio.test_sslproto\",\n    \"test.test_asyncio.test_streams\",\n    \"test.test_asyncio.test_subprocess\",\n    \"test.test_asyncio.test_tasks\",\n    \"test.test_asyncio.test_threads\",\n    \"test.test_asyncio.test_transports\",\n    \"test.test_asyncio.test_unix_events\",\n    \"test.test_asyncio.test_waitfor\",\n    \"test.test_asyncio.test_windows_events\",\n    \"test.test_asyncio.test_windows_utils\",\n    \"test.test_asyncio.utils\",\n    \"test.test_asyncore\",\n    \"test.test_atexit\",\n    \"test.test_audioop\",\n    \"test.test_audit\",\n    \"test.test_augassign\",\n    \"test.test_base64\",\n    \"test.test_baseexception\",\n    \"test.test_bdb\",\n    \"test.test_bigaddrspace\",\n    \"test.test_bigmem\",\n    \"test.test_binascii\",\n    \"test.test_binhex\",\n    \"test.test_binop\",\n    \"test.test_bisect\",\n    \"test.test_bool\",\n    \"test.test_buffer\",\n    \"test.test_bufio\",\n    \"test.test_builtin\",\n    \"test.test_bytes\",\n    \"test.test_bz2\",\n    \"test.test_c_locale_coercion\",\n    \"test.test_calendar\",\n    \"test.test_call\",\n    \"test.test_capi\",\n    \"test.test_cgi\",\n    \"test.test_cgitb\",\n    \"test.test_charmapcodec\",\n    \"test.test_check_c_globals\",\n    \"test.test_class\",\n    \"test.test_clinic\",\n    \"test.test_cmath\",\n    \"test.test_cmd\",\n    \"test.test_cmd_line\",\n    \"test.test_cmd_line_script\",\n    \"test.test_code\",\n    \"test.test_code_module\",\n    \"test.test_codeccallbacks\",\n    \"test.test_codecencodings_cn\",\n    \"test.test_codecencodings_hk\",\n    \"test.test_codecencodings_iso2022\",\n    \"test.test_codecencodings_jp\",\n    \"test.test_codecencodings_kr\",\n    \"test.test_codecencodings_tw\",\n    \"test.test_codecmaps_cn\",\n    \"test.test_codecmaps_hk\",\n    \"test.test_codecmaps_jp\",\n    \"test.test_codecmaps_kr\",\n    \"test.test_codecmaps_tw\",\n    \"test.test_codecs\",\n    \"test.test_codeop\",\n    \"test.test_collections\",\n    \"test.test_colorsys\",\n    \"test.test_compare\",\n    \"test.test_compile\",\n    \"test.test_compileall\",\n    \"test.test_complex\",\n    \"test.test_concurrent_futures\",\n    \"test.test_configparser\",\n    \"test.test_contains\",\n    \"test.test_context\",\n    \"test.test_contextlib\",\n    \"test.test_contextlib_async\",\n    \"test.test_copy\",\n    \"test.test_copyreg\",\n    \"test.test_coroutines\",\n    \"test.test_cprofile\",\n    \"test.test_crashers\",\n    \"test.test_crypt\",\n    \"test.test_csv\",\n    \"test.test_ctypes\",\n    \"test.test_curses\",\n    \"test.test_dataclasses\",\n    \"test.test_datetime\",\n    \"test.test_dbm\",\n    \"test.test_dbm_dumb\",\n    \"test.test_dbm_gnu\",\n    \"test.test_dbm_ndbm\",\n    \"test.test_decimal\",\n    \"test.test_decorators\",\n    \"test.test_defaultdict\",\n    \"test.test_deque\",\n    \"test.test_descr\",\n    \"test.test_descrtut\",\n    \"test.test_devpoll\",\n    \"test.test_dict\",\n    \"test.test_dict_version\",\n    \"test.test_dictcomps\",\n    \"test.test_dictviews\",\n    \"test.test_difflib\",\n    \"test.test_dis\",\n    \"test.test_distutils\",\n    \"test.test_doctest\",\n    \"test.test_doctest2\",\n    \"test.test_docxmlrpc\",\n    \"test.test_dtrace\",\n    \"test.test_dynamic\",\n    \"test.test_dynamicclassattribute\",\n    \"test.test_eintr\",\n    \"test.test_email\",\n    \"test.test_email.__main__\",\n    \"test.test_email.test__encoded_words\",\n    \"test.test_email.test__header_value_parser\",\n    \"test.test_email.test_asian_codecs\",\n    \"test.test_email.test_contentmanager\",\n    \"test.test_email.test_defect_handling\",\n    \"test.test_email.test_email\",\n    \"test.test_email.test_generator\",\n    \"test.test_email.test_headerregistry\",\n    \"test.test_email.test_inversion\",\n    \"test.test_email.test_message\",\n    \"test.test_email.test_parser\",\n    \"test.test_email.test_pickleable\",\n    \"test.test_email.test_policy\",\n    \"test.test_email.test_utils\",\n    \"test.test_email.torture_test\",\n    \"test.test_embed\",\n    \"test.test_ensurepip\",\n    \"test.test_enum\",\n    \"test.test_enumerate\",\n    \"test.test_eof\",\n    \"test.test_epoll\",\n    \"test.test_errno\",\n    \"test.test_exception_hierarchy\",\n    \"test.test_exception_variations\",\n    \"test.test_exceptions\",\n    \"test.test_extcall\",\n    \"test.test_faulthandler\",\n    \"test.test_fcntl\",\n    \"test.test_file\",\n    \"test.test_file_eintr\",\n    \"test.test_filecmp\",\n    \"test.test_fileinput\",\n    \"test.test_fileio\",\n    \"test.test_finalization\",\n    \"test.test_float\",\n    \"test.test_flufl\",\n    \"test.test_fnmatch\",\n    \"test.test_fork1\",\n    \"test.test_format\",\n    \"test.test_fractions\",\n    \"test.test_frame\",\n    \"test.test_frozen\",\n    \"test.test_fstring\",\n    \"test.test_ftplib\",\n    \"test.test_funcattrs\",\n    \"test.test_functools\",\n    \"test.test_future\",\n    \"test.test_future3\",\n    \"test.test_future4\",\n    \"test.test_future5\",\n    \"test.test_gc\",\n    \"test.test_gdb\",\n    \"test.test_generator_stop\",\n    \"test.test_generators\",\n    \"test.test_genericalias\",\n    \"test.test_genericclass\",\n    \"test.test_genericpath\",\n    \"test.test_genexps\",\n    \"test.test_getargs2\",\n    \"test.test_getopt\",\n    \"test.test_getpass\",\n    \"test.test_gettext\",\n    \"test.test_glob\",\n    \"test.test_global\",\n    \"test.test_grammar\",\n    \"test.test_graphlib\",\n    \"test.test_grp\",\n    \"test.test_gzip\",\n    \"test.test_hash\",\n    \"test.test_hashlib\",\n    \"test.test_heapq\",\n    \"test.test_hmac\",\n    \"test.test_html\",\n    \"test.test_htmlparser\",\n    \"test.test_http_cookiejar\",\n    \"test.test_http_cookies\",\n    \"test.test_httplib\",\n    \"test.test_httpservers\",\n    \"test.test_idle\",\n    \"test.test_imaplib\",\n    \"test.test_imghdr\",\n    \"test.test_imp\",\n    \"test.test_import\",\n    \"test.test_import.__main__\",\n    \"test.test_importlib\",\n    \"test.test_importlib.__main__\",\n    \"test.test_importlib.abc\",\n    \"test.test_importlib.builtin\",\n    \"test.test_importlib.builtin.__main__\",\n    \"test.test_importlib.builtin.test_finder\",\n    \"test.test_importlib.builtin.test_loader\",\n    \"test.test_importlib.data\",\n    \"test.test_importlib.data01\",\n    \"test.test_importlib.data01.subdirectory\",\n    \"test.test_importlib.data02\",\n    \"test.test_importlib.data02.one\",\n    \"test.test_importlib.data02.two\",\n    \"test.test_importlib.data03\",\n    \"test.test_importlib.extension\",\n    \"test.test_importlib.extension.__main__\",\n    \"test.test_importlib.extension.test_case_sensitivity\",\n    \"test.test_importlib.extension.test_finder\",\n    \"test.test_importlib.extension.test_loader\",\n    \"test.test_importlib.extension.test_path_hook\",\n    \"test.test_importlib.fixtures\",\n    \"test.test_importlib.frozen\",\n    \"test.test_importlib.frozen.__main__\",\n    \"test.test_importlib.frozen.test_finder\",\n    \"test.test_importlib.frozen.test_loader\",\n    \"test.test_importlib.import_\",\n    \"test.test_importlib.import_.__main__\",\n    \"test.test_importlib.import_.test___loader__\",\n    \"test.test_importlib.import_.test___package__\",\n    \"test.test_importlib.import_.test_api\",\n    \"test.test_importlib.import_.test_caching\",\n    \"test.test_importlib.import_.test_fromlist\",\n    \"test.test_importlib.import_.test_meta_path\",\n    \"test.test_importlib.import_.test_packages\",\n    \"test.test_importlib.import_.test_path\",\n    \"test.test_importlib.import_.test_relative_imports\",\n    \"test.test_importlib.source\",\n    \"test.test_importlib.source.__main__\",\n    \"test.test_importlib.source.test_case_sensitivity\",\n    \"test.test_importlib.source.test_file_loader\",\n    \"test.test_importlib.source.test_finder\",\n    \"test.test_importlib.source.test_path_hook\",\n    \"test.test_importlib.source.test_source_encoding\",\n    \"test.test_importlib.stubs\",\n    \"test.test_importlib.test_abc\",\n    \"test.test_importlib.test_api\",\n    \"test.test_importlib.test_files\",\n    \"test.test_importlib.test_lazy\",\n    \"test.test_importlib.test_locks\",\n    \"test.test_importlib.test_main\",\n    \"test.test_importlib.test_metadata_api\",\n    \"test.test_importlib.test_namespace_pkgs\",\n    \"test.test_importlib.test_open\",\n    \"test.test_importlib.test_path\",\n    \"test.test_importlib.test_pkg_import\",\n    \"test.test_importlib.test_read\",\n    \"test.test_importlib.test_resource\",\n    \"test.test_importlib.test_spec\",\n    \"test.test_importlib.test_threaded_import\",\n    \"test.test_importlib.test_util\",\n    \"test.test_importlib.test_windows\",\n    \"test.test_importlib.test_zip\",\n    \"test.test_importlib.threaded_import_hangers\",\n    \"test.test_importlib.util\",\n    \"test.test_importlib.zipdata01\",\n    \"test.test_importlib.zipdata02\",\n    \"test.test_index\",\n    \"test.test_inspect\",\n    \"test.test_int\",\n    \"test.test_int_literal\",\n    \"test.test_io\",\n    \"test.test_ioctl\",\n    \"test.test_ipaddress\",\n    \"test.test_isinstance\",\n    \"test.test_iter\",\n    \"test.test_iterlen\",\n    \"test.test_itertools\",\n    \"test.test_json\",\n    \"test.test_json.__main__\",\n    \"test.test_json.test_decode\",\n    \"test.test_json.test_default\",\n    \"test.test_json.test_dump\",\n    \"test.test_json.test_encode_basestring_ascii\",\n    \"test.test_json.test_enum\",\n    \"test.test_json.test_fail\",\n    \"test.test_json.test_float\",\n    \"test.test_json.test_indent\",\n    \"test.test_json.test_pass1\",\n    \"test.test_json.test_pass2\",\n    \"test.test_json.test_pass3\",\n    \"test.test_json.test_recursion\",\n    \"test.test_json.test_scanstring\",\n    \"test.test_json.test_separators\",\n    \"test.test_json.test_speedups\",\n    \"test.test_json.test_tool\",\n    \"test.test_json.test_unicode\",\n    \"test.test_keyword\",\n    \"test.test_keywordonlyarg\",\n    \"test.test_kqueue\",\n    \"test.test_largefile\",\n    \"test.test_lib2to3\",\n    \"test.test_linecache\",\n    \"test.test_list\",\n    \"test.test_listcomps\",\n    \"test.test_lltrace\",\n    \"test.test_locale\",\n    \"test.test_logging\",\n    \"test.test_long\",\n    \"test.test_longexp\",\n    \"test.test_lzma\",\n    \"test.test_mailbox\",\n    \"test.test_mailcap\",\n    \"test.test_marshal\",\n    \"test.test_math\",\n    \"test.test_memoryio\",\n    \"test.test_memoryview\",\n    \"test.test_metaclass\",\n    \"test.test_mimetypes\",\n    \"test.test_minidom\",\n    \"test.test_mmap\",\n    \"test.test_module\",\n    \"test.test_modulefinder\",\n    \"test.test_msilib\",\n    \"test.test_multibytecodec\",\n    \"test.test_multiprocessing_fork\",\n    \"test.test_multiprocessing_forkserver\",\n    \"test.test_multiprocessing_main_handling\",\n    \"test.test_multiprocessing_spawn\",\n    \"test.test_named_expressions\",\n    \"test.test_netrc\",\n    \"test.test_nis\",\n    \"test.test_nntplib\",\n    \"test.test_ntpath\",\n    \"test.test_numeric_tower\",\n    \"test.test_opcodes\",\n    \"test.test_openpty\",\n    \"test.test_operator\",\n    \"test.test_optparse\",\n    \"test.test_ordered_dict\",\n    \"test.test_os\",\n    \"test.test_ossaudiodev\",\n    \"test.test_osx_env\",\n    \"test.test_parser\",\n    \"test.test_pathlib\",\n    \"test.test_pdb\",\n    \"test.test_peepholer\",\n    \"test.test_peg_generator\",\n    \"test.test_peg_generator.__main__\",\n    \"test.test_peg_generator.test_c_parser\",\n    \"test.test_peg_generator.test_first_sets\",\n    \"test.test_peg_generator.test_pegen\",\n    \"test.test_peg_parser\",\n    \"test.test_pickle\",\n    \"test.test_picklebuffer\",\n    \"test.test_pickletools\",\n    \"test.test_pipes\",\n    \"test.test_pkg\",\n    \"test.test_pkgutil\",\n    \"test.test_platform\",\n    \"test.test_plistlib\",\n    \"test.test_poll\",\n    \"test.test_popen\",\n    \"test.test_poplib\",\n    \"test.test_positional_only_arg\",\n    \"test.test_posix\",\n    \"test.test_posixpath\",\n    \"test.test_pow\",\n    \"test.test_pprint\",\n    \"test.test_print\",\n    \"test.test_profile\",\n    \"test.test_property\",\n    \"test.test_pstats\",\n    \"test.test_pty\",\n    \"test.test_pulldom\",\n    \"test.test_pwd\",\n    \"test.test_py_compile\",\n    \"test.test_pyclbr\",\n    \"test.test_pydoc\",\n    \"test.test_pyexpat\",\n    \"test.test_queue\",\n    \"test.test_quopri\",\n    \"test.test_raise\",\n    \"test.test_random\",\n    \"test.test_range\",\n    \"test.test_re\",\n    \"test.test_readline\",\n    \"test.test_regrtest\",\n    \"test.test_repl\",\n    \"test.test_reprlib\",\n    \"test.test_resource\",\n    \"test.test_richcmp\",\n    \"test.test_rlcompleter\",\n    \"test.test_robotparser\",\n    \"test.test_runpy\",\n    \"test.test_sax\",\n    \"test.test_sched\",\n    \"test.test_scope\",\n    \"test.test_script_helper\",\n    \"test.test_secrets\",\n    \"test.test_select\",\n    \"test.test_selectors\",\n    \"test.test_set\",\n    \"test.test_setcomps\",\n    \"test.test_shelve\",\n    \"test.test_shlex\",\n    \"test.test_shutil\",\n    \"test.test_signal\",\n    \"test.test_site\",\n    \"test.test_slice\",\n    \"test.test_smtpd\",\n    \"test.test_smtplib\",\n    \"test.test_smtpnet\",\n    \"test.test_sndhdr\",\n    \"test.test_socket\",\n    \"test.test_socketserver\",\n    \"test.test_sort\",\n    \"test.test_source_encoding\",\n    \"test.test_spwd\",\n    \"test.test_sqlite\",\n    \"test.test_ssl\",\n    \"test.test_startfile\",\n    \"test.test_stat\",\n    \"test.test_statistics\",\n    \"test.test_strftime\",\n    \"test.test_string\",\n    \"test.test_string_literals\",\n    \"test.test_stringprep\",\n    \"test.test_strptime\",\n    \"test.test_strtod\",\n    \"test.test_struct\",\n    \"test.test_structmembers\",\n    \"test.test_structseq\",\n    \"test.test_subclassinit\",\n    \"test.test_subprocess\",\n    \"test.test_sunau\",\n    \"test.test_sundry\",\n    \"test.test_super\",\n    \"test.test_support\",\n    \"test.test_symbol\",\n    \"test.test_symtable\",\n    \"test.test_syntax\",\n    \"test.test_sys\",\n    \"test.test_sys_setprofile\",\n    \"test.test_sys_settrace\",\n    \"test.test_sysconfig\",\n    \"test.test_syslog\",\n    \"test.test_tabnanny\",\n    \"test.test_tarfile\",\n    \"test.test_tcl\",\n    \"test.test_telnetlib\",\n    \"test.test_tempfile\",\n    \"test.test_textwrap\",\n    \"test.test_thread\",\n    \"test.test_threadedtempfile\",\n    \"test.test_threading\",\n    \"test.test_threading_local\",\n    \"test.test_threadsignals\",\n    \"test.test_time\",\n    \"test.test_timeit\",\n    \"test.test_timeout\",\n    \"test.test_tix\",\n    \"test.test_tk\",\n    \"test.test_tokenize\",\n    \"test.test_tools\",\n    \"test.test_tools.__main__\",\n    \"test.test_tools.test_fixcid\",\n    \"test.test_tools.test_gprof2html\",\n    \"test.test_tools.test_i18n\",\n    \"test.test_tools.test_lll\",\n    \"test.test_tools.test_md5sum\",\n    \"test.test_tools.test_pathfix\",\n    \"test.test_tools.test_pdeps\",\n    \"test.test_tools.test_pindent\",\n    \"test.test_tools.test_reindent\",\n    \"test.test_tools.test_sundry\",\n    \"test.test_trace\",\n    \"test.test_traceback\",\n    \"test.test_tracemalloc\",\n    \"test.test_ttk_guionly\",\n    \"test.test_ttk_textonly\",\n    \"test.test_tuple\",\n    \"test.test_turtle\",\n    \"test.test_type_comments\",\n    \"test.test_typechecks\",\n    \"test.test_types\",\n    \"test.test_typing\",\n    \"test.test_ucn\",\n    \"test.test_unary\",\n    \"test.test_unicode\",\n    \"test.test_unicode_file\",\n    \"test.test_unicode_file_functions\",\n    \"test.test_unicode_identifiers\",\n    \"test.test_unicodedata\",\n    \"test.test_unittest\",\n    \"test.test_univnewlines\",\n    \"test.test_unpack\",\n    \"test.test_unpack_ex\",\n    \"test.test_unparse\",\n    \"test.test_urllib\",\n    \"test.test_urllib2\",\n    \"test.test_urllib2_localnet\",\n    \"test.test_urllib2net\",\n    \"test.test_urllib_response\",\n    \"test.test_urllibnet\",\n    \"test.test_urlparse\",\n    \"test.test_userdict\",\n    \"test.test_userlist\",\n    \"test.test_userstring\",\n    \"test.test_utf8_mode\",\n    \"test.test_utf8source\",\n    \"test.test_uu\",\n    \"test.test_uuid\",\n    \"test.test_venv\",\n    \"test.test_wait3\",\n    \"test.test_wait4\",\n    \"test.test_warnings\",\n    \"test.test_warnings.__main__\",\n    \"test.test_wave\",\n    \"test.test_weakref\",\n    \"test.test_weakset\",\n    \"test.test_webbrowser\",\n    \"test.test_winconsoleio\",\n    \"test.test_winreg\",\n    \"test.test_winsound\",\n    \"test.test_with\",\n    \"test.test_wsgiref\",\n    \"test.test_xdrlib\",\n    \"test.test_xml_dom_minicompat\",\n    \"test.test_xml_etree\",\n    \"test.test_xml_etree_c\",\n    \"test.test_xmlrpc\",\n    \"test.test_xmlrpc_net\",\n    \"test.test_xxtestfuzz\",\n    \"test.test_yield_from\",\n    \"test.test_zipapp\",\n    \"test.test_zipfile\",\n    \"test.test_zipfile64\",\n    \"test.test_zipimport\",\n    \"test.test_zipimport_support\",\n    \"test.test_zlib\",\n    \"test.test_zoneinfo\",\n    \"test.test_zoneinfo.__main__\",\n    \"test.test_zoneinfo._support\",\n    \"test.test_zoneinfo.test_zoneinfo\",\n    \"test.testcodec\",\n    \"test.tf_inherit_check\",\n    \"test.time_hashlib\",\n    \"test.tracedmodules\",\n    \"test.tracedmodules.testmod\",\n    \"test.win_console_handler\",\n    \"test.xmltests\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_colorchooser\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_simpledialog\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest._log\",\n    \"unittest.async_case\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_async_case\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testasync\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsealable\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.parsers.expat.errors\",\n    \"xml.parsers.expat.errors.dom\",\n    \"xml.parsers.expat.errors.etree\",\n    \"xml.parsers.expat.errors.parsers\",\n    \"xml.parsers.expat.errors.sax\",\n    \"xml.parsers.expat.model\",\n    \"xml.parsers.expat.model.dom\",\n    \"xml.parsers.expat.model.etree\",\n    \"xml.parsers.expat.model.parsers\",\n    \"xml.parsers.expat.model.sax\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxsubtype\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\",\n    \"zoneinfo\",\n    \"zoneinfo._common\",\n    \"zoneinfo._tzpath\",\n    \"zoneinfo._zoneinfo\"\n  ],\n  \"3.10\": [\n    \"__future__\",\n    \"__main__\",\n    \"_abc\",\n    \"_aix_support\",\n    \"_ast\",\n    \"_asyncio\",\n    \"_bisect\",\n    \"_blake2\",\n    \"_bootsubprocess\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_contextvars\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_msi\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_overlapped\",\n    \"_pickle\",\n    \"_posixshmem\",\n    \"_posixsubprocess\",\n    \"_py_abc\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_queue\",\n    \"_random\",\n    \"_scproxy\",\n    \"_sha1\",\n    \"_sha256\",\n    \"_sha3\",\n    \"_sha512\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_statistics\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tracemalloc\",\n    \"_uuid\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"_winapi\",\n    \"_zoneinfo\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.__main__\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.exceptions\",\n    \"asyncio.format_helpers\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.mixins\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.runners\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.staggered\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.tasks\",\n    \"asyncio.threads\",\n    \"asyncio.transports\",\n    \"asyncio.trsock\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"binhex\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"contextvars\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._aix\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"dataclasses\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils._collections\",\n    \"distutils._functools\",\n    \"distutils._macos_compat\",\n    \"distutils._msvccompiler\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command._framework_compat\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.py37compat\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.py38compat\",\n    \"distutils.py39compat\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._bundled\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"graphlib\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.__main__\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.format\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_format\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_sidebar\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_util\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.idle_test.test_zzdummy\",\n    \"idlelib.idle_test.tkinter_testing_utils\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.sidebar\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.util\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._abc\",\n    \"importlib._adapters\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib._common\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.metadata\",\n    \"importlib.metadata._adapters\",\n    \"importlib.metadata._collections\",\n    \"importlib.metadata._functools\",\n    \"importlib.metadata._itertools\",\n    \"importlib.metadata._meta\",\n    \"importlib.metadata._text\",\n    \"importlib.readers\",\n    \"importlib.resources\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.resource_tracker\",\n    \"multiprocessing.shared_memory\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"nt\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"pyexpat.errors\",\n    \"pyexpat.model\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sqlite3.test\",\n    \"sqlite3.test.backup\",\n    \"sqlite3.test.dbapi\",\n    \"sqlite3.test.dump\",\n    \"sqlite3.test.factory\",\n    \"sqlite3.test.hooks\",\n    \"sqlite3.test.regression\",\n    \"sqlite3.test.transactions\",\n    \"sqlite3.test.types\",\n    \"sqlite3.test.userfunctions\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_colorchooser\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_messagebox\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_simpledialog\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest._log\",\n    \"unittest.async_case\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_async_case\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testasync\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsealable\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxsubtype\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\",\n    \"zoneinfo\",\n    \"zoneinfo._common\",\n    \"zoneinfo._tzpath\",\n    \"zoneinfo._zoneinfo\"\n  ],\n  \"3.11\": [\n    \"__future__\",\n    \"__main__\",\n    \"_abc\",\n    \"_aix_support\",\n    \"_ast\",\n    \"_asyncio\",\n    \"_bisect\",\n    \"_blake2\",\n    \"_bootsubprocess\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_contextvars\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_msi\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_overlapped\",\n    \"_pickle\",\n    \"_posixshmem\",\n    \"_posixsubprocess\",\n    \"_py_abc\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_queue\",\n    \"_random\",\n    \"_scproxy\",\n    \"_sha1\",\n    \"_sha256\",\n    \"_sha3\",\n    \"_sha512\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_statistics\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tokenize\",\n    \"_tracemalloc\",\n    \"_typing\",\n    \"_uuid\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"_winapi\",\n    \"_zoneinfo\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asynchat\",\n    \"asyncio\",\n    \"asyncio.__main__\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.exceptions\",\n    \"asyncio.format_helpers\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.mixins\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.runners\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.staggered\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.taskgroups\",\n    \"asyncio.tasks\",\n    \"asyncio.threads\",\n    \"asyncio.timeouts\",\n    \"asyncio.transports\",\n    \"asyncio.trsock\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"asyncore\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"contextvars\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._aix\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.test\",\n    \"ctypes.test.__main__\",\n    \"ctypes.test.test_anon\",\n    \"ctypes.test.test_array_in_pointer\",\n    \"ctypes.test.test_arrays\",\n    \"ctypes.test.test_as_parameter\",\n    \"ctypes.test.test_bitfields\",\n    \"ctypes.test.test_buffers\",\n    \"ctypes.test.test_bytes\",\n    \"ctypes.test.test_byteswap\",\n    \"ctypes.test.test_callbacks\",\n    \"ctypes.test.test_cast\",\n    \"ctypes.test.test_cfuncs\",\n    \"ctypes.test.test_checkretval\",\n    \"ctypes.test.test_delattr\",\n    \"ctypes.test.test_errno\",\n    \"ctypes.test.test_find\",\n    \"ctypes.test.test_frombuffer\",\n    \"ctypes.test.test_funcptr\",\n    \"ctypes.test.test_functions\",\n    \"ctypes.test.test_incomplete\",\n    \"ctypes.test.test_init\",\n    \"ctypes.test.test_internals\",\n    \"ctypes.test.test_keeprefs\",\n    \"ctypes.test.test_libc\",\n    \"ctypes.test.test_loading\",\n    \"ctypes.test.test_macholib\",\n    \"ctypes.test.test_memfunctions\",\n    \"ctypes.test.test_numbers\",\n    \"ctypes.test.test_objects\",\n    \"ctypes.test.test_parameters\",\n    \"ctypes.test.test_pep3118\",\n    \"ctypes.test.test_pickling\",\n    \"ctypes.test.test_pointers\",\n    \"ctypes.test.test_prototypes\",\n    \"ctypes.test.test_python_api\",\n    \"ctypes.test.test_random_things\",\n    \"ctypes.test.test_refcounts\",\n    \"ctypes.test.test_repr\",\n    \"ctypes.test.test_returnfuncptrs\",\n    \"ctypes.test.test_simplesubclasses\",\n    \"ctypes.test.test_sizes\",\n    \"ctypes.test.test_slicing\",\n    \"ctypes.test.test_stringptr\",\n    \"ctypes.test.test_strings\",\n    \"ctypes.test.test_struct_fields\",\n    \"ctypes.test.test_structures\",\n    \"ctypes.test.test_unaligned_structures\",\n    \"ctypes.test.test_unicode\",\n    \"ctypes.test.test_values\",\n    \"ctypes.test.test_varsize_struct\",\n    \"ctypes.test.test_win32\",\n    \"ctypes.test.test_wintypes\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"dataclasses\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"distutils\",\n    \"distutils._collections\",\n    \"distutils._functools\",\n    \"distutils._macos_compat\",\n    \"distutils._msvccompiler\",\n    \"distutils.archive_util\",\n    \"distutils.bcppcompiler\",\n    \"distutils.ccompiler\",\n    \"distutils.cmd\",\n    \"distutils.command\",\n    \"distutils.command._framework_compat\",\n    \"distutils.command.bdist\",\n    \"distutils.command.bdist_dumb\",\n    \"distutils.command.bdist_rpm\",\n    \"distutils.command.build\",\n    \"distutils.command.build_clib\",\n    \"distutils.command.build_ext\",\n    \"distutils.command.build_py\",\n    \"distutils.command.build_scripts\",\n    \"distutils.command.check\",\n    \"distutils.command.clean\",\n    \"distutils.command.config\",\n    \"distutils.command.install\",\n    \"distutils.command.install_data\",\n    \"distutils.command.install_egg_info\",\n    \"distutils.command.install_headers\",\n    \"distutils.command.install_lib\",\n    \"distutils.command.install_scripts\",\n    \"distutils.command.py37compat\",\n    \"distutils.command.register\",\n    \"distutils.command.sdist\",\n    \"distutils.command.upload\",\n    \"distutils.config\",\n    \"distutils.core\",\n    \"distutils.cygwinccompiler\",\n    \"distutils.debug\",\n    \"distutils.dep_util\",\n    \"distutils.dir_util\",\n    \"distutils.dist\",\n    \"distutils.errors\",\n    \"distutils.extension\",\n    \"distutils.fancy_getopt\",\n    \"distutils.file_util\",\n    \"distutils.filelist\",\n    \"distutils.log\",\n    \"distutils.msvc9compiler\",\n    \"distutils.msvccompiler\",\n    \"distutils.py38compat\",\n    \"distutils.py39compat\",\n    \"distutils.spawn\",\n    \"distutils.sysconfig\",\n    \"distutils.text_file\",\n    \"distutils.unixccompiler\",\n    \"distutils.util\",\n    \"distutils.version\",\n    \"distutils.versionpredicate\",\n    \"doctest\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"graphlib\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.__main__\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.format\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_format\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_sidebar\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_util\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.idle_test.test_zzdummy\",\n    \"idlelib.idle_test.tkinter_testing_utils\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.sidebar\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.util\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"imghdr\",\n    \"imp\",\n    \"importlib\",\n    \"importlib._abc\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.metadata\",\n    \"importlib.metadata._adapters\",\n    \"importlib.metadata._collections\",\n    \"importlib.metadata._functools\",\n    \"importlib.metadata._itertools\",\n    \"importlib.metadata._meta\",\n    \"importlib.metadata._text\",\n    \"importlib.readers\",\n    \"importlib.resources\",\n    \"importlib.resources._adapters\",\n    \"importlib.resources._common\",\n    \"importlib.resources._itertools\",\n    \"importlib.resources._legacy\",\n    \"importlib.resources.abc\",\n    \"importlib.resources.readers\",\n    \"importlib.resources.simple\",\n    \"importlib.simple\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"lib2to3.tests\",\n    \"lib2to3.tests.__main__\",\n    \"lib2to3.tests.pytree_idempotency\",\n    \"lib2to3.tests.support\",\n    \"lib2to3.tests.test_all_fixers\",\n    \"lib2to3.tests.test_fixers\",\n    \"lib2to3.tests.test_main\",\n    \"lib2to3.tests.test_parser\",\n    \"lib2to3.tests.test_pytree\",\n    \"lib2to3.tests.test_refactor\",\n    \"lib2to3.tests.test_util\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.resource_tracker\",\n    \"multiprocessing.shared_memory\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"nt\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"pyexpat.errors\",\n    \"pyexpat.model\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"re._casefix\",\n    \"re._compiler\",\n    \"re._constants\",\n    \"re._parser\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtpd\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.test\",\n    \"tkinter.test.support\",\n    \"tkinter.test.test_tkinter\",\n    \"tkinter.test.test_tkinter.test_colorchooser\",\n    \"tkinter.test.test_tkinter.test_font\",\n    \"tkinter.test.test_tkinter.test_geometry_managers\",\n    \"tkinter.test.test_tkinter.test_images\",\n    \"tkinter.test.test_tkinter.test_loadtk\",\n    \"tkinter.test.test_tkinter.test_messagebox\",\n    \"tkinter.test.test_tkinter.test_misc\",\n    \"tkinter.test.test_tkinter.test_simpledialog\",\n    \"tkinter.test.test_tkinter.test_text\",\n    \"tkinter.test.test_tkinter.test_variables\",\n    \"tkinter.test.test_tkinter.test_widgets\",\n    \"tkinter.test.test_ttk\",\n    \"tkinter.test.test_ttk.test_extensions\",\n    \"tkinter.test.test_ttk.test_style\",\n    \"tkinter.test.test_ttk.test_widgets\",\n    \"tkinter.test.widget_tests\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"tomllib\",\n    \"tomllib._parser\",\n    \"tomllib._re\",\n    \"tomllib._types\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest._log\",\n    \"unittest.async_case\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.test\",\n    \"unittest.test.__main__\",\n    \"unittest.test._test_warnings\",\n    \"unittest.test.dummy\",\n    \"unittest.test.support\",\n    \"unittest.test.test_assertions\",\n    \"unittest.test.test_async_case\",\n    \"unittest.test.test_break\",\n    \"unittest.test.test_case\",\n    \"unittest.test.test_discovery\",\n    \"unittest.test.test_functiontestcase\",\n    \"unittest.test.test_loader\",\n    \"unittest.test.test_program\",\n    \"unittest.test.test_result\",\n    \"unittest.test.test_runner\",\n    \"unittest.test.test_setups\",\n    \"unittest.test.test_skipping\",\n    \"unittest.test.test_suite\",\n    \"unittest.test.testmock\",\n    \"unittest.test.testmock.__main__\",\n    \"unittest.test.testmock.support\",\n    \"unittest.test.testmock.testasync\",\n    \"unittest.test.testmock.testcallable\",\n    \"unittest.test.testmock.testhelpers\",\n    \"unittest.test.testmock.testmagicmethods\",\n    \"unittest.test.testmock.testmock\",\n    \"unittest.test.testmock.testpatch\",\n    \"unittest.test.testmock.testsealable\",\n    \"unittest.test.testmock.testsentinel\",\n    \"unittest.test.testmock.testwith\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.types\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"xxsubtype\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipimport\",\n    \"zlib\",\n    \"zoneinfo\",\n    \"zoneinfo._common\",\n    \"zoneinfo._tzpath\",\n    \"zoneinfo._zoneinfo\"\n  ],\n  \"3.12\": [\n    \"__future__\",\n    \"__main__\",\n    \"_abc\",\n    \"_aix_support\",\n    \"_ast\",\n    \"_asyncio\",\n    \"_bisect\",\n    \"_blake2\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_contextvars\",\n    \"_crypt\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_io\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_msi\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_overlapped\",\n    \"_pickle\",\n    \"_posixshmem\",\n    \"_posixsubprocess\",\n    \"_py_abc\",\n    \"_pydatetime\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_pylong\",\n    \"_queue\",\n    \"_random\",\n    \"_scproxy\",\n    \"_sha1\",\n    \"_sha2\",\n    \"_sha3\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_statistics\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_symtable\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tokenize\",\n    \"_tracemalloc\",\n    \"_typing\",\n    \"_uuid\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"_winapi\",\n    \"_wmi\",\n    \"_zoneinfo\",\n    \"abc\",\n    \"aifc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asyncio\",\n    \"asyncio.__main__\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.exceptions\",\n    \"asyncio.format_helpers\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.mixins\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.runners\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.staggered\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.taskgroups\",\n    \"asyncio.tasks\",\n    \"asyncio.threads\",\n    \"asyncio.timeouts\",\n    \"asyncio.transports\",\n    \"asyncio.trsock\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"atexit\",\n    \"audioop\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cgi\",\n    \"cgitb\",\n    \"chunk\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"contextvars\",\n    \"copy\",\n    \"copyreg\",\n    \"crypt\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._aix\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"dataclasses\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"doctest\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"graphlib\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.__main__\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.format\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_format\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_sidebar\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_util\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.idle_test.test_zzdummy\",\n    \"idlelib.idle_test.tkinter_testing_utils\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.sidebar\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.util\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"imghdr\",\n    \"importlib\",\n    \"importlib._abc\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.metadata\",\n    \"importlib.metadata._adapters\",\n    \"importlib.metadata._collections\",\n    \"importlib.metadata._functools\",\n    \"importlib.metadata._itertools\",\n    \"importlib.metadata._meta\",\n    \"importlib.metadata._text\",\n    \"importlib.readers\",\n    \"importlib.resources\",\n    \"importlib.resources._adapters\",\n    \"importlib.resources._common\",\n    \"importlib.resources._itertools\",\n    \"importlib.resources._legacy\",\n    \"importlib.resources.abc\",\n    \"importlib.resources.readers\",\n    \"importlib.resources.simple\",\n    \"importlib.simple\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"lib2to3\",\n    \"lib2to3.__main__\",\n    \"lib2to3.btm_matcher\",\n    \"lib2to3.btm_utils\",\n    \"lib2to3.fixer_base\",\n    \"lib2to3.fixer_util\",\n    \"lib2to3.fixes\",\n    \"lib2to3.fixes.fix_apply\",\n    \"lib2to3.fixes.fix_asserts\",\n    \"lib2to3.fixes.fix_basestring\",\n    \"lib2to3.fixes.fix_buffer\",\n    \"lib2to3.fixes.fix_dict\",\n    \"lib2to3.fixes.fix_except\",\n    \"lib2to3.fixes.fix_exec\",\n    \"lib2to3.fixes.fix_execfile\",\n    \"lib2to3.fixes.fix_exitfunc\",\n    \"lib2to3.fixes.fix_filter\",\n    \"lib2to3.fixes.fix_funcattrs\",\n    \"lib2to3.fixes.fix_future\",\n    \"lib2to3.fixes.fix_getcwdu\",\n    \"lib2to3.fixes.fix_has_key\",\n    \"lib2to3.fixes.fix_idioms\",\n    \"lib2to3.fixes.fix_import\",\n    \"lib2to3.fixes.fix_imports\",\n    \"lib2to3.fixes.fix_imports2\",\n    \"lib2to3.fixes.fix_input\",\n    \"lib2to3.fixes.fix_intern\",\n    \"lib2to3.fixes.fix_isinstance\",\n    \"lib2to3.fixes.fix_itertools\",\n    \"lib2to3.fixes.fix_itertools_imports\",\n    \"lib2to3.fixes.fix_long\",\n    \"lib2to3.fixes.fix_map\",\n    \"lib2to3.fixes.fix_metaclass\",\n    \"lib2to3.fixes.fix_methodattrs\",\n    \"lib2to3.fixes.fix_ne\",\n    \"lib2to3.fixes.fix_next\",\n    \"lib2to3.fixes.fix_nonzero\",\n    \"lib2to3.fixes.fix_numliterals\",\n    \"lib2to3.fixes.fix_operator\",\n    \"lib2to3.fixes.fix_paren\",\n    \"lib2to3.fixes.fix_print\",\n    \"lib2to3.fixes.fix_raise\",\n    \"lib2to3.fixes.fix_raw_input\",\n    \"lib2to3.fixes.fix_reduce\",\n    \"lib2to3.fixes.fix_reload\",\n    \"lib2to3.fixes.fix_renames\",\n    \"lib2to3.fixes.fix_repr\",\n    \"lib2to3.fixes.fix_set_literal\",\n    \"lib2to3.fixes.fix_standarderror\",\n    \"lib2to3.fixes.fix_sys_exc\",\n    \"lib2to3.fixes.fix_throw\",\n    \"lib2to3.fixes.fix_tuple_params\",\n    \"lib2to3.fixes.fix_types\",\n    \"lib2to3.fixes.fix_unicode\",\n    \"lib2to3.fixes.fix_urllib\",\n    \"lib2to3.fixes.fix_ws_comma\",\n    \"lib2to3.fixes.fix_xrange\",\n    \"lib2to3.fixes.fix_xreadlines\",\n    \"lib2to3.fixes.fix_zip\",\n    \"lib2to3.main\",\n    \"lib2to3.patcomp\",\n    \"lib2to3.pgen2\",\n    \"lib2to3.pgen2.conv\",\n    \"lib2to3.pgen2.driver\",\n    \"lib2to3.pgen2.grammar\",\n    \"lib2to3.pgen2.literals\",\n    \"lib2to3.pgen2.parse\",\n    \"lib2to3.pgen2.pgen\",\n    \"lib2to3.pgen2.token\",\n    \"lib2to3.pgen2.tokenize\",\n    \"lib2to3.pygram\",\n    \"lib2to3.pytree\",\n    \"lib2to3.refactor\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"mailbox\",\n    \"mailcap\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msilib\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.resource_tracker\",\n    \"multiprocessing.shared_memory\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nis\",\n    \"nntplib\",\n    \"nt\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"ossaudiodev\",\n    \"pathlib\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pipes\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"pyexpat.errors\",\n    \"pyexpat.model\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"re._casefix\",\n    \"re._compiler\",\n    \"re._constants\",\n    \"re._parser\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtplib\",\n    \"sndhdr\",\n    \"socket\",\n    \"socketserver\",\n    \"spwd\",\n    \"sqlite3\",\n    \"sqlite3.__main__\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"sunau\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"telnetlib\",\n    \"tempfile\",\n    \"termios\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.tix\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"tomllib\",\n    \"tomllib._parser\",\n    \"tomllib._re\",\n    \"tomllib._types\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest._log\",\n    \"unittest.async_case\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uu\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.types\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xdrlib\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipfile.__main__\",\n    \"zipfile._path\",\n    \"zipfile._path.glob\",\n    \"zipimport\",\n    \"zlib\",\n    \"zoneinfo\",\n    \"zoneinfo._common\",\n    \"zoneinfo._tzpath\",\n    \"zoneinfo._zoneinfo\"\n  ],\n  \"3.13\": [\n    \"__future__\",\n    \"__main__\",\n    \"_abc\",\n    \"_aix_support\",\n    \"_android_support\",\n    \"_apple_support\",\n    \"_ast\",\n    \"_asyncio\",\n    \"_bisect\",\n    \"_blake2\",\n    \"_bz2\",\n    \"_codecs\",\n    \"_codecs_cn\",\n    \"_codecs_hk\",\n    \"_codecs_iso2022\",\n    \"_codecs_jp\",\n    \"_codecs_kr\",\n    \"_codecs_tw\",\n    \"_collections\",\n    \"_collections_abc\",\n    \"_colorize\",\n    \"_compat_pickle\",\n    \"_compression\",\n    \"_contextvars\",\n    \"_csv\",\n    \"_ctypes\",\n    \"_curses\",\n    \"_curses_panel\",\n    \"_datetime\",\n    \"_dbm\",\n    \"_decimal\",\n    \"_elementtree\",\n    \"_frozen_importlib\",\n    \"_frozen_importlib_external\",\n    \"_functools\",\n    \"_gdbm\",\n    \"_hashlib\",\n    \"_heapq\",\n    \"_imp\",\n    \"_interpchannels\",\n    \"_interpqueues\",\n    \"_interpreters\",\n    \"_io\",\n    \"_ios_support\",\n    \"_json\",\n    \"_locale\",\n    \"_lsprof\",\n    \"_lzma\",\n    \"_markupbase\",\n    \"_md5\",\n    \"_multibytecodec\",\n    \"_multiprocessing\",\n    \"_opcode\",\n    \"_opcode_metadata\",\n    \"_operator\",\n    \"_osx_support\",\n    \"_overlapped\",\n    \"_pickle\",\n    \"_posixshmem\",\n    \"_posixsubprocess\",\n    \"_py_abc\",\n    \"_pydatetime\",\n    \"_pydecimal\",\n    \"_pyio\",\n    \"_pylong\",\n    \"_pyrepl\",\n    \"_pyrepl.__main__\",\n    \"_pyrepl._minimal_curses\",\n    \"_pyrepl._threading_handler\",\n    \"_pyrepl.commands\",\n    \"_pyrepl.completing_reader\",\n    \"_pyrepl.console\",\n    \"_pyrepl.curses\",\n    \"_pyrepl.fancy_termios\",\n    \"_pyrepl.historical_reader\",\n    \"_pyrepl.input\",\n    \"_pyrepl.keymap\",\n    \"_pyrepl.main\",\n    \"_pyrepl.pager\",\n    \"_pyrepl.reader\",\n    \"_pyrepl.readline\",\n    \"_pyrepl.simple_interact\",\n    \"_pyrepl.trace\",\n    \"_pyrepl.types\",\n    \"_pyrepl.unix_console\",\n    \"_pyrepl.unix_eventqueue\",\n    \"_pyrepl.utils\",\n    \"_pyrepl.windows_console\",\n    \"_queue\",\n    \"_random\",\n    \"_scproxy\",\n    \"_sha1\",\n    \"_sha2\",\n    \"_sha3\",\n    \"_signal\",\n    \"_sitebuiltins\",\n    \"_socket\",\n    \"_sqlite3\",\n    \"_sre\",\n    \"_ssl\",\n    \"_stat\",\n    \"_statistics\",\n    \"_string\",\n    \"_strptime\",\n    \"_struct\",\n    \"_suggestions\",\n    \"_symtable\",\n    \"_sysconfig\",\n    \"_thread\",\n    \"_threading_local\",\n    \"_tkinter\",\n    \"_tokenize\",\n    \"_tracemalloc\",\n    \"_typing\",\n    \"_uuid\",\n    \"_warnings\",\n    \"_weakref\",\n    \"_weakrefset\",\n    \"_winapi\",\n    \"_wmi\",\n    \"_zoneinfo\",\n    \"abc\",\n    \"antigravity\",\n    \"argparse\",\n    \"array\",\n    \"ast\",\n    \"asyncio\",\n    \"asyncio.__main__\",\n    \"asyncio.base_events\",\n    \"asyncio.base_futures\",\n    \"asyncio.base_subprocess\",\n    \"asyncio.base_tasks\",\n    \"asyncio.constants\",\n    \"asyncio.coroutines\",\n    \"asyncio.events\",\n    \"asyncio.exceptions\",\n    \"asyncio.format_helpers\",\n    \"asyncio.futures\",\n    \"asyncio.locks\",\n    \"asyncio.log\",\n    \"asyncio.mixins\",\n    \"asyncio.proactor_events\",\n    \"asyncio.protocols\",\n    \"asyncio.queues\",\n    \"asyncio.runners\",\n    \"asyncio.selector_events\",\n    \"asyncio.sslproto\",\n    \"asyncio.staggered\",\n    \"asyncio.streams\",\n    \"asyncio.subprocess\",\n    \"asyncio.taskgroups\",\n    \"asyncio.tasks\",\n    \"asyncio.threads\",\n    \"asyncio.timeouts\",\n    \"asyncio.transports\",\n    \"asyncio.trsock\",\n    \"asyncio.unix_events\",\n    \"asyncio.windows_events\",\n    \"asyncio.windows_utils\",\n    \"atexit\",\n    \"base64\",\n    \"bdb\",\n    \"binascii\",\n    \"bisect\",\n    \"builtins\",\n    \"bz2\",\n    \"cProfile\",\n    \"calendar\",\n    \"cmath\",\n    \"cmd\",\n    \"code\",\n    \"codecs\",\n    \"codeop\",\n    \"collections\",\n    \"collections.abc\",\n    \"colorsys\",\n    \"compileall\",\n    \"concurrent\",\n    \"concurrent.futures\",\n    \"concurrent.futures._base\",\n    \"concurrent.futures.process\",\n    \"concurrent.futures.thread\",\n    \"configparser\",\n    \"contextlib\",\n    \"contextvars\",\n    \"copy\",\n    \"copyreg\",\n    \"csv\",\n    \"ctypes\",\n    \"ctypes._aix\",\n    \"ctypes._endian\",\n    \"ctypes.macholib\",\n    \"ctypes.macholib.dyld\",\n    \"ctypes.macholib.dylib\",\n    \"ctypes.macholib.framework\",\n    \"ctypes.util\",\n    \"ctypes.wintypes\",\n    \"curses\",\n    \"curses.ascii\",\n    \"curses.has_key\",\n    \"curses.panel\",\n    \"curses.textpad\",\n    \"dataclasses\",\n    \"datetime\",\n    \"dbm\",\n    \"dbm.dumb\",\n    \"dbm.gnu\",\n    \"dbm.ndbm\",\n    \"dbm.sqlite3\",\n    \"decimal\",\n    \"difflib\",\n    \"dis\",\n    \"doctest\",\n    \"email\",\n    \"email._encoded_words\",\n    \"email._header_value_parser\",\n    \"email._parseaddr\",\n    \"email._policybase\",\n    \"email.base64mime\",\n    \"email.charset\",\n    \"email.contentmanager\",\n    \"email.encoders\",\n    \"email.errors\",\n    \"email.feedparser\",\n    \"email.generator\",\n    \"email.header\",\n    \"email.headerregistry\",\n    \"email.iterators\",\n    \"email.message\",\n    \"email.mime\",\n    \"email.mime.application\",\n    \"email.mime.audio\",\n    \"email.mime.base\",\n    \"email.mime.image\",\n    \"email.mime.message\",\n    \"email.mime.multipart\",\n    \"email.mime.nonmultipart\",\n    \"email.mime.text\",\n    \"email.parser\",\n    \"email.policy\",\n    \"email.quoprimime\",\n    \"email.utils\",\n    \"encodings\",\n    \"encodings.aliases\",\n    \"encodings.ascii\",\n    \"encodings.base64_codec\",\n    \"encodings.big5\",\n    \"encodings.big5hkscs\",\n    \"encodings.bz2_codec\",\n    \"encodings.charmap\",\n    \"encodings.cp037\",\n    \"encodings.cp1006\",\n    \"encodings.cp1026\",\n    \"encodings.cp1125\",\n    \"encodings.cp1140\",\n    \"encodings.cp1250\",\n    \"encodings.cp1251\",\n    \"encodings.cp1252\",\n    \"encodings.cp1253\",\n    \"encodings.cp1254\",\n    \"encodings.cp1255\",\n    \"encodings.cp1256\",\n    \"encodings.cp1257\",\n    \"encodings.cp1258\",\n    \"encodings.cp273\",\n    \"encodings.cp424\",\n    \"encodings.cp437\",\n    \"encodings.cp500\",\n    \"encodings.cp720\",\n    \"encodings.cp737\",\n    \"encodings.cp775\",\n    \"encodings.cp850\",\n    \"encodings.cp852\",\n    \"encodings.cp855\",\n    \"encodings.cp856\",\n    \"encodings.cp857\",\n    \"encodings.cp858\",\n    \"encodings.cp860\",\n    \"encodings.cp861\",\n    \"encodings.cp862\",\n    \"encodings.cp863\",\n    \"encodings.cp864\",\n    \"encodings.cp865\",\n    \"encodings.cp866\",\n    \"encodings.cp869\",\n    \"encodings.cp874\",\n    \"encodings.cp875\",\n    \"encodings.cp932\",\n    \"encodings.cp949\",\n    \"encodings.cp950\",\n    \"encodings.euc_jis_2004\",\n    \"encodings.euc_jisx0213\",\n    \"encodings.euc_jp\",\n    \"encodings.euc_kr\",\n    \"encodings.gb18030\",\n    \"encodings.gb2312\",\n    \"encodings.gbk\",\n    \"encodings.hex_codec\",\n    \"encodings.hp_roman8\",\n    \"encodings.hz\",\n    \"encodings.idna\",\n    \"encodings.iso2022_jp\",\n    \"encodings.iso2022_jp_1\",\n    \"encodings.iso2022_jp_2\",\n    \"encodings.iso2022_jp_2004\",\n    \"encodings.iso2022_jp_3\",\n    \"encodings.iso2022_jp_ext\",\n    \"encodings.iso2022_kr\",\n    \"encodings.iso8859_1\",\n    \"encodings.iso8859_10\",\n    \"encodings.iso8859_11\",\n    \"encodings.iso8859_13\",\n    \"encodings.iso8859_14\",\n    \"encodings.iso8859_15\",\n    \"encodings.iso8859_16\",\n    \"encodings.iso8859_2\",\n    \"encodings.iso8859_3\",\n    \"encodings.iso8859_4\",\n    \"encodings.iso8859_5\",\n    \"encodings.iso8859_6\",\n    \"encodings.iso8859_7\",\n    \"encodings.iso8859_8\",\n    \"encodings.iso8859_9\",\n    \"encodings.johab\",\n    \"encodings.koi8_r\",\n    \"encodings.koi8_t\",\n    \"encodings.koi8_u\",\n    \"encodings.kz1048\",\n    \"encodings.latin_1\",\n    \"encodings.mac_arabic\",\n    \"encodings.mac_croatian\",\n    \"encodings.mac_cyrillic\",\n    \"encodings.mac_farsi\",\n    \"encodings.mac_greek\",\n    \"encodings.mac_iceland\",\n    \"encodings.mac_latin2\",\n    \"encodings.mac_roman\",\n    \"encodings.mac_romanian\",\n    \"encodings.mac_turkish\",\n    \"encodings.mbcs\",\n    \"encodings.oem\",\n    \"encodings.palmos\",\n    \"encodings.ptcp154\",\n    \"encodings.punycode\",\n    \"encodings.quopri_codec\",\n    \"encodings.raw_unicode_escape\",\n    \"encodings.rot_13\",\n    \"encodings.shift_jis\",\n    \"encodings.shift_jis_2004\",\n    \"encodings.shift_jisx0213\",\n    \"encodings.tis_620\",\n    \"encodings.undefined\",\n    \"encodings.unicode_escape\",\n    \"encodings.utf_16\",\n    \"encodings.utf_16_be\",\n    \"encodings.utf_16_le\",\n    \"encodings.utf_32\",\n    \"encodings.utf_32_be\",\n    \"encodings.utf_32_le\",\n    \"encodings.utf_7\",\n    \"encodings.utf_8\",\n    \"encodings.utf_8_sig\",\n    \"encodings.uu_codec\",\n    \"encodings.zlib_codec\",\n    \"ensurepip\",\n    \"ensurepip.__main__\",\n    \"ensurepip._uninstall\",\n    \"enum\",\n    \"errno\",\n    \"faulthandler\",\n    \"fcntl\",\n    \"filecmp\",\n    \"fileinput\",\n    \"fnmatch\",\n    \"fractions\",\n    \"ftplib\",\n    \"functools\",\n    \"gc\",\n    \"genericpath\",\n    \"getopt\",\n    \"getpass\",\n    \"gettext\",\n    \"glob\",\n    \"graphlib\",\n    \"grp\",\n    \"gzip\",\n    \"hashlib\",\n    \"heapq\",\n    \"hmac\",\n    \"html\",\n    \"html.entities\",\n    \"html.parser\",\n    \"http\",\n    \"http.client\",\n    \"http.cookiejar\",\n    \"http.cookies\",\n    \"http.server\",\n    \"idlelib\",\n    \"idlelib.__main__\",\n    \"idlelib.autocomplete\",\n    \"idlelib.autocomplete_w\",\n    \"idlelib.autoexpand\",\n    \"idlelib.browser\",\n    \"idlelib.calltip\",\n    \"idlelib.calltip_w\",\n    \"idlelib.codecontext\",\n    \"idlelib.colorizer\",\n    \"idlelib.config\",\n    \"idlelib.config_key\",\n    \"idlelib.configdialog\",\n    \"idlelib.debugger\",\n    \"idlelib.debugger_r\",\n    \"idlelib.debugobj\",\n    \"idlelib.debugobj_r\",\n    \"idlelib.delegator\",\n    \"idlelib.dynoption\",\n    \"idlelib.editor\",\n    \"idlelib.filelist\",\n    \"idlelib.format\",\n    \"idlelib.grep\",\n    \"idlelib.help\",\n    \"idlelib.help_about\",\n    \"idlelib.history\",\n    \"idlelib.hyperparser\",\n    \"idlelib.idle\",\n    \"idlelib.idle_test\",\n    \"idlelib.idle_test.htest\",\n    \"idlelib.idle_test.mock_idle\",\n    \"idlelib.idle_test.mock_tk\",\n    \"idlelib.idle_test.template\",\n    \"idlelib.idle_test.test_autocomplete\",\n    \"idlelib.idle_test.test_autocomplete_w\",\n    \"idlelib.idle_test.test_autoexpand\",\n    \"idlelib.idle_test.test_browser\",\n    \"idlelib.idle_test.test_calltip\",\n    \"idlelib.idle_test.test_calltip_w\",\n    \"idlelib.idle_test.test_codecontext\",\n    \"idlelib.idle_test.test_colorizer\",\n    \"idlelib.idle_test.test_config\",\n    \"idlelib.idle_test.test_config_key\",\n    \"idlelib.idle_test.test_configdialog\",\n    \"idlelib.idle_test.test_debugger\",\n    \"idlelib.idle_test.test_debugger_r\",\n    \"idlelib.idle_test.test_debugobj\",\n    \"idlelib.idle_test.test_debugobj_r\",\n    \"idlelib.idle_test.test_delegator\",\n    \"idlelib.idle_test.test_editmenu\",\n    \"idlelib.idle_test.test_editor\",\n    \"idlelib.idle_test.test_filelist\",\n    \"idlelib.idle_test.test_format\",\n    \"idlelib.idle_test.test_grep\",\n    \"idlelib.idle_test.test_help\",\n    \"idlelib.idle_test.test_help_about\",\n    \"idlelib.idle_test.test_history\",\n    \"idlelib.idle_test.test_hyperparser\",\n    \"idlelib.idle_test.test_iomenu\",\n    \"idlelib.idle_test.test_macosx\",\n    \"idlelib.idle_test.test_mainmenu\",\n    \"idlelib.idle_test.test_multicall\",\n    \"idlelib.idle_test.test_outwin\",\n    \"idlelib.idle_test.test_parenmatch\",\n    \"idlelib.idle_test.test_pathbrowser\",\n    \"idlelib.idle_test.test_percolator\",\n    \"idlelib.idle_test.test_pyparse\",\n    \"idlelib.idle_test.test_pyshell\",\n    \"idlelib.idle_test.test_query\",\n    \"idlelib.idle_test.test_redirector\",\n    \"idlelib.idle_test.test_replace\",\n    \"idlelib.idle_test.test_rpc\",\n    \"idlelib.idle_test.test_run\",\n    \"idlelib.idle_test.test_runscript\",\n    \"idlelib.idle_test.test_scrolledlist\",\n    \"idlelib.idle_test.test_search\",\n    \"idlelib.idle_test.test_searchbase\",\n    \"idlelib.idle_test.test_searchengine\",\n    \"idlelib.idle_test.test_sidebar\",\n    \"idlelib.idle_test.test_squeezer\",\n    \"idlelib.idle_test.test_stackviewer\",\n    \"idlelib.idle_test.test_statusbar\",\n    \"idlelib.idle_test.test_text\",\n    \"idlelib.idle_test.test_textview\",\n    \"idlelib.idle_test.test_tooltip\",\n    \"idlelib.idle_test.test_tree\",\n    \"idlelib.idle_test.test_undo\",\n    \"idlelib.idle_test.test_util\",\n    \"idlelib.idle_test.test_warning\",\n    \"idlelib.idle_test.test_window\",\n    \"idlelib.idle_test.test_zoomheight\",\n    \"idlelib.idle_test.test_zzdummy\",\n    \"idlelib.idle_test.tkinter_testing_utils\",\n    \"idlelib.iomenu\",\n    \"idlelib.macosx\",\n    \"idlelib.mainmenu\",\n    \"idlelib.multicall\",\n    \"idlelib.outwin\",\n    \"idlelib.parenmatch\",\n    \"idlelib.pathbrowser\",\n    \"idlelib.percolator\",\n    \"idlelib.pyparse\",\n    \"idlelib.pyshell\",\n    \"idlelib.query\",\n    \"idlelib.redirector\",\n    \"idlelib.replace\",\n    \"idlelib.rpc\",\n    \"idlelib.run\",\n    \"idlelib.runscript\",\n    \"idlelib.scrolledlist\",\n    \"idlelib.search\",\n    \"idlelib.searchbase\",\n    \"idlelib.searchengine\",\n    \"idlelib.sidebar\",\n    \"idlelib.squeezer\",\n    \"idlelib.stackviewer\",\n    \"idlelib.statusbar\",\n    \"idlelib.textview\",\n    \"idlelib.tooltip\",\n    \"idlelib.tree\",\n    \"idlelib.undo\",\n    \"idlelib.util\",\n    \"idlelib.window\",\n    \"idlelib.zoomheight\",\n    \"idlelib.zzdummy\",\n    \"imaplib\",\n    \"importlib\",\n    \"importlib._abc\",\n    \"importlib._bootstrap\",\n    \"importlib._bootstrap_external\",\n    \"importlib.abc\",\n    \"importlib.machinery\",\n    \"importlib.metadata\",\n    \"importlib.metadata._adapters\",\n    \"importlib.metadata._collections\",\n    \"importlib.metadata._functools\",\n    \"importlib.metadata._itertools\",\n    \"importlib.metadata._meta\",\n    \"importlib.metadata._text\",\n    \"importlib.metadata.diagnose\",\n    \"importlib.readers\",\n    \"importlib.resources\",\n    \"importlib.resources._adapters\",\n    \"importlib.resources._common\",\n    \"importlib.resources._functional\",\n    \"importlib.resources._itertools\",\n    \"importlib.resources.abc\",\n    \"importlib.resources.readers\",\n    \"importlib.resources.simple\",\n    \"importlib.simple\",\n    \"importlib.util\",\n    \"inspect\",\n    \"io\",\n    \"ipaddress\",\n    \"itertools\",\n    \"json\",\n    \"json.decoder\",\n    \"json.encoder\",\n    \"json.scanner\",\n    \"json.tool\",\n    \"keyword\",\n    \"linecache\",\n    \"locale\",\n    \"logging\",\n    \"logging.config\",\n    \"logging.handlers\",\n    \"lzma\",\n    \"mailbox\",\n    \"marshal\",\n    \"math\",\n    \"mimetypes\",\n    \"mmap\",\n    \"modulefinder\",\n    \"msvcrt\",\n    \"multiprocessing\",\n    \"multiprocessing.connection\",\n    \"multiprocessing.context\",\n    \"multiprocessing.dummy\",\n    \"multiprocessing.dummy.connection\",\n    \"multiprocessing.forkserver\",\n    \"multiprocessing.heap\",\n    \"multiprocessing.managers\",\n    \"multiprocessing.pool\",\n    \"multiprocessing.popen_fork\",\n    \"multiprocessing.popen_forkserver\",\n    \"multiprocessing.popen_spawn_posix\",\n    \"multiprocessing.popen_spawn_win32\",\n    \"multiprocessing.process\",\n    \"multiprocessing.queues\",\n    \"multiprocessing.reduction\",\n    \"multiprocessing.resource_sharer\",\n    \"multiprocessing.resource_tracker\",\n    \"multiprocessing.shared_memory\",\n    \"multiprocessing.sharedctypes\",\n    \"multiprocessing.spawn\",\n    \"multiprocessing.synchronize\",\n    \"multiprocessing.util\",\n    \"netrc\",\n    \"nt\",\n    \"ntpath\",\n    \"nturl2path\",\n    \"numbers\",\n    \"opcode\",\n    \"operator\",\n    \"optparse\",\n    \"os\",\n    \"os.path\",\n    \"pathlib\",\n    \"pathlib._abc\",\n    \"pathlib._local\",\n    \"pdb\",\n    \"pickle\",\n    \"pickletools\",\n    \"pkgutil\",\n    \"platform\",\n    \"plistlib\",\n    \"poplib\",\n    \"posix\",\n    \"posixpath\",\n    \"pprint\",\n    \"profile\",\n    \"pstats\",\n    \"pty\",\n    \"pwd\",\n    \"py_compile\",\n    \"pyclbr\",\n    \"pydoc\",\n    \"pydoc_data\",\n    \"pydoc_data.topics\",\n    \"pyexpat\",\n    \"pyexpat.errors\",\n    \"pyexpat.model\",\n    \"queue\",\n    \"quopri\",\n    \"random\",\n    \"re\",\n    \"re._casefix\",\n    \"re._compiler\",\n    \"re._constants\",\n    \"re._parser\",\n    \"readline\",\n    \"reprlib\",\n    \"resource\",\n    \"rlcompleter\",\n    \"runpy\",\n    \"sched\",\n    \"secrets\",\n    \"select\",\n    \"selectors\",\n    \"shelve\",\n    \"shlex\",\n    \"shutil\",\n    \"signal\",\n    \"site\",\n    \"smtplib\",\n    \"socket\",\n    \"socketserver\",\n    \"sqlite3\",\n    \"sqlite3.__main__\",\n    \"sqlite3.dbapi2\",\n    \"sqlite3.dump\",\n    \"sre_compile\",\n    \"sre_constants\",\n    \"sre_parse\",\n    \"ssl\",\n    \"stat\",\n    \"statistics\",\n    \"string\",\n    \"stringprep\",\n    \"struct\",\n    \"subprocess\",\n    \"symtable\",\n    \"sys\",\n    \"sysconfig\",\n    \"sysconfig.__main__\",\n    \"syslog\",\n    \"tabnanny\",\n    \"tarfile\",\n    \"tempfile\",\n    \"termios\",\n    \"textwrap\",\n    \"this\",\n    \"threading\",\n    \"time\",\n    \"timeit\",\n    \"tkinter\",\n    \"tkinter.__main__\",\n    \"tkinter.colorchooser\",\n    \"tkinter.commondialog\",\n    \"tkinter.constants\",\n    \"tkinter.dialog\",\n    \"tkinter.dnd\",\n    \"tkinter.filedialog\",\n    \"tkinter.font\",\n    \"tkinter.messagebox\",\n    \"tkinter.scrolledtext\",\n    \"tkinter.simpledialog\",\n    \"tkinter.ttk\",\n    \"token\",\n    \"tokenize\",\n    \"tomllib\",\n    \"tomllib._parser\",\n    \"tomllib._re\",\n    \"tomllib._types\",\n    \"trace\",\n    \"traceback\",\n    \"tracemalloc\",\n    \"tty\",\n    \"turtle\",\n    \"turtledemo\",\n    \"turtledemo.__main__\",\n    \"turtledemo.bytedesign\",\n    \"turtledemo.chaos\",\n    \"turtledemo.clock\",\n    \"turtledemo.colormixer\",\n    \"turtledemo.forest\",\n    \"turtledemo.fractalcurves\",\n    \"turtledemo.lindenmayer\",\n    \"turtledemo.minimal_hanoi\",\n    \"turtledemo.nim\",\n    \"turtledemo.paint\",\n    \"turtledemo.peace\",\n    \"turtledemo.penrose\",\n    \"turtledemo.planet_and_moon\",\n    \"turtledemo.rosette\",\n    \"turtledemo.round_dance\",\n    \"turtledemo.sorting_animate\",\n    \"turtledemo.tree\",\n    \"turtledemo.two_canvases\",\n    \"turtledemo.yinyang\",\n    \"types\",\n    \"typing\",\n    \"unicodedata\",\n    \"unittest\",\n    \"unittest.__main__\",\n    \"unittest._log\",\n    \"unittest.async_case\",\n    \"unittest.case\",\n    \"unittest.loader\",\n    \"unittest.main\",\n    \"unittest.mock\",\n    \"unittest.result\",\n    \"unittest.runner\",\n    \"unittest.signals\",\n    \"unittest.suite\",\n    \"unittest.util\",\n    \"urllib\",\n    \"urllib.error\",\n    \"urllib.parse\",\n    \"urllib.request\",\n    \"urllib.response\",\n    \"urllib.robotparser\",\n    \"uuid\",\n    \"venv\",\n    \"venv.__main__\",\n    \"warnings\",\n    \"wave\",\n    \"weakref\",\n    \"webbrowser\",\n    \"winreg\",\n    \"winsound\",\n    \"wsgiref\",\n    \"wsgiref.handlers\",\n    \"wsgiref.headers\",\n    \"wsgiref.simple_server\",\n    \"wsgiref.types\",\n    \"wsgiref.util\",\n    \"wsgiref.validate\",\n    \"xml\",\n    \"xml.dom\",\n    \"xml.dom.NodeFilter\",\n    \"xml.dom.domreg\",\n    \"xml.dom.expatbuilder\",\n    \"xml.dom.minicompat\",\n    \"xml.dom.minidom\",\n    \"xml.dom.pulldom\",\n    \"xml.dom.xmlbuilder\",\n    \"xml.etree\",\n    \"xml.etree.ElementInclude\",\n    \"xml.etree.ElementPath\",\n    \"xml.etree.ElementTree\",\n    \"xml.etree.cElementTree\",\n    \"xml.parsers\",\n    \"xml.parsers.expat\",\n    \"xml.sax\",\n    \"xml.sax._exceptions\",\n    \"xml.sax.expatreader\",\n    \"xml.sax.handler\",\n    \"xml.sax.saxutils\",\n    \"xml.sax.xmlreader\",\n    \"xmlrpc\",\n    \"xmlrpc.client\",\n    \"xmlrpc.server\",\n    \"zipapp\",\n    \"zipfile\",\n    \"zipfile.__main__\",\n    \"zipfile._path\",\n    \"zipfile._path.glob\",\n    \"zipimport\",\n    \"zlib\",\n    \"zoneinfo\",\n    \"zoneinfo._common\",\n    \"zoneinfo._tzpath\",\n    \"zoneinfo._zoneinfo\"\n  ]\n}\n"
  },
  {
    "path": "src/scripts/generate_python_stdlib_list/script.sh",
    "content": "#!/bin/bash\n# script.sh\n# This script dynamically retrieves the list of major Python versions from Docker Hub,\n# handling pagination, and uses a single Docker container (python:latest) to generate the\n# standard library module lists for each version using the stdlib-list package.\n# The output is written to a single JSON file where each key is a Python version.\n\n# Ensure required tools are available\ncommand -v curl >/dev/null 2>&1 || { echo >&2 \"curl is required but it's not installed. Aborting.\"; exit 1; }\ncommand -v jq >/dev/null 2>&1 || { echo >&2 \"jq is required but it's not installed. Aborting.\"; exit 1; }\n\n# Initial API URL with max page_size\nAPI_URL=\"https://registry.hub.docker.com/v2/repositories/library/python/tags/?page_size=100\"\necho \"Querying Docker Hub for Python image tags...\"\n\nall_tags=\"\"\n# Loop over pages until there is no next URL.\nwhile [ -n \"$API_URL\" ]; do\n    echo \"Fetching tags from: $API_URL\"\n    response=$(curl -s \"$API_URL\")\n    # Append tags from this page.\n    page_tags=$(echo \"$response\" | jq -r '.results[].name')\n    all_tags+=\"$page_tags\"$'\\n'\n    # Get next URL.\n    API_URL=$(echo \"$response\" | jq -r '.next')\n    if [ \"$API_URL\" == \"null\" ]; then\n        API_URL=\"\"\n    fi\ndone\n\n# Filter out tags that match the major version pattern (e.g., 3.8, 3.9)\nversions=$(echo \"$all_tags\" | grep -E '^[0-9]+\\.[0-9]+$' | sort -V | uniq)\n\nif [ -z \"$versions\" ]; then\n    echo \"No major Python versions found. Exiting.\"\n    exit 1\nfi\n\necho \"Found Python versions:\"\necho \"$versions\"\n\n# Convert the list to a space-separated string for environment passing.\nversions_list=$(echo \"$versions\" | tr '\\n' ' ')\necho \"Using versions: $versions_list\"\n\n# Use a single Docker container (python:latest) to generate the stdlib lists.\n# Pass the versions list to the container via the PYTHON_VERSIONS environment variable.\ndocker run --rm -e PYTHON_VERSIONS=\"$versions_list\" -v \"$(dirname \"$0\")/output.json\":/output.json python:latest bash -c '\npip install stdlib-list > /dev/null 2>&1 && python - << \"EOF\"\nimport json\nimport os\nfrom stdlib_list import stdlib_list\n\n# Retrieve the space-separated versions from the environment variable.\nversions = os.environ.get(\"PYTHON_VERSIONS\", \"\").split()\nif not versions:\n    print(\"No Python versions provided.\")\n    exit(1)\n\nresult = {}\nfor v in versions:\n    try:\n        modules = stdlib_list(v)\n    except Exception as e:\n        modules = {\"error\": str(e)}\n    result[v] = modules\n\n# Write the result to a JSON file.\nwith open(\"output.json\", \"w\") as f:\n    json.dump(result, f, indent=2)\nEOF\n'\n\necho \"Standard library list generated in the 'output.json' file.\"\n"
  },
  {
    "path": "src/symbolExtractor/c/index.ts",
    "content": "import type { ExtractedFilesMap } from \"../types.ts\";\nimport { CExtractor } from \"../../languagePlugins/c/extractor/index.ts\";\nimport type { DependencyManifest } from \"../../manifest/dependencyManifest/types.ts\";\nimport type { z } from \"zod\";\nimport type { localConfigSchema } from \"../../cli/middlewares/napiConfig.ts\";\n\nexport function extractCSymbols(\n  files: Map<string, { path: string; content: string }>,\n  dependencyManifest: DependencyManifest,\n  symbolsToExtract: Map<string, { filePath: string; symbols: Set<string> }>,\n  _napiConfig: z.infer<typeof localConfigSchema>,\n): ExtractedFilesMap {\n  console.time(`Extracted ${symbolsToExtract.size} symbol(s)`);\n  const extractor = new CExtractor(files, dependencyManifest, _napiConfig);\n  const extractedFiles = extractor.extractSymbols(symbolsToExtract);\n  console.timeEnd(`Extracted ${symbolsToExtract.size} symbol(s)`);\n  return extractedFiles;\n}\n"
  },
  {
    "path": "src/symbolExtractor/csharp/index.ts",
    "content": "import type { z } from \"zod\";\nimport { join, SEPARATOR } from \"@std/path\";\nimport type { localConfigSchema } from \"../../cli/middlewares/napiConfig.ts\";\nimport type { ExtractedFilesMap } from \"../types.ts\";\nimport {\n  CSharpExtractor,\n  type ExtractedFile,\n} from \"../../languagePlugins/csharp/extractor/index.ts\";\nimport type { DependencyManifest } from \"../../manifest/dependencyManifest/types.ts\";\nimport type { DotNetProject } from \"../../languagePlugins/csharp/projectMapper/index.ts\";\nimport type Parser from \"tree-sitter\";\nimport { csharpParser } from \"../../helpers/treeSitter/parsers.ts\";\nimport { CSharpNamespaceMapper } from \"../../languagePlugins/csharp/namespaceMapper/index.ts\";\nimport { CSharpProjectMapper } from \"../../languagePlugins/csharp/projectMapper/index.ts\";\nimport {\n  CSharpUsingResolver,\n  ExternalSymbol,\n  InternalSymbol,\n} from \"../../languagePlugins/csharp/usingResolver/index.ts\";\nimport { CSharpInvocationResolver } from \"../../languagePlugins/csharp/invocationResolver/index.ts\";\n\n/**\n * Extracts C# symbols from the given files.\n * @param files - A map of file paths to their content.\n * @param dependencyManifest - The dependency manifest.\n * @param symbolsToExtract - A map of symbols to extract, where the key is the symbol name and the value is an object containing the file path and a set of symbols.\n * @param _napiConfig - The NAPI configuration.\n * @returns - A map of extracted files, where the key is the file path and the value is an object containing the file path and content.\n */\nexport function extractCSharpSymbols(\n  files: Map<string, { path: string; content: string }>,\n  dependencyManifest: DependencyManifest,\n  symbolsToExtract: Map<string, { filePath: string; symbols: Set<string> }>,\n  _napiConfig: z.infer<typeof localConfigSchema>,\n): ExtractedFilesMap {\n  console.time(`Extracted ${symbolsToExtract.size} symbol(s)`);\n  const extractor = new CSharpExtractor(files, dependencyManifest);\n  const extractedFiles: ExtractedFile[] = [];\n  const parsedFiles = new Map<\n    string,\n    { path: string; rootNode: Parser.SyntaxNode }\n  >();\n  const csprojFiles = new Map<string, { path: string; content: string }>();\n  // Extract symbols from the files\n  for (const symbolSet of symbolsToExtract.values()) {\n    for (const symbol of symbolSet.symbols) {\n      const extractedFile = extractor.extractSymbolFromFile(\n        symbolSet.filePath,\n        symbol,\n      );\n      if (extractedFile) {\n        // Add the extracted file to the list of extracted files\n        extractedFiles.push(...extractedFile);\n        // Add the extracted file to the parsed files map\n        // This is used to create a representation of the exported project\n        // It will help us to find which namespaces cannot be used in using directives anymore\n        for (const file of extractedFile) {\n          const filePath = file.name;\n          if (!parsedFiles.has(filePath)) {\n            parsedFiles.set(filePath, {\n              path: filePath,\n              rootNode: csharpParser.parse(extractor.getContent(file)).rootNode,\n            });\n          }\n          // Add the csproj file to the csproj files map\n          const subproject = file.subproject;\n          if (!csprojFiles.has(subproject.csprojPath)) {\n            csprojFiles.set(subproject.csprojPath, {\n              path: subproject.csprojPath,\n              content: subproject.csprojContent,\n            });\n            const globalUsingsPath = join(\n              subproject.rootFolder,\n              \"GlobalUsings.cs\",\n            );\n            const globalUsingsContent = extractor.generateGlobalUsings(\n              subproject,\n            );\n            if (!parsedFiles.has(globalUsingsPath)) {\n              parsedFiles.set(globalUsingsPath, {\n                path: globalUsingsPath,\n                rootNode: csharpParser.parse(globalUsingsContent).rootNode,\n              });\n            }\n          }\n        }\n      }\n    }\n  }\n  // For each extracted file, check if the using directives are still valid and useful\n  const nsMapper = new CSharpNamespaceMapper(parsedFiles);\n  const pjMapper = new CSharpProjectMapper(csprojFiles);\n  const usingResolver = new CSharpUsingResolver(nsMapper, pjMapper);\n  const invocationResolver = new CSharpInvocationResolver(nsMapper, pjMapper);\n  for (const extractedFile of extractedFiles) {\n    const imports = extractedFile.imports;\n    const invocations = invocationResolver.getInvocationsFromFile(\n      extractedFile.name,\n    );\n    for (const importDirective of imports) {\n      const resolvedInNewFile = usingResolver.resolveUsingDirective(\n        importDirective,\n      );\n      const resolvedInOldFile = extractor.usingResolver.resolveUsingDirective(\n        importDirective,\n      );\n      if (\n        (resolvedInNewFile instanceof ExternalSymbol &&\n          resolvedInOldFile instanceof InternalSymbol) ||\n        !invocationResolver.isUsingUseful(invocations, importDirective)\n      ) {\n        extractedFile.imports = extractedFile.imports.filter(\n          (imp) => imp !== importDirective,\n        );\n      }\n    }\n  }\n  // Actually extract the files\n  const subprojects: DotNetProject[] = [];\n  const extractedFilesMap: ExtractedFilesMap = new Map();\n  for (const extractedFile of extractedFiles) {\n    const { subproject, namespace, name } = extractedFile;\n    if (!subprojects.includes(subproject)) {\n      subprojects.push(subproject);\n    }\n    // File path for the extracted file\n    const fakeprojectpath = subproject.name.split(\".\").join(SEPARATOR);\n    const spindex = namespace.split(\".\").indexOf(subproject.name);\n    const nspath = namespace\n      .split(\".\")\n      .slice(spindex !== -1 ? spindex : 0)\n      .join(SEPARATOR)\n      .replace(fakeprojectpath, subproject.name);\n    const key = join(\n      spindex !== -1 || nspath.startsWith(subproject.name)\n        ? \"\"\n        : subproject.name,\n      nspath,\n      `${name}.cs`,\n    );\n    if (!extractedFilesMap.has(key)) {\n      const filecontent = extractor.getContent(extractedFile);\n      // Add the extracted file to the output map\n      extractedFilesMap.set(key, {\n        path: key,\n        content: filecontent,\n      });\n    }\n  }\n  // Add the .csproj and GlobalUsings.cs files for each subproject\n  for (const subproject of subprojects) {\n    const projectPath = join(subproject.name, `${subproject.name}.csproj`);\n    const globalUsingPath = join(subproject.name, \"GlobalUsings.cs\");\n    if (!extractedFilesMap.has(projectPath)) {\n      // Add the project files to the output map\n      extractedFilesMap.set(projectPath, {\n        path: projectPath,\n        content: subproject.csprojContent,\n      });\n      extractedFilesMap.set(globalUsingPath, {\n        path: globalUsingPath,\n        content: extractor.generateGlobalUsings(subproject),\n      });\n    }\n  }\n  console.timeEnd(`Extracted ${symbolsToExtract.size} symbol(s)`);\n  return extractedFilesMap;\n}\n"
  },
  {
    "path": "src/symbolExtractor/index.ts",
    "content": "import type { ExtractedFilesMap } from \"./types.ts\";\nimport { extractPythonSymbols } from \"./python/index.ts\";\nimport { extractCSharpSymbols } from \"./csharp/index.ts\";\nimport { extractCSymbols } from \"./c/index.ts\";\nimport { extractJavaSymbols } from \"./java/index.ts\";\nimport type { localConfigSchema } from \"../cli/middlewares/napiConfig.ts\";\nimport type z from \"zod\";\nimport type { DependencyManifest } from \"../manifest/dependencyManifest/types.ts\";\n\nconst handlerMap: Record<\n  string,\n  (\n    files: Map<string, { path: string; content: string }>,\n    dependencyManifest: DependencyManifest,\n    symbolsToExtract: Map<string, { filePath: string; symbols: Set<string> }>,\n    napiConfig: z.infer<typeof localConfigSchema>,\n  ) => ExtractedFilesMap\n> = {\n  python: extractPythonSymbols,\n  \"c-sharp\": extractCSharpSymbols,\n  c: extractCSymbols,\n  java: extractJavaSymbols,\n};\n\nexport class UnsupportedLanguageError extends Error {\n  constructor(language: string) {\n    const supportedLanguages = Object.keys(handlerMap).join(\", \");\n    super(\n      `Unsupported language: ${language}. Supported languages: ${supportedLanguages}`,\n    );\n  }\n}\n\nexport function extractSymbols(\n  files: Map<string, { path: string; content: string }>,\n  dependencyManifest: DependencyManifest,\n  symbolsToExtract: Map<string, { filePath: string; symbols: Set<string> }>,\n  napiConfig: z.infer<typeof localConfigSchema>,\n): ExtractedFilesMap {\n  const languageName = napiConfig.language;\n  const handler = handlerMap[languageName];\n  if (!handler) {\n    throw new UnsupportedLanguageError(languageName);\n  }\n\n  const extractedFiles = handler(\n    files,\n    dependencyManifest,\n    symbolsToExtract,\n    napiConfig,\n  );\n\n  return extractedFiles;\n}\n"
  },
  {
    "path": "src/symbolExtractor/java/index.ts",
    "content": "import type { ExtractedFilesMap } from \"../types.ts\";\nimport { JavaExtractor } from \"../../languagePlugins/java/extractor/index.ts\";\nimport type { DependencyManifest } from \"../../manifest/dependencyManifest/types.ts\";\nimport type { z } from \"zod\";\nimport type { localConfigSchema } from \"../../cli/middlewares/napiConfig.ts\";\n\nexport function extractJavaSymbols(\n  files: Map<string, { path: string; content: string }>,\n  dependencyManifest: DependencyManifest,\n  symbolsToExtract: Map<string, { filePath: string; symbols: Set<string> }>,\n  _napiConfig: z.infer<typeof localConfigSchema>,\n): ExtractedFilesMap {\n  console.time(`Extracted ${symbolsToExtract.size} symbol(s)`);\n  const extractor = new JavaExtractor(files, dependencyManifest);\n  const extractedFiles = extractor.extractSymbols(symbolsToExtract);\n  console.timeEnd(`Extracted ${symbolsToExtract.size} symbol(s)`);\n  return extractedFiles;\n}\n"
  },
  {
    "path": "src/symbolExtractor/python/index.ts",
    "content": "import type z from \"zod\";\nimport type { localConfigSchema } from \"../../cli/middlewares/napiConfig.ts\";\nimport type { ExtractedFilesMap } from \"../types.ts\";\nimport type Parser from \"tree-sitter\";\nimport { pythonParser } from \"../../helpers/treeSitter/parsers.ts\";\nimport type { DependencyManifest } from \"../../manifest/dependencyManifest/types.ts\";\nimport { PythonExportExtractor } from \"../../languagePlugins/python/exportExtractor/index.ts\";\nimport { PythonImportExtractor } from \"../../languagePlugins/python/importExtractor/index.ts\";\nimport { PythonModuleResolver } from \"../../languagePlugins/python/moduleResolver/index.ts\";\nimport { PythonItemResolver } from \"../../languagePlugins/python/itemResolver/index.ts\";\nimport { PythonUsageResolver } from \"../../languagePlugins/python/usageResolver/index.ts\";\nimport { PythonSymbolExtractor } from \"../../languagePlugins/python/symbolExtractor/index.ts\";\n\nexport function extractPythonSymbols(\n  files: Map<string, { path: string; content: string }>,\n  dependencyManifest: DependencyManifest,\n  symbolsToExtract: Map<string, { filePath: string; symbols: Set<string> }>,\n  napiConfig: z.infer<typeof localConfigSchema>,\n): ExtractedFilesMap {\n  const pythonVersion = napiConfig.python?.version;\n  if (!pythonVersion) {\n    throw new Error(\"Python version is required\");\n  }\n\n  const parsedFiles = new Map<\n    string,\n    { path: string; rootNode: Parser.SyntaxNode }\n  >();\n  for (const { path, content } of files.values()) {\n    const rootNode = pythonParser.parse(content, undefined, {\n      bufferSize: content.length + 10,\n    }).rootNode;\n    parsedFiles.set(path, { path, rootNode });\n  }\n\n  const exportExtractor = new PythonExportExtractor(pythonParser, parsedFiles);\n  const importExtractor = new PythonImportExtractor(pythonParser, parsedFiles);\n  const moduleResolver = new PythonModuleResolver(\n    new Set(parsedFiles.keys()),\n    pythonVersion,\n  );\n  const itemResolver = new PythonItemResolver(\n    exportExtractor,\n    importExtractor,\n    moduleResolver,\n  );\n  const usageResolver = new PythonUsageResolver(pythonParser, exportExtractor);\n  const symbolExtractor = new PythonSymbolExtractor(\n    pythonParser,\n    parsedFiles,\n    exportExtractor,\n    importExtractor,\n    moduleResolver,\n    itemResolver,\n    usageResolver,\n    dependencyManifest,\n  );\n\n  const extractedFiles = symbolExtractor.extractSymbol(symbolsToExtract);\n\n  return extractedFiles;\n}\n"
  },
  {
    "path": "src/symbolExtractor/types.ts",
    "content": "export type ExtractedFilesMap = Map<\n  string,\n  {\n    path: string;\n    content: string;\n  }\n>;\n"
  },
  {
    "path": "viewer/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "viewer/README.md",
    "content": "# React + TypeScript + Vite\n\nThis template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.\n\nCurrently, two official plugins are available:\n\n- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)\n- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)\n\n## React Compiler\n\nThe React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).\n\n## Expanding the ESLint configuration\n\nIf you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:\n\n```js\nexport default defineConfig([\n  globalIgnores(['dist']),\n  {\n    files: ['**/*.{ts,tsx}'],\n    extends: [\n      // Other configs...\n\n      // Remove tseslint.configs.recommended and replace with this\n      tseslint.configs.recommendedTypeChecked,\n      // Alternatively, use this for stricter rules\n      tseslint.configs.strictTypeChecked,\n      // Optionally, add this for stylistic rules\n      tseslint.configs.stylisticTypeChecked,\n\n      // Other configs...\n    ],\n    languageOptions: {\n      parserOptions: {\n        project: ['./tsconfig.node.json', './tsconfig.app.json'],\n        tsconfigRootDir: import.meta.dirname,\n      },\n      // other options...\n    },\n  },\n])\n```\n\nYou can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:\n\n```js\n// eslint.config.js\nimport reactX from 'eslint-plugin-react-x'\nimport reactDom from 'eslint-plugin-react-dom'\n\nexport default defineConfig([\n  globalIgnores(['dist']),\n  {\n    files: ['**/*.{ts,tsx}'],\n    extends: [\n      // Other configs...\n      // Enable lint rules for React\n      reactX.configs['recommended-typescript'],\n      // Enable lint rules for React DOM\n      reactDom.configs.recommended,\n    ],\n    languageOptions: {\n      parserOptions: {\n        project: ['./tsconfig.node.json', './tsconfig.app.json'],\n        tsconfigRootDir: import.meta.dirname,\n      },\n      // other options...\n    },\n  },\n])\n```\n"
  },
  {
    "path": "viewer/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>NanoAPI Viewer</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "viewer/src/App.tsx",
    "content": "import { BrowserRouter, Routes, Route } from \"react-router-dom\";\nimport { ThemeProvider } from \"./contexts/ThemeProvider\";\nimport { TooltipProvider } from \"./components/shadcn/Tooltip\";\nimport ManifestList from \"./pages/ManifestList\";\nimport ManifestView from \"./pages/ManifestView\";\n\nexport default function App() {\n  return (\n    <ThemeProvider>\n      <TooltipProvider>\n        <BrowserRouter>\n          <Routes>\n            <Route path=\"/\" element={<ManifestList />} />\n            <Route path=\"/manifests/:id\" element={<ManifestView />} />\n          </Routes>\n        </BrowserRouter>\n      </TooltipProvider>\n    </ThemeProvider>\n  );\n}\n"
  },
  {
    "path": "viewer/src/api.ts",
    "content": "import type { DependencyManifest } from \"./types/dependencyManifest\";\nimport type { AuditManifest } from \"./types/auditManifest\";\n\nexport interface ManifestListItem {\n  id: string;\n  branch: string;\n  commitSha: string;\n  commitShaDate: string;\n  createdAt: string;\n  fileCount: number;\n}\n\nexport interface ManifestEnvelope {\n  id: string;\n  branch: string;\n  commitSha: string;\n  commitShaDate: string;\n  createdAt: string;\n  manifest: DependencyManifest;\n}\n\nconst BASE = \"/api\";\n\nexport async function fetchManifests(): Promise<ManifestListItem[]> {\n  const res = await fetch(`${BASE}/manifests`);\n  return res.json();\n}\n\nexport async function fetchManifest(id: string): Promise<ManifestEnvelope> {\n  const res = await fetch(`${BASE}/manifests/${id}`);\n  if (!res.ok) throw new Error(\"Manifest not found\");\n  return res.json();\n}\n\nexport async function fetchAudit(id: string): Promise<AuditManifest> {\n  const res = await fetch(`${BASE}/manifests/${id}/audit`);\n  if (!res.ok) throw new Error(\"Audit not found\");\n  return res.json();\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/DependencyVisualizer.tsx",
    "content": "import { useState } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport type { DependencyManifest } from \"../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../types/auditManifest.ts\";\nimport { SidebarProvider, SidebarTrigger } from \"../shadcn/Sidebar.tsx\";\nimport {\n  FileExplorerSidebar,\n  type ExplorerNodeData,\n} from \"./components/FileExplorerSidebar.tsx\";\nimport BreadcrumbNav from \"./components/BreadcrumbNav.tsx\";\nimport ProjectVisualizer from \"./visualizers/ProjectVisualizer.tsx\";\nimport FileVisualizer from \"./visualizers/FileVisualizer.tsx\";\nimport SymbolVisualizer from \"./visualizers/SymbolVisualizer.tsx\";\n\nexport default function DependencyVisualizer(props: {\n  manifestId: string;\n  dependencyManifest: DependencyManifest;\n  auditManifest: AuditManifest;\n}) {\n  const [searchParams] = useSearchParams();\n  const [highlightedCytoscapeRef, setHighlightedCytoscapeRef] = useState<\n    { filePath: string; symbolId: string | undefined } | undefined\n  >(undefined);\n\n  function handleHighlight(node: ExplorerNodeData) {\n    if (!node.fileId) return;\n    const newRef = {\n      filePath: node.fileId,\n      symbolId: node.symbolId,\n    };\n    if (\n      highlightedCytoscapeRef?.filePath === newRef.filePath &&\n      highlightedCytoscapeRef?.symbolId === newRef.symbolId\n    ) {\n      setHighlightedCytoscapeRef(undefined);\n    } else {\n      setHighlightedCytoscapeRef(newRef);\n    }\n  }\n\n  function toDetails(node: ExplorerNodeData) {\n    if (node.symbolId && node.fileId) {\n      const p = new URLSearchParams(searchParams);\n      p.set(\"fileId\", node.fileId);\n      p.set(\"instanceId\", node.symbolId);\n      return `?${p.toString()}`;\n    } else if (node.fileId) {\n      const p = new URLSearchParams(searchParams);\n      p.set(\"fileId\", node.fileId);\n      p.delete(\"instanceId\");\n      return `?${p.toString()}`;\n    } else {\n      const p = new URLSearchParams(searchParams);\n      p.delete(\"fileId\");\n      p.delete(\"instanceId\");\n      return `?${p.toString()}`;\n    }\n  }\n\n  const fileId = searchParams.get(\"fileId\");\n  const instanceId = searchParams.get(\"instanceId\");\n\n  return (\n    <SidebarProvider\n      defaultOpen={false}\n      style={{ \"--sidebar-width\": \"30rem\" } as React.CSSProperties}\n      className=\"grow flex min-h-0\"\n    >\n      <FileExplorerSidebar\n        dependencyManifest={props.dependencyManifest}\n        auditManifest={props.auditManifest}\n        onHighlightInCytoscape={handleHighlight}\n        toDetails={toDetails}\n      />\n      <div className=\"w-full flex flex-col overflow-hidden\">\n        <div className=\"flex items-center py-2 justify-between\">\n          <div className=\"flex items-center gap-2 ml-2\">\n            <SidebarTrigger />\n            <BreadcrumbNav\n              toProjectLink={() => {\n                const p = new URLSearchParams(searchParams);\n                p.delete(\"fileId\");\n                p.delete(\"instanceId\");\n                return `?${p.toString()}`;\n              }}\n              fileId={fileId}\n              toFileIdLink={(fId) => {\n                const p = new URLSearchParams(searchParams);\n                p.set(\"fileId\", fId);\n                p.delete(\"instanceId\");\n                return `?${p.toString()}`;\n              }}\n              instanceId={instanceId}\n              toInstanceIdLink={(fId, iId) => {\n                const p = new URLSearchParams(searchParams);\n                p.set(\"fileId\", fId);\n                p.set(\"instanceId\", iId);\n                return `?${p.toString()}`;\n              }}\n            />\n          </div>\n        </div>\n        <div className=\"grow w-full border-t\">\n          {fileId && instanceId ? (\n            <SymbolVisualizer\n              key={`${fileId}-${instanceId}`}\n              fileId={fileId}\n              instanceId={instanceId}\n              manifestId={props.manifestId}\n              dependencyManifest={props.dependencyManifest}\n              auditManifest={props.auditManifest}\n              highlightedCytoscapeRef={highlightedCytoscapeRef}\n            />\n          ) : fileId ? (\n            <FileVisualizer\n              key={fileId}\n              fileId={fileId}\n              manifestId={props.manifestId}\n              dependencyManifest={props.dependencyManifest}\n              auditManifest={props.auditManifest}\n              highlightedCytoscapeRef={highlightedCytoscapeRef}\n            />\n          ) : (\n            <ProjectVisualizer\n              manifestId={props.manifestId}\n              dependencyManifest={props.dependencyManifest}\n              auditManifest={props.auditManifest}\n              highlightedCytoscapeRef={highlightedCytoscapeRef}\n            />\n          )}\n        </div>\n      </div>\n    </SidebarProvider>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/BreadcrumbNav.tsx",
    "content": "import { Link } from \"react-router-dom\";\nimport {\n  Breadcrumb,\n  BreadcrumbItem,\n  BreadcrumbLink,\n  BreadcrumbList,\n  BreadcrumbSeparator,\n} from \"../../shadcn/Breadcrumb.tsx\";\n\nexport default function BreadcrumbNav(props: {\n  toProjectLink: () => string;\n  fileId: string | null;\n  toFileIdLink: (fileId: string) => string;\n  instanceId: string | null;\n  toInstanceIdLink: (fileId: string, instanceId: string) => string;\n}) {\n  return (\n    <Breadcrumb>\n      <BreadcrumbList>\n        <BreadcrumbItem>\n          <BreadcrumbLink asChild>\n            <Link to={props.toProjectLink()}>Project</Link>\n          </BreadcrumbLink>\n        </BreadcrumbItem>\n        {props.fileId && (\n          <>\n            <BreadcrumbSeparator />\n            <BreadcrumbItem>\n              <BreadcrumbLink asChild>\n                <Link to={props.toFileIdLink(props.fileId)}>\n                  {props.fileId}\n                </Link>\n              </BreadcrumbLink>\n            </BreadcrumbItem>\n            {props.instanceId && (\n              <>\n                <BreadcrumbSeparator />\n                <BreadcrumbItem>\n                  <BreadcrumbLink asChild>\n                    <Link\n                      to={props.toInstanceIdLink(\n                        props.fileId,\n                        props.instanceId,\n                      )}\n                    >\n                      {props.instanceId}\n                    </Link>\n                  </BreadcrumbLink>\n                </BreadcrumbItem>\n              </>\n            )}\n          </>\n        )}\n      </BreadcrumbList>\n    </Breadcrumb>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/DisplayNameWithTooltip.tsx",
    "content": "import {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"../../shadcn/Tooltip.tsx\";\n\nexport default function DisplayNameWithTooltip(props: {\n  name: string;\n  maxChar?: number;\n  truncateBefore?: boolean;\n}) {\n  const maxChar = props.maxChar || 30;\n\n  if (props.name.length > maxChar) {\n    const displayedName = props.truncateBefore\n      ? \"...\" + props.name.slice(0, maxChar)\n      : props.name.slice(0, maxChar) + \"...\";\n\n    return (\n      <Tooltip delayDuration={500}>\n        <TooltipTrigger asChild>\n          <span>{displayedName}</span>\n        </TooltipTrigger>\n        <TooltipContent>\n          <div className=\"text-sm\">{props.name}</div>\n        </TooltipContent>\n      </Tooltip>\n    );\n  }\n  return <span>{props.name}</span>;\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/FileExplorerSidebar.tsx",
    "content": "import { useEffect, useMemo, useState } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport type { DependencyManifest } from \"../../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../../types/auditManifest.ts\";\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarGroup,\n  SidebarHeader,\n  SidebarRail,\n} from \"../../shadcn/Sidebar.tsx\";\nimport { Button } from \"../../shadcn/Button.tsx\";\nimport { Input } from \"../../shadcn/Input.tsx\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"../../shadcn/Tooltip.tsx\";\nimport {\n  ChevronDown,\n  ChevronRight,\n  Code,\n  File,\n  FolderClosed,\n  FolderOpen,\n  ScanEye,\n  Search,\n  SearchCode,\n  X,\n} from \"lucide-react\";\nimport { ScrollArea, ScrollBar } from \"../../shadcn/Scrollarea.tsx\";\nimport DisplayNameWithTooltip from \"./DisplayNameWithTooltip.tsx\";\n\nexport interface ExplorerNodeData {\n  id: string;\n  displayName: string;\n  fileId?: string;\n  symbolId?: string;\n  children: Map<string, ExplorerNodeData>;\n}\n\nfunction buildExplorerTree(\n  dependencyManifest: DependencyManifest,\n  filteredSymbols: { fileId: string; symbolId: string }[],\n): ExplorerNodeData | undefined {\n  const getExplorerNodeId = (filePath: string, instanceId?: string) => {\n    if (instanceId) {\n      return `${filePath}#${instanceId}`;\n    }\n    return filePath;\n  };\n\n  const root: ExplorerNodeData = {\n    id: \"root\",\n    displayName: \"Project\",\n    children: new Map(),\n  };\n\n  const filteredSymbolsSet = new Set(\n    filteredSymbols.map((s) => `${s.fileId}#${s.symbolId}`),\n  );\n\n  const filesWithFilteredSymbols = new Set(\n    filteredSymbols.map((s) => s.fileId),\n  );\n\n  const shouldShowAll = filteredSymbols.length === 0;\n\n  let hasMatchingNodes = false;\n\n  for (const fileDependencyManifest of Object.values(dependencyManifest)) {\n    const filePath = fileDependencyManifest.filePath;\n\n    const fileShouldBeIncluded = shouldShowAll ||\n      filesWithFilteredSymbols.has(filePath);\n\n    if (!fileShouldBeIncluded) {\n      continue;\n    }\n\n    hasMatchingNodes = true;\n\n    const parts = filePath.split(\"/\");\n    let currentNode: ExplorerNodeData = root;\n    for (const part of parts) {\n      const id = getExplorerNodeId(part);\n      if (!currentNode.children.has(id)) {\n        currentNode.children.set(id, {\n          id: id,\n          displayName: part,\n          children: new Map(),\n        });\n      }\n      currentNode = currentNode.children.get(id)!;\n    }\n    currentNode.fileId = getExplorerNodeId(filePath);\n\n    for (const instanceId of Object.keys(fileDependencyManifest.symbols)) {\n      const symbolKey = `${filePath}#${instanceId}`;\n      if (shouldShowAll || filteredSymbolsSet.has(symbolKey)) {\n        const id = getExplorerNodeId(filePath, instanceId);\n        currentNode.children.set(id, {\n          id: id,\n          displayName: instanceId,\n          fileId: filePath,\n          symbolId: instanceId,\n          children: new Map(),\n        });\n      }\n    }\n  }\n\n  if (!shouldShowAll && !hasMatchingNodes) {\n    return undefined;\n  }\n\n  const flattenTree = (node: ExplorerNodeData): ExplorerNodeData => {\n    if (node.children.size > 0) {\n      const flattenedChildren = new Map<string, ExplorerNodeData>();\n\n      const folders: Array<[string, ExplorerNodeData]> = [];\n      const files: Array<[string, ExplorerNodeData]> = [];\n\n      for (const [id, child] of node.children) {\n        const flattenedChild = flattenTree(child);\n        if (flattenedChild.fileId) {\n          files.push([id, flattenedChild]);\n        } else {\n          folders.push([id, flattenedChild]);\n        }\n      }\n\n      for (const [id, folder] of folders) {\n        flattenedChildren.set(id, folder);\n      }\n      for (const [id, file] of files) {\n        flattenedChildren.set(id, file);\n      }\n\n      node.children = flattenedChildren;\n    }\n\n    while (node.children.size === 1) {\n      const childEntry = Array.from(node.children.entries())[0];\n      const child = childEntry[1];\n\n      if (child.fileId) {\n        break;\n      }\n\n      node.displayName = `${node.displayName}/${child.displayName}`;\n      node.id = child.id;\n      node.children = child.children;\n    }\n\n    return node;\n  };\n\n  return flattenTree(root);\n}\n\nfunction computeFilteredSymbols(\n  dependencyManifest: DependencyManifest,\n  searchTerm: string,\n): { fileId: string; symbolId: string }[] {\n  const term = searchTerm.toLowerCase();\n  const results: { fileId: string; symbolId: string }[] = [];\n\n  for (const file of Object.values(dependencyManifest)) {\n    const filePathMatch = file.filePath.toLowerCase().includes(term);\n\n    for (const symbolId of Object.keys(file.symbols)) {\n      if (filePathMatch || symbolId.toLowerCase().includes(term)) {\n        results.push({ fileId: file.filePath, symbolId });\n      }\n    }\n  }\n\n  return results;\n}\n\nexport function FileExplorerSidebar(props: {\n  dependencyManifest: DependencyManifest;\n  auditManifest: AuditManifest;\n  onHighlightInCytoscape: (node: ExplorerNodeData) => void;\n  toDetails: (node: ExplorerNodeData) => string;\n}) {\n  const [searchTerm, setSearchTerm] = useState(\"\");\n  const [activeSearch, setActiveSearch] = useState(\"\");\n\n  const filteredSymbols = useMemo(() => {\n    if (!activeSearch) return [];\n    return computeFilteredSymbols(props.dependencyManifest, activeSearch);\n  }, [props.dependencyManifest, activeSearch]);\n\n  const [explorerTree, setExplorerTree] = useState<ExplorerNodeData>();\n\n  useEffect(() => {\n    const tree = buildExplorerTree(props.dependencyManifest, filteredSymbols);\n    setExplorerTree(tree);\n  }, [props.dependencyManifest, filteredSymbols]);\n\n  function onSubmitSearch(e: React.FormEvent) {\n    e.preventDefault();\n    if (searchTerm.trim()) {\n      setActiveSearch(searchTerm.trim());\n    }\n  }\n\n  function onClearSearch() {\n    setSearchTerm(\"\");\n    setActiveSearch(\"\");\n  }\n\n  return (\n    <Sidebar>\n      <SidebarHeader>\n        <a\n          href=\"https://nanoapi.io\"\n          target=\"_blank\"\n          rel=\"noreferrer\"\n          className=\"flex items-center space-x-3\"\n        >\n          <img src=\"/logo.png\" alt=\"logo\" className=\"h-10\" />\n          <div className=\"text-xl font-bold\">NanoAPI</div>\n        </a>\n      </SidebarHeader>\n\n      <SidebarContent>\n        <SidebarGroup className=\"flex h-full\">\n          <ScrollArea>\n            <form\n              onSubmit={onSubmitSearch}\n              className=\"flex items-center gap-2\"\n            >\n              <Input\n                value={searchTerm}\n                onChange={(e) => setSearchTerm(e.target.value)}\n                placeholder=\"Search files & symbols\"\n              />\n              <Button\n                type=\"submit\"\n                variant=\"ghost\"\n                size=\"icon\"\n              >\n                <Search />\n              </Button>\n              {activeSearch && (\n                <Button\n                  onClick={onClearSearch}\n                  variant=\"ghost\"\n                  size=\"icon\"\n                >\n                  <X />\n                </Button>\n              )}\n            </form>\n\n            <div className=\"pt-2\">\n              {!explorerTree\n                ? (\n                  <div className=\"text-sm font-muted italic\">\n                    No matching files found\n                  </div>\n                )\n                : (\n                  <ExplorerNode\n                    node={explorerTree}\n                    level={0}\n                    onHighlightInCytoscape={props.onHighlightInCytoscape}\n                    toDetails={props.toDetails}\n                  />\n                )}\n            </div>\n            <ScrollBar orientation=\"vertical\" />\n          </ScrollArea>\n        </SidebarGroup>\n      </SidebarContent>\n      <SidebarRail />\n    </Sidebar>\n  );\n}\n\nfunction ExplorerNode(props: {\n  node: ExplorerNodeData;\n  level: number;\n  onHighlightInCytoscape: (node: ExplorerNodeData) => void;\n  toDetails: (node: ExplorerNodeData) => string;\n}) {\n  const [showChildren, setShowChildren] = useState<boolean>(false);\n\n  const type: \"folder\" | \"file\" | \"symbol\" = props.node.symbolId\n    ? \"symbol\"\n    : props.node.fileId\n      ? \"file\"\n      : \"folder\";\n\n  return (\n    <div\n      className=\"w-full space-y-1\"\n      style={{ paddingLeft: `${props.level / 2}rem` }}\n    >\n      {(() => {\n        switch (type) {\n          case \"folder\":\n            return (\n              <Button\n                variant=\"ghost\"\n                size=\"sm\"\n                onClick={() => setShowChildren(!showChildren)}\n                className=\"w-full justify-start\"\n              >\n                {showChildren ? <FolderOpen /> : <FolderClosed />}\n                <DisplayNameWithTooltip\n                  name={props.node.displayName}\n                  maxChar={Math.max(5, 30 - props.level * 2)}\n                />\n              </Button>\n            );\n          case \"file\":\n            return (\n              <div className=\"flex justify-between items-center gap-2\">\n                <div className=\"grow flex items-center gap-2\">\n                  <Button\n                    variant=\"ghost\"\n                    size=\"sm\"\n                    onClick={() => setShowChildren(!showChildren)}\n                    className=\"w-full justify-start\"\n                  >\n                    {showChildren\n                      ? <ChevronDown size={16} />\n                      : <ChevronRight size={16} />}\n                    <File />\n                    <DisplayNameWithTooltip\n                      name={props.node.displayName}\n                      maxChar={Math.max(5, 30 - props.level * 2)}\n                    />\n                  </Button>\n                </div>\n                <div className=\"flex items-center gap-1\">\n                  <Tooltip delayDuration={500}>\n                    <TooltipTrigger asChild>\n                      <Button\n                        variant=\"secondary\"\n                        size=\"sm\"\n                        onClick={() =>\n                          props.onHighlightInCytoscape(props.node)}\n                      >\n                        <ScanEye />\n                      </Button>\n                    </TooltipTrigger>\n                    <TooltipContent className=\"text-xs\">\n                      Highlight in graph\n                    </TooltipContent>\n                  </Tooltip>\n                  <Tooltip delayDuration={500}>\n                    <TooltipTrigger asChild>\n                      <Button asChild variant=\"secondary\" size=\"sm\">\n                        <Link to={props.toDetails(props.node)}>\n                          <SearchCode />\n                        </Link>\n                      </Button>\n                    </TooltipTrigger>\n                    <TooltipContent className=\"text-xs\">\n                      View graph for this file\n                    </TooltipContent>\n                  </Tooltip>\n                </div>\n              </div>\n            );\n          case \"symbol\":\n            return (\n              <div className=\"flex justify-between items-center gap-2\">\n                <div className=\"grow flex items-center gap-2\">\n                  <Code size={12} />\n                  <DisplayNameWithTooltip\n                    name={props.node.displayName}\n                    maxChar={Math.max(5, 30 - props.level * 2)}\n                  />\n                </div>\n                <div className=\"flex items-center gap-1\">\n                  <Tooltip delayDuration={500}>\n                    <TooltipTrigger asChild>\n                      <Button\n                        variant=\"secondary\"\n                        size=\"sm\"\n                        onClick={() =>\n                          props.onHighlightInCytoscape(props.node)}\n                      >\n                        <ScanEye />\n                      </Button>\n                    </TooltipTrigger>\n                    <TooltipContent className=\"text-xs\">\n                      Highlight in graph\n                    </TooltipContent>\n                  </Tooltip>\n                  <Tooltip delayDuration={500}>\n                    <TooltipTrigger asChild>\n                      <Button asChild variant=\"secondary\" size=\"sm\">\n                        <Link to={props.toDetails(props.node)}>\n                          <SearchCode />\n                        </Link>\n                      </Button>\n                    </TooltipTrigger>\n                    <TooltipContent className=\"text-xs\">\n                      View graph for this symbol\n                    </TooltipContent>\n                  </Tooltip>\n                </div>\n              </div>\n            );\n        }\n      })()}\n      {showChildren &&\n        Array.from(props.node.children.values()).map((child) => (\n          <ExplorerNode\n            key={child.id}\n            node={child}\n            level={props.level + 1}\n            onHighlightInCytoscape={props.onHighlightInCytoscape}\n            toDetails={props.toDetails}\n          />\n        ))}\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/SymbolExtractionDialog.tsx",
    "content": "import { useState } from \"react\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"../../shadcn/Dialog.tsx\";\nimport { Button } from \"../../shadcn/Button.tsx\";\nimport {\n  Card,\n  CardContent,\n  CardHeader,\n  CardTitle,\n} from \"../../shadcn/Card.tsx\";\nimport { Alert, AlertDescription, AlertTitle } from \"../../shadcn/Alert.tsx\";\nimport { Separator } from \"../../shadcn/Separator.tsx\";\nimport { Code, Copy, Info, Terminal } from \"lucide-react\";\nimport { ScrollArea } from \"../../shadcn/Scrollarea.tsx\";\n\nexport default function SymbolExtractionDialog(props: {\n  manifestId: string;\n  children: React.ReactNode;\n  filePath: string;\n  symbolIds: string[];\n}) {\n  const [open, setOpen] = useState(false);\n  const [copied, setCopied] = useState(false);\n\n  const generateCommand = () => {\n    const symbolOptions = props.symbolIds.map((symbolId) => {\n      return `--symbol=\"${props.filePath}|${symbolId}\"`;\n    });\n    return `napi extract --manifestId=${props.manifestId} ${symbolOptions.join(\" \")}`;\n  };\n\n  const copyToClipboard = async () => {\n    try {\n      await navigator.clipboard.writeText(generateCommand());\n      setCopied(true);\n      setTimeout(() => setCopied(false), 2000);\n    } catch (err) {\n      console.error(\"Failed to copy to clipboard:\", err);\n    }\n  };\n\n  return (\n    <Dialog open={open} onOpenChange={setOpen}>\n      <DialogTrigger asChild>\n        {props.children}\n      </DialogTrigger>\n      <DialogContent className=\"max-h-screen overflow-y-auto\">\n        <ScrollArea>\n          <DialogHeader>\n            <DialogTitle className=\"flex items-center gap-2\">\n              <Terminal />\n              Extract Symbol(s) via CLI\n            </DialogTitle>\n            <DialogDescription>\n              Use the napi CLI to extract symbols from your program.\n            </DialogDescription>\n          </DialogHeader>\n          <div className=\"space-y-4\">\n            <Alert>\n              <AlertTitle className=\"flex items-center gap-2\">\n                <Info />\n                Prerequisites\n              </AlertTitle>\n              <AlertDescription className=\"flex flex-col gap-2\">\n                <div>\n                  Ensure you have a napi manifest generated locally:\n                </div>\n                <code className=\"bg-muted p-1 rounded text-sm\">\n                  napi generate\n                </code>\n              </AlertDescription>\n            </Alert>\n            <Separator />\n            <Card>\n              <CardHeader>\n                <CardTitle className=\"flex items-center justify-between gap-2\">\n                  <div className=\"flex items-center gap-2\">\n                    <Code />\n                    Command\n                  </div>\n                  <Button variant=\"outline\" onClick={copyToClipboard}>\n                    <Copy />\n                    {copied ? \"Copied!\" : \"Copy\"}\n                  </Button>\n                </CardTitle>\n              </CardHeader>\n              <CardContent>\n                <code className=\"text-sm font-mono flex-1 break-all\">\n                  {generateCommand()}\n                </code>\n              </CardContent>\n            </Card>\n          </div>\n        </ScrollArea>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/contextMenu/FileContextMenu.tsx",
    "content": "import { Link, useSearchParams } from \"react-router-dom\";\nimport type { DependencyManifest } from \"../../../../types/dependencyManifest.ts\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"../../../shadcn/Dropdownmenu.tsx\";\nimport { PanelRight, SearchCode } from \"lucide-react\";\nimport DisplayNameWithTooltip from \"../DisplayNameWithTooltip.tsx\";\n\nexport default function FileContextMenu(props: {\n  context:\n    | {\n      position: { x: number; y: number };\n      fileDependencyManifest: DependencyManifest[string];\n    }\n    | undefined;\n  onClose: () => void;\n  onOpenDetails: (filePath: string) => void;\n}) {\n  const [searchParams] = useSearchParams();\n\n  function getToFileLink(filePath: string) {\n    const newSearchParams = new URLSearchParams(searchParams);\n    newSearchParams.set(\"fileId\", filePath);\n    newSearchParams.delete(\"instanceId\");\n    return `?${newSearchParams.toString()}`;\n  }\n\n  return (\n    <div\n      className=\"absolute z-50\"\n      style={{\n        top: props.context?.position.y,\n        left: props.context?.position.x,\n      }}\n    >\n      <DropdownMenu\n        open={props.context !== undefined}\n        onOpenChange={() => props.onClose()}\n      >\n        <DropdownMenuTrigger />\n        <DropdownMenuContent>\n          <DropdownMenuLabel>\n            <DisplayNameWithTooltip\n              name={props.context?.fileDependencyManifest?.filePath || \"\"}\n              maxChar={30}\n            />\n          </DropdownMenuLabel>\n          <DropdownMenuSeparator />\n          <DropdownMenuItem\n            onClick={() => {\n              props.context?.fileDependencyManifest &&\n                props.onOpenDetails(\n                  props.context.fileDependencyManifest.filePath,\n                );\n              props.onClose();\n            }}\n          >\n            <div className=\"flex items-center space-x-2\">\n              <PanelRight />\n              <div>Show details</div>\n            </div>\n          </DropdownMenuItem>\n          <DropdownMenuItem asChild>\n            <Link\n              to={getToFileLink(\n                props.context?.fileDependencyManifest?.filePath || \"\",\n              )}\n            >\n              <div className=\"flex items-center space-x-2\">\n                <SearchCode />\n                <div>Inspect symbols</div>\n              </div>\n            </Link>\n          </DropdownMenuItem>\n        </DropdownMenuContent>\n      </DropdownMenu>\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/contextMenu/SymbolContextMenu.tsx",
    "content": "import { Link, useSearchParams } from \"react-router-dom\";\nimport type { DependencyManifest } from \"../../../../types/dependencyManifest.ts\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"../../../shadcn/Dropdownmenu.tsx\";\nimport { PanelRight, SearchCode } from \"lucide-react\";\nimport DisplayNameWithTooltip from \"../DisplayNameWithTooltip.tsx\";\n\nexport default function SymbolContextMenu(props: {\n  context:\n    | {\n      position: { x: number; y: number };\n      fileDependencyManifest: DependencyManifest[string];\n      symbolDependencyManifest: DependencyManifest[string][\"symbols\"][string];\n    }\n    | undefined;\n  onClose: () => void;\n  onOpenDetails: (filePath: string, symbolId: string) => void;\n}) {\n  const [searchParams] = useSearchParams();\n\n  function getToSymbolLink(filePath: string, symbolId: string) {\n    const newSearchParams = new URLSearchParams(searchParams);\n    newSearchParams.set(\"fileId\", filePath);\n    newSearchParams.set(\"instanceId\", symbolId);\n    return `?${newSearchParams.toString()}`;\n  }\n\n  return (\n    <div\n      className=\"absolute z-50\"\n      style={{\n        top: props.context?.position.y,\n        left: props.context?.position.x,\n      }}\n    >\n      <DropdownMenu\n        open={props.context !== undefined}\n        onOpenChange={() => props.onClose()}\n      >\n        <DropdownMenuTrigger />\n        <DropdownMenuContent>\n          <DropdownMenuLabel>\n            <DisplayNameWithTooltip\n              name={`${props.context?.symbolDependencyManifest?.id} (${props.context?.symbolDependencyManifest?.type})`}\n              maxChar={30}\n              truncateBefore\n            />\n          </DropdownMenuLabel>\n          <DropdownMenuSeparator />\n          <DropdownMenuItem\n            onClick={() => {\n              props.context?.fileDependencyManifest &&\n                props.onOpenDetails(\n                  props.context.fileDependencyManifest.filePath,\n                  props.context.symbolDependencyManifest.id,\n                );\n              props.onClose();\n            }}\n          >\n            <div className=\"flex items-center space-x-2\">\n              <PanelRight />\n              <div>Show details</div>\n            </div>\n          </DropdownMenuItem>\n          <DropdownMenuItem asChild>\n            <Link\n              to={getToSymbolLink(\n                props.context?.fileDependencyManifest?.filePath || \"\",\n                props.context?.symbolDependencyManifest?.id || \"\",\n              )}\n            >\n              <div className=\"flex items-center space-x-2\">\n                <SearchCode />\n                <div>Inspect symbol</div>\n              </div>\n            </Link>\n          </DropdownMenuItem>\n        </DropdownMenuContent>\n      </DropdownMenu>\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/controls/ControlExtensions/FiltersExtension.tsx",
    "content": "import { type MouseEvent, useEffect, useState } from \"react\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"../../../../shadcn/Tooltip.tsx\";\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"../../../../shadcn/Dropdownmenu.tsx\";\nimport { Button } from \"../../../../shadcn/Button.tsx\";\nimport { Funnel } from \"lucide-react\";\n\nexport default function FiltersExtension(props: {\n  busy: boolean;\n  currentFileName?: string;\n  onFilterChange: (\n    showExternal: boolean,\n    showVariables: boolean,\n    showFunctions: boolean,\n    showClasses: boolean,\n    showStructs: boolean,\n    showEnums: boolean,\n    showInterfaces: boolean,\n    showRecords: boolean,\n    showDelegates: boolean,\n  ) => void;\n}) {\n  const [showExternal, setShowExternal] = useState(true);\n  const [showVariables, setShowVariables] = useState(true);\n  const [showFunctions, setShowFunctions] = useState(true);\n  const [showClasses, setShowClasses] = useState(true);\n  const [showStructs, setShowStructs] = useState(true);\n  const [showEnums, setShowEnums] = useState(true);\n  const [showInterfaces, setShowInterfaces] = useState(true);\n  const [showRecords, setShowRecords] = useState(true);\n  const [showDelegates, setShowDelegates] = useState(true);\n\n  useEffect(() => {\n    props.onFilterChange(\n      showExternal, showVariables, showFunctions, showClasses,\n      showStructs, showEnums, showInterfaces, showRecords, showDelegates,\n    );\n  }, [showExternal, showVariables, showFunctions, showClasses, showStructs, showEnums, showInterfaces, showRecords, showDelegates]);\n\n  function handleFilterClick(\n    e: MouseEvent<HTMLDivElement, globalThis.MouseEvent>,\n    set: React.Dispatch<React.SetStateAction<boolean>>,\n  ) {\n    e.preventDefault();\n    set((prev) => !prev);\n  }\n\n  return (\n    <Tooltip delayDuration={500}>\n      <DropdownMenu>\n        <DropdownMenuTrigger asChild>\n          <TooltipTrigger asChild>\n            <Button variant=\"ghost\" disabled={props.busy}>\n              <Funnel />\n            </Button>\n          </TooltipTrigger>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent>\n          <DropdownMenuLabel>Filters</DropdownMenuLabel>\n          <DropdownMenuSeparator />\n          {[\n            { label: \"Show external\", checked: showExternal, set: setShowExternal },\n            { label: \"Show variables\", checked: showVariables, set: setShowVariables },\n            { label: \"Show functions\", checked: showFunctions, set: setShowFunctions },\n            { label: \"Show classes\", checked: showClasses, set: setShowClasses },\n            { label: \"Show structs\", checked: showStructs, set: setShowStructs },\n            { label: \"Show enums\", checked: showEnums, set: setShowEnums },\n            { label: \"Show interfaces\", checked: showInterfaces, set: setShowInterfaces },\n            { label: \"Show records\", checked: showRecords, set: setShowRecords },\n            { label: \"Show delegates\", checked: showDelegates, set: setShowDelegates },\n          ].map(({ label, checked, set }) => (\n            <DropdownMenuCheckboxItem\n              key={label}\n              checked={checked}\n              onClick={(e) => handleFilterClick(e, set)}\n            >\n              {label}\n            </DropdownMenuCheckboxItem>\n          ))}\n        </DropdownMenuContent>\n      </DropdownMenu>\n      <TooltipContent>Hide or show specific elements in the graph.</TooltipContent>\n    </Tooltip>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/controls/ControlExtensions/GraphDepthExtension.tsx",
    "content": "import { useState } from \"react\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"../../../../shadcn/Tooltip.tsx\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuTrigger,\n} from \"../../../../shadcn/Dropdownmenu.tsx\";\nimport { Button } from \"../../../../shadcn/Button.tsx\";\nimport { Settings2 } from \"lucide-react\";\nimport { Slider } from \"../../../../shadcn/Slider.tsx\";\nimport { Input } from \"../../../../shadcn/Input.tsx\";\nimport { Label } from \"../../../../shadcn/Label.tsx\";\n\nexport default function GraphDepthExtension(props: {\n  busy: boolean;\n  dependencyState: {\n    depth: number;\n    setDepth: (depth: number) => void;\n  };\n  dependentState: {\n    depth: number;\n    setDepth: (depth: number) => void;\n  };\n}) {\n  const { dependencyState, dependentState } = props;\n  const [tempDependencyDepth, setTempDependencyDepth] = useState(dependencyState.depth);\n  const [tempDependentDepth, setTempDependentDepth] = useState(dependentState.depth);\n\n  function handleSubmit(e: React.FormEvent<HTMLFormElement>) {\n    e.preventDefault();\n    if (tempDependencyDepth !== dependencyState.depth) {\n      dependencyState.setDepth(tempDependencyDepth);\n    }\n    if (tempDependentDepth !== dependentState.depth) {\n      dependentState.setDepth(tempDependentDepth);\n    }\n  }\n\n  return (\n    <Tooltip delayDuration={500}>\n      <DropdownMenu>\n        <DropdownMenuTrigger asChild>\n          <TooltipTrigger asChild>\n            <Button variant=\"ghost\" disabled={props.busy}>\n              <Settings2 />\n            </Button>\n          </TooltipTrigger>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent>\n          <form onSubmit={handleSubmit} className=\"flex flex-col space-y-2 min-w-[200px] p-1\">\n            <Label htmlFor=\"dependency-depth\">Dependency Depth</Label>\n            <div className=\"flex items-center space-x-2\">\n              <Slider min={0} max={20} step={1} value={[tempDependencyDepth]} disabled={props.busy}\n                onValueChange={(value) => setTempDependencyDepth(value[0])} />\n              <Input id=\"dependency-depth\" type=\"number\" value={tempDependencyDepth} min={0} step={1}\n                onChange={(e) => setTempDependencyDepth(parseInt(e.target.value, 10))} className=\"w-20\" />\n            </div>\n            <Label htmlFor=\"dependent-depth\">Dependent Depth</Label>\n            <div className=\"flex items-center space-x-2\">\n              <Slider min={0} max={20} step={1} value={[tempDependentDepth]} disabled={props.busy}\n                onValueChange={(value) => setTempDependentDepth(value[0])} />\n              <Input id=\"dependent-depth\" type=\"number\" value={tempDependentDepth} min={0} step={1}\n                onChange={(e) => setTempDependentDepth(parseInt(e.target.value, 10))} disabled={props.busy} className=\"w-20\" />\n            </div>\n            <Button type=\"submit\" disabled={props.busy}>Apply</Button>\n          </form>\n        </DropdownMenuContent>\n      </DropdownMenu>\n      <TooltipContent>Set the depth of the dependencies shown on the graph.</TooltipContent>\n    </Tooltip>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/controls/ControlExtensions/MetricsExtension.tsx",
    "content": "import {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"../../../../shadcn/Tooltip.tsx\";\nimport { Button } from \"../../../../shadcn/Button.tsx\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"../../../../shadcn/Dropdownmenu.tsx\";\nimport type { Metric } from \"../../../../../types/dependencyManifest.ts\";\nimport {\n  metricLinesCount,\n  metricCodeLineCount,\n  metricCharacterCount,\n  metricCodeCharacterCount,\n  metricDependencyCount,\n  metricDependentCount,\n  metricCyclomaticComplexity,\n} from \"../../../../../types/dependencyManifest.ts\";\n\nexport default function MetricsExtension(props: {\n  busy: boolean;\n  metricState: {\n    metric: Metric | undefined;\n    setMetric: (metric: Metric | undefined) => void;\n  };\n}) {\n  const metric = props.metricState.metric;\n\n  function getMetricLabel(metric: Metric | undefined) {\n    switch (metric) {\n      case metricLinesCount: return \"Lines\";\n      case metricCodeLineCount: return \"Code Lines\";\n      case metricCharacterCount: return \"Chars\";\n      case metricCodeCharacterCount: return \"Code Chars\";\n      case metricDependencyCount: return \"Dependencies\";\n      case metricDependentCount: return \"Dependents\";\n      case metricCyclomaticComplexity: return \"Complexity\";\n      default: return \"None\";\n    }\n  }\n\n  return (\n    <Tooltip delayDuration={500}>\n      <DropdownMenu>\n        <DropdownMenuTrigger asChild>\n          <TooltipTrigger asChild>\n            <Button variant=\"ghost\" disabled={props.busy}>\n              {getMetricLabel(metric)}\n            </Button>\n          </TooltipTrigger>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent>\n          {([\n            { metric: undefined, label: \"No Metric\" },\n            { metric: metricLinesCount, label: \"Lines\" },\n            { metric: metricCodeLineCount, label: \"Code Lines\" },\n            { metric: metricCharacterCount, label: \"Total Characters\" },\n            { metric: metricCodeCharacterCount, label: \"Code Characters\" },\n            { metric: metricDependencyCount, label: \"Dependencies\" },\n            { metric: metricDependentCount, label: \"Dependents\" },\n            { metric: metricCyclomaticComplexity, label: \"Complexity\" },\n          ] as { metric: Metric | undefined; label: string }[]).map((val) => (\n            <DropdownMenuItem\n              key={val.label}\n              onClick={() => props.metricState?.setMetric?.(val.metric)}\n            >\n              {val.label}\n            </DropdownMenuItem>\n          ))}\n        </DropdownMenuContent>\n      </DropdownMenu>\n      <TooltipContent>Select a metric to display on the graph</TooltipContent>\n    </Tooltip>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/controls/Controls.tsx",
    "content": "import type { ReactNode } from \"react\";\nimport { Button } from \"../../../shadcn/Button.tsx\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"../../../shadcn/Tooltip.tsx\";\nimport type { Core } from \"cytoscape\";\nimport { Focus, Network, ZoomIn, ZoomOut } from \"lucide-react\";\n\nexport default function Controls(props: {\n  busy: boolean;\n  cy: Core | undefined;\n  onLayout: () => void;\n  children?: ReactNode;\n}) {\n  function handleFit() {\n    if (!props.cy) return;\n    const elements = props.cy.elements();\n    const padding = 10;\n    props.cy.center(elements).fit(elements, padding);\n  }\n\n  function handleZoom(zoom: number) {\n    if (!props.cy) return;\n    const level = props.cy.zoom() * zoom;\n    const x = props.cy.width() / 2;\n    const y = props.cy.height() / 2;\n    props.cy.zoom({ level, renderedPosition: { x, y } });\n  }\n\n  return (\n    <div className=\"flex space-x-1 p-1 rounded-lg border bg-background\">\n      <Tooltip delayDuration={500}>\n        <TooltipTrigger asChild>\n          <Button size=\"icon\" variant=\"ghost\" disabled={props.busy} onClick={handleFit}>\n            <Focus />\n          </Button>\n        </TooltipTrigger>\n        <TooltipContent>Fit to screen</TooltipContent>\n      </Tooltip>\n      <Tooltip delayDuration={500}>\n        <TooltipTrigger asChild>\n          <Button size=\"icon\" variant=\"ghost\" disabled={props.busy} onClick={() => props.onLayout()}>\n            <Network />\n          </Button>\n        </TooltipTrigger>\n        <TooltipContent>Reset layout</TooltipContent>\n      </Tooltip>\n      <Tooltip delayDuration={500}>\n        <TooltipTrigger asChild>\n          <Button size=\"icon\" variant=\"ghost\" disabled={props.busy} onClick={() => handleZoom(0.9)}>\n            <ZoomOut />\n          </Button>\n        </TooltipTrigger>\n        <TooltipContent>Zoom out</TooltipContent>\n      </Tooltip>\n      <Tooltip delayDuration={500}>\n        <TooltipTrigger asChild>\n          <Button size=\"icon\" variant=\"ghost\" disabled={props.busy} onClick={() => handleZoom(1.1)}>\n            <ZoomIn />\n          </Button>\n        </TooltipTrigger>\n        <TooltipContent>Zoom in</TooltipContent>\n      </Tooltip>\n      {props.children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/detailsPanes/AlertBadge.tsx",
    "content": "import { Check, TriangleAlert } from \"lucide-react\";\n\nexport default function AlertBadge(props: { count: number }) {\n  return (\n    <div className=\"flex items-center space-x-2\">\n      {props.count > 0\n        ? (\n          <>\n            <TriangleAlert color=\"red\" />\n            {props.count}\n          </>\n        )\n        : <Check color=\"green\" />}\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/detailsPanes/FileDetailsPane.tsx",
    "content": "import type { DependencyManifest } from \"../../../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../../../types/auditManifest.ts\";\nimport {\n  Sheet,\n  SheetContent,\n  SheetHeader,\n  SheetTitle,\n} from \"../../../shadcn/Sheet.tsx\";\nimport {\n  Card,\n  CardContent,\n  CardHeader,\n  CardTitle,\n} from \"../../../shadcn/Card.tsx\";\nimport { Code, File, SearchCode } from \"lucide-react\";\nimport { ScrollArea } from \"../../../shadcn/Scrollarea.tsx\";\nimport { Button } from \"../../../shadcn/Button.tsx\";\nimport { Link, useSearchParams } from \"react-router-dom\";\nimport DisplayNameWithTooltip from \"../DisplayNameWithTooltip.tsx\";\nimport Metrics from \"./Metrics.tsx\";\nimport AlertBadge from \"./AlertBadge.tsx\";\n\nexport default function FileDetailsPane(props: {\n  context:\n    | {\n      manifestId: string;\n      fileDependencyManifest: DependencyManifest[string];\n      fileAuditManifest: AuditManifest[string];\n    }\n    | undefined;\n  onClose: () => void;\n}) {\n  const [searchParams] = useSearchParams();\n\n  function getToFileLink(filePath: string) {\n    const newSearchParams = new URLSearchParams(searchParams);\n    newSearchParams.set(\"fileId\", filePath);\n    newSearchParams.delete(\"instanceId\");\n    return `?${newSearchParams.toString()}`;\n  }\n\n  return (\n    <div className=\"absolute z-50\">\n      <Sheet\n        open={props.context !== undefined}\n        onOpenChange={() => props.onClose()}\n      >\n        <SheetContent>\n          <SheetHeader>\n            <SheetTitle>\n              <div className=\"flex items-center space-x-2\">\n                <File />\n                <DisplayNameWithTooltip\n                  name={props.context?.fileDependencyManifest.filePath || \"\"}\n                  maxChar={30}\n                />\n              </div>\n            </SheetTitle>\n            <Button\n              asChild\n              variant=\"secondary\"\n              size=\"sm\"\n              onClick={props.onClose}\n            >\n              <Link\n                to={getToFileLink(\n                  props.context?.fileDependencyManifest.filePath || \"\",\n                )}\n              >\n                <SearchCode />\n                View graph for this file\n              </Link>\n            </Button>\n          </SheetHeader>\n          <ScrollArea>\n            <div className=\"flex flex-col space-y-2\">\n              <Card>\n                <CardHeader>\n                  <CardTitle className=\"flex justify-between items-center\">\n                    <div className=\"flex items-center space-x-2\">\n                      <File />\n                      <div>File Metrics</div>\n                    </div>\n                    <AlertBadge\n                      count={Object.keys(\n                        props.context?.fileAuditManifest?.alerts || {},\n                      ).length || 0}\n                    />\n                  </CardTitle>\n                </CardHeader>\n                <CardContent>\n                  <Metrics\n                    dependencyManifest={props.context?.fileDependencyManifest}\n                    auditManifest={props.context?.fileAuditManifest}\n                  />\n                </CardContent>\n                <CardHeader>\n                  <CardTitle>\n                    <div className=\"flex items-center space-x-2\">\n                      <Code />\n                      <div>\n                        Symbols (\n                        {Object.keys(\n                          props.context?.fileDependencyManifest?.symbols || {},\n                        ).length || 0}\n                        )\n                      </div>\n                    </div>\n                  </CardTitle>\n                </CardHeader>\n                <CardContent>\n                  <div className=\"flex flex-col space-y-2\">\n                    {Object.entries(\n                      Object.values(\n                        props.context?.fileDependencyManifest?.symbols || {},\n                      ).reduce(\n                        (acc, symbol) => {\n                          acc[symbol.type] = (acc[symbol.type] || 0) + 1;\n                          return acc;\n                        },\n                        {} as Record<string, number>,\n                      ),\n                    ).map(([type, count]) => (\n                      <div\n                        key={type}\n                        className=\"flex items-center justify-between\"\n                      >\n                        <div>{type}</div>\n                        <div>{count}</div>\n                      </div>\n                    ))}\n                  </div>\n                </CardContent>\n              </Card>\n              {Object.values(\n                props.context?.fileDependencyManifest?.symbols || {},\n              ).map((symbol) => (\n                <Card key={symbol.id}>\n                  <CardHeader>\n                    <CardTitle>\n                      <div className=\"flex items-center space-x-2\">\n                        <Code />\n                        <DisplayNameWithTooltip\n                          name={`${symbol.id} (${symbol.type})`}\n                          maxChar={30}\n                          truncateBefore\n                        />\n                      </div>\n                    </CardTitle>\n                  </CardHeader>\n                  <CardContent>\n                    <Metrics\n                      dependencyManifest={symbol}\n                      auditManifest={\n                        props.context?.fileAuditManifest?.symbols?.[symbol.id]\n                      }\n                    />\n                  </CardContent>\n                </Card>\n              ))}\n            </div>\n          </ScrollArea>\n        </SheetContent>\n      </Sheet>\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/detailsPanes/Metrics.tsx",
    "content": "import type { DependencyManifest } from \"../../../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../../../types/auditManifest.ts\";\nimport { Alert, AlertDescription } from \"../../../shadcn/Alert.tsx\";\n\nexport default function Metrics(props: {\n  dependencyManifest:\n    | DependencyManifest[string]\n    | DependencyManifest[string][\"symbols\"][string]\n    | undefined;\n  auditManifest:\n    | AuditManifest[string]\n    | AuditManifest[string][\"symbols\"][string]\n    | undefined;\n}) {\n  function metricToHumanString(metric: string) {\n    switch (metric) {\n      case \"linesCount\":\n        return \"Lines\";\n      case \"codeLineCount\":\n        return \"Code Lines\";\n      case \"characterCount\":\n        return \"Characters\";\n      case \"codeCharacterCount\":\n        return \"Code Characters\";\n      case \"dependencyCount\":\n        return \"Dependencies\";\n      case \"dependentCount\":\n        return \"Dependents\";\n      case \"cyclomaticComplexity\":\n        return \"Cyclomatic Complexity\";\n      default:\n        return metric;\n    }\n  }\n\n  return (\n    <div className=\"flex flex-col space-y-2\">\n      {Object.entries(props.dependencyManifest?.metrics || {}).map((\n        [key, value],\n      ) => (\n        <div key={key} className=\"flex flex-col space-y-2\">\n          <div className=\"flex items-center justify-between\">\n            <div className=\"text-sm font-medium\">\n              {metricToHumanString(key)}\n            </div>\n            <div>{value}</div>\n          </div>\n          {(props.auditManifest?.alerts || {})?.[key] && (\n            <Alert variant=\"destructive\">\n              <AlertDescription>\n                {props.auditManifest?.alerts?.[key]?.message?.long}\n              </AlertDescription>\n            </Alert>\n          )}\n        </div>\n      ))}\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/components/detailsPanes/SymbolDetailsPane.tsx",
    "content": "import type { DependencyManifest } from \"../../../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../../../types/auditManifest.ts\";\nimport {\n  Sheet,\n  SheetContent,\n  SheetHeader,\n  SheetTitle,\n  SheetTrigger,\n} from \"../../../shadcn/Sheet.tsx\";\nimport {\n  Card,\n  CardContent,\n  CardHeader,\n  CardTitle,\n} from \"../../../shadcn/Card.tsx\";\nimport { Code, File, SearchCode } from \"lucide-react\";\nimport { ScrollArea } from \"../../../shadcn/Scrollarea.tsx\";\nimport { Button } from \"../../../shadcn/Button.tsx\";\nimport { Link, useSearchParams } from \"react-router-dom\";\nimport DisplayNameWithTooltip from \"../DisplayNameWithTooltip.tsx\";\nimport Metrics from \"./Metrics.tsx\";\nimport AlertBadge from \"./AlertBadge.tsx\";\n\nexport default function SymbolDetailsPane(props: {\n  context:\n    | {\n      manifestId: string;\n      fileDependencyManifest: DependencyManifest[string];\n      symbolDependencyManifest: DependencyManifest[string][\"symbols\"][string];\n      fileAuditManifest: AuditManifest[string];\n      symbolAuditManifest: AuditManifest[string][\"symbols\"][string];\n    }\n    | undefined;\n  onClose: () => void;\n}) {\n  const [searchParams] = useSearchParams();\n\n  function getToSymbolLink(filePath: string, symbolId: string) {\n    const newSearchParams = new URLSearchParams(searchParams);\n    newSearchParams.set(\"fileId\", filePath);\n    newSearchParams.set(\"instanceId\", symbolId);\n    return `?${newSearchParams.toString()}`;\n  }\n\n  function getToFileLink(filePath: string) {\n    const newSearchParams = new URLSearchParams(searchParams);\n    newSearchParams.set(\"fileId\", filePath);\n    newSearchParams.delete(\"instanceId\");\n    return `?${newSearchParams.toString()}`;\n  }\n\n  return (\n    <div className=\"absolute z-50\">\n      <Sheet\n        open={props.context !== undefined}\n        onOpenChange={() => props.onClose()}\n      >\n        <SheetTrigger />\n        <SheetContent\n          className=\"flex flex-col space-y-2\"\n          aria-describedby={undefined}\n        >\n          <SheetHeader>\n            <SheetTitle className=\"flex flex-col space-y-2\">\n              <div className=\"flex items-center space-x-2\">\n                <Code />\n                <DisplayNameWithTooltip\n                  name={`${props.context?.symbolDependencyManifest?.id} (${props.context?.symbolDependencyManifest?.type})`}\n                  maxChar={30}\n                  truncateBefore\n                />\n              </div>\n              <div className=\"flex items-center space-x-2\">\n                <File />\n                <DisplayNameWithTooltip\n                  name={props.context?.fileDependencyManifest?.filePath || \"\"}\n                  maxChar={30}\n                />\n              </div>\n            </SheetTitle>\n            <Button\n              asChild\n              variant=\"secondary\"\n              size=\"sm\"\n              onClick={props.onClose}\n            >\n              <Link\n                to={getToSymbolLink(\n                  props.context?.fileDependencyManifest?.filePath || \"\",\n                  props.context?.symbolDependencyManifest?.id || \"\",\n                )}\n              >\n                <SearchCode />\n                View graph for this symbol\n              </Link>\n            </Button>\n            <Button\n              asChild\n              variant=\"secondary\"\n              size=\"sm\"\n              onClick={props.onClose}\n            >\n              <Link\n                to={getToFileLink(\n                  props.context?.fileDependencyManifest?.filePath || \"\",\n                )}\n              >\n                <SearchCode />\n                View graph for this file\n              </Link>\n            </Button>\n          </SheetHeader>\n          <ScrollArea>\n            <div className=\"flex flex-col space-y-2\">\n              <Card>\n                <CardHeader>\n                  <CardTitle className=\"flex justify-between items-center\">\n                    <div className=\"flex items-center space-x-2\">\n                      <Code />\n                      <div>Symbol Metrics</div>\n                    </div>\n                    <AlertBadge\n                      count={Object.keys(\n                        props.context?.symbolAuditManifest?.alerts || {},\n                      ).length || 0}\n                    />\n                  </CardTitle>\n                </CardHeader>\n                <CardContent>\n                  <Metrics\n                    dependencyManifest={\n                      props.context?.symbolDependencyManifest\n                    }\n                    auditManifest={props.context?.symbolAuditManifest}\n                  />\n                </CardContent>\n              </Card>\n              <Card>\n                <CardHeader>\n                  <CardTitle className=\"flex justify-between items-center\">\n                    <div className=\"flex items-center space-x-2\">\n                      <File />\n                      <div>File Metrics</div>\n                    </div>\n                    <AlertBadge\n                      count={Object.keys(\n                        props.context?.fileAuditManifest?.alerts || {},\n                      ).length || 0}\n                    />\n                  </CardTitle>\n                </CardHeader>\n                <CardContent>\n                  <Metrics\n                    dependencyManifest={props.context?.fileDependencyManifest}\n                    auditManifest={props.context?.fileAuditManifest}\n                  />\n                </CardContent>\n              </Card>\n            </div>\n          </ScrollArea>\n        </SheetContent>\n      </Sheet>\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/visualizers/FileVisualizer.tsx",
    "content": "import { useEffect, useRef, useState } from \"react\";\nimport { useNavigate, useSearchParams } from \"react-router-dom\";\nimport Controls from \"../components/controls/Controls.tsx\";\nimport MetricsExtension from \"../components/controls/ControlExtensions/MetricsExtension.tsx\";\nimport FiltersExtension from \"../components/controls/ControlExtensions/FiltersExtension.tsx\";\nimport SymbolContextMenu from \"../components/contextMenu/SymbolContextMenu.tsx\";\nimport SymbolDetailsPane from \"../components/detailsPanes/SymbolDetailsPane.tsx\";\nimport { FileDependencyVisualizer } from \"../../../cytoscape/fileDependencyVisualizer/index.ts\";\nimport type { DependencyManifest, Metric } from \"../../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../../types/auditManifest.ts\";\nimport { useTheme } from \"../../../contexts/ThemeProvider.tsx\";\n\ninterface FileVisualizerProps {\n  manifestId: string;\n  dependencyManifest: DependencyManifest;\n  auditManifest: AuditManifest;\n  highlightedCytoscapeRef:\n    | { filePath: string; symbolId: string | undefined }\n    | undefined;\n  fileId: string;\n}\n\nexport default function FileVisualizer(props: FileVisualizerProps) {\n  const navigate = useNavigate();\n  const { theme } = useTheme();\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  const containerRef = useRef<HTMLDivElement | null>(null);\n  const [busy, setBusy] = useState<boolean>(true);\n  const [fileVisualizer, setFileVisualizer] = useState<\n    FileDependencyVisualizer | undefined\n  >(undefined);\n\n  const metricFromUrl = (searchParams.get(\"metric\") || undefined) as\n    | Metric\n    | undefined;\n\n  const [metric, setMetric] = useState<Metric | undefined>(metricFromUrl);\n\n  function handleMetricChange(newMetric: Metric | undefined) {\n    if (newMetric) {\n      searchParams.set(\"metric\", newMetric);\n      setSearchParams(searchParams);\n    } else {\n      searchParams.delete(\"metric\");\n      setSearchParams(searchParams);\n    }\n    setMetric(newMetric);\n  }\n\n  const [contextMenu, setContextMenu] = useState<\n    | {\n        position: { x: number; y: number };\n        fileDependencyManifest: DependencyManifest[string];\n        symbolDependencyManifest: DependencyManifest[string][\"symbols\"][string];\n      }\n    | undefined\n  >(undefined);\n\n  const [detailsPane, setDetailsPane] = useState<\n    | {\n        manifestId: string;\n        fileDependencyManifest: DependencyManifest[string];\n        symbolDependencyManifest: DependencyManifest[string][\"symbols\"][string];\n        fileAuditManifest: AuditManifest[string];\n        symbolAuditManifest: AuditManifest[string][\"symbols\"][string];\n      }\n    | undefined\n  >(undefined);\n\n  useEffect(() => {\n    setBusy(true);\n    const visualizer = new FileDependencyVisualizer(\n      containerRef.current as HTMLElement,\n      props.fileId,\n      props.dependencyManifest,\n      props.auditManifest,\n      {\n        theme,\n        defaultMetric: metric,\n        onAfterNodeRightClick: (value: {\n          position: { x: number; y: number };\n          filePath: string;\n          symbolId: string;\n        }) => {\n          const fileDependencyManifest =\n            props.dependencyManifest[value.filePath];\n          const symbolDependencyManifest =\n            fileDependencyManifest.symbols[value.symbolId];\n          setContextMenu({\n            position: value.position,\n            fileDependencyManifest,\n            symbolDependencyManifest,\n          });\n        },\n        onAfterNodeDblClick: (filePath: string, symbolId: string) => {\n          const newSearchParams = new URLSearchParams(searchParams);\n          newSearchParams.set(\"fileId\", filePath);\n          newSearchParams.set(\"instanceId\", symbolId);\n          navigate(`?${newSearchParams.toString()}`);\n        },\n      },\n    );\n\n    setFileVisualizer(visualizer);\n    setBusy(false);\n\n    return () => {\n      visualizer?.cy.destroy();\n      setFileVisualizer(undefined);\n    };\n  }, [props.dependencyManifest, props.auditManifest, props.fileId]);\n\n  useEffect(() => {\n    if (fileVisualizer) {\n      fileVisualizer.setTargetMetric(metric);\n    }\n  }, [metric]);\n\n  useEffect(() => {\n    if (fileVisualizer) {\n      if (props.highlightedCytoscapeRef) {\n        fileVisualizer.highlightNode(props.highlightedCytoscapeRef);\n      } else {\n        fileVisualizer.unhighlightNodes();\n      }\n    }\n  }, [props.highlightedCytoscapeRef]);\n\n  useEffect(() => {\n    if (fileVisualizer) {\n      fileVisualizer.updateTheme(theme);\n    }\n  }, [theme]);\n\n  function handleFilterChange(\n    showExternal: boolean,\n    showVariables: boolean,\n    showFunctions: boolean,\n    showClasses: boolean,\n    showStructs: boolean,\n    showEnums: boolean,\n    showInterfaces: boolean,\n    showRecords: boolean,\n    showDelegates: boolean,\n  ) {\n    if (fileVisualizer) {\n      fileVisualizer.filterNodes(\n        showExternal,\n        showVariables,\n        showFunctions,\n        showClasses,\n        showStructs,\n        showEnums,\n        showInterfaces,\n        showRecords,\n        showDelegates,\n      );\n    }\n  }\n\n  return (\n    <div className=\"relative w-full h-full\">\n      <div ref={containerRef} className=\"absolute w-full h-full z-10\" />\n\n      <div className=\"absolute bottom-10 left-1/2 transform -translate-x-1/2 z-20\">\n        <Controls\n          busy={busy}\n          cy={fileVisualizer?.cy}\n          onLayout={() => fileVisualizer?.layoutGraph(fileVisualizer.cy)}\n        >\n          <FiltersExtension\n            busy={false}\n            currentFileName={props.fileId}\n            onFilterChange={handleFilterChange}\n          />\n          <MetricsExtension\n            busy={false}\n            metricState={{\n              metric,\n              setMetric: handleMetricChange,\n            }}\n          />\n        </Controls>\n      </div>\n\n      <SymbolContextMenu\n        context={contextMenu}\n        onClose={() => setContextMenu(undefined)}\n        onOpenDetails={(filePath, symbolId) => {\n          const fileDependencyManifest = props.dependencyManifest[filePath];\n          const symbolDependencyManifest =\n            fileDependencyManifest.symbols[symbolId];\n          const fileAuditManifest = props.auditManifest[filePath];\n          const symbolAuditManifest = fileAuditManifest.symbols[symbolId];\n          setDetailsPane({\n            manifestId: props.manifestId,\n            fileDependencyManifest,\n            symbolDependencyManifest,\n            fileAuditManifest,\n            symbolAuditManifest,\n          });\n        }}\n      />\n\n      <SymbolDetailsPane\n        context={detailsPane}\n        onClose={() => setDetailsPane(undefined)}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/visualizers/ProjectVisualizer.tsx",
    "content": "import { useEffect, useRef, useState } from \"react\";\nimport { useNavigate, useSearchParams } from \"react-router-dom\";\nimport Controls from \"../components/controls/Controls.tsx\";\nimport MetricsExtension from \"../components/controls/ControlExtensions/MetricsExtension.tsx\";\nimport FileContextMenu from \"../components/contextMenu/FileContextMenu.tsx\";\nimport FileDetailsPane from \"../components/detailsPanes/FileDetailsPane.tsx\";\nimport { ProjectDependencyVisualizer } from \"../../../cytoscape/projectDependencyVisualizer/index.ts\";\nimport type { DependencyManifest, Metric } from \"../../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../../types/auditManifest.ts\";\nimport { useTheme } from \"../../../contexts/ThemeProvider.tsx\";\n\ninterface ProjectVisualizerProps {\n  manifestId: string;\n  dependencyManifest: DependencyManifest;\n  auditManifest: AuditManifest;\n  highlightedCytoscapeRef:\n    | { filePath: string; symbolId: string | undefined }\n    | undefined;\n}\n\nexport default function ProjectVisualizer(props: ProjectVisualizerProps) {\n  const navigate = useNavigate();\n  const { theme } = useTheme();\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  const containerRef = useRef<HTMLDivElement | null>(null);\n  const [busy, setBusy] = useState<boolean>(true);\n  const [projectVisualizer, setProjectVisualizer] = useState<\n    ProjectDependencyVisualizer | undefined\n  >(undefined);\n\n  const metricFromUrl = (searchParams.get(\"metric\") || undefined) as\n    | Metric\n    | undefined;\n\n  const [metric, setMetric] = useState<Metric | undefined>(metricFromUrl);\n\n  function handleMetricChange(newMetric: Metric | undefined) {\n    if (newMetric) {\n      searchParams.set(\"metric\", newMetric);\n      setSearchParams(searchParams);\n    } else {\n      searchParams.delete(\"metric\");\n      setSearchParams(searchParams);\n    }\n    setMetric(newMetric);\n  }\n\n  const [contextMenu, setContextMenu] = useState<\n    | {\n        position: { x: number; y: number };\n        fileDependencyManifest: DependencyManifest[string];\n      }\n    | undefined\n  >(undefined);\n\n  const [detailsPane, setDetailsPane] = useState<\n    | {\n        manifestId: string;\n        fileDependencyManifest: DependencyManifest[string];\n        fileAuditManifest: AuditManifest[string];\n      }\n    | undefined\n  >(undefined);\n\n  useEffect(() => {\n    setBusy(true);\n    const visualizer = new ProjectDependencyVisualizer(\n      containerRef.current as HTMLElement,\n      props.dependencyManifest,\n      props.auditManifest,\n      {\n        theme,\n        defaultMetric: metric,\n        onAfterNodeRightClick: (value: {\n          position: { x: number; y: number };\n          filePath: string;\n        }) => {\n          setContextMenu({\n            position: value.position,\n            fileDependencyManifest: props.dependencyManifest[value.filePath],\n          });\n        },\n        onAfterNodeDblClick: (filePath: string) => {\n          const newSearchParams = new URLSearchParams(searchParams);\n          newSearchParams.set(\"fileId\", filePath);\n          newSearchParams.delete(\"instanceId\");\n          navigate(`?${newSearchParams.toString()}`);\n        },\n      },\n    );\n\n    setProjectVisualizer(visualizer);\n    setBusy(false);\n\n    return () => {\n      visualizer?.cy.destroy();\n      setProjectVisualizer(undefined);\n    };\n  }, [props.dependencyManifest, props.auditManifest]);\n\n  useEffect(() => {\n    if (projectVisualizer) {\n      projectVisualizer.setTargetMetric(metric);\n    }\n  }, [metric]);\n\n  useEffect(() => {\n    if (projectVisualizer) {\n      if (props.highlightedCytoscapeRef) {\n        projectVisualizer.highlightNode(props.highlightedCytoscapeRef);\n      } else {\n        projectVisualizer.unhighlightNodes();\n      }\n    }\n  }, [props.highlightedCytoscapeRef]);\n\n  useEffect(() => {\n    if (projectVisualizer) {\n      projectVisualizer.updateTheme(theme);\n    }\n  }, [theme]);\n\n  return (\n    <div className=\"relative w-full h-full\">\n      <div ref={containerRef} className=\"absolute w-full h-full z-10\" />\n\n      <div className=\"absolute bottom-10 left-1/2 transform -translate-x-1/2 z-20\">\n        <Controls\n          busy={busy}\n          cy={projectVisualizer?.cy}\n          onLayout={() => projectVisualizer?.layoutGraph(projectVisualizer.cy)}\n        >\n          <MetricsExtension\n            busy={busy}\n            metricState={{\n              metric,\n              setMetric: handleMetricChange,\n            }}\n          />\n        </Controls>\n      </div>\n\n      <FileDetailsPane\n        context={detailsPane}\n        onClose={() => setDetailsPane(undefined)}\n      />\n\n      <FileContextMenu\n        context={contextMenu}\n        onClose={() => setContextMenu(undefined)}\n        onOpenDetails={(filePath) => {\n          setDetailsPane({\n            manifestId: props.manifestId,\n            fileDependencyManifest: props.dependencyManifest[filePath],\n            fileAuditManifest: props.auditManifest[filePath],\n          });\n        }}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/DependencyVisualizer/visualizers/SymbolVisualizer.tsx",
    "content": "import { useEffect, useRef, useState } from \"react\";\nimport { useNavigate, useSearchParams } from \"react-router-dom\";\nimport Controls from \"../components/controls/Controls.tsx\";\nimport GraphDepthExtension from \"../components/controls/ControlExtensions/GraphDepthExtension.tsx\";\nimport SymbolContextMenu from \"../components/contextMenu/SymbolContextMenu.tsx\";\nimport SymbolDetailsPane from \"../components/detailsPanes/SymbolDetailsPane.tsx\";\nimport { SymbolDependencyVisualizer } from \"../../../cytoscape/symbolDependencyVisualizer/index.ts\";\nimport type { DependencyManifest } from \"../../../types/dependencyManifest.ts\";\nimport type { AuditManifest } from \"../../../types/auditManifest.ts\";\nimport { useTheme } from \"../../../contexts/ThemeProvider.tsx\";\n\ninterface SymbolVisualizerProps {\n  manifestId: string;\n  dependencyManifest: DependencyManifest;\n  auditManifest: AuditManifest;\n  highlightedCytoscapeRef:\n    | { filePath: string; symbolId: string | undefined }\n    | undefined;\n  fileId: string;\n  instanceId: string;\n}\n\nexport default function SymbolVisualizer(props: SymbolVisualizerProps) {\n  const navigate = useNavigate();\n  const { theme } = useTheme();\n  const [searchParams, setSearchParams] = useSearchParams();\n\n  const containerRef = useRef<HTMLDivElement | null>(null);\n  const [busy, setBusy] = useState<boolean>(true);\n  const [symbolVisualizer, setSymbolVisualizer] = useState<\n    SymbolDependencyVisualizer | undefined\n  >(undefined);\n\n  const dependencyDepthFromUrl =\n    (searchParams.get(\"dependencyDepth\") || undefined) as number | undefined;\n  const dependentDepthFromUrl =\n    (searchParams.get(\"dependentDepth\") || undefined) as number | undefined;\n\n  const [dependencyDepth, setDependencyDepth] = useState<number>(\n    dependencyDepthFromUrl || 3,\n  );\n  const [dependentDepth, setDependentDepth] = useState<number>(\n    dependentDepthFromUrl || 0,\n  );\n\n  function handleDependencyDepthChange(depth: number) {\n    searchParams.set(\"dependencyDepth\", depth.toString());\n    setSearchParams(searchParams);\n    setDependencyDepth(depth);\n  }\n\n  function handleDependentDepthChange(depth: number) {\n    searchParams.set(\"dependentDepth\", depth.toString());\n    setSearchParams(searchParams);\n    setDependentDepth(depth);\n  }\n\n  const [contextMenu, setContextMenu] = useState<\n    | {\n        position: { x: number; y: number };\n        fileDependencyManifest: DependencyManifest[string];\n        symbolDependencyManifest: DependencyManifest[string][\"symbols\"][string];\n      }\n    | undefined\n  >(undefined);\n\n  const [detailsPane, setDetailsPane] = useState<\n    | {\n        manifestId: string;\n        fileDependencyManifest: DependencyManifest[string];\n        symbolDependencyManifest: DependencyManifest[string][\"symbols\"][string];\n        fileAuditManifest: AuditManifest[string];\n        symbolAuditManifest: AuditManifest[string][\"symbols\"][string];\n      }\n    | undefined\n  >(undefined);\n\n  useEffect(() => {\n    setBusy(true);\n\n    if (!props.fileId || !props.instanceId) {\n      return;\n    }\n\n    const visualizer = new SymbolDependencyVisualizer(\n      containerRef.current as HTMLElement,\n      props.fileId,\n      props.instanceId,\n      dependencyDepth,\n      dependentDepth,\n      props.dependencyManifest,\n      props.auditManifest,\n      {\n        theme,\n        onAfterNodeRightClick: (value: {\n          position: { x: number; y: number };\n          filePath: string;\n          symbolId: string;\n        }) => {\n          const fileDependencyManifest =\n            props.dependencyManifest[value.filePath];\n          const symbolDependencyManifest =\n            fileDependencyManifest.symbols[value.symbolId];\n          setContextMenu({\n            position: value.position,\n            fileDependencyManifest,\n            symbolDependencyManifest,\n          });\n        },\n        onAfterNodeDblClick: (filePath: string, symbolId: string) => {\n          const newSearchParams = new URLSearchParams(searchParams);\n          newSearchParams.set(\"fileId\", filePath);\n          newSearchParams.set(\"instanceId\", symbolId);\n          navigate(`?${newSearchParams.toString()}`);\n        },\n      },\n    );\n\n    setSymbolVisualizer(visualizer);\n    setBusy(false);\n\n    return () => {\n      visualizer?.cy.destroy();\n      setSymbolVisualizer(undefined);\n    };\n  }, [\n    props.dependencyManifest,\n    props.auditManifest,\n    props.fileId,\n    props.instanceId,\n    dependencyDepth,\n    dependentDepth,\n  ]);\n\n  useEffect(() => {\n    if (symbolVisualizer) {\n      if (props.highlightedCytoscapeRef) {\n        symbolVisualizer.highlightNode(props.highlightedCytoscapeRef);\n      } else {\n        symbolVisualizer.unhighlightNodes();\n      }\n    }\n  }, [props.highlightedCytoscapeRef]);\n\n  useEffect(() => {\n    if (symbolVisualizer) {\n      symbolVisualizer.updateTheme(theme);\n    }\n  }, [theme]);\n\n  return (\n    <div className=\"relative w-full h-full\">\n      <div ref={containerRef} className=\"absolute w-full h-full z-10\" />\n\n      <div className=\"absolute bottom-10 left-1/2 transform -translate-x-1/2 z-20\">\n        <Controls\n          busy={busy}\n          cy={symbolVisualizer?.cy}\n          onLayout={() => symbolVisualizer?.layoutGraph(symbolVisualizer.cy)}\n        >\n          <GraphDepthExtension\n            busy={busy}\n            dependencyState={{\n              depth: dependencyDepth,\n              setDepth: handleDependencyDepthChange,\n            }}\n            dependentState={{\n              depth: dependentDepth,\n              setDepth: handleDependentDepthChange,\n            }}\n          />\n        </Controls>\n      </div>\n\n      <SymbolContextMenu\n        context={contextMenu}\n        onClose={() => setContextMenu(undefined)}\n        onOpenDetails={(filePath, symbolId) => {\n          const fileDependencyManifest = props.dependencyManifest[filePath];\n          const symbolDependencyManifest =\n            fileDependencyManifest.symbols[symbolId];\n          const fileAuditManifest = props.auditManifest[filePath];\n          const symbolAuditManifest = fileAuditManifest.symbols[symbolId];\n          setDetailsPane({\n            manifestId: props.manifestId,\n            fileDependencyManifest,\n            symbolDependencyManifest,\n            fileAuditManifest,\n            symbolAuditManifest,\n          });\n        }}\n      />\n\n      <SymbolDetailsPane\n        context={detailsPane}\n        onClose={() => setDetailsPane(undefined)}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/components/shadcn/Alert.tsx",
    "content": "import type * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nconst alertVariants = cva(\n  \"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-card text-card-foreground\",\n        destructive:\n          \"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n);\n\nfunction Alert({\n  className,\n  variant,\n  ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof alertVariants>) {\n  return (\n    <div\n      data-slot=\"alert\"\n      role=\"alert\"\n      className={cn(alertVariants({ variant }), className)}\n      {...props}\n    />\n  );\n}\n\nfunction AlertTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"alert-title\"\n      className={cn(\n        \"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction AlertDescription({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"alert-description\"\n      className={cn(\n        \"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Alert, AlertDescription, AlertTitle };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Breadcrumb.tsx",
    "content": "import type * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { ChevronRight, MoreHorizontal } from \"lucide-react\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Breadcrumb({ ...props }: React.ComponentProps<\"nav\">) {\n  return <nav aria-label=\"breadcrumb\" data-slot=\"breadcrumb\" {...props} />;\n}\n\nfunction BreadcrumbList({ className, ...props }: React.ComponentProps<\"ol\">) {\n  return (\n    <ol\n      data-slot=\"breadcrumb-list\"\n      className={cn(\n        \"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction BreadcrumbItem({ className, ...props }: React.ComponentProps<\"li\">) {\n  return (\n    <li\n      data-slot=\"breadcrumb-item\"\n      className={cn(\"inline-flex items-center gap-1.5\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction BreadcrumbLink({\n  asChild,\n  className,\n  ...props\n}: React.ComponentProps<\"a\"> & {\n  asChild?: boolean;\n}) {\n  const Comp = asChild ? Slot : \"a\";\n\n  return (\n    <Comp\n      data-slot=\"breadcrumb-link\"\n      className={cn(\"hover:text-foreground transition-colors\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction BreadcrumbPage({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"breadcrumb-page\"\n      role=\"link\"\n      aria-disabled=\"true\"\n      aria-current=\"page\"\n      className={cn(\"text-foreground font-normal\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction BreadcrumbSeparator({\n  children,\n  className,\n  ...props\n}: React.ComponentProps<\"li\">) {\n  return (\n    <li\n      data-slot=\"breadcrumb-separator\"\n      role=\"presentation\"\n      aria-hidden=\"true\"\n      className={cn(\"[&>svg]:size-3.5\", className)}\n      {...props}\n    >\n      {children ?? <ChevronRight />}\n    </li>\n  );\n}\n\nfunction BreadcrumbEllipsis({\n  className,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"breadcrumb-ellipsis\"\n      role=\"presentation\"\n      aria-hidden=\"true\"\n      className={cn(\"flex size-9 items-center justify-center\", className)}\n      {...props}\n    >\n      <MoreHorizontal className=\"size-4\" />\n      <span className=\"sr-only\">More</span>\n    </span>\n  );\n}\n\nexport {\n  Breadcrumb,\n  BreadcrumbEllipsis,\n  BreadcrumbItem,\n  BreadcrumbLink,\n  BreadcrumbList,\n  BreadcrumbPage,\n  BreadcrumbSeparator,\n};\n"
  },
  {
    "path": "viewer/src/components/shadcn/Button.tsx",
    "content": "import type * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n        outline:\n          \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n        secondary:\n          \"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80\",\n        ghost:\n          \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n        sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n        lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n        icon: \"size-9\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n);\n\nfunction Button({\n  className,\n  variant,\n  size,\n  asChild = false,\n  ...props\n}:\n  & React.ComponentProps<\"button\">\n  & VariantProps<typeof buttonVariants>\n  & {\n    asChild?: boolean;\n  }) {\n  const Comp = asChild ? Slot : \"button\";\n\n  return (\n    <Comp\n      data-slot=\"button\"\n      className={cn(buttonVariants({ variant, size, className }))}\n      {...props}\n    />\n  );\n}\n\nexport { Button, buttonVariants };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Card.tsx",
    "content": "import type * as React from \"react\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Card({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card\"\n      className={cn(\n        \"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-header\"\n      className={cn(\n        \"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-title\"\n      className={cn(\"leading-none font-semibold\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-description\"\n      className={cn(\"text-muted-foreground text-sm\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-action\"\n      className={cn(\n        \"col-start-2 row-span-2 row-start-1 self-start justify-self-end\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-content\"\n      className={cn(\"px-6\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-footer\"\n      className={cn(\"flex items-center px-6 [.border-t]:pt-6\", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Card,\n  CardAction,\n  CardContent,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n};\n"
  },
  {
    "path": "viewer/src/components/shadcn/Dialog.tsx",
    "content": "import type * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } from \"lucide-react\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Dialog({\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n  return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n  return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n  return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Close>) {\n  return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n  return (\n    <DialogPrimitive.Overlay\n      data-slot=\"dialog-overlay\"\n      className={cn(\n        \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogContent({\n  className,\n  children,\n  showCloseButton = true,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n  showCloseButton?: boolean;\n}) {\n  return (\n    <DialogPortal data-slot=\"dialog-portal\">\n      <DialogOverlay />\n      <DialogPrimitive.Content\n        data-slot=\"dialog-content\"\n        className={cn(\n          \"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        {showCloseButton && (\n          <DialogPrimitive.Close\n            data-slot=\"dialog-close\"\n            className=\"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n          >\n            <XIcon />\n            <span className=\"sr-only\">Close</span>\n          </DialogPrimitive.Close>\n        )}\n      </DialogPrimitive.Content>\n    </DialogPortal>\n  );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dialog-header\"\n      className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dialog-footer\"\n      className={cn(\n        \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogTitle({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n  return (\n    <DialogPrimitive.Title\n      data-slot=\"dialog-title\"\n      className={cn(\"text-lg leading-none font-semibold\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DialogDescription({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n  return (\n    <DialogPrimitive.Description\n      data-slot=\"dialog-description\"\n      className={cn(\"text-muted-foreground text-sm\", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogOverlay,\n  DialogPortal,\n  DialogTitle,\n  DialogTrigger,\n};\n"
  },
  {
    "path": "viewer/src/components/shadcn/Dropdownmenu.tsx",
    "content": "import type * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction DropdownMenu({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n  return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\n}\n\nfunction DropdownMenuPortal({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\n  return (\n    <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />\n  );\n}\n\nfunction DropdownMenuTrigger({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n  return (\n    <DropdownMenuPrimitive.Trigger\n      data-slot=\"dropdown-menu-trigger\"\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuContent({\n  className,\n  sideOffset = 4,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n  return (\n    <DropdownMenuPrimitive.Portal>\n      <DropdownMenuPrimitive.Content\n        data-slot=\"dropdown-menu-content\"\n        sideOffset={sideOffset}\n        className={cn(\n          \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md\",\n          className,\n        )}\n        {...props}\n      />\n    </DropdownMenuPrimitive.Portal>\n  );\n}\n\nfunction DropdownMenuGroup({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n  return (\n    <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />\n  );\n}\n\nfunction DropdownMenuItem({\n  className,\n  inset,\n  variant = \"default\",\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n  inset?: boolean;\n  variant?: \"default\" | \"destructive\";\n}) {\n  return (\n    <DropdownMenuPrimitive.Item\n      data-slot=\"dropdown-menu-item\"\n      data-inset={inset}\n      data-variant={variant}\n      className={cn(\n        \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuCheckboxItem({\n  className,\n  children,\n  checked,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n  return (\n    <DropdownMenuPrimitive.CheckboxItem\n      data-slot=\"dropdown-menu-checkbox-item\"\n      className={cn(\n        \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className,\n      )}\n      checked={checked}\n      {...props}\n    >\n      <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n        <DropdownMenuPrimitive.ItemIndicator>\n          <CheckIcon className=\"size-4\" />\n        </DropdownMenuPrimitive.ItemIndicator>\n      </span>\n      {children}\n    </DropdownMenuPrimitive.CheckboxItem>\n  );\n}\n\nfunction DropdownMenuRadioGroup({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\n  return (\n    <DropdownMenuPrimitive.RadioGroup\n      data-slot=\"dropdown-menu-radio-group\"\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuRadioItem({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\n  return (\n    <DropdownMenuPrimitive.RadioItem\n      data-slot=\"dropdown-menu-radio-item\"\n      className={cn(\n        \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className,\n      )}\n      {...props}\n    >\n      <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n        <DropdownMenuPrimitive.ItemIndicator>\n          <CircleIcon className=\"size-2 fill-current\" />\n        </DropdownMenuPrimitive.ItemIndicator>\n      </span>\n      {children}\n    </DropdownMenuPrimitive.RadioItem>\n  );\n}\n\nfunction DropdownMenuLabel({\n  className,\n  inset,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n  inset?: boolean;\n}) {\n  return (\n    <DropdownMenuPrimitive.Label\n      data-slot=\"dropdown-menu-label\"\n      data-inset={inset}\n      className={cn(\n        \"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n  return (\n    <DropdownMenuPrimitive.Separator\n      data-slot=\"dropdown-menu-separator\"\n      className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuShortcut({\n  className,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"dropdown-menu-shortcut\"\n      className={cn(\n        \"text-muted-foreground ml-auto text-xs tracking-widest\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuSub({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n  return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nfunction DropdownMenuSubTrigger({\n  className,\n  inset,\n  children,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n  inset?: boolean;\n}) {\n  return (\n    <DropdownMenuPrimitive.SubTrigger\n      data-slot=\"dropdown-menu-sub-trigger\"\n      data-inset={inset}\n      className={cn(\n        \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8\",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      <ChevronRightIcon className=\"ml-auto size-4\" />\n    </DropdownMenuPrimitive.SubTrigger>\n  );\n}\n\nfunction DropdownMenuSubContent({\n  className,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n  return (\n    <DropdownMenuPrimitive.SubContent\n      data-slot=\"dropdown-menu-sub-content\"\n      className={cn(\n        \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuPortal,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuTrigger,\n};\n"
  },
  {
    "path": "viewer/src/components/shadcn/Input.tsx",
    "content": "import type * as React from \"react\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n  return (\n    <input\n      type={type}\n      data-slot=\"input\"\n      className={cn(\n        \"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n        \"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n        \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Input };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Label.tsx",
    "content": "import type * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Label({\n  className,\n  ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n  return (\n    <LabelPrimitive.Root\n      data-slot=\"label\"\n      className={cn(\n        \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Label };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Scrollarea.tsx",
    "content": "import type * as React from \"react\";\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction ScrollArea({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {\n  return (\n    <ScrollAreaPrimitive.Root\n      data-slot=\"scroll-area\"\n      className={cn(\"relative\", className)}\n      {...props}\n    >\n      <ScrollAreaPrimitive.Viewport\n        data-slot=\"scroll-area-viewport\"\n        className=\"focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1\"\n      >\n        {children}\n      </ScrollAreaPrimitive.Viewport>\n      <ScrollBar />\n      <ScrollAreaPrimitive.Corner />\n    </ScrollAreaPrimitive.Root>\n  );\n}\n\nfunction ScrollBar({\n  className,\n  orientation = \"vertical\",\n  ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {\n  return (\n    <ScrollAreaPrimitive.ScrollAreaScrollbar\n      data-slot=\"scroll-area-scrollbar\"\n      orientation={orientation}\n      className={cn(\n        \"flex touch-none p-px transition-colors select-none\",\n        orientation === \"vertical\" &&\n          \"h-full w-2.5 border-l border-l-transparent\",\n        orientation === \"horizontal\" &&\n          \"h-2.5 flex-col border-t border-t-transparent\",\n        className,\n      )}\n      {...props}\n    >\n      <ScrollAreaPrimitive.ScrollAreaThumb\n        data-slot=\"scroll-area-thumb\"\n        className=\"bg-border relative flex-1 rounded-full\"\n      />\n    </ScrollAreaPrimitive.ScrollAreaScrollbar>\n  );\n}\n\nexport { ScrollArea, ScrollBar };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Separator.tsx",
    "content": "import type * as React from \"react\";\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Separator({\n  className,\n  orientation = \"horizontal\",\n  decorative = true,\n  ...props\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\n  return (\n    <SeparatorPrimitive.Root\n      data-slot=\"separator\"\n      decorative={decorative}\n      orientation={orientation}\n      className={cn(\n        \"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Separator };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Sheet.tsx",
    "content": "import type * as React from \"react\";\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } from \"lucide-react\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {\n  return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />;\n}\n\nfunction SheetTrigger({\n  ...props\n}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {\n  return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />;\n}\n\nfunction SheetClose({\n  ...props\n}: React.ComponentProps<typeof SheetPrimitive.Close>) {\n  return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />;\n}\n\nfunction SheetPortal({\n  ...props\n}: React.ComponentProps<typeof SheetPrimitive.Portal>) {\n  return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />;\n}\n\nfunction SheetOverlay({\n  className,\n  ...props\n}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {\n  return (\n    <SheetPrimitive.Overlay\n      data-slot=\"sheet-overlay\"\n      className={cn(\n        \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SheetContent({\n  className,\n  children,\n  side = \"right\",\n  ...props\n}: React.ComponentProps<typeof SheetPrimitive.Content> & {\n  side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n}) {\n  return (\n    <SheetPortal>\n      <SheetOverlay />\n      <SheetPrimitive.Content\n        data-slot=\"sheet-content\"\n        className={cn(\n          \"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n          side === \"right\" &&\n            \"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm\",\n          side === \"left\" &&\n            \"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm\",\n          side === \"top\" &&\n            \"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b\",\n          side === \"bottom\" &&\n            \"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        <SheetPrimitive.Close className=\"ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none\">\n          <XIcon className=\"size-4\" />\n          <span className=\"sr-only\">Close</span>\n        </SheetPrimitive.Close>\n      </SheetPrimitive.Content>\n    </SheetPortal>\n  );\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sheet-header\"\n      className={cn(\"flex flex-col gap-1.5 p-4\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sheet-footer\"\n      className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SheetTitle({\n  className,\n  ...props\n}: React.ComponentProps<typeof SheetPrimitive.Title>) {\n  return (\n    <SheetPrimitive.Title\n      data-slot=\"sheet-title\"\n      className={cn(\"text-foreground font-semibold\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SheetDescription({\n  className,\n  ...props\n}: React.ComponentProps<typeof SheetPrimitive.Description>) {\n  return (\n    <SheetPrimitive.Description\n      data-slot=\"sheet-description\"\n      className={cn(\"text-muted-foreground text-sm\", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Sheet,\n  SheetClose,\n  SheetContent,\n  SheetDescription,\n  SheetFooter,\n  SheetHeader,\n  SheetTitle,\n  SheetTrigger,\n};\n"
  },
  {
    "path": "viewer/src/components/shadcn/Sidebar.tsx",
    "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { PanelLeftIcon } from \"lucide-react\";\n\nimport { useIsMobile } from \"./hooks/use-mobile.tsx\";\nimport { cn } from \"../../lib/utils.ts\";\nimport { Button } from \"./Button.tsx\";\nimport { Input } from \"./Input.tsx\";\nimport { Separator } from \"./Separator.tsx\";\nimport {\n  Sheet,\n  SheetContent,\n  SheetDescription,\n  SheetHeader,\n  SheetTitle,\n} from \"./Sheet.tsx\";\nimport { Skeleton } from \"./Skeleton.tsx\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"./Tooltip.tsx\";\n\nconst SIDEBAR_COOKIE_NAME = \"sidebar_state\";\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;\nconst SIDEBAR_WIDTH = \"16rem\";\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\";\nconst SIDEBAR_WIDTH_ICON = \"3rem\";\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\";\n\ntype SidebarContextProps = {\n  state: \"expanded\" | \"collapsed\";\n  open: boolean;\n  setOpen: (open: boolean) => void;\n  openMobile: boolean;\n  setOpenMobile: (open: boolean) => void;\n  isMobile: boolean;\n  toggleSidebar: () => void;\n};\n\nconst SidebarContext = React.createContext<SidebarContextProps | null>(null);\n\nfunction useSidebar() {\n  const context = React.useContext(SidebarContext);\n  if (!context) {\n    throw new Error(\"useSidebar must be used within a SidebarProvider.\");\n  }\n\n  return context;\n}\n\nfunction SidebarProvider({\n  defaultOpen = true,\n  open: openProp,\n  onOpenChange: setOpenProp,\n  className,\n  style,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n}) {\n  const isMobile = useIsMobile();\n  const [openMobile, setOpenMobile] = React.useState(false);\n\n  // This is the internal state of the sidebar.\n  // We use openProp and setOpenProp for control from outside the component.\n  const [_open, _setOpen] = React.useState(defaultOpen);\n  const open = openProp ?? _open;\n  const setOpen = React.useCallback(\n    (value: boolean | ((value: boolean) => boolean)) => {\n      const openState = typeof value === \"function\" ? value(open) : value;\n      if (setOpenProp) {\n        setOpenProp(openState);\n      } else {\n        _setOpen(openState);\n      }\n\n      // This sets the cookie to keep the sidebar state.\n      document.cookie =\n        `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;\n    },\n    [setOpenProp, open],\n  );\n\n  // Helper to toggle the sidebar.\n  const toggleSidebar = React.useCallback(() => {\n    return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);\n  }, [isMobile, setOpen, setOpenMobile]);\n\n  // Adds a keyboard shortcut to toggle the sidebar.\n  React.useEffect(() => {\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (\n        event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n        (event.metaKey || event.ctrlKey)\n      ) {\n        event.preventDefault();\n        toggleSidebar();\n      }\n    };\n\n    globalThis.addEventListener(\"keydown\", handleKeyDown);\n    return () => globalThis.removeEventListener(\"keydown\", handleKeyDown);\n  }, [toggleSidebar]);\n\n  // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n  // This makes it easier to style the sidebar with Tailwind classes.\n  const state = open ? \"expanded\" : \"collapsed\";\n\n  const contextValue = React.useMemo<SidebarContextProps>(\n    () => ({\n      state,\n      open,\n      setOpen,\n      isMobile,\n      openMobile,\n      setOpenMobile,\n      toggleSidebar,\n    }),\n    [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],\n  );\n\n  return (\n    <SidebarContext.Provider value={contextValue}>\n      <TooltipProvider delayDuration={0}>\n        <div\n          data-slot=\"sidebar-wrapper\"\n          style={{\n            \"--sidebar-width\": SIDEBAR_WIDTH,\n            \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n            ...style,\n          } as React.CSSProperties}\n          className={cn(\n            \"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full\",\n            className,\n          )}\n          {...props}\n        >\n          {children}\n        </div>\n      </TooltipProvider>\n    </SidebarContext.Provider>\n  );\n}\n\nfunction Sidebar({\n  side = \"left\",\n  variant = \"sidebar\",\n  collapsible = \"offcanvas\",\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  side?: \"left\" | \"right\";\n  variant?: \"sidebar\" | \"floating\" | \"inset\";\n  collapsible?: \"offcanvas\" | \"icon\" | \"none\";\n}) {\n  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\n\n  if (collapsible === \"none\") {\n    return (\n      <div\n        data-slot=\"sidebar\"\n        className={cn(\n          \"bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n      </div>\n    );\n  }\n\n  if (isMobile) {\n    return (\n      <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\n        <SheetContent\n          data-sidebar=\"sidebar\"\n          data-slot=\"sidebar\"\n          data-mobile=\"true\"\n          className=\"bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden\"\n          style={{\n            \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n          } as React.CSSProperties}\n          side={side}\n        >\n          <SheetHeader className=\"sr-only\">\n            <SheetTitle>Sidebar</SheetTitle>\n            <SheetDescription>Displays the mobile sidebar.</SheetDescription>\n          </SheetHeader>\n          <div className=\"flex h-full w-full flex-col\">{children}</div>\n        </SheetContent>\n      </Sheet>\n    );\n  }\n\n  return (\n    <div\n      className=\"group peer text-sidebar-foreground hidden md:block\"\n      data-state={state}\n      data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n      data-variant={variant}\n      data-side={side}\n      data-slot=\"sidebar\"\n    >\n      {/* This is what handles the sidebar gap on desktop */}\n      <div\n        data-slot=\"sidebar-gap\"\n        className={cn(\n          \"relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear\",\n          \"group-data-[collapsible=offcanvas]:w-0\",\n          \"group-data-[side=right]:rotate-180\",\n          variant === \"floating\" || variant === \"inset\"\n            ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]\"\n            : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon)\",\n        )}\n      />\n      <div\n        data-slot=\"sidebar-container\"\n        className={cn(\n          \"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex\",\n          side === \"left\"\n            ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n            : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n          // Adjust the padding for floating and inset variants.\n          variant === \"floating\" || variant === \"inset\"\n            ? \"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]\"\n            : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l\",\n          className,\n        )}\n        {...props}\n      >\n        <div\n          data-sidebar=\"sidebar\"\n          data-slot=\"sidebar-inner\"\n          className=\"bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm\"\n        >\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction SidebarTrigger({\n  className,\n  onClick,\n  ...props\n}: React.ComponentProps<typeof Button>) {\n  const { toggleSidebar } = useSidebar();\n\n  return (\n    <Button\n      data-sidebar=\"trigger\"\n      data-slot=\"sidebar-trigger\"\n      variant=\"ghost\"\n      size=\"icon\"\n      className={cn(\"size-7\", className)}\n      onClick={(event) => {\n        onClick?.(event);\n        toggleSidebar();\n      }}\n      {...props}\n    >\n      <PanelLeftIcon />\n      <span className=\"sr-only\">Toggle Sidebar</span>\n    </Button>\n  );\n}\n\nfunction SidebarRail({ className, ...props }: React.ComponentProps<\"button\">) {\n  const { toggleSidebar } = useSidebar();\n\n  return (\n    <button\n      data-sidebar=\"rail\"\n      data-slot=\"sidebar-rail\"\n      aria-label=\"Toggle Sidebar\"\n      tabIndex={-1}\n      onClick={toggleSidebar}\n      title=\"Toggle Sidebar\"\n      className={cn(\n        \"hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex\",\n        \"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize\",\n        \"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize\",\n        \"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full\",\n        \"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2\",\n        \"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarInset({ className, ...props }: React.ComponentProps<\"main\">) {\n  return (\n    <main\n      data-slot=\"sidebar-inset\"\n      className={cn(\n        \"bg-background relative flex w-full flex-1 flex-col\",\n        \"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarInput({\n  className,\n  ...props\n}: React.ComponentProps<typeof Input>) {\n  return (\n    <Input\n      data-slot=\"sidebar-input\"\n      data-sidebar=\"input\"\n      className={cn(\"bg-background h-8 w-full shadow-none\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-header\"\n      data-sidebar=\"header\"\n      className={cn(\"flex flex-col gap-2 p-2\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-footer\"\n      data-sidebar=\"footer\"\n      className={cn(\"flex flex-col gap-2 p-2\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof Separator>) {\n  return (\n    <Separator\n      data-slot=\"sidebar-separator\"\n      data-sidebar=\"separator\"\n      className={cn(\"bg-sidebar-border mx-2 w-auto\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-content\"\n      data-sidebar=\"content\"\n      className={cn(\n        \"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-group\"\n      data-sidebar=\"group\"\n      className={cn(\"relative flex w-full min-w-0 flex-col p-2\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarGroupLabel({\n  className,\n  asChild = false,\n  ...props\n}: React.ComponentProps<\"div\"> & { asChild?: boolean }) {\n  const Comp = asChild ? Slot : \"div\";\n\n  return (\n    <Comp\n      data-slot=\"sidebar-group-label\"\n      data-sidebar=\"group-label\"\n      className={cn(\n        \"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n        \"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarGroupAction({\n  className,\n  asChild = false,\n  ...props\n}: React.ComponentProps<\"button\"> & { asChild?: boolean }) {\n  const Comp = asChild ? Slot : \"button\";\n\n  return (\n    <Comp\n      data-slot=\"sidebar-group-action\"\n      data-sidebar=\"group-action\"\n      className={cn(\n        \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n        // Increases the hit area of the button on mobile.\n        \"after:absolute after:-inset-2 md:after:hidden\",\n        \"group-data-[collapsible=icon]:hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarGroupContent({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-group-content\"\n      data-sidebar=\"group-content\"\n      className={cn(\"w-full text-sm\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenu({ className, ...props }: React.ComponentProps<\"ul\">) {\n  return (\n    <ul\n      data-slot=\"sidebar-menu\"\n      data-sidebar=\"menu\"\n      className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuItem({ className, ...props }: React.ComponentProps<\"li\">) {\n  return (\n    <li\n      data-slot=\"sidebar-menu-item\"\n      data-sidebar=\"menu-item\"\n      className={cn(\"group/menu-item relative\", className)}\n      {...props}\n    />\n  );\n}\n\nconst sidebarMenuButtonVariants = cva(\n  \"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        default: \"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\",\n        outline:\n          \"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]\",\n      },\n      size: {\n        default: \"h-8 text-sm\",\n        sm: \"h-7 text-xs\",\n        lg: \"h-12 text-sm group-data-[collapsible=icon]:p-0!\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n);\n\nfunction SidebarMenuButton({\n  asChild = false,\n  isActive = false,\n  variant = \"default\",\n  size = \"default\",\n  tooltip,\n  className,\n  ...props\n}: React.ComponentProps<\"button\"> & {\n  asChild?: boolean;\n  isActive?: boolean;\n  tooltip?: string | React.ComponentProps<typeof TooltipContent>;\n} & VariantProps<typeof sidebarMenuButtonVariants>) {\n  const Comp = asChild ? Slot : \"button\";\n  const { isMobile, state } = useSidebar();\n\n  const button = (\n    <Comp\n      data-slot=\"sidebar-menu-button\"\n      data-sidebar=\"menu-button\"\n      data-size={size}\n      data-active={isActive}\n      className={cn(sidebarMenuButtonVariants({ variant, size }), className)}\n      {...props}\n    />\n  );\n\n  if (!tooltip) {\n    return button;\n  }\n\n  if (typeof tooltip === \"string\") {\n    tooltip = {\n      children: tooltip,\n    };\n  }\n\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>{button}</TooltipTrigger>\n      <TooltipContent\n        side=\"right\"\n        align=\"center\"\n        hidden={state !== \"collapsed\" || isMobile}\n        {...tooltip}\n      />\n    </Tooltip>\n  );\n}\n\nfunction SidebarMenuAction({\n  className,\n  asChild = false,\n  showOnHover = false,\n  ...props\n}: React.ComponentProps<\"button\"> & {\n  asChild?: boolean;\n  showOnHover?: boolean;\n}) {\n  const Comp = asChild ? Slot : \"button\";\n\n  return (\n    <Comp\n      data-slot=\"sidebar-menu-action\"\n      data-sidebar=\"menu-action\"\n      className={cn(\n        \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n        // Increases the hit area of the button on mobile.\n        \"after:absolute after:-inset-2 md:after:hidden\",\n        \"peer-data-[size=sm]/menu-button:top-1\",\n        \"peer-data-[size=default]/menu-button:top-1.5\",\n        \"peer-data-[size=lg]/menu-button:top-2.5\",\n        \"group-data-[collapsible=icon]:hidden\",\n        showOnHover &&\n          \"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuBadge({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-menu-badge\"\n      data-sidebar=\"menu-badge\"\n      className={cn(\n        \"text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none\",\n        \"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground\",\n        \"peer-data-[size=sm]/menu-button:top-1\",\n        \"peer-data-[size=default]/menu-button:top-1.5\",\n        \"peer-data-[size=lg]/menu-button:top-2.5\",\n        \"group-data-[collapsible=icon]:hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuSkeleton({\n  className,\n  showIcon = false,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  showIcon?: boolean;\n}) {\n  // Random width between 50 to 90%.\n  const width = React.useMemo(() => {\n    return `${Math.floor(Math.random() * 40) + 50}%`;\n  }, []);\n\n  return (\n    <div\n      data-slot=\"sidebar-menu-skeleton\"\n      data-sidebar=\"menu-skeleton\"\n      className={cn(\"flex h-8 items-center gap-2 rounded-md px-2\", className)}\n      {...props}\n    >\n      {showIcon && (\n        <Skeleton\n          className=\"size-4 rounded-md\"\n          data-sidebar=\"menu-skeleton-icon\"\n        />\n      )}\n      <Skeleton\n        className=\"h-4 max-w-(--skeleton-width) flex-1\"\n        data-sidebar=\"menu-skeleton-text\"\n        style={{\n          \"--skeleton-width\": width,\n        } as React.CSSProperties}\n      />\n    </div>\n  );\n}\n\nfunction SidebarMenuSub({ className, ...props }: React.ComponentProps<\"ul\">) {\n  return (\n    <ul\n      data-slot=\"sidebar-menu-sub\"\n      data-sidebar=\"menu-sub\"\n      className={cn(\n        \"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5\",\n        \"group-data-[collapsible=icon]:hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuSubItem({\n  className,\n  ...props\n}: React.ComponentProps<\"li\">) {\n  return (\n    <li\n      data-slot=\"sidebar-menu-sub-item\"\n      data-sidebar=\"menu-sub-item\"\n      className={cn(\"group/menu-sub-item relative\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuSubButton({\n  asChild = false,\n  size = \"md\",\n  isActive = false,\n  className,\n  ...props\n}: React.ComponentProps<\"a\"> & {\n  asChild?: boolean;\n  size?: \"sm\" | \"md\";\n  isActive?: boolean;\n}) {\n  const Comp = asChild ? Slot : \"a\";\n\n  return (\n    <Comp\n      data-slot=\"sidebar-menu-sub-button\"\n      data-sidebar=\"menu-sub-button\"\n      data-size={size}\n      data-active={isActive}\n      className={cn(\n        \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n        \"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground\",\n        size === \"sm\" && \"text-xs\",\n        size === \"md\" && \"text-sm\",\n        \"group-data-[collapsible=icon]:hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupAction,\n  SidebarGroupContent,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarInput,\n  SidebarInset,\n  SidebarMenu,\n  SidebarMenuAction,\n  SidebarMenuBadge,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  SidebarMenuSkeleton,\n  SidebarMenuSub,\n  SidebarMenuSubButton,\n  SidebarMenuSubItem,\n  SidebarProvider,\n  SidebarRail,\n  SidebarSeparator,\n  SidebarTrigger,\n  useSidebar,\n};\n"
  },
  {
    "path": "viewer/src/components/shadcn/Skeleton.tsx",
    "content": "import { cn } from \"../../lib/utils.ts\";\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"skeleton\"\n      className={cn(\"bg-accent animate-pulse rounded-md\", className)}\n      {...props}\n    />\n  );\n}\n\nexport { Skeleton };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Slider.tsx",
    "content": "import * as React from \"react\";\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction Slider({\n  className,\n  defaultValue,\n  value,\n  min = 0,\n  max = 100,\n  ...props\n}: React.ComponentProps<typeof SliderPrimitive.Root>) {\n  const _values = React.useMemo(\n    () =>\n      Array.isArray(value)\n        ? value\n        : Array.isArray(defaultValue)\n        ? defaultValue\n        : [min, max],\n    [value, defaultValue, min, max],\n  );\n\n  return (\n    <SliderPrimitive.Root\n      data-slot=\"slider\"\n      defaultValue={defaultValue}\n      value={value}\n      min={min}\n      max={max}\n      className={cn(\n        \"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col\",\n        className,\n      )}\n      {...props}\n    >\n      <SliderPrimitive.Track\n        data-slot=\"slider-track\"\n        className={cn(\n          \"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5\",\n        )}\n      >\n        <SliderPrimitive.Range\n          data-slot=\"slider-range\"\n          className={cn(\n            \"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full\",\n          )}\n        />\n      </SliderPrimitive.Track>\n      {Array.from(\n        { length: _values.length },\n        (_, index) => (\n          <SliderPrimitive.Thumb\n            data-slot=\"slider-thumb\"\n            key={index}\n            className=\"border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50\"\n          />\n        ),\n      )}\n    </SliderPrimitive.Root>\n  );\n}\n\nexport { Slider };\n"
  },
  {
    "path": "viewer/src/components/shadcn/Tooltip.tsx",
    "content": "import type * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { cn } from \"../../lib/utils.ts\";\n\nfunction TooltipProvider({\n  delayDuration = 0,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n  return (\n    <TooltipPrimitive.Provider\n      data-slot=\"tooltip-provider\"\n      delayDuration={delayDuration}\n      {...props}\n    />\n  );\n}\n\nfunction Tooltip({\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n  return (\n    <TooltipProvider>\n      <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n    </TooltipProvider>\n  );\n}\n\nfunction TooltipTrigger({\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n  return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />;\n}\n\nfunction TooltipContent({\n  className,\n  sideOffset = 0,\n  children,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\n  return (\n    <TooltipPrimitive.Portal>\n      <TooltipPrimitive.Content\n        data-slot=\"tooltip-content\"\n        sideOffset={sideOffset}\n        className={cn(\n          \"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        <TooltipPrimitive.Arrow className=\"bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]\" />\n      </TooltipPrimitive.Content>\n    </TooltipPrimitive.Portal>\n  );\n}\n\nexport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };\n"
  },
  {
    "path": "viewer/src/components/shadcn/hooks/use-mobile.tsx",
    "content": "import * as React from \"react\";\n\nconst MOBILE_BREAKPOINT = 768;\n\nexport function useIsMobile() {\n  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(\n    undefined,\n  );\n\n  React.useEffect(() => {\n    const mql = globalThis.matchMedia(\n      `(max-width: ${MOBILE_BREAKPOINT - 1}px)`,\n    );\n    const onChange = () => {\n      setIsMobile(globalThis.innerWidth < MOBILE_BREAKPOINT);\n    };\n    mql.addEventListener(\"change\", onChange);\n    setIsMobile(globalThis.innerWidth < MOBILE_BREAKPOINT);\n    return () => mql.removeEventListener(\"change\", onChange);\n  }, []);\n\n  return !!isMobile;\n}\n"
  },
  {
    "path": "viewer/src/contexts/ThemeProvider.tsx",
    "content": "import { createContext, useContext, useEffect, useState } from \"react\";\n\ntype Theme = \"dark\" | \"light\";\n\ntype ThemeProviderProps = {\n  children: React.ReactNode;\n  defaultTheme?: Theme;\n  storageKey?: string;\n};\n\ntype ThemeProviderState = {\n  theme: Theme;\n  setTheme: (theme: Theme) => void;\n};\n\nfunction getSystemTheme() {\n  const systemTheme = globalThis.matchMedia(\"(prefers-color-scheme: dark)\");\n  return systemTheme.matches ? \"dark\" : \"light\";\n}\n\nconst initialState: ThemeProviderState = {\n  theme: getSystemTheme(),\n  setTheme: () => null,\n};\n\nconst ThemeProviderContext = createContext<ThemeProviderState>(initialState);\n\nexport function ThemeProvider({\n  children,\n  defaultTheme = getSystemTheme(),\n  storageKey = \"napi-ui-theme\",\n}: ThemeProviderProps) {\n  const [theme, setTheme] = useState<Theme>(\n    () => (localStorage.getItem(storageKey) as Theme) || defaultTheme,\n  );\n\n  useEffect(() => {\n    const root = globalThis.document.documentElement;\n    root.classList.remove(\"light\", \"dark\");\n    root.classList.add(theme);\n  }, [theme]);\n\n  const value = {\n    theme,\n    setTheme: (theme: Theme) => {\n      localStorage.setItem(storageKey, theme);\n      setTheme(theme);\n    },\n  };\n\n  return (\n    <ThemeProviderContext.Provider value={value}>\n      {children}\n    </ThemeProviderContext.Provider>\n  );\n}\n\nexport const useTheme = () => {\n  const context = useContext(ThemeProviderContext);\n  if (context === undefined) {\n    throw new Error(\"useTheme must be used within a ThemeProvider\");\n  }\n  return context;\n};\n"
  },
  {
    "path": "viewer/src/cytoscape/elements/file.ts",
    "content": "import type * as auditManifestTypes from \"../../types/auditManifest\";\nimport * as dependencyManifestTypes from \"../../types/dependencyManifest\";\nimport {\n  getCollapsedSymbolNodeLabel,\n  getExpandedSymbolNodeLabel,\n  getNodeWidthAndHeightFromLabel,\n} from \"../label/index.ts\";\nimport { getMetricsSeverityForNode } from \"../metrics/index.ts\";\nimport type { SymbolNapiNodeData } from \"./types.ts\";\nimport type { EdgeDefinition, NodeDefinition } from \"cytoscape\";\n\nexport function computeNodeId(fileId: string, symbolId: string) {\n  return `${fileId}:${symbolId}`;\n}\n\nfunction createNodeData(params: {\n  id: string;\n  fileName: string;\n  symbolName: string;\n  symbolType: string;\n  isExternal: boolean;\n  metricsSeverity: {\n    [dependencyManifestTypes.metricLinesCount]: number;\n    [dependencyManifestTypes.metricCodeLineCount]: number;\n    [dependencyManifestTypes.metricCodeCharacterCount]: number;\n    [dependencyManifestTypes.metricCharacterCount]: number;\n    [dependencyManifestTypes.metricDependencyCount]: number;\n    [dependencyManifestTypes.metricDependentCount]: number;\n    [dependencyManifestTypes.metricCyclomaticComplexity]: number;\n  };\n  expandedLabel: string;\n  collapsedLabel: string;\n}): SymbolNapiNodeData {\n  // Calculate dimensions for expanded and collapsed views\n  const { width: expandedWidth, height: expandedHeight } =\n    getNodeWidthAndHeightFromLabel(\n      params.expandedLabel,\n    );\n\n  const { width: collapsedWidth, height: collapsedHeight } =\n    getNodeWidthAndHeightFromLabel(\n      params.collapsedLabel,\n    );\n\n  // Create the node data structure\n  return {\n    id: params.id,\n    position: { x: 0, y: 0 },\n    fileName: params.fileName,\n    isExternal: params.isExternal,\n    symbolName: params.symbolName,\n    symbolType: params.symbolType,\n    metricsSeverity: params.metricsSeverity,\n    expanded: {\n      label: params.expandedLabel,\n      width: expandedWidth,\n      height: expandedHeight,\n    },\n    collapsed: {\n      label: params.collapsedLabel,\n      width: collapsedWidth,\n      height: collapsedHeight,\n    },\n  };\n}\n\ninterface CustomNodeDefinition extends NodeDefinition {\n  data: SymbolNapiNodeData & object;\n}\n\nfunction processDependencies(\n  symbol: dependencyManifestTypes.DependencyManifest[string][\"symbols\"][string],\n  symbolNodeId: string,\n  dependencyManifest: dependencyManifestTypes.DependencyManifest,\n  auditManifest: auditManifestTypes.AuditManifest,\n  nodes: CustomNodeDefinition[],\n  edges: EdgeDefinition[],\n) {\n  Object.values(symbol.dependencies).forEach((dep) => {\n    let depDependencyManifest:\n      | dependencyManifestTypes.DependencyManifest[string]\n      | undefined;\n    let depAuditManifest: auditManifestTypes.AuditManifest[string] | undefined;\n\n    if (!dep.isExternal) {\n      depDependencyManifest = dependencyManifest[dep.id];\n      depAuditManifest = auditManifest[dep.id];\n    }\n\n    // For each symbol this depends on, create an edge\n    Object.keys(dep.symbols).forEach((depSymbolName) => {\n      const depSymbolNodeId = computeNodeId(dep.id, depSymbolName);\n\n      // Check if node already exists\n      const existingNode = nodes.find(\n        (node) => node.data.id === depSymbolNodeId,\n      );\n\n      if (!existingNode) {\n        let depSymbolType: dependencyManifestTypes.SymbolType | \"unknown\" =\n          \"unknown\";\n        if (depDependencyManifest) {\n          depSymbolType = depDependencyManifest.symbols[depSymbolName].type;\n        }\n\n        // Create label for dependency nodes\n        const expandedLabel = getExpandedSymbolNodeLabel({\n          currentFileId: dep.id,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          symbolAuditManifest: depAuditManifest?.symbols[depSymbolName],\n        });\n\n        const collapsedLabel = getCollapsedSymbolNodeLabel({\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n        });\n\n        const metricsSeverity = getMetricsSeverityForNode(\n          depAuditManifest?.symbols[depSymbolName],\n        );\n\n        const nodeData = createNodeData({\n          id: depSymbolNodeId,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          isExternal: dep.isExternal,\n          metricsSeverity,\n          expandedLabel,\n          collapsedLabel,\n        });\n\n        nodes.push({ data: nodeData });\n      }\n\n      // Add the edge\n      const edgeId = `${depSymbolNodeId}->${symbolNodeId}`;\n      edges.push({\n        data: {\n          id: edgeId,\n          source: depSymbolNodeId,\n          target: symbolNodeId,\n        },\n      });\n    });\n  });\n}\n\nfunction processDependents(\n  symbol: dependencyManifestTypes.DependencyManifest[string][\"symbols\"][string],\n  symbolNodeId: string,\n  dependencyManifest: dependencyManifestTypes.DependencyManifest,\n  auditManifest: auditManifestTypes.AuditManifest,\n  nodes: CustomNodeDefinition[],\n  edges: EdgeDefinition[],\n) {\n  Object.values(symbol.dependents).forEach((dep) => {\n    const depDependencyManifest = dependencyManifest[dep.id];\n    const depAuditManifest = auditManifest[dep.id];\n\n    Object.keys(dep.symbols).forEach((depSymbolName) => {\n      const depSymbolNodeId = computeNodeId(dep.id, depSymbolName);\n\n      // Check if node already exists\n      const existingNode = nodes.find(\n        (node) => node.data.id === depSymbolNodeId,\n      );\n\n      if (!existingNode) {\n        const depSymbolType = depDependencyManifest.symbols[depSymbolName].type;\n\n        // Create label for dependent nodes\n        const expandedLabel = getExpandedSymbolNodeLabel({\n          currentFileId: dep.id,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          symbolAuditManifest: depAuditManifest?.symbols[depSymbolName],\n        });\n\n        const metricsSeverity = getMetricsSeverityForNode(\n          depAuditManifest?.symbols[depSymbolName],\n        );\n\n        const isExternal = !dependencyManifest[dep.id];\n\n        const nodeData = createNodeData({\n          id: depSymbolNodeId,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          isExternal,\n          metricsSeverity,\n          expandedLabel,\n          collapsedLabel: depSymbolName,\n        });\n\n        nodes.push({ data: nodeData });\n      }\n\n      // Add the edge\n      const edgeId = `${symbolNodeId}->${depSymbolNodeId}`;\n      edges.push({\n        data: {\n          id: edgeId,\n          source: symbolNodeId,\n          target: depSymbolNodeId,\n        },\n      });\n    });\n  });\n}\n\nexport function getSymbolElementsInFile(\n  fileId: string,\n  dependencyManifest: dependencyManifestTypes.DependencyManifest,\n  auditManifest: auditManifestTypes.AuditManifest,\n) {\n  const fileManifest = dependencyManifest[fileId];\n  if (!fileManifest) {\n    console.error(`File manifest not found for ${fileId}`);\n    return [];\n  }\n\n  const fileAuditManifest = auditManifest[fileId];\n  const nodes: CustomNodeDefinition[] = [];\n  const edges: EdgeDefinition[] = [];\n\n  // First pass: Create nodes for each symbol in the file\n  Object.values(fileManifest.symbols).forEach((symbol) => {\n    const symbolAuditManifest = fileAuditManifest.symbols[symbol.id];\n    const symbolNodeId = computeNodeId(fileId, symbol.id);\n\n    // Create labels for the symbol\n    const expandedLabel = getExpandedSymbolNodeLabel({\n      currentFileId: fileId,\n      fileName: fileId,\n      symbolName: symbol.id,\n      symbolType: symbol.type,\n      symbolAuditManifest,\n    });\n    const collapsedLabel = getCollapsedSymbolNodeLabel({\n      symbolName: symbol.id,\n      symbolType: symbol.type,\n    });\n\n    const metricsSeverity = getMetricsSeverityForNode(symbolAuditManifest);\n\n    const isExternal = !dependencyManifest[fileId];\n\n    const nodeData = createNodeData({\n      id: symbolNodeId,\n      fileName: fileId,\n      symbolName: symbol.id,\n      symbolType: symbol.type,\n      isExternal,\n      metricsSeverity,\n      expandedLabel,\n      collapsedLabel,\n    });\n\n    nodes.push({ data: nodeData });\n  });\n\n  // Second pass: Create nodes and edges for dependencies and dependents\n  Object.values(fileManifest.symbols).forEach((symbol) => {\n    const symbolNodeId = computeNodeId(fileId, symbol.id);\n\n    // Process dependencies\n    processDependencies(\n      symbol,\n      symbolNodeId,\n      dependencyManifest,\n      auditManifest,\n      nodes,\n      edges,\n    );\n\n    // Process dependents\n    processDependents(\n      symbol,\n      symbolNodeId,\n      dependencyManifest,\n      auditManifest,\n      nodes,\n      edges,\n    );\n  });\n\n  return [...nodes, ...edges];\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/elements/project.ts",
    "content": "import type {\n  EdgeDefinition,\n  ElementDefinition,\n  NodeDefinition,\n} from \"cytoscape\";\nimport type * as dependencyManifestTypes from \"../../types/dependencyManifest\";\nimport type * as auditManifestTypes from \"../../types/auditManifest\";\nimport {\n  getCollapsedFileNodeLabel,\n  getExpandedFileNodeLabel,\n  getNodeWidthAndHeightFromLabel,\n} from \"../label/index.ts\";\nimport { getMetricsSeverityForNode } from \"../metrics/index.ts\";\nimport type { FileNapiNodeData } from \"./types.ts\";\n\nexport function getFileElementsInProject(\n  dependencyManifest: dependencyManifestTypes.DependencyManifest,\n  auditManifest: auditManifestTypes.AuditManifest,\n): ElementDefinition[] {\n  interface CustomNodeDefinition extends NodeDefinition {\n    data: FileNapiNodeData & object;\n  }\n\n  const nodes: CustomNodeDefinition[] = [];\n  const edges: EdgeDefinition[] = [];\n\n  Object.values(dependencyManifest).forEach((fileDependencyManifest) => {\n    const fileAuditManifest = auditManifest[fileDependencyManifest.id];\n\n    const expandedLabel = getExpandedFileNodeLabel({\n      fileName: fileDependencyManifest.id,\n      fileAuditManifest,\n    });\n    const { width: expandedWitdh, height: expandedHeight } =\n      getNodeWidthAndHeightFromLabel(\n        expandedLabel,\n      );\n\n    const collapsedLabel = getCollapsedFileNodeLabel({\n      fileName: fileDependencyManifest.id,\n      fileAuditManifest,\n    });\n    const { width: collapsedWidth, height: collapsedHeight } =\n      getNodeWidthAndHeightFromLabel(\n        collapsedLabel,\n      );\n\n    const metricsColors = getMetricsSeverityForNode(fileAuditManifest);\n\n    const nodeElement: CustomNodeDefinition = {\n      data: {\n        id: fileDependencyManifest.id,\n        // initial node position - will be updated by layout\n        position: { x: 0, y: 0 },\n        fileName: fileDependencyManifest.id,\n        isExternal: false,\n        metricsSeverity: metricsColors,\n        expanded: {\n          label: expandedLabel,\n          width: expandedWitdh,\n          height: expandedHeight,\n        },\n        collapsed: {\n          label: collapsedLabel,\n          width: collapsedWidth,\n          height: collapsedHeight,\n        },\n      },\n    };\n\n    nodes.push(nodeElement);\n\n    for (\n      const fileDependency of Object.values(fileDependencyManifest.dependencies)\n    ) {\n      if (fileDependency.isExternal) {\n        continue;\n      }\n\n      if (fileDependency.id === fileDependencyManifest.id) {\n        // ignore self-references\n        continue;\n      }\n\n      edges.push({\n        data: {\n          source: fileDependencyManifest.id,\n          target: fileDependency.id,\n        },\n      });\n    }\n  });\n\n  return [...nodes, ...edges];\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/elements/symbol.ts",
    "content": "import type * as auditManifestTypes from \"../../types/auditManifest\";\nimport * as dependencyManifestTypes from \"../../types/dependencyManifest\";\nimport {\n  getCollapsedSymbolNodeLabel,\n  getExpandedSymbolNodeLabel,\n  getNodeWidthAndHeightFromLabel,\n} from \"../label/index.ts\";\nimport { getMetricsSeverityForNode } from \"../metrics/index.ts\";\nimport type { SymbolNapiNodeData } from \"./types.ts\";\nimport type { EdgeDefinition, NodeDefinition } from \"cytoscape\";\nimport { computeNodeId } from \"./file.ts\";\n\nfunction createNodeData(params: {\n  id: string;\n  fileName: string;\n  symbolName: string;\n  symbolType: string;\n  isExternal: boolean;\n  metricsSeverity: {\n    [dependencyManifestTypes.metricLinesCount]: number;\n    [dependencyManifestTypes.metricCodeLineCount]: number;\n    [dependencyManifestTypes.metricCodeCharacterCount]: number;\n    [dependencyManifestTypes.metricCharacterCount]: number;\n    [dependencyManifestTypes.metricDependencyCount]: number;\n    [dependencyManifestTypes.metricDependentCount]: number;\n    [dependencyManifestTypes.metricCyclomaticComplexity]: number;\n  };\n  expandedLabel: string;\n  collapsedLabel: string;\n}): SymbolNapiNodeData {\n  // Calculate dimensions for expanded and collapsed views\n  const { width: expandedWidth, height: expandedHeight } =\n    getNodeWidthAndHeightFromLabel(\n      params.expandedLabel,\n    );\n\n  const { width: collapsedWidth, height: collapsedHeight } =\n    getNodeWidthAndHeightFromLabel(\n      params.collapsedLabel,\n    );\n\n  // Create the node data structure\n  return {\n    id: params.id,\n    position: { x: 0, y: 0 },\n    fileName: params.fileName,\n    isExternal: params.isExternal,\n    symbolName: params.symbolName,\n    symbolType: params.symbolType,\n    metricsSeverity: params.metricsSeverity,\n    expanded: {\n      label: params.expandedLabel,\n      width: expandedWidth,\n      height: expandedHeight,\n    },\n    collapsed: {\n      label: params.collapsedLabel,\n      width: collapsedWidth,\n      height: collapsedHeight,\n    },\n  };\n}\n\ninterface CustomNodeDefinition extends NodeDefinition {\n  data: SymbolNapiNodeData & object;\n}\n\nfunction traverseSymbolGraph(\n  symbol: dependencyManifestTypes.DependencyManifest[string][\"symbols\"][string],\n  symbolNodeId: string,\n  dependencyManifest: dependencyManifestTypes.DependencyManifest,\n  auditManifest: auditManifestTypes.AuditManifest,\n  nodeMap: Map<string, CustomNodeDefinition>,\n  edgeMap: Map<string, EdgeDefinition>,\n  currentDepth: number,\n  maxDepsDepth: number,\n  maxDependentsDepth: number,\n  visited: Set<string>,\n) {\n  // Process dependencies if we haven't reached max depth\n  if (currentDepth < maxDepsDepth) {\n    Object.values(symbol.dependencies).forEach((dep) => {\n      let depDependencyManifest:\n        | dependencyManifestTypes.DependencyManifest[string]\n        | undefined;\n      let depAuditManifest:\n        | auditManifestTypes.AuditManifest[string]\n        | undefined;\n\n      if (!dep.isExternal) {\n        depDependencyManifest = dependencyManifest[dep.id];\n        depAuditManifest = auditManifest[dep.id];\n      }\n\n      Object.keys(dep.symbols).forEach((depSymbolName) => {\n        const depSymbolNodeId = computeNodeId(dep.id, depSymbolName);\n\n        // Skip if already visited\n        if (visited.has(depSymbolNodeId)) {\n          // Add edge even if node was already visited\n          const edgeId = `${depSymbolNodeId}->${symbolNodeId}`;\n          edgeMap.set(edgeId, {\n            data: {\n              id: edgeId,\n              source: depSymbolNodeId,\n              target: symbolNodeId,\n            },\n          });\n          return;\n        }\n        visited.add(depSymbolNodeId);\n\n        let depSymbolType: dependencyManifestTypes.SymbolType | \"unknown\" =\n          \"unknown\";\n        if (depDependencyManifest) {\n          depSymbolType = depDependencyManifest.symbols[depSymbolName].type;\n        }\n\n        const expandedLabel = getExpandedSymbolNodeLabel({\n          currentFileId: dep.id,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          symbolAuditManifest: depAuditManifest?.symbols[depSymbolName],\n        });\n\n        const collapsedLabel = getCollapsedSymbolNodeLabel({\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n        });\n\n        const metricsSeverity = getMetricsSeverityForNode(\n          depAuditManifest?.symbols[depSymbolName],\n        );\n\n        const nodeData = createNodeData({\n          id: depSymbolNodeId,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          isExternal: dep.isExternal,\n          metricsSeverity,\n          expandedLabel,\n          collapsedLabel,\n        });\n\n        nodeMap.set(depSymbolNodeId, { data: nodeData });\n\n        // Add the edge\n        const edgeId = `${depSymbolNodeId}->${symbolNodeId}`;\n        edgeMap.set(edgeId, {\n          data: {\n            id: edgeId,\n            source: depSymbolNodeId,\n            target: symbolNodeId,\n          },\n        });\n\n        // Recursively process dependencies if not external\n        if (!dep.isExternal && depDependencyManifest) {\n          traverseSymbolGraph(\n            depDependencyManifest.symbols[depSymbolName],\n            depSymbolNodeId,\n            dependencyManifest,\n            auditManifest,\n            nodeMap,\n            edgeMap,\n            currentDepth + 1,\n            maxDepsDepth,\n            maxDependentsDepth,\n            visited,\n          );\n        }\n      });\n    });\n  }\n\n  // Process dependents if we haven't reached max depth\n  if (currentDepth < maxDependentsDepth) {\n    Object.values(symbol.dependents).forEach((dep) => {\n      const depDependencyManifest = dependencyManifest[dep.id];\n      const depAuditManifest = auditManifest[dep.id];\n\n      Object.keys(dep.symbols).forEach((depSymbolName) => {\n        const depSymbolNodeId = computeNodeId(dep.id, depSymbolName);\n\n        // Skip if already visited\n        if (visited.has(depSymbolNodeId)) {\n          // Add edge even if node was already visited\n          const edgeId = `${symbolNodeId}->${depSymbolNodeId}`;\n          edgeMap.set(edgeId, {\n            data: {\n              id: edgeId,\n              source: symbolNodeId,\n              target: depSymbolNodeId,\n            },\n          });\n          return;\n        }\n        visited.add(depSymbolNodeId);\n\n        const depSymbolType = depDependencyManifest.symbols[depSymbolName].type;\n\n        const expandedLabel = getExpandedSymbolNodeLabel({\n          currentFileId: dep.id,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          symbolAuditManifest: depAuditManifest?.symbols[depSymbolName],\n        });\n\n        const metricsSeverity = getMetricsSeverityForNode(\n          depAuditManifest?.symbols[depSymbolName],\n        );\n\n        const isExternal = !dependencyManifest[dep.id];\n\n        const nodeData = createNodeData({\n          id: depSymbolNodeId,\n          fileName: dep.id,\n          symbolName: depSymbolName,\n          symbolType: depSymbolType,\n          isExternal,\n          metricsSeverity,\n          expandedLabel,\n          collapsedLabel: depSymbolName,\n        });\n\n        nodeMap.set(depSymbolNodeId, { data: nodeData });\n\n        // Add the edge\n        const edgeId = `${symbolNodeId}->${depSymbolNodeId}`;\n        edgeMap.set(edgeId, {\n          data: {\n            id: edgeId,\n            source: symbolNodeId,\n            target: depSymbolNodeId,\n          },\n        });\n\n        // Recursively process dependents\n        if (depDependencyManifest) {\n          traverseSymbolGraph(\n            depDependencyManifest.symbols[depSymbolName],\n            depSymbolNodeId,\n            dependencyManifest,\n            auditManifest,\n            nodeMap,\n            edgeMap,\n            currentDepth + 1,\n            maxDepsDepth,\n            maxDependentsDepth,\n            visited,\n          );\n        }\n      });\n    });\n  }\n}\n\nexport function getSymbolElementsForSymbol(\n  fileName: string,\n  symbolId: string,\n  dependencyDepth: number,\n  dependentDepth: number,\n  dependencyManifest: dependencyManifestTypes.DependencyManifest,\n  auditManifest: auditManifestTypes.AuditManifest,\n) {\n  const fileManifest = dependencyManifest[fileName];\n  if (!fileManifest) {\n    console.error(`File manifest not found for ${fileName}`);\n    return [];\n  }\n  const symbolManifest = fileManifest.symbols[symbolId];\n  if (!symbolManifest) {\n    console.error(`Symbol manifest not found for ${symbolId}`);\n    return [];\n  }\n\n  const fileAuditManifest = auditManifest[fileName];\n  const symbolAuditManifest = fileAuditManifest.symbols[symbolId];\n\n  const nodeMap: Map<string, CustomNodeDefinition> = new Map();\n  const edgeMap: Map<string, EdgeDefinition> = new Map();\n  const visited = new Set<string>();\n\n  const symbolNodeId = computeNodeId(fileName, symbolManifest.id);\n  visited.add(symbolNodeId);\n\n  // Create labels for the symbol\n  const expandedLabel = getExpandedSymbolNodeLabel({\n    currentFileId: fileName,\n    fileName,\n    symbolName: symbolManifest.id,\n    symbolType: symbolManifest.type,\n    symbolAuditManifest,\n  });\n  const collapsedLabel = getCollapsedSymbolNodeLabel({\n    symbolName: symbolManifest.id,\n    symbolType: symbolManifest.type,\n  });\n\n  const metricsSeverity = getMetricsSeverityForNode(symbolAuditManifest);\n\n  const isExternal = !dependencyManifest[fileName];\n\n  const nodeData = createNodeData({\n    id: symbolNodeId,\n    fileName,\n    symbolName: symbolManifest.id,\n    symbolType: symbolManifest.type,\n    isExternal,\n    metricsSeverity,\n    expandedLabel,\n    collapsedLabel,\n  });\n\n  nodeMap.set(symbolNodeId, { data: nodeData });\n\n  // Use the recursive function to process dependencies and dependents\n  traverseSymbolGraph(\n    symbolManifest,\n    symbolNodeId,\n    dependencyManifest,\n    auditManifest,\n    nodeMap,\n    edgeMap,\n    0,\n    dependencyDepth,\n    dependentDepth,\n    visited,\n  );\n\n  const nodes = Array.from(nodeMap.values());\n  const edges = Array.from(edgeMap.values());\n\n  return [...nodes, ...edges];\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/elements/types.ts",
    "content": "import * as dependencyManifestTypes from \"../../types/dependencyManifest\";\n\nexport interface NapiNodeData {\n  id: string;\n  position: { x: number; y: number };\n  metricsSeverity: {\n    [dependencyManifestTypes.metricLinesCount]: number;\n    [dependencyManifestTypes.metricCodeLineCount]: number;\n    [dependencyManifestTypes.metricCodeCharacterCount]: number;\n    [dependencyManifestTypes.metricCharacterCount]: number;\n    [dependencyManifestTypes.metricDependencyCount]: number;\n    [dependencyManifestTypes.metricDependentCount]: number;\n    [dependencyManifestTypes.metricCyclomaticComplexity]: number;\n  };\n  expanded: {\n    label: string;\n    width: number;\n    height: number;\n  };\n  collapsed: {\n    label: string;\n    width: number;\n    height: number;\n  };\n}\n\nexport interface FileNapiNodeData extends NapiNodeData {\n  fileName: string;\n  isExternal: boolean;\n}\n\nexport interface SymbolNapiNodeData extends NapiNodeData {\n  fileName: string;\n  isExternal: boolean;\n  symbolName: string;\n  symbolType: string;\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/fileDependencyVisualizer/index.ts",
    "content": "import type * as auditManifestTypes from \"../../types/auditManifest\";\nimport * as dependencyManifestTypes from \"../../types/dependencyManifest\";\nimport type {\n  Collection,\n  Core,\n  EdgeSingular,\n  EventObjectNode,\n  NodeSingular,\n} from \"cytoscape\";\nimport fcose from \"cytoscape-fcose\";\nimport type { SymbolNapiNodeData } from \"../elements/types.ts\";\nimport cytoscape from \"cytoscape\";\nimport { mainLayout } from \"../layout/index.ts\";\nimport { getCytoscapeStylesheet } from \"../styles/index.ts\";\nimport { computeNodeId, getSymbolElementsInFile } from \"../elements/file.ts\";\n\n/**\n * FileDependencyVisualizer creates an interactive graph of symbol dependencies within a file.\n *\n * This visualization provides a detailed view of internal file structure where:\n * - Nodes represent symbols (functions, classes, variables) within the file\n * - Edges represent dependencies between symbols\n * - Node shapes indicate symbol types (hexagon for classes, ellipse for functions, etc.)\n * - Colors indicate metrics severity for each symbol\n *\n * Key features:\n * - Focus on a single file's internal structure and external dependencies\n * - Different node shapes for different symbol types (classes, functions, variables)\n * - Theme-aware visualization with optimized colors for light/dark modes\n * - Selectable metric visualization for symbol-level analysis\n * - Interactive node selection with focus on direct dependencies\n * - Visual distinction between internal and external symbols\n * - Comprehensive display of audit information for each symbol\n *\n * The visualization is designed to help developers understand code organization\n * at the symbol level, identify complex relationships, and analyze internal\n * dependencies within files.\n */\nexport class FileDependencyVisualizer {\n  public cy: Core;\n  private theme: \"light\" | \"dark\";\n  private layout = mainLayout;\n  private fileId: string;\n  /** Current metric used for node coloring */\n  private targetMetric: dependencyManifestTypes.Metric | undefined;\n  /** Currently selected node in the graph */\n  private selectedNodeId: string | undefined;\n  /** Callback functions triggered by graph interactions */\n  private externalCallbacks: {\n    onAfterNodeClick: () => void;\n    onAfterNodeDblClick: (filePath: string, symbolId: string) => void;\n    onAfterNodeRightClick: (data: {\n      position: { x: number; y: number };\n      filePath: string;\n      symbolId: string;\n    }) => void;\n  };\n\n  constructor(\n    container: HTMLElement,\n    fileId: string,\n    dependencyManifest: dependencyManifestTypes.DependencyManifest,\n    auditManifest: auditManifestTypes.AuditManifest,\n    options?: {\n      theme?: \"light\" | \"dark\";\n      defaultMetric?: dependencyManifestTypes.Metric | undefined;\n      onAfterNodeClick?: () => void;\n      onAfterNodeRightClick?: (data: {\n        position: { x: number; y: number };\n        filePath: string;\n        symbolId: string;\n      }) => void;\n      onAfterNodeDblClick?: (filePath: string, symbolId: string) => void;\n    },\n  ) {\n    this.fileId = fileId;\n\n    const defaultOptions = {\n      onAfterNodeClick: () => {},\n      onAfterNodeDblClick: () => {},\n      onAfterNodeRightClick: () => {},\n      theme: \"light\" as const,\n      defaultMetric: undefined,\n    };\n\n    const mergedOptions = { ...defaultOptions, ...options };\n\n    this.targetMetric = mergedOptions.defaultMetric;\n\n    this.externalCallbacks = {\n      onAfterNodeClick: mergedOptions.onAfterNodeClick,\n      onAfterNodeDblClick: mergedOptions.onAfterNodeDblClick,\n      onAfterNodeRightClick: mergedOptions.onAfterNodeRightClick,\n    };\n\n    this.theme = mergedOptions.theme;\n\n    cytoscape.use(fcose);\n    this.cy = cytoscape();\n    this.cy.mount(container);\n\n    this.cy.batch(() => {\n      const elements = getSymbolElementsInFile(\n        fileId,\n        dependencyManifest,\n        auditManifest,\n      );\n      this.cy.add(elements);\n\n      const allNodes = this.cy.nodes();\n\n      const currentFileNode = allNodes.filter(\n        `node[fileName=\"${this.fileId}\"]`,\n      );\n\n      allNodes.addClass(\"symbol\");\n      currentFileNode.addClass(\"collapsed\");\n\n      const stylesheet = getCytoscapeStylesheet(\n        this.targetMetric,\n        this.theme,\n      );\n      this.cy.style(stylesheet);\n\n      this.layoutGraph(this.cy);\n\n      this.createEventListeners();\n    });\n  }\n\n  /**\n   * Updates the theme of the visualization between light and dark mode\n   *\n   * @param theme - The theme to switch to (\"light\" or \"dark\")\n   */\n  public updateTheme(theme: \"light\" | \"dark\") {\n    this.theme = theme;\n    const stylesheet = getCytoscapeStylesheet(\n      this.targetMetric,\n      this.theme,\n    );\n    this.cy.style(stylesheet);\n  }\n\n  /**\n   * Highlights a specific node in the graph\n   *\n   * @param nodeId - The ID of the node to highlight\n   */\n  public highlightNode(ref: { filePath: string; symbolId?: string }) {\n    if (ref.symbolId) {\n      const nodeId = computeNodeId(ref.filePath, ref.symbolId);\n\n      const highlightedNode = this.cy.nodes(`node[id=\"${nodeId}\"]`);\n      const allElements = this.cy.elements();\n      const otherElements = allElements.difference(highlightedNode);\n\n      otherElements.removeClass(\"highlighted\");\n      highlightedNode.addClass(\"highlighted\");\n    }\n  }\n\n  /**\n   * Unhighlights all nodes in the graph\n   */\n  public unhighlightNodes() {\n    const allElements = this.cy.elements();\n\n    allElements.removeClass(\"highlighted\");\n  }\n\n  /**\n   * Changes the metric used for coloring nodes and updates the visualization\n   *\n   * @param metric - The new metric to use for node coloring\n   */\n  public setTargetMetric(metric: dependencyManifestTypes.Metric | undefined) {\n    this.targetMetric = metric;\n\n    const stylesheet = getCytoscapeStylesheet(\n      this.targetMetric,\n      this.theme,\n    );\n    this.cy.style(stylesheet);\n  }\n\n  public filterNodes(\n    showExternal: boolean,\n    showVariables: boolean,\n    showFunctions: boolean,\n    showClasses: boolean,\n    showStructs: boolean,\n    showEnums: boolean,\n    showInterfaces: boolean,\n    showRecords: boolean,\n    showDelegates: boolean,\n  ) {\n    const nodesToHide = this.cy.nodes().filter((node: NodeSingular) => {\n      const data = node.data() as SymbolNapiNodeData;\n      if (data.fileName === this.fileId) {\n        // never hide symbols from the current file\n        return false;\n      }\n\n      if (!showExternal && data.isExternal) {\n        return true;\n      }\n\n      const symbolTypeFilters = {\n        [dependencyManifestTypes.symbolTypeVariable]: showVariables,\n        [dependencyManifestTypes.symbolTypeFunction]: showFunctions,\n        [dependencyManifestTypes.symbolTypeClass]: showClasses,\n        [dependencyManifestTypes.symbolTypeStruct]: showStructs,\n        [dependencyManifestTypes.symbolTypeEnum]: showEnums,\n        [dependencyManifestTypes.symbolTypeInterface]: showInterfaces,\n        [dependencyManifestTypes.symbolTypeRecord]: showRecords,\n        [dependencyManifestTypes.symbolTypeDelegate]: showDelegates,\n      };\n      for (const [symbolType, show] of Object.entries(symbolTypeFilters)) {\n        if (!show && data.symbolType === symbolType) {\n          return true;\n        }\n      }\n\n      return false;\n    });\n\n    const elementsToHide = nodesToHide.connectedEdges().union(nodesToHide);\n    const otherElements = this.cy.elements().difference(elementsToHide);\n\n    elementsToHide.addClass(\"hidden\");\n    otherElements.removeClass(\"hidden\");\n  }\n\n  /**\n   * Sets up event listeners for node interactions:\n   * - Click: Selects a node and highlights its connections\n   * - Double-click: Triggers the external double-click callback\n   * - Right-click: Opens context menu via the external callback\n   */\n  private createEventListeners() {\n    this.cy.on(\"onetap\", \"node\", (evt: EventObjectNode) => {\n      const nodeId = evt.target.id();\n      const nodeData = evt.target.data() as SymbolNapiNodeData;\n      const isAlreadySelected = this.selectedNodeId === nodeId;\n      this.selectedNodeId = nodeId;\n\n      const isCurrentFile = nodeData.fileName === this.fileId;\n      if (!isCurrentFile) {\n        // Only allow selection of nodes within the current file\n        return;\n      }\n\n      const allElements = this.cy.elements();\n\n      const selectedNode = this.cy.nodes(`node[id=\"${this.selectedNodeId}\"]`);\n\n      const connectedNodes = selectedNode\n        .closedNeighborhood()\n        .nodes()\n        .difference(selectedNode);\n\n      const dependentEdges = selectedNode\n        .connectedEdges()\n        .filter(\n          (edge: EdgeSingular) => edge.source().id() === this.selectedNodeId,\n        );\n\n      const dependencyEdges = selectedNode\n        .connectedEdges()\n        .filter(\n          (edge: EdgeSingular) => edge.target().id() === this.selectedNodeId,\n        );\n\n      const focusedElements = selectedNode.closedNeighborhood();\n\n      const backgroundElements = allElements.difference(focusedElements);\n\n      this.cy.batch(() => {\n        // remove all, clean state\n        allElements.removeClass([\n          \"collapsed\",\n          \"expanded\",\n          \"selected\",\n          \"background\",\n          \"dependency\",\n          \"dependent\",\n        ]);\n\n        if (!isAlreadySelected) {\n          backgroundElements.addClass(\"background\");\n\n          connectedNodes.addClass(\"collapsed\");\n\n          selectedNode.addClass(\"expanded\");\n          selectedNode.addClass(\"selected\");\n\n          dependencyEdges.addClass(\"dependency\");\n          dependentEdges.addClass(\"dependent\");\n\n          focusedElements.layout(this.layout).run();\n        } else {\n          this.selectedNodeId = undefined;\n\n          const fileNodes = this.cy.nodes().filter(\n            `node[fileName=\"${this.fileId}\"]`,\n          );\n          fileNodes.addClass(\"collapsed\");\n        }\n      });\n\n      this.externalCallbacks.onAfterNodeClick();\n    });\n\n    this.cy.on(\"dbltap\", \"node\", (evt: EventObjectNode) => {\n      const node = evt.target;\n      const data = node.data() as SymbolNapiNodeData;\n\n      // If the node is external, ignore it\n      if (data.isExternal) return;\n\n      this.externalCallbacks.onAfterNodeDblClick(\n        data.fileName,\n        data.symbolName,\n      );\n    });\n\n    this.cy.on(\"cxttap\", \"node\", (evt: EventObjectNode) => {\n      const node = evt.target;\n      const data = node.data() as SymbolNapiNodeData;\n\n      // If the node is external, ignore it\n      if (data.isExternal) return;\n\n      const { x, y } = node.renderedPosition();\n      this.externalCallbacks.onAfterNodeRightClick({\n        position: { x, y },\n        filePath: data.fileName,\n        symbolId: data.symbolName,\n      });\n    });\n  }\n\n  /**\n   * Applies the graph layout algorithm to position nodes optimally\n   *\n   * @param collection - The collection of elements to layout (defaults to all nodes)\n   */\n  public layoutGraph(collection: Collection | Core) {\n    const collectionToLayout = collection || this.cy.nodes();\n    collectionToLayout.layout(this.layout).run();\n  }\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/label/index.ts",
    "content": "import type * as auditManifestTypes from \"../../types/auditManifest\";\n\n/**\n * Calculates the optimal width and height for a node based on its label text.\n *\n * Determines dimensions by measuring the label's text length and line count,\n * applying appropriate font size, line height, and padding.\n * Enforces minimum dimensions to ensure nodes are visually distinguishable.\n *\n * @param label - The label text to be displayed in the node\n * @param options - Configuration options for calculating dimensions\n * @returns Object containing calculated width and height\n */\nexport function getNodeWidthAndHeightFromLabel(\n  label: string,\n  options = {\n    fontSize: 10,\n    lineHeight: 1.5,\n    padding: 10,\n    minHeight: 60,\n    minWidth: 60,\n  },\n) {\n  const lines = label.split(\"\\n\");\n\n  const height = Math.max(\n    lines.length * options.fontSize * options.lineHeight + 2 * options.padding,\n    options.minHeight,\n  );\n\n  const width = Math.max(\n    ...lines.map(\n      (line) => line.length * options.fontSize + 2 * options.padding,\n    ),\n    options.minWidth,\n  );\n\n  return { width, height };\n}\n\nconst successChar = \"🎉\";\nconst errorChar = \"⚠️\";\n\n/**\n * Generates the collapsed label for a file node with summarized audit information.\n *\n * Creates a compact representation showing:\n * - Truncated file name (if longer than maximum length)\n * - Alert count with warning icon (if issues exist)\n *\n * Used for the default, non-selected state of nodes in the graph visualization.\n *\n * @param data - Object containing file name and audit information\n * @returns Formatted label string for collapsed node view\n */\nexport function getCollapsedFileNodeLabel(data: {\n  fileName: string;\n  fileAuditManifest: auditManifestTypes.AuditManifest[string];\n}) {\n  const fileNameMaxLength = 25;\n  const fileName = data.fileName.length > fileNameMaxLength\n    ? `...${data.fileName.slice(-fileNameMaxLength)}`\n    : data.fileName;\n\n  let label = fileName;\n\n  const alerts = Object.values(data.fileAuditManifest.alerts);\n\n  if (alerts.length > 0) {\n    label += `\\n${errorChar}(${alerts.length})`;\n  }\n\n  return label;\n}\n\n/**\n * Generates the expanded label for a file node with detailed audit information.\n *\n * Creates a comprehensive display showing:\n * - Full file name without truncation\n * - List of all alerts with warning icons and short messages\n * - Success message if no issues are found\n *\n * Used when a node is selected to provide more detailed information.\n *\n * @param data - Object containing file name and audit information\n * @returns Formatted label string for expanded node view\n */\nexport function getExpandedFileNodeLabel(data: {\n  fileName: string;\n  fileAuditManifest: auditManifestTypes.AuditManifest[string];\n}) {\n  let label = data.fileName;\n\n  const alerts = Object.values(data.fileAuditManifest.alerts);\n\n  if (alerts.length > 0) {\n    alerts.forEach((alert) => {\n      label += `\\n${errorChar} ${alert.message.short}`;\n    });\n  } else {\n    label += `\\n${successChar} No issues found`;\n  }\n\n  return label;\n}\n\n/**\n * Generates the collapsed label for a symbol node with minimal information.\n *\n * Shows only the symbol name for a compact representation in the non-selected state.\n *\n * @param data - Object containing symbol name\n * @returns Symbol name as the collapsed label\n */\nexport function getCollapsedSymbolNodeLabel(data: {\n  symbolName: string;\n  symbolType: string;\n}) {\n  return `${data.symbolName} (${data.symbolType})`;\n}\n\n/**\n * Generates the expanded label for a symbol node with detailed information.\n *\n * Creates a comprehensive display showing:\n * - Symbol name and type\n * - Source file information\n * - List of alerts with warning icons (if any)\n * - Success message if no issues are found\n *\n * Used when a symbol node is selected to provide more detailed information.\n *\n * @param data - Object containing symbol information and audit data\n * @returns Formatted label string for expanded symbol node view\n */\nexport function getExpandedSymbolNodeLabel(data: {\n  currentFileId: string;\n  fileName: string;\n  symbolName: string;\n  symbolType: string;\n  symbolAuditManifest:\n    | auditManifestTypes.AuditManifest[string][\"symbols\"][string]\n    | undefined;\n}) {\n  // Create the basic label with symbol name and type\n  let label = `${data.symbolName} (${data.symbolType})`;\n  // Add file information\n  label += `\\nSource: ${data.fileName}`;\n\n  // Add alerts information if available and not an external symbol\n  if (data.symbolAuditManifest) {\n    const alertList = Object.values(data.symbolAuditManifest.alerts);\n\n    if (alertList.length > 0) {\n      alertList.forEach((alert) => {\n        label += `\\n${errorChar} ${alert.message.short}`;\n      });\n    } else {\n      label += `\\n${successChar} No issues`;\n    }\n  }\n\n  return label;\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/layout/index.ts",
    "content": "import type { FcoseLayoutOptions } from \"cytoscape-fcose\";\n\n/**\n * Main layout configuration for dependency visualization graphs.\n *\n * Uses the F-COSE (Force-directed Compound Spring Embedder) algorithm optimized for:\n * - High quality graph rendering with \"proof\" quality setting\n * - Strong node repulsion to prevent overlapping (1,000,000 force units)\n * - Ideal edge length of 200px for readability\n * - Gentle gravity (0.1) to pull components toward center\n * - Component packing to utilize space efficiently\n * - Node dimensions that include labels to prevent text overlap\n */\nexport const mainLayout = {\n  name: \"fcose\",\n  quality: \"proof\",\n  nodeRepulsion: 1000000,\n  idealEdgeLength: 200,\n  gravity: 0.1,\n  packComponents: true,\n  nodeDimensionsIncludeLabels: true,\n} as FcoseLayoutOptions;\n"
  },
  {
    "path": "viewer/src/cytoscape/metrics/index.ts",
    "content": "import type * as auditManifestTypes from \"../../types/auditManifest\";\nimport * as dependencyManifestTypes from \"../../types/dependencyManifest\";\n\n/**\n * Extracts metric severity levels from an audit manifest for visualization.\n *\n * Processes the audit manifest to build a record of severity values (0-5) for each\n * supported metric type. Default severity is 0 (no issues) for metrics not present\n * in the audit manifest.\n *\n * @param auditManifest - Audit information for a file or symbol\n * @returns Object mapping each metric type to its severity level (0-5)\n */\nexport function getMetricsSeverityForNode(\n  auditManifest:\n    | auditManifestTypes.AuditManifest[string]\n    | auditManifestTypes.AuditManifest[string][\"symbols\"][string]\n    | undefined,\n) {\n  const metricsSeverity: Record<dependencyManifestTypes.Metric, number> = {\n    [dependencyManifestTypes.metricLinesCount]: 0,\n    [dependencyManifestTypes.metricCodeLineCount]: 0,\n    [dependencyManifestTypes.metricCodeCharacterCount]: 0,\n    [dependencyManifestTypes.metricCharacterCount]: 0,\n    [dependencyManifestTypes.metricDependencyCount]: 0,\n    [dependencyManifestTypes.metricDependentCount]: 0,\n    [dependencyManifestTypes.metricCyclomaticComplexity]: 0,\n  };\n\n  if (auditManifest) {\n    Object.entries(auditManifest.alerts).forEach(([metric, value]) => {\n      metricsSeverity[metric as dependencyManifestTypes.Metric] =\n        value.severity;\n    });\n  }\n\n  return metricsSeverity;\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/projectDependencyVisualizer/index.ts",
    "content": "import cytoscape, {\n  type Collection,\n  type EdgeSingular,\n  type EventObjectNode,\n} from \"cytoscape\";\nimport type { Core } from \"cytoscape\";\nimport fcose from \"cytoscape-fcose\";\nimport type * as dependencyManifestTypes from \"../../types/dependencyManifest\";\nimport type * as auditManifestTypes from \"../../types/auditManifest\";\nimport type { NapiNodeData } from \"../elements/types.ts\";\nimport { mainLayout } from \"../layout/index.ts\";\nimport { getCytoscapeStylesheet } from \"../styles/index.ts\";\nimport { getFileElementsInProject } from \"../elements/project.ts\";\n\n/**\n * ProjectDependencyVisualizer creates an interactive graph of project-level dependencies.\n *\n * This visualization provides a comprehensive view of the project's architecture where:\n * - Nodes represent individual project files, sized according to complexity metrics\n * - Edges represent import/export relationships between files\n * - Colors indicate metrics severity (code size, complexity, dependency count)\n * - Interactive features allow exploration of dependency relationships\n *\n * Key features:\n * - Theme-aware visualization with optimized colors for light/dark modes\n * - Selectable metric visualization (LOC, character count, cyclomatic complexity)\n * - Interactive node selection with focus on direct dependencies\n * - Automatic layout using F-COSE algorithm for optimal readability\n * - Support for different node states (selected, connected, highlighted)\n * - Visual representation of audit alerts and warnings\n *\n * The visualization is designed to help developers understand project structure,\n * identify problematic dependencies, and analyze code complexity at the file level.\n */\nexport class ProjectDependencyVisualizer {\n  public cy: Core;\n  private theme: \"light\" | \"dark\";\n  /** Layout configuration for organizing the dependency graph */\n  private layout = mainLayout;\n  /** Current metric used for node coloring */\n  private targetMetric: dependencyManifestTypes.Metric | undefined;\n  /** Currently selected node in the graph */\n  private selectedNodeId: string | undefined;\n  /** Callback functions triggered by graph interactions */\n  private externalCallbacks: {\n    onAfterNodeClick: () => void;\n    onAfterNodeDblClick: (filePath: string) => void;\n    onAfterNodeRightClick: (data: {\n      position: { x: number; y: number };\n      filePath: string;\n    }) => void;\n  };\n\n  /**\n   * Creates a new CodeDependencyVisualizer instance.\n   *\n   * @param container - The HTML element to mount the Cytoscape graph onto\n   * @param dependencyManifest - Object containing dependency information for project files\n   * @param auditManifest - Object containing audit information (errors/warnings) for project files\n   * @param options - Optional configuration parameters\n   */\n  constructor(\n    container: HTMLElement,\n    dependencyManifest: dependencyManifestTypes.DependencyManifest,\n    auditManifest: auditManifestTypes.AuditManifest,\n    options?: {\n      theme?: \"light\" | \"dark\";\n      defaultMetric?: dependencyManifestTypes.Metric | undefined;\n      onAfterNodeClick?: () => void;\n      onAfterNodeRightClick?: (data: {\n        position: { x: number; y: number };\n        filePath: string;\n      }) => void;\n      onAfterNodeDblClick?: (filePath: string) => void;\n    },\n  ) {\n    const defaultOptions = {\n      onAfterNodeClick: () => {},\n      onAfterNodeDblClick: () => {},\n      onAfterNodeRightClick: () => {},\n      theme: \"light\" as const,\n      defaultMetric: undefined,\n    };\n\n    const mergedOptions = { ...defaultOptions, ...options };\n\n    this.targetMetric = mergedOptions.defaultMetric;\n\n    this.externalCallbacks = {\n      onAfterNodeClick: mergedOptions.onAfterNodeClick,\n      onAfterNodeDblClick: mergedOptions.onAfterNodeDblClick,\n      onAfterNodeRightClick: mergedOptions.onAfterNodeRightClick,\n    };\n\n    cytoscape.use(fcose);\n    this.cy = cytoscape();\n    this.cy.mount(container);\n    this.theme = mergedOptions.theme;\n\n    const elements = getFileElementsInProject(\n      dependencyManifest,\n      auditManifest,\n    );\n    this.cy.add(elements);\n\n    const stylesheet = getCytoscapeStylesheet(\n      this.targetMetric,\n      this.theme,\n    );\n    this.cy.style(stylesheet);\n\n    this.layoutGraph(this.cy);\n\n    this.createEventListeners();\n  }\n\n  /**\n   * Updates the theme of the visualization between light and dark mode\n   *\n   * @param theme - The theme to switch to (\"light\" or \"dark\")\n   */\n  public updateTheme(theme: \"light\" | \"dark\") {\n    this.theme = theme;\n    const stylesheet = getCytoscapeStylesheet(\n      this.targetMetric,\n      this.theme,\n    );\n    this.cy.style(stylesheet);\n  }\n\n  /**\n   * Highlights a specific node in the graph\n   *\n   * @param nodeId - The ID of the node to highlight\n   */\n  public highlightNode(ref: { filePath: string; symbolId?: string }) {\n    const nodeId = ref.filePath;\n\n    const highlightedNode = this.cy.nodes(`node[id=\"${nodeId}\"]`);\n    const allElements = this.cy.elements();\n    const otherElements = allElements.difference(highlightedNode);\n\n    otherElements.removeClass(\"highlighted\");\n    highlightedNode.addClass(\"highlighted\");\n  }\n\n  /**\n   * Unhighlights all nodes in the graph\n   */\n  public unhighlightNodes() {\n    const allElements = this.cy.elements();\n\n    allElements.removeClass(\"highlighted\");\n  }\n\n  /**\n   * Sets up event listeners for node interactions:\n   * - Click: Selects a node and highlights its connections\n   * - Double-click: Triggers the external double-click callback\n   * - Right-click: Opens context menu via the external callback\n   */\n  private createEventListeners() {\n    this.cy.on(\"onetap\", \"node\", (evt: EventObjectNode) => {\n      const nodeId = evt.target.id();\n      const isAlreadySelected = this.selectedNodeId === nodeId;\n      this.selectedNodeId = nodeId;\n\n      const allElements = this.cy.elements();\n\n      const selectedNode = this.cy.nodes(`node[id=\"${this.selectedNodeId}\"]`);\n\n      const connectedNodes = selectedNode\n        .closedNeighborhood()\n        .nodes()\n        .difference(selectedNode);\n\n      const dependentEdges = selectedNode\n        .connectedEdges()\n        .filter(\n          (edge: EdgeSingular) => edge.source().id() === this.selectedNodeId,\n        );\n\n      const dependencyEdges = selectedNode\n        .connectedEdges()\n        .filter(\n          (edge: EdgeSingular) => edge.target().id() === this.selectedNodeId,\n        );\n\n      const focusedElements = selectedNode.closedNeighborhood();\n\n      const backgroundElements = allElements.difference(focusedElements);\n\n      this.cy.batch(() => {\n        // remove all, clean state\n        allElements.removeClass([\n          \"file\",\n          \"collapsed\",\n          \"expanded\",\n          \"selected\",\n          \"dependency\",\n          \"dependent\",\n          \"background\",\n        ]);\n\n        if (!isAlreadySelected) {\n          backgroundElements.addClass(\"background\");\n\n          connectedNodes.addClass(\"collapsed\");\n          connectedNodes.addClass(\"file\");\n\n          selectedNode.addClass(\"expanded\");\n          selectedNode.addClass(\"selected\");\n          selectedNode.addClass(\"file\");\n\n          dependencyEdges.addClass(\"dependency\");\n          dependentEdges.addClass(\"dependent\");\n\n          // layout the closed neighborhood\n          focusedElements.layout(this.layout).run();\n        } else {\n          this.selectedNodeId = undefined;\n        }\n      });\n\n      this.externalCallbacks.onAfterNodeClick();\n    });\n\n    this.cy.on(\"dbltap\", \"node\", (evt: EventObjectNode) => {\n      const node = evt.target;\n      const data = node.data() as NapiNodeData;\n      this.externalCallbacks.onAfterNodeDblClick(data.id);\n    });\n\n    this.cy.on(\"cxttap\", \"node\", (evt: EventObjectNode) => {\n      const node = evt.target;\n      const { x, y } = node.renderedPosition();\n\n      this.externalCallbacks.onAfterNodeRightClick({\n        position: { x, y },\n        filePath: node.id(),\n      });\n    });\n  }\n\n  /**\n   * Applies the graph layout algorithm to position nodes optimally\n   *\n   * @param collection - The collection of elements to layout (defaults to all nodes)\n   */\n  public layoutGraph(collection: Collection | Core) {\n    const collectionToLayout = collection || this.cy.nodes();\n    collectionToLayout.layout(this.layout).run();\n  }\n\n  /**\n   * Changes the metric used for coloring nodes and updates the visualization\n   *\n   * @param metric - The new metric to use for node coloring (e.g., LOC, characters, dependencies)\n   */\n  public setTargetMetric(metric: dependencyManifestTypes.Metric | undefined) {\n    this.targetMetric = metric;\n\n    const stylesheet = getCytoscapeStylesheet(\n      this.targetMetric,\n      this.theme,\n    );\n    this.cy.style(stylesheet);\n  }\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/styles/index.ts",
    "content": "import type { NodeSingular, StylesheetJson } from \"cytoscape\";\nimport * as dependencyManifestTypes from \"../../types/dependencyManifest\";\nimport type { NapiNodeData, SymbolNapiNodeData } from \"../elements/types.ts\";\n\ninterface CytoscapeStyles {\n  node: {\n    colors: {\n      text: {\n        default: string;\n        selected: string;\n        external: string;\n      };\n      background: {\n        default: string;\n        highlighted: string;\n        selected: string;\n        external: string;\n      };\n      border: {\n        default: string;\n        severity: {\n          0: string;\n          1: string;\n          2: string;\n          3: string;\n          4: string;\n          5: string;\n        };\n      };\n    };\n    width: {\n      default: number;\n      highlighted: number;\n    };\n  };\n  edge: {\n    colors: {\n      default: string;\n      dependency: string;\n      dependent: string;\n    };\n    width: {\n      default: number;\n      highlighted: number;\n    };\n  };\n}\n\nfunction getSeverityColor(styles: CytoscapeStyles, level: number) {\n  const severityLevels = styles.node.colors.border.severity;\n  const targetColor = level in severityLevels\n    ? severityLevels[level as keyof typeof severityLevels]\n    : undefined;\n\n  return targetColor || styles.node.colors.border.default;\n}\n\nfunction getCytoscapeStyles(theme: \"light\" | \"dark\" = \"light\") {\n  return {\n    node: {\n      colors: {\n        text: {\n          default: theme === \"light\" ? \"#3B0764\" : \"#FFFFFF\",\n          selected: theme === \"light\" ? \"#FFFFFF\" : \"#3B0764\",\n          external: theme === \"light\" ? \"#3B0764\" : \"#FFFFFF\",\n        },\n        background: {\n          default: theme === \"light\" ? \"#F3E8FF\" : \"#6D28D9\",\n          selected: theme === \"light\" ? \"#A259D9\" : \"#CBA6F7\",\n          external: theme === \"light\" ? \"#F1F5F9\" : \"#334155\",\n          highlighted: theme === \"light\" ? \"#eab308\" : \"#facc15\",\n        },\n        border: {\n          default: theme === \"light\" ? \"#A259D9\" : \"#CBA6F7\",\n          severity: {\n            0: theme === \"light\" ? \"#A259D9\" : \"#CBA6F7\",\n            1: theme === \"light\" ? \"#65a30d\" : \"#a3e635\",\n            2: theme === \"light\" ? \"#ca8a04\" : \"#facc15\",\n            3: theme === \"light\" ? \"#d97706\" : \"#fbbf24\",\n            4: theme === \"light\" ? \"#ea580c\" : \"#fb923c\",\n            5: theme === \"light\" ? \"#dc2626\" : \"#f87171\",\n          },\n        },\n      },\n      width: {\n        default: 5,\n        highlighted: 10,\n      },\n    },\n    edge: {\n      colors: {\n        default: theme === \"light\" ? \"#1a1a1a\" : \"#ffffff\",\n        dependency: theme === \"light\" ? \"#0284c7\" : \"#38bdf8\",\n        dependent: theme === \"light\" ? \"#9333ea\" : \"#a78bfa\",\n      },\n      width: {\n        default: 1,\n        highlighted: 3,\n      },\n    },\n  } as CytoscapeStyles;\n}\n\nexport function getCytoscapeStylesheet(\n  targetMetric: dependencyManifestTypes.Metric | undefined,\n  theme: \"light\" | \"dark\" = \"light\",\n) {\n  const styles = getCytoscapeStyles(theme);\n\n  const stylesheet = [\n    // Node specific styles\n    {\n      selector: \"node\",\n      style: {\n        \"text-wrap\": \"wrap\",\n        color: styles.node.colors.text.default,\n        \"border-width\": styles.node.width.default,\n        \"border-color\": (node: NodeSingular) => {\n          const data = node.data() as NapiNodeData;\n          if (targetMetric) {\n            return getSeverityColor(styles, data.metricsSeverity[targetMetric]);\n          }\n          return styles.node.colors.border.default;\n        },\n        \"background-color\": styles.node.colors.background.default,\n        shape: \"ellipse\",\n        \"text-valign\": \"center\",\n        \"text-halign\": \"center\",\n        \"width\": 20,\n        \"height\": 20,\n        opacity: 0.9,\n      },\n    },\n    {\n      selector: \"node.file\",\n      style: {\n        shape: \"roundrectangle\",\n      },\n    },\n    {\n      selector: \"node.symbol\",\n      style: {\n        \"background-color\": (node: NodeSingular) => {\n          const data = node.data() as SymbolNapiNodeData;\n          return data.isExternal\n            ? styles.node.colors.background.external\n            : styles.node.colors.background.default;\n        },\n        \"color\": (node: NodeSingular) => {\n          const data = node.data() as SymbolNapiNodeData;\n          return data.isExternal\n            ? styles.node.colors.text.external\n            : styles.node.colors.text.default;\n        },\n        shape: (node: NodeSingular) => {\n          const data = node.data() as SymbolNapiNodeData;\n          if (data.isExternal) return \"octagon\";\n          switch (data.symbolType) {\n            case dependencyManifestTypes.symbolTypeClass:\n            case dependencyManifestTypes.symbolTypeInterface:\n            case dependencyManifestTypes.symbolTypeStruct:\n            case dependencyManifestTypes.symbolTypeEnum:\n            case dependencyManifestTypes.symbolTypeRecord:\n              return \"hexagon\";\n            case dependencyManifestTypes.symbolTypeFunction:\n            case dependencyManifestTypes.symbolTypeDelegate:\n              return \"roundrectangle\";\n            case dependencyManifestTypes.symbolTypeVariable:\n              return \"ellipse\";\n            default:\n              return \"ellipse\";\n          }\n        },\n        \"border-style\": (node: NodeSingular) => {\n          const data = node.data() as SymbolNapiNodeData;\n          return data.isExternal ? \"dashed\" : \"solid\";\n        },\n      },\n    },\n    {\n      selector: \"node.collapsed\",\n      style: {\n        label: \"data(collapsed.label)\",\n        width: \"data(collapsed.width)\",\n        height: \"data(collapsed.height)\",\n        \"z-index\": 1000,\n      },\n    },\n    {\n      selector: \"node.expanded\",\n      style: {\n        label: \"data(expanded.label)\",\n        width: \"data(expanded.width)\",\n        height: \"data(expanded.height)\",\n        \"z-index\": 2000,\n      },\n    },\n    {\n      selector: \"node.highlighted\",\n      style: {\n        \"border-width\": styles.node.width.highlighted,\n        \"background-color\": styles.node.colors.background.highlighted,\n      },\n    },\n    {\n      selector: \"node.selected\",\n      style: {\n        \"background-color\": styles.node.colors.background.selected,\n        \"color\": styles.node.colors.text.selected,\n      },\n    },\n\n    // Edge specific styles\n    {\n      selector: \"edge\",\n      style: {\n        width: styles.edge.width.default,\n        \"line-color\": styles.edge.colors.default,\n        \"target-arrow-color\": styles.edge.colors.default,\n        \"target-arrow-shape\": \"triangle\",\n        \"curve-style\": \"bezier\",\n      },\n    },\n    {\n      selector: \"edge.dependency\",\n      style: {\n        width: styles.edge.width.highlighted,\n        \"line-color\": styles.edge.colors.dependency,\n        \"target-arrow-color\": styles.edge.colors.dependency,\n      },\n    },\n    {\n      selector: \"edge.dependent\",\n      style: {\n        width: styles.edge.width.highlighted,\n        \"line-color\": styles.edge.colors.dependent,\n        \"target-arrow-color\": styles.edge.colors.dependent,\n      },\n    },\n\n    // All elements styles\n    {\n      selector: \".background\",\n      style: {\n        \"opacity\": 0.1,\n      },\n    },\n    {\n      selector: \".hidden\",\n      style: {\n        \"opacity\": 0,\n      },\n    },\n  ] as StylesheetJson;\n\n  return stylesheet;\n}\n"
  },
  {
    "path": "viewer/src/cytoscape/symbolDependencyVisualizer/index.ts",
    "content": "import type * as auditManifestTypes from \"../../types/auditManifest\";\nimport * as dependencyManifestTypes from \"../../types/dependencyManifest\";\nimport type {\n  Collection,\n  Core,\n  EventObjectNode,\n  NodeSingular,\n} from \"cytoscape\";\nimport fcose from \"cytoscape-fcose\";\nimport type { SymbolNapiNodeData } from \"../elements/types.ts\";\nimport cytoscape from \"cytoscape\";\nimport { mainLayout } from \"../layout/index.ts\";\nimport { getCytoscapeStylesheet } from \"../styles/index.ts\";\nimport { computeNodeId } from \"../elements/file.ts\";\nimport { getSymbolElementsForSymbol } from \"../elements/symbol.ts\";\n\n/**\n * FileDependencyVisualizer creates an interactive graph of symbol dependencies within a file.\n *\n * This visualization provides a detailed view of internal file structure where:\n * - Nodes represent symbols (functions, classes, variables) within the file\n * - Edges represent dependencies between symbols\n * - Node shapes indicate symbol types (hexagon for classes, ellipse for functions, etc.)\n * - Colors indicate metrics severity for each symbol\n *\n * Key features:\n * - Focus on a single file's internal structure and external dependencies\n * - Different node shapes for different symbol types (classes, functions, variables)\n * - Theme-aware visualization with optimized colors for light/dark modes\n * - Selectable metric visualization for symbol-level analysis\n * - Interactive node selection with focus on direct dependencies\n * - Visual distinction between internal and external symbols\n * - Comprehensive display of audit information for each symbol\n *\n * The visualization is designed to help developers understand code organization\n * at the symbol level, identify complex relationships, and analyze internal\n * dependencies within files.\n */\nexport class SymbolDependencyVisualizer {\n  public cy: Core;\n  private theme: \"light\" | \"dark\";\n  private layout = mainLayout;\n  private filePath: string;\n  private symbolId: string;\n  /** Currently selected node in the graph */\n  private _selectedNodeId: string | undefined;\n  /** Callback functions triggered by graph interactions */\n  private externalCallbacks: {\n    onAfterNodeClick: () => void;\n    onAfterNodeDblClick: (filePath: string, symbolId: string) => void;\n    onAfterNodeRightClick: (data: {\n      position: { x: number; y: number };\n      filePath: string;\n      symbolId: string;\n    }) => void;\n  };\n\n  constructor(\n    container: HTMLElement,\n    filePath: string,\n    symbolId: string,\n    dependencyDepth: number,\n    dependentDepth: number,\n    dependencyManifest: dependencyManifestTypes.DependencyManifest,\n    auditManifest: auditManifestTypes.AuditManifest,\n    options?: {\n      theme?: \"light\" | \"dark\";\n      defaultMetric?: dependencyManifestTypes.Metric | undefined;\n      onAfterNodeClick?: () => void;\n      onAfterNodeRightClick?: (data: {\n        position: { x: number; y: number };\n        filePath: string;\n        symbolId: string;\n      }) => void;\n      onAfterNodeDblClick?: (filePath: string, symbolId: string) => void;\n    },\n  ) {\n    this.filePath = filePath;\n    this.symbolId = symbolId;\n\n    const defaultOptions = {\n      onAfterNodeClick: () => {},\n      onAfterNodeDblClick: () => {},\n      onAfterNodeRightClick: () => {},\n      theme: \"light\" as const,\n      defaultMetric: undefined,\n    };\n\n    const mergedOptions = { ...defaultOptions, ...options };\n\n    this.externalCallbacks = {\n      onAfterNodeClick: mergedOptions.onAfterNodeClick,\n      onAfterNodeDblClick: mergedOptions.onAfterNodeDblClick,\n      onAfterNodeRightClick: mergedOptions.onAfterNodeRightClick,\n    };\n\n    this.theme = mergedOptions.theme;\n\n    cytoscape.use(fcose);\n    this.cy = cytoscape();\n    this.cy.mount(container);\n\n    this.cy.batch(() => {\n      const elements = getSymbolElementsForSymbol(\n        this.filePath,\n        this.symbolId,\n        dependencyDepth,\n        dependentDepth,\n        dependencyManifest,\n        auditManifest,\n      );\n      this.cy.add(elements);\n\n      const allNodes = this.cy.nodes();\n      const selectedNode = this.cy.nodes().filter(\n        (node) => {\n          const data = node.data() as SymbolNapiNodeData;\n          return data.fileName === this.filePath &&\n            data.symbolName === this.symbolId;\n        },\n      );\n      selectedNode.addClass(\"selected\");\n      selectedNode.addClass(\"expanded\");\n\n      allNodes.addClass(\"symbol\");\n\n      const stylesheet = getCytoscapeStylesheet(\n        undefined,\n        this.theme,\n      );\n      this.cy.style(stylesheet);\n\n      this.layoutGraph(this.cy);\n\n      this.createEventListeners();\n    });\n  }\n\n  /**\n   * Updates the theme of the visualization between light and dark mode\n   *\n   * @param theme - The theme to switch to (\"light\" or \"dark\")\n   */\n  public updateTheme(theme: \"light\" | \"dark\") {\n    this.theme = theme;\n    const stylesheet = getCytoscapeStylesheet(\n      undefined,\n      this.theme,\n    );\n    this.cy.style(stylesheet);\n  }\n\n  /**\n   * Highlights a specific node in the graph\n   *\n   * @param nodeId - The ID of the node to highlight\n   */\n  public highlightNode(ref: { filePath: string; symbolId?: string }) {\n    if (ref.symbolId) {\n      const nodeId = computeNodeId(ref.filePath, ref.symbolId);\n\n      const highlightedNode = this.cy.nodes(`node[id=\"${nodeId}\"]`);\n      const allElements = this.cy.elements();\n      const otherElements = allElements.difference(highlightedNode);\n\n      otherElements.removeClass(\"highlighted\");\n      highlightedNode.addClass(\"highlighted\");\n    }\n  }\n\n  /**\n   * Unhighlights all nodes in the graph\n   */\n  public unhighlightNodes() {\n    const allElements = this.cy.elements();\n\n    allElements.removeClass(\"highlighted\");\n  }\n\n  public filterNodes(\n    showExternal: boolean,\n    showVariables: boolean,\n    showFunctions: boolean,\n    showClasses: boolean,\n    showStructs: boolean,\n    showEnums: boolean,\n  ) {\n    const nodesToHide = this.cy.nodes().filter((node: NodeSingular) => {\n      const data = node.data() as SymbolNapiNodeData;\n      if (\n        data.fileName === this.filePath && data.symbolName === this.symbolId\n      ) {\n        // never hide the current symbol\n        return false;\n      }\n\n      if (!showExternal && data.isExternal) {\n        return true;\n      }\n      if (\n        !showVariables &&\n        data.symbolType === dependencyManifestTypes.symbolTypeVariable\n      ) {\n        return true;\n      }\n      if (\n        !showFunctions &&\n        data.symbolType === dependencyManifestTypes.symbolTypeFunction\n      ) {\n        return true;\n      }\n      if (\n        !showClasses &&\n        data.symbolType === dependencyManifestTypes.symbolTypeClass\n      ) {\n        return true;\n      }\n      if (\n        !showStructs &&\n        data.symbolType === dependencyManifestTypes.symbolTypeStruct\n      ) {\n        return true;\n      }\n      if (\n        !showEnums && data.symbolType === dependencyManifestTypes.symbolTypeEnum\n      ) {\n        return true;\n      }\n      return false;\n    });\n\n    const elementsToHide = nodesToHide.connectedEdges().union(nodesToHide);\n    const otherElements = this.cy.elements().difference(elementsToHide);\n\n    elementsToHide.addClass(\"hidden\");\n    otherElements.removeClass(\"hidden\");\n  }\n\n  /**\n   * Sets up event listeners for node interactions:\n   * - Click: Selects a node and highlights its connections\n   * - Double-click: Triggers the external double-click callback\n   * - Right-click: Opens context menu via the external callback\n   */\n  private createEventListeners() {\n    this.cy.on(\"onetap\", \"node\", (evt: EventObjectNode) => {\n      const nodeId = evt.target.id();\n      this._selectedNodeId = nodeId;\n\n      const selectedNode = this.cy.nodes(`node[id=\"${nodeId}\"]`);\n      const isAlreadyExpanded = selectedNode[0].hasClass(\"expanded\");\n\n      if (isAlreadyExpanded) {\n        selectedNode.removeClass(\"expanded\");\n      } else {\n        selectedNode.addClass(\"expanded\");\n      }\n\n      this.externalCallbacks.onAfterNodeClick();\n    });\n\n    this.cy.on(\"dbltap\", \"node\", (evt: EventObjectNode) => {\n      const node = evt.target;\n      const data = node.data() as SymbolNapiNodeData;\n\n      // If the node is external, ignore it\n      if (data.isExternal) return;\n\n      this.externalCallbacks.onAfterNodeDblClick(\n        data.fileName,\n        data.id,\n      );\n    });\n\n    this.cy.on(\"cxttap\", \"node\", (evt: EventObjectNode) => {\n      const node = evt.target;\n      const data = node.data() as SymbolNapiNodeData;\n\n      // If the node is external, ignore it\n      if (data.isExternal) return;\n\n      const { x, y } = node.renderedPosition();\n      this.externalCallbacks.onAfterNodeRightClick({\n        position: { x, y },\n        filePath: data.fileName,\n        symbolId: data.symbolName,\n      });\n    });\n  }\n\n  /**\n   * Applies the graph layout algorithm to position nodes optimally\n   *\n   * @param collection - The collection of elements to layout (defaults to all nodes)\n   */\n  public layoutGraph(collection: Collection | Core) {\n    const collectionToLayout = collection || this.cy.nodes();\n    collectionToLayout.layout(this.layout).run();\n  }\n}\n"
  },
  {
    "path": "viewer/src/index.css",
    "content": "@import \"tailwindcss\";\n@import \"tw-animate-css\";\n\n@custom-variant dark (&:is(.dark *));\n\n:root {\n  --background: oklch(1 0 0);\n  --foreground: oklch(0.145 0 0);\n  --card: oklch(1 0 0);\n  --card-foreground: oklch(0.145 0 0);\n  --popover: oklch(1 0 0);\n  --popover-foreground: oklch(0.145 0 0);\n  --primary: oklch(0.205 0 0);\n  --primary-foreground: oklch(0.985 0 0);\n  --secondary: oklch(0.97 0 0);\n  --secondary-foreground: oklch(0.205 0 0);\n  --muted: oklch(0.97 0 0);\n  --muted-foreground: oklch(0.556 0 0);\n  --accent: oklch(0.97 0 0);\n  --accent-foreground: oklch(0.205 0 0);\n  --destructive: oklch(0.577 0.245 27.325);\n  --destructive-foreground: oklch(0.577 0.245 27.325);\n  --border: oklch(0.922 0 0);\n  --input: oklch(0.922 0 0);\n  --ring: oklch(0.708 0 0);\n  --chart-1: oklch(0.646 0.222 41.116);\n  --chart-2: oklch(0.6 0.118 184.704);\n  --chart-3: oklch(0.398 0.07 227.392);\n  --chart-4: oklch(0.828 0.189 84.429);\n  --chart-5: oklch(0.769 0.188 70.08);\n  --radius: 0.625rem;\n  --sidebar: oklch(0.985 0 0);\n  --sidebar-foreground: oklch(0.145 0 0);\n  --sidebar-primary: oklch(0.205 0 0);\n  --sidebar-primary-foreground: oklch(0.985 0 0);\n  --sidebar-accent: oklch(0.97 0 0);\n  --sidebar-accent-foreground: oklch(0.205 0 0);\n  --sidebar-border: oklch(0.922 0 0);\n  --sidebar-ring: oklch(0.708 0 0);\n}\n\n.dark {\n  --background: oklch(0.145 0 0);\n  --foreground: oklch(0.985 0 0);\n  --card: oklch(0.145 0 0);\n  --card-foreground: oklch(0.985 0 0);\n  --popover: oklch(0.145 0 0);\n  --popover-foreground: oklch(0.985 0 0);\n  --primary: oklch(0.985 0 0);\n  --primary-foreground: oklch(0.205 0 0);\n  --secondary: oklch(0.269 0 0);\n  --secondary-foreground: oklch(0.985 0 0);\n  --muted: oklch(0.269 0 0);\n  --muted-foreground: oklch(0.708 0 0);\n  --accent: oklch(0.269 0 0);\n  --accent-foreground: oklch(0.985 0 0);\n  --destructive: oklch(0.396 0.141 25.723);\n  --destructive-foreground: oklch(0.637 0.237 25.331);\n  --border: oklch(0.269 0 0);\n  --input: oklch(0.269 0 0);\n  --ring: oklch(0.439 0 0);\n  --chart-1: oklch(0.488 0.243 264.376);\n  --chart-2: oklch(0.696 0.17 162.48);\n  --chart-3: oklch(0.769 0.188 70.08);\n  --chart-4: oklch(0.627 0.265 303.9);\n  --chart-5: oklch(0.645 0.246 16.439);\n  --sidebar: oklch(0.205 0 0);\n  --sidebar-foreground: oklch(0.985 0 0);\n  --sidebar-primary: oklch(0.488 0.243 264.376);\n  --sidebar-primary-foreground: oklch(0.985 0 0);\n  --sidebar-accent: oklch(0.269 0 0);\n  --sidebar-accent-foreground: oklch(0.985 0 0);\n  --sidebar-border: oklch(0.269 0 0);\n  --sidebar-ring: oklch(0.439 0 0);\n}\n\n@theme inline {\n  --color-background: var(--background);\n  --color-foreground: var(--foreground);\n  --color-card: var(--card);\n  --color-card-foreground: var(--card-foreground);\n  --color-popover: var(--popover);\n  --color-popover-foreground: var(--popover-foreground);\n  --color-primary: var(--primary);\n  --color-primary-foreground: var(--primary-foreground);\n  --color-secondary: var(--secondary);\n  --color-secondary-foreground: var(--secondary-foreground);\n  --color-muted: var(--muted);\n  --color-muted-foreground: var(--muted-foreground);\n  --color-accent: var(--accent);\n  --color-accent-foreground: var(--accent-foreground);\n  --color-destructive: var(--destructive);\n  --color-destructive-foreground: var(--destructive-foreground);\n  --color-border: var(--border);\n  --color-input: var(--input);\n  --color-ring: var(--ring);\n  --color-chart-1: var(--chart-1);\n  --color-chart-2: var(--chart-2);\n  --color-chart-3: var(--chart-3);\n  --color-chart-4: var(--chart-4);\n  --color-chart-5: var(--chart-5);\n  --radius-sm: calc(var(--radius) - 4px);\n  --radius-md: calc(var(--radius) - 2px);\n  --radius-lg: var(--radius);\n  --radius-xl: calc(var(--radius) + 4px);\n  --color-sidebar: var(--sidebar);\n  --color-sidebar-foreground: var(--sidebar-foreground);\n  --color-sidebar-primary: var(--sidebar-primary);\n  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n  --color-sidebar-accent: var(--sidebar-accent);\n  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n  --color-sidebar-border: var(--sidebar-border);\n  --color-sidebar-ring: var(--sidebar-ring);\n}\n\n@layer base {\n  * {\n    @apply border-border outline-ring/50;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n\n@layer base {\n  :root {\n    --sidebar: oklch(0.985 0 0);\n    --sidebar-foreground: oklch(0.145 0 0);\n    --sidebar-primary: oklch(0.205 0 0);\n    --sidebar-primary-foreground: oklch(0.985 0 0);\n    --sidebar-accent: oklch(0.97 0 0);\n    --sidebar-accent-foreground: oklch(0.205 0 0);\n    --sidebar-border: oklch(0.922 0 0);\n    --sidebar-ring: oklch(0.708 0 0);\n  }\n\n  .dark {\n    --sidebar: oklch(0.205 0 0);\n    --sidebar-foreground: oklch(0.985 0 0);\n    --sidebar-primary: oklch(0.488 0.243 264.376);\n    --sidebar-primary-foreground: oklch(0.985 0 0);\n    --sidebar-accent: oklch(0.269 0 0);\n    --sidebar-accent-foreground: oklch(0.985 0 0);\n    --sidebar-border: oklch(1 0 0 / 10%);\n    --sidebar-ring: oklch(0.439 0 0);\n  }\n}\n"
  },
  {
    "path": "viewer/src/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"
  },
  {
    "path": "viewer/src/main.tsx",
    "content": "import { StrictMode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport App from \"./App\";\nimport \"./index.css\";\n\ncreateRoot(document.getElementById(\"root\")!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n"
  },
  {
    "path": "viewer/src/pages/ManifestList.tsx",
    "content": "import { useEffect, useState } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport { fetchManifests, type ManifestListItem } from \"../api\";\nimport { useTheme } from \"../contexts/ThemeProvider\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"../components/shadcn/Card\";\nimport { Button } from \"../components/shadcn/Button\";\nimport { Moon, Sun } from \"lucide-react\";\n\nexport default function ManifestList() {\n  const [manifests, setManifests] = useState<ManifestListItem[]>([]);\n  const [loading, setLoading] = useState(true);\n  const { theme, setTheme } = useTheme();\n\n  useEffect(() => {\n    fetchManifests()\n      .then(setManifests)\n      .finally(() => setLoading(false));\n  }, []);\n\n  if (loading) {\n    return (\n      <div className=\"flex items-center justify-center h-screen\">\n        <p className=\"text-muted-foreground\">Loading manifests...</p>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"min-h-screen bg-background\">\n      <div className=\"max-w-4xl mx-auto p-8\">\n        <div className=\"flex items-center justify-between mb-8\">\n          <h1 className=\"text-3xl font-bold tracking-tight\">\n            NanoAPI - Manifests\n          </h1>\n          <Button\n            variant=\"ghost\"\n            size=\"icon\"\n            onClick={() => setTheme(theme === \"dark\" ? \"light\" : \"dark\")}\n          >\n            {theme === \"dark\" ? <Sun /> : <Moon />}\n          </Button>\n        </div>\n\n        {manifests.length === 0 ? (\n          <Card>\n            <CardContent className=\"flex flex-col items-center justify-center py-12 gap-2\">\n              <p className=\"text-muted-foreground\">No manifests found.</p>\n              <p className=\"text-sm text-muted-foreground\">\n                Run{\" \"}\n                <code className=\"bg-muted px-1.5 py-0.5 rounded text-sm font-mono\">\n                  napi generate\n                </code>{\" \"}\n                to create your first manifest.\n              </p>\n            </CardContent>\n          </Card>\n        ) : (\n          <div className=\"space-y-3\">\n            {manifests.map((m) => (\n              <Card key={m.id}>\n                <CardHeader className=\"pb-2\">\n                  <div className=\"flex items-center justify-between\">\n                    <CardTitle className=\"text-base\">\n                      <span className=\"bg-primary text-primary-foreground px-2 py-0.5 rounded-md text-xs font-medium mr-2\">\n                        {m.branch}\n                      </span>\n                      <code className=\"text-sm font-mono text-muted-foreground\">\n                        {m.commitSha.substring(0, 7)}\n                      </code>\n                    </CardTitle>\n                    <Button asChild size=\"sm\">\n                      <Link to={`/manifests/${m.id}`}>View</Link>\n                    </Button>\n                  </div>\n                  <CardDescription>\n                    {m.fileCount} files &middot; Commit{\" \"}\n                    {new Date(m.commitShaDate).toLocaleDateString()} &middot;\n                    Generated {new Date(m.createdAt).toLocaleString()}\n                  </CardDescription>\n                </CardHeader>\n              </Card>\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/pages/ManifestView.tsx",
    "content": "import { useEffect, useState } from \"react\";\nimport { useParams, Link } from \"react-router-dom\";\nimport { fetchManifest, fetchAudit, type ManifestEnvelope } from \"../api\";\nimport type { DependencyManifest } from \"../types/dependencyManifest\";\nimport type { AuditManifest } from \"../types/auditManifest\";\nimport DependencyVisualizer from \"../components/DependencyVisualizer/DependencyVisualizer\";\n\nexport default function ManifestView() {\n  const { id } = useParams<{ id: string }>();\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n  const [envelope, setEnvelope] = useState<ManifestEnvelope | null>(null);\n  const [auditManifest, setAuditManifest] = useState<AuditManifest | null>(\n    null,\n  );\n\n  useEffect(() => {\n    if (!id) return;\n    setLoading(true);\n    Promise.all([fetchManifest(id), fetchAudit(id)])\n      .then(([env, audit]) => {\n        setEnvelope(env);\n        setAuditManifest(audit);\n      })\n      .catch((e) => setError(e.message))\n      .finally(() => setLoading(false));\n  }, [id]);\n\n  if (loading) {\n    return (\n      <div className=\"flex items-center justify-center h-screen\">\n        <p className=\"text-muted-foreground\">Loading manifest...</p>\n      </div>\n    );\n  }\n\n  if (error || !envelope || !auditManifest) {\n    return (\n      <div className=\"flex flex-col items-center justify-center h-screen gap-4\">\n        <p className=\"text-destructive\">Error: {error || \"Unknown error\"}</p>\n        <Link to=\"/\" className=\"text-primary underline\">\n          Back to manifests\n        </Link>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"flex flex-col h-screen\">\n      <DependencyVisualizer\n        manifestId={id!}\n        dependencyManifest={envelope.manifest}\n        auditManifest={auditManifest}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "viewer/src/types/auditManifest.ts",
    "content": "import type { Metric } from \"./dependencyManifest\";\n\nexport type AuditAlert = {\n  metric: Metric;\n  severity: number;\n  message: {\n    short: string;\n    long: string;\n  };\n};\n\nexport type SymbolAuditManifest = {\n  id: string;\n  alerts: Record<string, AuditAlert>;\n};\n\nexport type FileAuditManifest = {\n  id: string;\n  alerts: Record<string, AuditAlert>;\n  symbols: Record<string, SymbolAuditManifest>;\n};\n\nexport type AuditManifest = Record<string, FileAuditManifest>;\n"
  },
  {
    "path": "viewer/src/types/dependencyManifest.ts",
    "content": "export const classSymbolType = \"class\";\nexport const structSymbolType = \"struct\";\nexport const enumSymbolType = \"enum\";\nexport const unionSymbolType = \"union\";\nexport const typedefSymbolType = \"typedef\";\nexport const interfaceSymbolType = \"interface\";\nexport const recordSymbolType = \"record\";\nexport const delegateSymbolType = \"delegate\";\nexport const functionSymbolType = \"function\";\nexport const variableSymbolType = \"variable\";\n\nexport type SymbolType =\n  | typeof classSymbolType\n  | typeof functionSymbolType\n  | typeof variableSymbolType\n  | typeof structSymbolType\n  | typeof enumSymbolType\n  | typeof unionSymbolType\n  | typeof typedefSymbolType\n  | typeof interfaceSymbolType\n  | typeof recordSymbolType\n  | typeof delegateSymbolType;\n\n// Aliases used by cytoscape code\nexport const symbolTypeClass = classSymbolType;\nexport const symbolTypeStruct = structSymbolType;\nexport const symbolTypeEnum = enumSymbolType;\nexport const symbolTypeUnion = unionSymbolType;\nexport const symbolTypeTypedef = typedefSymbolType;\nexport const symbolTypeInterface = interfaceSymbolType;\nexport const symbolTypeRecord = recordSymbolType;\nexport const symbolTypeDelegate = delegateSymbolType;\nexport const symbolTypeFunction = functionSymbolType;\nexport const symbolTypeVariable = variableSymbolType;\n\nexport const metricLinesCount = \"linesCount\";\nexport const metricCodeLineCount = \"codeLineCount\";\nexport const metricCharacterCount = \"characterCount\";\nexport const metricCodeCharacterCount = \"codeCharacterCount\";\nexport const metricDependencyCount = \"dependencyCount\";\nexport const metricDependentCount = \"dependentCount\";\nexport const metricCyclomaticComplexity = \"cyclomaticComplexity\";\n\nexport type Metric =\n  | typeof metricLinesCount\n  | typeof metricCodeLineCount\n  | typeof metricCharacterCount\n  | typeof metricCodeCharacterCount\n  | typeof metricDependencyCount\n  | typeof metricDependentCount\n  | typeof metricCyclomaticComplexity;\n\nexport interface DependencyInfo {\n  id: string;\n  isExternal: boolean;\n  symbols: Record<string, string>;\n}\n\nexport interface DependentInfo {\n  id: string;\n  symbols: Record<string, string>;\n}\n\nexport interface SymbolDependencyManifest {\n  id: string;\n  type: SymbolType;\n  positions: {\n    start: { index: number; row: number; column: number };\n    end: { index: number; row: number; column: number };\n  }[];\n  metrics: {\n    [metricLinesCount]: number;\n    [metricCodeLineCount]: number;\n    [metricCharacterCount]: number;\n    [metricCodeCharacterCount]: number;\n    [metricDependencyCount]: number;\n    [metricDependentCount]: number;\n    [metricCyclomaticComplexity]: number;\n  };\n  description: string;\n  dependencies: Record<string, DependencyInfo>;\n  dependents: Record<string, DependentInfo>;\n}\n\nexport interface FileDependencyManifest {\n  id: string;\n  filePath: string;\n  language: string;\n  metrics: {\n    [metricLinesCount]: number;\n    [metricCodeLineCount]: number;\n    [metricCharacterCount]: number;\n    [metricCodeCharacterCount]: number;\n    [metricDependencyCount]: number;\n    [metricDependentCount]: number;\n    [metricCyclomaticComplexity]: number;\n  };\n  dependencies: Record<string, DependencyInfo>;\n  dependents: Record<string, DependentInfo>;\n  symbols: Record<string, SymbolDependencyManifest>;\n}\n\nexport type DependencyManifest = Record<string, FileDependencyManifest>;\n\n// V1 alias used by some cytoscape code\nexport type DependencyManifestV1 = DependencyManifest;\n"
  },
  {
    "path": "viewer/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport react from \"@vitejs/plugin-react\";\nimport deno from \"@deno/vite-plugin\";\nimport tailwindcss from \"@tailwindcss/vite\";\n\nexport default defineConfig({\n  plugins: [\n    react(),\n    deno(),\n    tailwindcss(),\n  ],\n  optimizeDeps: {\n    include: [\"react/jsx-runtime\"],\n  },\n});\n"
  }
]