[
  {
    "path": ".all-contributorsrc",
    "content": "{\n  \"files\": [\n    \"README.md\"\n  ],\n  \"imageSize\": 100,\n  \"commit\": false,\n  \"contributors\": [\n    {\n      \"login\": \"jotamusik\",\n      \"name\": \"Jorge Aguiar Martín\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/14940638?v=4\",\n      \"profile\": \"https://github.com/jotamusik\",\n      \"contributions\": [\n        \"code\",\n        \"ideas\",\n        \"test\",\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"derberg\",\n      \"name\": \"Lukasz Gornicki\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/6995927?v=4\",\n      \"profile\": \"https://www.brainfart.dev/\",\n      \"contributions\": [\n        \"ideas\",\n        \"code\",\n        \"review\",\n        \"maintenance\"\n      ]\n    },\n    {\n      \"login\": \"Souvikns\",\n      \"name\": \"souvik\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/41781438?v=4\",\n      \"profile\": \"https://souvik.vercel.app/\",\n      \"contributions\": [\n        \"code\",\n        \"ideas\",\n        \"test\",\n        \"review\",\n        \"maintenance\",\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"boyney123\",\n      \"name\": \"David Boyne\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/3268013?v=4\",\n      \"profile\": \"https://boyney.io/\",\n      \"contributions\": [\n        \"code\",\n        \"ideas\",\n        \"maintenance\"\n      ]\n    },\n    {\n      \"login\": \"fmvilas\",\n      \"name\": \"Fran Méndez\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/242119?v=4\",\n      \"profile\": \"http://www.fmvilas.com/\",\n      \"contributions\": [\n        \"code\",\n        \"ideas\",\n        \"review\"\n      ]\n    },\n    {\n      \"login\": \"magicmatatjahu\",\n      \"name\": \"Maciej Urbańczyk\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/20404945?v=4\",\n      \"profile\": \"https://github.com/magicmatatjahu\",\n      \"contributions\": [\n        \"review\",\n        \"maintenance\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"aayushmau5\",\n      \"name\": \"Aayush Kumar Sahu\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/54525741?v=4\",\n      \"profile\": \"https://aayushsahu.com/\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"mihirterna\",\n      \"name\": \"Mihir Kulkarni\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/31316452?v=4\",\n      \"profile\": \"https://github.com/mihirterna\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"imabp\",\n      \"name\": \"Abir\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/53480076?v=4\",\n      \"profile\": \"https://imabp.github.io/resume/\",\n      \"contributions\": [\n        \"test\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"peter-rr\",\n      \"name\": \"Peter Ramos\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/81691177?v=4\",\n      \"profile\": \"https://github.com/peter-rr\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Samridhi-98\",\n      \"name\": \"Samriddhi\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/54466041?v=4\",\n      \"profile\": \"https://samridhi-98.github.io/Portfolio\",\n      \"contributions\": [\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"pranay202\",\n      \"name\": \"Pranay Kharabe\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/68046838?v=4\",\n      \"profile\": \"https://linktr.ee/KharabePranay\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"activus-d\",\n      \"name\": \"Damilola Oladele\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/98895460?v=4\",\n      \"profile\": \"https://d-m-oladele.netlify.app/\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"prayutsu\",\n      \"name\": \"Abhay Garg\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/54636525?v=4\",\n      \"profile\": \"https://github.com/prayutsu\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"sambhavgupta0705\",\n      \"name\": \"Sambhav Gupta\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/81870866?v=4\",\n      \"profile\": \"https://github.com/sambhavgupta0705\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"CyberHippo\",\n      \"name\": \"Hippolyte Vergnol\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/18269437?v=4\",\n      \"profile\": \"https://github.com/CyberHippo\",\n      \"contributions\": [\n        \"code\",\n        \"infra\"\n      ]\n    },\n    {\n      \"login\": \"Vetsoo\",\n      \"name\": \"Jente Vets\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/22449126?v=4\",\n      \"profile\": \"https://www.jentevets.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"kaushik-rishi\",\n      \"name\": \"Rishi\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/52498617?v=4\",\n      \"profile\": \"https://github.com/kaushik-rishi\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Shurtu-gal\",\n      \"name\": \"Ashish Padhy\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/100484401?v=4\",\n      \"profile\": \"http://ashishpadhy.live\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"meetagrawal09\",\n      \"name\": \"Meet Agrawal\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/73902874?v=4\",\n      \"profile\": \"https://github.com/meetagrawal09\",\n      \"contributions\": [\n        \"infra\"\n      ]\n    },\n    {\n      \"login\": \"chinma-yyy\",\n      \"name\": \"Chinmay Shewale\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/112387862?v=4\",\n      \"profile\": \"https://www.chinmayyy.tech\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"mhmohona\",\n      \"name\": \"Mahfuza Humayra Mohona\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/14244685?v=4\",\n      \"profile\": \"https://github.com/mhmohona\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"GreenRover\",\n      \"name\": \"Heiko Henning\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/512850?v=4\",\n      \"profile\": \"https://github.com/GreenRover\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"AayushSaini101\",\n      \"name\": \"Zack_Aayush\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/60972989?v=4\",\n      \"profile\": \"https://www.linkedin.com/in/aayush-saini-0a25931b1/\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"ayushnau\",\n      \"name\": \"Ayush Nautiyal\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/78146753?v=4\",\n      \"profile\": \"https://github.com/ayushnau\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Anish Kacham\",\n      \"name\": \"AnishKacham\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/79566582?v=4\",\n      \"profile\": \"https://github.com/AnishKacham\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"aeworxet\",\n      \"name\": \"Viacheslav Turovskyi\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/16149591?v=4\",\n      \"profile\": \"https://github.com/aeworxet\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"amanbedi1\",\n      \"name\": \"Amanpreet Singh Bedi \",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/82234871?v=4\",\n      \"profile\": \"https://github.com/amanbedi1\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"ron-debajyoti\",\n      \"name\": \"Debajyoti Halder\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/22571664?v=4\",\n      \"profile\": \"https://github.com/ron-debajyoti\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Savio629\",\n      \"name\": \"Savio Dias\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/91362589?v=4\",\n      \"profile\": \"https://github.com/Savio629\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"jonaslagoni\",\n      \"name\": \"Jonas Lagoni\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/13396189?v=4\",\n      \"profile\": \"https://github.com/jonaslagoni\",\n      \"contributions\": [\n        \"code\",\n        \"ideas\",\n        \"review\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"KhudaDad414\",\n      \"name\": \"Khuda Dad Nomani\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/32505158?v=4\",\n      \"profile\": \"https://github.com/KhudaDad414\",\n      \"contributions\": [\n        \"code\",\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"smoya\",\n      \"name\": \"Sergio Moya \",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/1083296?v=4\",\n      \"profile\": \"https://github.com/smoya\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Vishal2002\",\n      \"name\": \"Vishal Sharma\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/35897449?v=4\",\n      \"profile\": \"https://github.com/Vishal2002\",\n      \"contributions\": [\n        \"code\"\n      ]\n    }\n  ],\n  \"contributorsPerLine\": 7,\n  \"projectName\": \"cli\",\n  \"projectOwner\": \"asyncapi\",\n  \"repoType\": \"github\",\n  \"repoHost\": \"https://github.com\",\n  \"skipCi\": false,\n  \"commitConvention\": \"angular\",\n  \"commitType\": \"docs\"\n}\n"
  },
  {
    "path": ".asyncapi-tool",
    "content": "title: AsyncAPI CLI\ndescription: |\n    One CLI to rule them all. \n    This is a CLI that aims to integrate all AsyncAPI tools that you need while AsyncAPI document development and maintainance. \n    You can use it to generate docs or code, validate AsyncAPI document and event create new documents.\nlinks:\n    websiteUrl: https://www.asyncapi.com/tools/cli\nfilters:\n    technology:\n        - TypeScript\n    categories:\n        - others\n        - cli\n    hasCommercial: false"
  },
  {
    "path": ".changeset/config.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/@changesets/config@2.3.0/schema.json\",\n  \"changelog\": [\"@changesets/changelog-git\", { \"repo\": \"asyncapi/cli\" }],\n  \"commit\": false,\n  \"fixed\": [],\n  \"linked\": [],\n  \"access\": \"public\",\n  \"baseBranch\": \"master\",\n  \"updateInternalDependencies\": \"patch\",\n  \"privatePackages\": {\n    \"version\": true,\n    \"tag\": true\n  }\n}\n"
  },
  {
    "path": ".dockerignore",
    "content": "node_modules\nnpm-debug.log\nDockerfile\n.dockerignore\n.git\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n"
  },
  {
    "path": ".github/workflows/add-good-first-issue-labels.yml",
    "content": "# This workflow is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# Purpose of this workflow is to enable anyone to label issue with 'Good First Issue' and 'area/*' with a single command.\nname: Add 'Good First Issue' and 'area/*' labels # if proper comment added\n\non:\n  issue_comment:\n    types:\n      - created\n\npermissions: {}\n\njobs:\n  add-labels:\n    name: Add 'Good First Issue' and 'area/*' labels\n    if: ${{(!github.event.issue.pull_request && github.event.issue.state != 'closed' && github.actor != 'asyncapi-bot') && (contains(github.event.comment.body, '/good-first-issue') || contains(github.event.comment.body, '/gfi' ))}}\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write    # This is needed to add labels to issues.\n    steps:\n      - name: Add label\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const areas = ['javascript', 'typescript', 'java' , 'go', 'docs', 'ci-cd', 'design'];\n            const words = context.payload.comment.body.trim().split(\" \");\n            const areaIndex = words.findIndex((word)=> word === '/gfi' || word === '/good-first-issue') + 1\n            let area = words[areaIndex];\n            switch(area){\n              case 'ts':\n                area = 'typescript';\n                break;\n              case 'js':\n                area = 'javascript';\n                break;\n              case 'markdown':\n                area = 'docs';\n                break;\n            }\n            if(!areas.includes(area)){\n              const message = `Hey @${context.payload.sender.login}, your message doesn't follow the requirements, you can try \\`/help\\`.`\n\n              await github.rest.issues.createComment({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                body: message\n              })\n            } else {\n\n              // remove area if there is any before adding new labels.\n              const currentLabels = (await github.rest.issues.listLabelsOnIssue({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n              })).data.map(label => label.name);\n\n              const shouldBeRemoved = currentLabels.filter(label => (label.startsWith('area/') && !label.endsWith(area)));\n              shouldBeRemoved.forEach(label => {\n                github.rest.issues.deleteLabel({\n                  owner: context.repo.owner,\n                  repo: context.repo.repo,\n                  name: label,\n                });\n              });\n\n               // Add new labels.\n              github.rest.issues.addLabels({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                labels: ['good first issue', `area/${area}`]\n              });\n            }\n"
  },
  {
    "path": ".github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml",
    "content": "# This workflow is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# Purpose of this workflow is to enable anyone to label PR with the following labels:\n# `ready-to-merge` and `do-not-merge` labels to get stuff merged or blocked from merging\n# `autoupdate` to keep a branch up-to-date with the target branch\n\nname: Label PRs # if proper comment added\n\non:\n  issue_comment:\n    types:\n      - created\n\npermissions: {}\n\njobs:\n  add-ready-to-merge-label:\n    name: Add ready-to-merge label\n    permissions:\n      issues: write # required to add labels and post comments on PR issues\n      pull-requests: write # required to read PR metadata from the issue pull_request URL\n      contents: read # required to compare PR branch commits against base\n    if: >\n      github.event.issue.pull_request && \n      github.event.issue.state != 'closed' && \n      github.actor != 'asyncapi-bot' && \n      (\n        contains(github.event.comment.body, '/ready-to-merge') || \n        contains(github.event.comment.body, '/rtm' )\n      )\n\n    runs-on: ubuntu-latest\n    steps:\n      - name: Add ready-to-merge label\n        uses: actions/github-script@v7\n        env:\n          GITHUB_ACTOR: ${{ github.actor }}\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const prDetailsUrl = context.payload.issue.pull_request.url;\n            const { data: pull } = await github.request(prDetailsUrl);\n            const { draft: isDraft} = pull;\n            if(!isDraft) {\n              console.log('adding ready-to-merge label...');\n              github.rest.issues.addLabels({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                labels: ['ready-to-merge']\n              })  \n            }\n\n            const { data: comparison } =\n            await github.rest.repos.compareCommitsWithBasehead({\n              owner: pull.head.repo.owner.login,\n              repo: pull.head.repo.name,\n              basehead: `${pull.base.label}...${pull.head.label}`,\n            });\n            if (comparison.behind_by !== 0 && pull.mergeable_state === 'behind') {\n              console.log(`This branch is behind the target by ${comparison.behind_by} commits`)\n              console.log('adding out-of-date comment...');\n              github.rest.issues.createComment({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                body: `Hello, @${process.env.GITHUB_ACTOR}! 👋🏼\n                       This PR is not up to date with the base branch and can't be merged.\n                       Please update your branch manually with the latest version of the base branch.\n                       PRO-TIP: To request an update from the upstream branch, simply comment \\`/u\\` or \\`/update\\` and our bot will handle the update operation promptly.\n                       \n                       The only requirement for this to work is to enable [Allow edits from maintainers](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) option in your PR. Also the update will not work if your fork is located in an organization, not under your personal profile.\n                       Thanks 😄`\n              })\n            }\n\n  add-do-not-merge-label:\n    name: Add do-not-merge label\n    permissions:\n      issues: write # required to add labels on PR issues\n      pull-requests: write # required to read PR metadata from the issue pull_request URL\n    if: >\n      github.event.issue.pull_request &&\n      github.event.issue.state != 'closed' &&\n      github.actor != 'asyncapi-bot' &&\n      (\n        contains(github.event.comment.body, '/do-not-merge') ||\n        contains(github.event.comment.body, '/dnm' )\n      )\n    runs-on: ubuntu-latest\n    steps:\n      - name: Add do-not-merge label\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            github.rest.issues.addLabels({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              labels: ['do-not-merge']\n            })\n  add-autoupdate-label:\n    name: Add autoupdate label\n    permissions:\n      issues: write # required to add labels on PR issues\n      pull-requests: write # required to read PR metadata from the issue pull_request URL\n    if: >\n      github.event.issue.pull_request && \n      github.event.issue.state != 'closed' && \n      github.actor != 'asyncapi-bot' &&\n      (\n        contains(github.event.comment.body, '/autoupdate') ||\n        contains(github.event.comment.body, '/au' )\n      )\n    runs-on: ubuntu-latest\n    steps:\n      - name: Add autoupdate label\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            github.rest.issues.addLabels({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              labels: ['autoupdate']\n            })\n"
  },
  {
    "path": ".github/workflows/automerge-for-humans-merging.yml",
    "content": "# This workflow is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# Purpose of this workflow is to allow people to merge PR without a need of maintainer doing it. If all checks are in place (including maintainers approval) - JUST MERGE IT!\nname: Automerge For Humans\n\non:\n  pull_request_target:\n    types:\n      - labeled\n      - unlabeled\n      - synchronize\n      - opened\n      - edited\n      - ready_for_review\n      - reopened\n      - unlocked                              # zizmor: ignore[dangerous-triggers] needed if we want author to be our bot\n\npermissions: {}\n\njobs:\n  automerge-for-humans:\n    name: Automerge PRs labeled with ready-to-merge\n    permissions:\n      contents: read # required for PR commit metadata reads\n      pull-requests: read # required to read pull request details in github-script steps\n    # it runs only if PR actor is not a bot, at least not a bot that we know\n    if: |\n      github.event.pull_request.draft == false && \n      !contains(fromJSON('[\"asyncapi-bot\",\"dependabot[bot]\",\"dependabot-preview[bot]\"]'), github.event.pull_request.user.login)\n    runs-on: ubuntu-latest\n    steps:\n      - name: Get PR authors\n        id: authors\n        uses: actions/github-script@v7\n        with:\n          script: |\n            // Get paginated list of all commits in the PR\n            try {\n              const commitOpts = github.rest.pulls.listCommits.endpoint.merge({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                pull_number: context.issue.number\n              });\n\n              const commits = await github.paginate(commitOpts);\n\n              if (commits.length === 0) {\n                core.setFailed('No commits found in the PR');\n                return '';\n              }\n\n              // Get unique authors from the commits list\n              const authors = commits.reduce((acc, commit) => {\n                const username = commit.author?.login || commit.commit.author?.name;\n                if (username && !acc[username]) {\n                  acc[username] = {\n                    name: commit.commit.author?.name,\n                    email: commit.commit.author?.email,\n                  }\n                }\n\n                return acc;\n              }, {});\n\n              return authors;\n            } catch (error) {\n              core.setFailed(error.message);\n              return [];\n            }\n\n      - name: Create commit message\n        id: create-commit-message\n        uses: actions/github-script@v7\n        env:\n          AUTHORS_JSON: ${{ steps.authors.outputs.result }}\n        with:\n          script: |\n            const authors = JSON.parse(process.env.AUTHORS_JSON);\n\n            if (Object.keys(authors).length === 0) {\n              core.setFailed('No authors found in the PR');\n              return '';\n            }\n\n            // Create a string of the form \"Co-authored-by: Name <email>\"\n            // ref: https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors\n            const coAuthors = Object.values(authors).map(author => {\n              return `Co-authored-by: ${author.name} <${author.email}>`;\n            }).join('\\n');\n\n            core.debug(coAuthors);;\n\n            return coAuthors;\n\n      - name: Automerge PR\n        uses: pascalgn/automerge-action@22948e0bc22f0aa673800da838595a3e7347e584 #v0.15.6 https://github.com/pascalgn/automerge-action/releases/tag/v0.15.6\n        env:\n          GITHUB_TOKEN: \"${{ secrets.GH_TOKEN }}\"\n          MERGE_LABELS: \"!do-not-merge,ready-to-merge\"\n          MERGE_METHOD: \"squash\"\n          # Using the output of the previous step (`Co-authored-by: ...` lines) as commit description.\n          # Important to keep 2 empty lines as https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors#creating-co-authored-commits-on-the-command-line mentions\n          MERGE_COMMIT_MESSAGE: \"{pullRequest.title} (#{pullRequest.number})\\n\\n\\n${{ fromJSON(steps.create-commit-message.outputs.result) }}\"\n          MERGE_RETRIES: \"20\"\n          MERGE_RETRY_SLEEP: \"30000\"\n"
  },
  {
    "path": ".github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml",
    "content": "# This workflow is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# Defence from evil contributor that after adding `ready-to-merge` all suddenly makes evil commit or evil change in PR title\n# Label is removed once above action is detected\nname: Remove ready-to-merge label\n\non:\n  pull_request:\n    types:\n      - synchronize\n      - edited\n\npermissions: {}\n\njobs:\n  remove-ready-label:\n    name: Remove ready-to-merge label\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write # required to remove labels and post comments on PR issues \n    steps:\n      - name: Remove label\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const labelToRemove = 'ready-to-merge';\n            const labels = context.payload.pull_request.labels;\n            const isLabelPresent = labels.some(label => label.name === labelToRemove)\n            if(!isLabelPresent) return;\n            github.rest.issues.removeLabel({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              name: labelToRemove\n            })\n"
  },
  {
    "path": ".github/workflows/automerge-orphans.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\nname: 'Notify on failing automerge'\n\non:\n  schedule:\n  - cron: \"0 0 * * *\"\n\npermissions: {}\n\njobs:\n  identify-orphans:\n    if: startsWith(github.repository, 'asyncapi/')\n    name: Find orphans and notify\n    permissions:\n      contents: read # required by checkout and repository metadata reads\n      pull-requests: read # required to list open pull requests\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Get list of orphans\n        uses: actions/github-script@v7\n        id: orphans\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const query = `query($owner:String!, $name:String!) {\n              repository(owner:$owner, name:$name){\n                pullRequests(first: 100, states: OPEN){\n                  nodes{\n                    title\n                    url\n                    author {\n                      resourcePath\n                    }\n                  }\n                }\n              }\n            }`;\n            const variables = {\n              owner: context.repo.owner,\n              name: context.repo.repo\n            };\n            const { repository: { pullRequests: { nodes } } } = await github.graphql(query, variables);\n\n            let orphans = nodes.filter( (pr) => pr.author.resourcePath === '/asyncapi-bot' || pr.author.resourcePath === '/apps/dependabot')\n\n            if (orphans.length) {\n              core.setOutput('found', 'true');\n              //Yes, this is very naive approach to assume there is just one PR causing issues, there can be a case that more PRs are affected the same day\n              //The thing is that handling multiple PRs will increase a complexity in this PR that in my opinion we should avoid\n              //The other PRs will be reported the next day the action runs, or person that checks first url will notice the other ones\n              core.setOutput('url', orphans[0].url);\n              core.setOutput('title', orphans[0].title);\n            }\n      - if: steps.orphans.outputs.found == 'true'\n        name: Convert markdown to slack markdown\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: issuemarkdown\n        with:\n          markdown: \"-> [${{steps.orphans.outputs.title}}](${{steps.orphans.outputs.url}})\"\n      - if: steps.orphans.outputs.found == 'true'\n        name: Send info about orphan to slack\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_CI_FAIL_NOTIFY}}\n          SLACK_TITLE: 🚨 Not merged PR that should be automerged 🚨\n          SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}}\n          MSG_MINIMAL: true"
  },
  {
    "path": ".github/workflows/automerge.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo.\n\nname: Automerge PRs from bots\n\non:\n  pull_request_target:          # Needed as GH_TOKEN_BOT_EVE needed for approval.\n    types:\n      - opened\n      - synchronize             # zizmor: ignore[dangerous-triggers]\n\npermissions: {}\n\njobs:\n  autoapprove-for-bot:\n    name: Autoapprove PR comming from a bot\n    if: >\n      contains(fromJson('[\"asyncapi-bot\", \"dependabot[bot]\", \"dependabot-preview[bot]\"]'), github.event.pull_request.user.login) &&\n      contains(fromJson('[\"asyncapi-bot\", \"dependabot[bot]\", \"dependabot-preview[bot]\"]'), github.actor) &&\n      !contains(github.event.pull_request.labels.*.name, 'released')\n    runs-on: ubuntu-latest\n    steps:\n      - name: Autoapproving\n        uses: hmarr/auto-approve-action@44888193675f29a83e04faf4002fa8c0b537b1e4   # v3.2.1 is used https://github.com/hmarr/auto-approve-action/releases/tag/v3.2.1\n        with:\n          github-token: \"${{ secrets.GH_TOKEN_BOT_EVE }}\"\n\n      - name: Label autoapproved\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.GH_TOKEN }}\n          script: |\n            github.rest.issues.addLabels({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              labels: ['autoapproved', 'autoupdate']\n            })\n\n  automerge-for-bot:\n    name: Automerge PR autoapproved by a bot\n    needs: [autoapprove-for-bot]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Automerging\n        uses: pascalgn/automerge-action@22948e0bc22f0aa673800da838595a3e7347e584 #v0.15.6 https://github.com/pascalgn/automerge-action/releases/tag/v0.15.6\n        env:\n          GITHUB_TOKEN: \"${{ secrets.GH_TOKEN }}\"\n          GITHUB_LOGIN: asyncapi-bot\n          MERGE_LABELS: \"!do-not-merge\"\n          MERGE_METHOD: \"squash\"\n          MERGE_COMMIT_MESSAGE: \"{pullRequest.title} (#{pullRequest.number})\"\n          MERGE_RETRIES: \"20\"\n          MERGE_RETRY_SLEEP: \"30000\"\n"
  },
  {
    "path": ".github/workflows/autoupdate.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# This workflow is designed to work with:\n# - autoapprove and automerge workflows for dependabot and asyncapibot.\n# - special release branches that we from time to time create in upstream repos. If we open up PRs for them from the very beginning of the release, the release branch will constantly update with new things from the destination branch they are opened against\n\n# It uses GitHub Action that auto-updates pull requests branches, whenever changes are pushed to their destination branch.\n# Autoupdating to latest destination branch works only in the context of upstream repo and not forks\n\nname: autoupdate\n\non:\n  push:\n    branches-ignore:  \n      - 'version-bump/**'\n      - 'dependabot/**'\n      - 'bot/**'\n      - 'all-contributors/**'\n\npermissions: {}\n\njobs:\n  autoupdate-for-bot:\n    if: startsWith(github.repository, 'asyncapi/')\n    name: Autoupdate autoapproved PR created in the upstream\n    runs-on: ubuntu-latest\n    steps:\n      - name: Autoupdating\n        uses: chinthakagodawita/autoupdate@0707656cd062a3b0cf8fa9b2cda1d1404d74437e\n        env:\n          GITHUB_TOKEN: '${{ secrets.GH_TOKEN_BOT_EVE }}'\n          PR_FILTER: \"labelled\"\n          PR_LABELS: \"autoupdate\"\n          PR_READY_STATE: \"ready_for_review\"\n          MERGE_CONFLICT_ACTION: \"ignore\"\n"
  },
  {
    "path": ".github/workflows/bounty-program-commands.yml",
    "content": "# This workflow is centrally managed at https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repository, as they will be overwritten with\n# changes made to the same file in the abovementioned repository.\n\n# The purpose of this workflow is to allow Bounty Team members\n# (https://github.com/orgs/asyncapi/teams/bounty_team) to issue commands to the\n# organization's global AsyncAPI bot related to the Bounty Program, while at the\n# same time preventing unauthorized users from misusing them.\n\nname: Bounty Program commands\n\non:\n  issue_comment:\n    types:\n      - created\n\nenv:\n  BOUNTY_PROGRAM_LABELS_JSON: |\n    [\n      {\"name\": \"bounty\", \"color\": \"0e8a16\", \"description\": \"Participation in the Bounty Program\"}\n    ]\n\npermissions: {}\n\njobs:\n  guard-against-unauthorized-use:\n    name: Guard against unauthorized use\n    permissions:\n      issues: write # required to post a comment on the issue/PR\n      pull-requests: write # required to post a comment on the issue/PR if it's a PR\n    if: >\n      !contains(fromJSON('[\"aeworxet\",\"thulieblack\"]'), github.actor) &&\n      (\n        startsWith(github.event.comment.body, '/bounty' )\n      )\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: ❌ @${{github.actor}} made an unauthorized attempt to use a Bounty Program's command\n        uses: actions/github-script@v7\n        env:\n          ACTOR: ${{ github.actor }}\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const commentText = `❌ @${process.env.ACTOR} is not authorized to use the Bounty Program's commands.\n            These commands can only be used by members of the [Bounty Team](https://github.com/orgs/asyncapi/teams/bounty_team).`;\n\n            console.log(`❌ @${process.env.ACTOR} made an unauthorized attempt to use a Bounty Program's command.`);\n            github.rest.issues.createComment({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                body: commentText\n              })\n\n  add-label-bounty:\n    name: Add bounty label\n    permissions:\n      issues: write # required to read/create labels and add labels on the issue/PR\n      pull-requests: write #  required to read/create labels and add labels on the issue/PR\n    if: >\n      contains(fromJSON('[\"aeworxet\",\"thulieblack\"]'), github.actor) &&\n      (\n        startsWith(github.event.comment.body, '/bounty' )\n      )\n\n    runs-on: ubuntu-latest\n    steps:\n      - name: Add label `bounty`\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const BOUNTY_PROGRAM_LABELS = JSON.parse(process.env.BOUNTY_PROGRAM_LABELS_JSON);\n            let LIST_OF_LABELS_FOR_REPO = await github.rest.issues.listLabelsForRepo({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                });\n                \n            LIST_OF_LABELS_FOR_REPO = LIST_OF_LABELS_FOR_REPO.data.map(key => key.name);\n\n            if (!LIST_OF_LABELS_FOR_REPO.includes(BOUNTY_PROGRAM_LABELS[0].name)) {\n              await github.rest.issues.createLabel({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                name: BOUNTY_PROGRAM_LABELS[0].name,\n                color: BOUNTY_PROGRAM_LABELS[0].color,\n                description: BOUNTY_PROGRAM_LABELS[0].description\n              });\n            }\n\n            console.log('Adding label `bounty`...');\n            github.rest.issues.addLabels({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              labels: [BOUNTY_PROGRAM_LABELS[0].name]\n            })\n\n  remove-label-bounty:\n    name: Remove bounty label\n    permissions:\n      issues: write # required to read/remove labels on the issue/PR\n      pull-requests: write # required to read/remove labels on the issue/PR if it's a PR\n    if: >\n      contains(fromJSON('[\"aeworxet\",\"thulieblack\"]'), github.actor) &&\n      (\n        startsWith(github.event.comment.body, '/unbounty' )\n      )\n    runs-on: ubuntu-latest\n    steps:\n      - name: Remove label `bounty`\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const BOUNTY_PROGRAM_LABELS = JSON.parse(process.env.BOUNTY_PROGRAM_LABELS_JSON);\n            let LIST_OF_LABELS_FOR_ISSUE = await github.rest.issues.listLabelsOnIssue({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                issue_number: context.issue.number,\n                });\n\n            LIST_OF_LABELS_FOR_ISSUE = LIST_OF_LABELS_FOR_ISSUE.data.map(key => key.name);\n\n            if (LIST_OF_LABELS_FOR_ISSUE.includes(BOUNTY_PROGRAM_LABELS[0].name)) {\n              console.log('Removing label `bounty`...');\n              github.rest.issues.removeLabel({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                name: [BOUNTY_PROGRAM_LABELS[0].name]\n              })\n            }\n"
  },
  {
    "path": ".github/workflows/bump-homebrew-formula.yml",
    "content": "name: Bump Homebrew formula for CLI\n\non:\n  # Since now release depends on schedule, might be that there is a situation we cannot wait for schedule and trigger release manually\n  workflow_dispatch:\n  # We cannot run brew release continusly every time we release something as sometimes we release a couple of times a day and overload brew pipelines\n  # More details https://github.com/asyncapi/cli/issues/503\n  schedule:\n    - cron: \"0 23 * * *\"\n\njobs:\n  bump-formula-in-homebrew:\n    if: github.repository == 'asyncapi/cli'\n    name: Bump the formula in homebrew-core repo\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n      - name: Get version from package.json\n        id: extractver\n        run: |\n          VERSION=$(npm run get-version --silent)\n          echo \"version=$VERSION\" >> $GITHUB_OUTPUT\n      - uses: mislav/bump-homebrew-formula-action@9d4d820e1d00b99927bd36e67d41fff1cfc8bd07 # v2\n        with:\n          # A PR will be sent to github.com/Homebrew/homebrew-core to update AsyncAPI CLI formula:\n          formula-name: asyncapi\n          # https://github.com/mislav/bump-homebrew-formula-action/issues/58\n          formula-path: Formula/a/asyncapi.rb\n          tag-name: ${{ steps.extractver.outputs.version }}\n          download-url: https://registry.npmjs.org/@asyncapi/cli/-/cli-${{ steps.extractver.outputs.version }}.tgz #we need to point to npm not github as there is a dist that is not on github\n        env:\n          COMMITTER_TOKEN: ${{ secrets.GH_TOKEN_BOT_EVE }}\n      - if: failure() # Only, on failure, send a message on the 94_bot-failing-ci slack channel\n        name: Report workflow run status to Slack\n        uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 # v3.15.1\n        with:\n          status: ${{ job.status }}\n          fields: repo,action,workflow\n          text: 'AsyncAPI CLI release to BREW failed'\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}"
  },
  {
    "path": ".github/workflows/bump.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# Purpose of this action is to update npm package in libraries that use it. It is like dependabot for asyncapi npm modules only. \n# It runs in a repo after merge of release commit and searches for other packages that use released package. Every found package gets updated with lates version\n\nname: Bump package version in dependent repos - if Node project\n\non:\n  # It cannot run on release event as when release is created then version is not yet bumped in package.json\n  # This means we cannot extract easily latest version and have a risk that package is not yet on npm\n  push:\n    branches:\n      - master\n\njobs:\n  bump-in-dependent-projects:\n    name: Bump this package in repositories that depend on it\n    if: startsWith(github.event.commits[0].message, 'chore(release):')\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v3\n      - name: Check if Node.js project and has package.json\n        id: packagejson\n        run: test -e ./package.json && echo \"exists=true\" >> $GITHUB_OUTPUT || echo \"exists=false\" >> $GITHUB_OUTPUT\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Bumping latest version of this package in other repositories\n        uses: derberg/npm-dependency-manager-for-your-github-org@1eafd3bf3974f21d395c1abac855cb04b295d570 # using v6.-.- https://github.com/derberg/npm-dependency-manager-for-your-github-org/releases/tag/v6\n        with:\n          github_token: ${{ secrets.GH_TOKEN }}\n          committer_username: asyncapi-bot\n          committer_email: info@asyncapi.io\n          repos_to_ignore: spec,bindings,saunter,server-api\n          custom_id: \"dependency update from asyncapi bot\"\n"
  },
  {
    "path": ".github/workflows/deploy/chocolatey/asyncapi-cli.nuspec",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->\n<package xmlns=\"http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd\">\n  <metadata>\n    <id>asyncapi</id>\n    <version>{{version}}</version>\n    <packageSourceUrl>https://github.com/asyncapi/cli/releases/v{{version}}</packageSourceUrl>\n    <owners>AsyncAPI_Initiative</owners>\n\n    <!-- == SOFTWARE SPECIFIC SECTION == -->\n    <!-- This section is about the software itself -->\n    <title>asyncapi-cli</title>\n    <authors>AsyncAPI_Initiative</authors>\n    <projectUrl>https://www.asyncapi.com/</projectUrl>\n    <iconUrl>https://avatars.githubusercontent.com/u/16401334?s=200</iconUrl>\n    <copyright>2023 AsyncAPI Initiative</copyright>\n    <licenseUrl>https://github.com/asyncapi/cli/blob/master/LICENSE</licenseUrl>\n    <requireLicenseAcceptance>false</requireLicenseAcceptance>\n    <releaseNotes>https://github.com/asyncapi/cli/releases/v{{version}}</releaseNotes>\n    <projectSourceUrl>https://github.com/asyncapi/cli</projectSourceUrl>\n    <docsUrl>https://www.asyncapi.com/docs/tools/cli</docsUrl>\n    <bugTrackerUrl>https://github.com/asyncapi/cli/issues/new/choose</bugTrackerUrl>\n    <tags>asyncapi-cli cli nodejs api asyncapi</tags>\n    <summary>CLI to work with your AsyncAPI files. You can validate them and in the future use a generator and even bootstrap a new file. Contributions are welcomed!</summary>\n    <description>CLI to work with your AsyncAPI files. You can validate them and in the future use a generator and even bootstrap a new file. Contributions are welcomed!</description>\n  </metadata>\n  <files>\n    <!-- this section controls what actually gets packaged into the Chocolatey package -->\n    <file src=\"tools\\**\" target=\"tools\" />\n  </files>\n</package>\n"
  },
  {
    "path": ".github/workflows/deploy/chocolatey/replace.ps1",
    "content": "param (\n  [Parameter(Mandatory=$true)]\n  [string]$version,\n  [string]$checksum,\n  [string]$checksum64\n)\n\n$filePaths = @(\n  './tools/chocolateyinstall.ps1'\n  './asyncapi-cli.nuspec'\n)\n\nforeach ($filePath in $filePaths) {\n  $fileContents = Get-Content $filePath\n  $fileContents = $fileContents -replace '{{version}}', $version\n  $fileContents = $fileContents -replace '{{checksum}}', $checksum\n  $fileContents = $fileContents -replace '{{checksum64}}', $checksum64\n  Set-Content $filePath $fileContents\n}"
  },
  {
    "path": ".github/workflows/deploy/chocolatey/tools/chocolateyinstall.ps1",
    "content": "﻿$ErrorActionPreference = 'Stop' # stop on all errors\n$toolsDir   = \"$(Split-Path -parent $MyInvocation.MyCommand.Definition)\"\n\n$url          = 'https://github.com/asyncapi/cli/releases/download/v{{version}}/asyncapi.x86.exe'\n$url64        = 'https://github.com/asyncapi/cli/releases/download/v{{version}}/asyncapi.x64.exe' \n\n$packageArgs = @{\n  packageName   = $env:ChocolateyPackageName\n  unzipLocation = $toolsDir\n  fileType      = 'EXE' \n  url           = $url\n  url64bit      = $url64\n  #file         = $fileLocation NOTE: Commented out because we are using url instead\n\n  softwareName  = 'asyncapi-cli*'\n\n  checksum      = '{{checksum}}'\n  checksumType  = 'sha256' #default is md5, can also be sha1, sha256 or sha512\n  checksum64    = '{{checksum64}}'\n  checksumType64= 'sha256' #default is checksumType\n\n  validExitCodes= @(0, 3010, 1641)\n  silentArgs   = '/S'           # NSIS\n}\n\nInstall-ChocolateyPackage @packageArgs # https://docs.chocolatey.org/en-us/create/functions/install-chocolateypackage\n"
  },
  {
    "path": ".github/workflows/help-command.yml",
    "content": "# This workflow is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\nname: Create help comment\n\non: \n  issue_comment:\n    types: \n      - created\n\npermissions: {}\n\njobs:\n  create_help_comment_pr:\n    name: Help Comment in PR\n    if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }}\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write                # To comment on Pull requests\n    steps:\n      - name: Add comment to PR\n        uses: actions/github-script@v7\n        env:\n          ACTOR: ${{ github.actor }}\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            //Yes to add comment to PR the same endpoint is use that we use to create a comment in issue\n            //For more details http://developer.github.com/v3/issues/comments/\n            //Also proved by this action https://github.com/actions-ecosystem/action-create-comment/blob/main/src/main.ts\n            github.rest.issues.createComment({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              body: `Hello, @${process.env.ACTOR}! 👋🏼 \n\n                    I'm 🧞🧞🧞 Genie 🧞🧞🧞 from the magic lamp. Looks like somebody needs a hand!\n        \n                    At the moment the following comments are supported in pull requests:\n        \n                    - \\`/please-take-a-look\\` or \\`/ptal\\` - This comment will add a comment to the PR asking for attention from the reviewrs who have not reviewed the PR yet.\n                    - \\`/ready-to-merge\\` or \\`/rtm\\` - This comment will trigger automerge of PR in case all required checks are green, approvals in place and do-not-merge label is not added\n                    - \\`/do-not-merge\\` or \\`/dnm\\` - This comment will block automerging even if all conditions are met and ready-to-merge label is added\n                    - \\`/autoupdate\\` or \\`/au\\` - This comment will add \\`autoupdate\\` label to the PR and keeps your PR up-to-date to the target branch's future changes. Unless there is a merge conflict or it is a draft PR. (Currently only works for upstream branches.)\n                    - \\`/update\\` or \\`/u\\` - This comment will update the PR with the latest changes from the target branch. Unless there is a merge conflict or it is a draft PR. NOTE: this only updates the PR once, so if you need to update again, you need to call the command again.`\n            })\n\n  create_help_comment_issue:\n    name: Help Comment in Issue\n    if: ${{ !github.event.issue.pull_request && startsWith(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }}\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write                       # To comment on Issues\n    steps:\n      - name: Add comment to Issue\n        uses: actions/github-script@v7\n        env:\n          ACTOR: ${{ github.actor }}\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            github.rest.issues.createComment({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              body: `Hello, @${process.env.ACTOR}! 👋🏼 \n\n              I'm 🧞🧞🧞 Genie 🧞🧞🧞 from the magic lamp. Looks like somebody needs a hand!\n  \n              At the moment the following comments are supported in issues:\n  \n              - \\`/good-first-issue {js | ts | java | go | docs | design | ci-cd}\\` or \\`/gfi {js | ts | java | go | docs | design | ci-cd}\\` - label an issue as a \\`good first issue\\`.\n              example: \\`/gfi js\\` or \\`/good-first-issue ci-cd\\`\n              - \\`/transfer-issue {repo-name}\\` or \\`/ti {repo-name}\\` - transfer issue from the source repository to the other repository passed by the user. example: \\`/ti cli\\` or \\`/transfer-issue cli\\`.`\n            })"
  },
  {
    "path": ".github/workflows/if-docker-pr-testing.yml",
    "content": "#This action is centrally managed in https://github.com/asyncapi/.github/\n#Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n#It does magic only if there is a Dockerfile in the root of the project\nname: PR testing - if Docker\n\non:\n  pull_request:\n    types: [opened, reopened, synchronize, ready_for_review]\n\nenv:\n  IMAGE_NAME: ${{ github.repository }}\n\npermissions:\n  contents: read\n\njobs:\n  test-docker-pr:\n    name: Test Docker build\n    runs-on: ubuntu-latest\n\n    steps:\n      - if: >\n          !github.event.pull_request.draft && !(\n            (github.event.pull_request.user.login == 'asyncapi-bot' && (\n              startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') || \n              startsWith(github.event.pull_request.title, 'chore(release):')\n            )) ||\n            (github.event.pull_request.user.login == 'asyncapi-bot-eve' && (\n              startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') || \n              startsWith(github.event.pull_request.title, 'chore(release):')\n            )) ||\n            (github.event.pull_request.user.login == 'allcontributors[bot]' && \n              startsWith(github.event.pull_request.title, 'docs: add')\n            )\n          )\n        id: should_run\n        name: Should Run\n        run: echo \"shouldrun=true\" >> \"$GITHUB_OUTPUT\"\n\n      - if: steps.should_run.outputs.shouldrun == 'true' \n        name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n\n      - if: steps.should_run.outputs.shouldrun == 'true' \n        name: Check if project has a Dockerfile\n        id: docker\n        run: test -e ./Dockerfile && echo \"exists=true\" >> \"$GITHUB_OUTPUT\" || echo \"exists=false\" >> \"$GITHUB_OUTPUT\"\n        shell: bash\n\n      - if: steps.docker.outputs.exists == 'true'\n        name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # use 3.10.0 https://github.com/docker/setup-buildx-action/releases/tag/v2.5.0\n\n      - if: steps.docker.outputs.exists == 'true'\n        name: Extract metadata for Docker\n        id: meta\n        uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # use 5.7.0 https://github.com/docker/metadata-action/releases/tag/v4.3.0\n        with:\n          images: ${{ env.IMAGE_NAME }}\n\n      - if: steps.docker.outputs.exists == 'true'\n        name: Build Docker image\n        uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4  # use 6.15.0 https://github.com/docker/build-push-action/releases/tag/v4.0.0\n        with:\n          context: .\n          push: false\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/if-nodejs-pr-testing.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# It does magic only if there is package.json file in the root of the project\nname: PR testing - if Node project\n\non:\n  pull_request:\n    types: [opened, reopened, synchronize, ready_for_review]\n\npermissions:\n  contents: read\n\njobs:\n  test-nodejs-pr:\n    name: Test NodeJS PR - ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n    steps:\n      - if: >\n          !github.event.pull_request.draft && !(\n            (github.event.pull_request.user.login == 'asyncapi-bot' && (\n              startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') || \n              startsWith(github.event.pull_request.title, 'chore(release):')\n            )) ||\n            (github.event.pull_request.user.login == 'asyncapi-bot-eve' && (\n              startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') || \n              startsWith(github.event.pull_request.title, 'chore(release):')\n            )) ||\n            (github.event.pull_request.user.login == 'allcontributors[bot]' && \n              startsWith(github.event.pull_request.title, 'docs: add')\n            )\n          )\n        id: should_run\n        name: Should Run\n        run: echo \"shouldrun=true\" >> \"$GITHUB_OUTPUT\"\n        shell: bash\n      - if: steps.should_run.outputs.shouldrun == 'true' \n        name: Set git to use LF #to once and for all finish neverending fight between Unix and Windows\n        run: |\n          git config --global core.autocrlf false\n          git config --global core.eol lf\n        shell: bash\n      - if: steps.should_run.outputs.shouldrun == 'true' \n        name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - if: steps.should_run.outputs.shouldrun == 'true' \n        name: Check if Node.js project and has package.json\n        id: packagejson\n        run: test -e ./package.json && echo \"exists=true\" >> \"$GITHUB_OUTPUT\" || echo \"exists=false\" >> \"$GITHUB_OUTPUT\"\n        shell: bash\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Determine what node version to use\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master # //NOSONAR\n        with:\n          node-version: ${{ vars.NODE_VERSION }}\n        id: lockversion\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: \"${{ steps.lockversion.outputs.version }}\"\n      - if: steps.lockversion.outputs.version == '18' && matrix.os == 'windows-latest'\n        #npm cli 10 is buggy because of some cache issue\n        name: Install npm cli 8\n        shell: bash\n        run: npm install -g npm@8.19.4\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Install dependencies\n        shell: bash\n        run: npm ci\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Test\n        run: npm test --if-present\n      - if: steps.packagejson.outputs.exists == 'true' && matrix.os == 'ubuntu-latest'\n        #linting should run just one and not on all possible operating systems\n        name: Run linter\n        run: npm run lint --if-present\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Run release assets generation to make sure PR does not break it\n        shell: bash\n        run: npm run generate:assets --if-present\n"
  },
  {
    "path": ".github/workflows/issues-prs-notifications.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# This action notifies community on slack whenever there is a new issue, PR or discussion started in given repository\nname: Notify slack\n\non:\n  issues:\n    types: [opened, reopened]\n\n  pull_request_target:\n    types: [opened, reopened, ready_for_review]   # zizmor: ignore[dangerous-triggers]   \n\n  discussion:\n    types: [created]\n\npermissions: {}\n\njobs:\n  issue:\n    if: github.event_name == 'issues' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]'\n    name: Notify slack on every new issue\n    runs-on: ubuntu-latest\n    steps:\n      - name: Convert markdown to slack markdown for issue\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: issuemarkdown\n        env:\n          ISSUE_TITLE: ${{github.event.issue.title}}\n          ISSUE_URL: ${{github.event.issue.html_url}}\n          ISSUE_BODY: ${{github.event.issue.body}}\n        with:\n          markdown: \"[${{ env.ISSUE_TITLE }}](${{ env.ISSUE_URL }}) \\n ${{ env.ISSUE_BODY }}\"\n      - name: Send info about issue\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}}\n          SLACK_TITLE: 🐛 New Issue in ${{github.repository}} 🐛\n          SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}}\n          MSG_MINIMAL: true\n\n  pull_request:\n    if: github.event_name == 'pull_request_target' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]'\n    name: Notify slack on every new pull request\n    runs-on: ubuntu-latest\n    steps:\n      - name: Convert markdown to slack markdown for pull request\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: prmarkdown\n        env:\n          PR_TITLE: ${{github.event.pull_request.title}}\n          PR_URL: ${{github.event.pull_request.html_url}}\n          PR_BODY: ${{github.event.pull_request.body}}\n        with:\n          markdown: \"[${{ env.PR_TITLE }}](${{ env.PR_URL }}) \\n ${{ env.PR_BODY }}\"\n      - name: Send info about pull request\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}}\n          SLACK_TITLE: 💪 New Pull Request in ${{github.repository}} 💪\n          SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}}\n          MSG_MINIMAL: true\n\n  discussion:\n    if: github.event_name == 'discussion' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]'\n    name: Notify slack on every new pull request\n    runs-on: ubuntu-latest\n    steps:\n      - name: Convert markdown to slack markdown for pull request\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: discussionmarkdown\n        env:\n          DISCUSSION_TITLE: ${{github.event.discussion.title}}\n          DISCUSSION_URL: ${{github.event.discussion.html_url}}\n          DISCUSSION_BODY: ${{github.event.discussion.body}}\n        with:\n          markdown: \"[${{ env.DISCUSSION_TITLE }}](${{ env.DISCUSSION_URL }}) \\n ${{ env.DISCUSSION_BODY }}\"\n      - name: Send info about pull request\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}}\n          SLACK_TITLE: 💬 New Discussion in ${{github.repository}} 💬\n          SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}}\n          MSG_MINIMAL: true\n"
  },
  {
    "path": ".github/workflows/lint-pr-title.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\nname: Lint PR title\n\non:\n  pull_request:\n    types: [opened, reopened, synchronize, edited, ready_for_review]\n\npermissions: {}\n\njobs:\n  lint-pr-title:\n    name: Lint PR title\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read            # To checkout code and read PR information\n      pull-requests: write      # To comment on PR if the title is not valid\n    steps:\n      # Since this workflow is REQUIRED for a PR to be mergable, we have to have this 'if' statement in step level instead of job level.\n      - if: ${{ !contains(fromJson('[\"asyncapi-bot\", \"dependabot[bot]\", \"dependabot-preview[bot]\", \"allcontributors[bot]\"]'), github.actor) }} # zizmor: ignore[obfuscation]\n        uses: amannn/action-semantic-pull-request@c3cd5d1ea3580753008872425915e343e351ab54 #version 5.2.0 https://github.com/amannn/action-semantic-pull-request/releases/tag/v5.2.0\n        id: lint_pr_title\n        env:\n          GITHUB_TOKEN: ${{ github.token }}\n        with:\n          subjectPattern: ^(?![A-Z]).+$\n          subjectPatternError: |\n            The subject \"{subject}\" found in the pull request title \"{title}\" should start with a lowercase character.\n\n      # Comments the error message from the above lint_pr_title action\n      - if: ${{ always() && steps.lint_pr_title.outputs.error_message != null && !contains(fromJson('[\"asyncapi-bot\", \"dependabot[bot]\", \"dependabot-preview[bot]\", \"allcontributors[bot]\"]'), github.actor)}} # zizmor: ignore[obfuscation]\n        name: Comment on PR\n        uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd #use 2.5.0 https://github.com/marocchino/sticky-pull-request-comment/releases/tag/v2.5.0\n        with:\n          header: pr-title-lint-error\n          GITHUB_TOKEN: ${{ github.token }}\n          message: |\n\n            We require all PRs to follow [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). \n            More details 👇🏼\n            ```\n             ${{ steps.lint_pr_title.outputs.error_message}}\n            ```\n        # deletes the error comment if the title is correct\n      - if: ${{ steps.lint_pr_title.outputs.error_message == null }}\n        name: delete the comment\n        uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd #use 2.5.0 https://github.com/marocchino/sticky-pull-request-comment/releases/tag/v2.5.0\n        with:\n          header: pr-title-lint-error\n          delete: true\n          GITHUB_TOKEN: ${{ github.token }}\n"
  },
  {
    "path": ".github/workflows/notify-tsc-members-mention.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# This action notifies community on slack whenever there is a new issue, PR or discussion started in given repository\nname: Notify slack and email subscribers whenever TSC members are mentioned in GitHub\n\non:\n  issue_comment:\n    types:\n      - created\n\n  discussion_comment:\n    types:\n      - created\n\n  issues:\n    types:\n      - opened\n\n  pull_request_target:      # Needed to access secrets. The checkout is done on base branch so script cannot be malicious.\n    types:\n      - opened              # zizmor: ignore[dangerous-triggers]\n  discussion:\n    types:\n      - created\n\npermissions:\n  contents: read            # To checkout repository\n\njobs:\n  issue:\n    if: github.event_name == 'issues' && contains(github.event.issue.body, '@asyncapi/tsc_members')\n    name: TSC notification on every new issue\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'npm'\n          cache-dependency-path: '**/package-lock.json'\n      #########\n      # Handling Slack notifications\n      #########\n      - name: Convert markdown to slack markdown\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: issuemarkdown\n        with:\n          markdown: \"[${{github.event.issue.title}}](${{github.event.issue.html_url}}) \\n ${{github.event.issue.body}}\"\n      - name: Send info about issue\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}}\n          SLACK_TITLE: 🆘 New issue that requires TSC Members attention 🆘\n          SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}}\n          MSG_MINIMAL: true\n      #########\n      # Handling Kit.com notifications\n      #########\n      - name: Install deps\n        run: npm install\n        working-directory: ./.github/workflows/scripts/kit\n      - name: Send email with Kit.com\n        uses: actions/github-script@v7\n        env:\n          KIT_API_KEY: ${{ secrets.KIT_API_KEY }}\n          KIT_TSC_TAG_ID: ${{ secrets.KIT_TSC_TAG_ID }}\n          TITLE: ${{ github.event.issue.title }}\n          HTML_URL: ${{ github.event.issue.html_url }}\n        with:\n          script: |\n            const sendEmail = require('./.github/workflows/scripts/kit/index.js');\n            return sendEmail(process.env.HTML_URL, process.env.TITLE);\n\n  pull_request:\n    if: github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@asyncapi/tsc_members')\n    name: TSC notification on every new pull request\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'npm'\n          cache-dependency-path: '**/package-lock.json'\n      #########\n      # Handling Slack notifications\n      #########\n      - name: Convert markdown to slack markdown\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: prmarkdown\n        with:\n          markdown: \"[${{github.event.pull_request.title}}](${{github.event.pull_request.html_url}}) \\n ${{github.event.pull_request.body}}\"\n      - name: Send info about pull request\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}}\n          SLACK_TITLE: 🆘 New PR that requires TSC Members attention 🆘\n          SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}}\n          MSG_MINIMAL: true\n      #########\n      # Handling Kit.com notifications\n      #########\n      - name: Install deps\n        run: npm install\n        working-directory: ./.github/workflows/scripts/kit\n      - name: Send email with Kit.com\n        uses: actions/github-script@v7\n        env:\n          KIT_API_KEY: ${{ secrets.KIT_API_KEY }}\n          KIT_TSC_TAG_ID: ${{ secrets.KIT_TSC_TAG_ID }}\n          TITLE: ${{ github.event.pull_request.title }}\n          HTML_URL: ${{ github.event.pull_request.html_url }}\n        with:\n          script: |\n            const sendEmail = require('./.github/workflows/scripts/kit/index.js');\n            return sendEmail(process.env.HTML_URL, process.env.TITLE);\n\n  discussion:\n    if: github.event_name == 'discussion' && contains(github.event.discussion.body, '@asyncapi/tsc_members')\n    name: TSC notification on every new discussion\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'npm'\n          cache-dependency-path: '**/package-lock.json'\n      #########\n      # Handling Slack notifications\n      #########\n      - name: Convert markdown to slack markdown\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: discussionmarkdown\n        with:\n          markdown: \"[${{github.event.discussion.title}}](${{github.event.discussion.html_url}}) \\n ${{github.event.discussion.body}}\"\n      - name: Send info about discussion\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}}\n          SLACK_TITLE: 🆘 New discussion that requires TSC Members attention 🆘\n          SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}}\n          MSG_MINIMAL: true\n      #########\n      # Handling Kit.com notifications\n      #########\n      - name: Install deps\n        run: npm install\n        working-directory: ./.github/workflows/scripts/kit\n      - name: Send email with Kit.com\n        uses: actions/github-script@v7\n        env:\n          KIT_API_KEY: ${{ secrets.KIT_API_KEY }}\n          KIT_TSC_TAG_ID: ${{ secrets.KIT_TSC_TAG_ID }}\n          TITLE: ${{ github.event.discussion.title }}\n          HTML_URL: ${{ github.event.discussion.html_url }}\n        with:\n          script: |\n            const sendEmail = require('./.github/workflows/scripts/kit/index.js');\n            return sendEmail(process.env.HTML_URL, process.env.TITLE);\n\n  issue_comment:\n    if: ${{ github.event_name == 'issue_comment' && !github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members') }}\n    name: TSC notification on every new comment in issue\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'npm'\n          cache-dependency-path: '**/package-lock.json'\n      #########\n      # Handling Slack notifications\n      #########\n      - name: Convert markdown to slack markdown\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: issuemarkdown\n        with:\n          markdown: \"[${{github.event.issue.title}}](${{github.event.comment.html_url}}) \\n ${{github.event.comment.body}}\"\n      - name: Send info about issue comment\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}}\n          SLACK_TITLE: 🆘 New comment under existing issue that requires TSC Members attention 🆘\n          SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}}\n          MSG_MINIMAL: true\n      #########\n      # Handling Kit.com notifications\n      #########\n      - name: Install deps\n        run: npm install\n        working-directory: ./.github/workflows/scripts/kit\n      - name: Send email with Kit.com\n        uses: actions/github-script@v7\n        env:\n          KIT_API_KEY: ${{ secrets.KIT_API_KEY }}\n          KIT_TSC_TAG_ID: ${{ secrets.KIT_TSC_TAG_ID }}\n          TITLE: ${{ github.event.issue.title }}\n          HTML_URL: ${{ github.event.comment.html_url }}\n        with:\n          script: |\n            const sendEmail = require('./.github/workflows/scripts/kit/index.js');\n            return sendEmail(process.env.HTML_URL, process.env.TITLE);\n\n  pr_comment:\n    if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members')\n    name: TSC notification on every new comment in pr\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'npm'\n          cache-dependency-path: '**/package-lock.json'\n      #########\n      # Handling Slack notifications\n      #########\n      - name: Convert markdown to slack markdown\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: prmarkdown\n        with:\n          markdown: \"[${{github.event.issue.title}}](${{github.event.comment.html_url}}) \\n ${{github.event.comment.body}}\"\n      - name: Send info about PR comment\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}}\n          SLACK_TITLE: 🆘 New comment under existing PR that requires TSC Members attention 🆘\n          SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}}\n          MSG_MINIMAL: true\n      #########\n      # Handling Kit.com notifications\n      #########\n      - name: Install deps\n        run: npm install\n        working-directory: ./.github/workflows/scripts/kit\n      - name: Send email with Kit.com\n        uses: actions/github-script@v7\n        env:\n          KIT_API_KEY: ${{ secrets.KIT_API_KEY }}\n          KIT_TSC_TAG_ID: ${{ secrets.KIT_TSC_TAG_ID }}\n          TITLE: ${{ github.event.issue.title }}\n          HTML_URL: ${{ github.event.comment.html_url }}\n        with:\n          script: |\n            const sendEmail = require('./.github/workflows/scripts/kit/index.js');\n            return sendEmail(process.env.HTML_URL, process.env.TITLE);\n\n  discussion_comment:\n    if: github.event_name == 'discussion_comment' && contains(github.event.comment.body, '@asyncapi/tsc_members')\n    name: TSC notification on every new comment in discussion\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'npm'\n          cache-dependency-path: '**/package-lock.json'\n      #########\n      # Handling Slack notifications\n      #########\n      - name: Convert markdown to slack markdown\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: discussionmarkdown\n        with:\n          markdown: \"[${{github.event.discussion.title}}](${{github.event.comment.html_url}}) \\n ${{github.event.comment.body}}\"\n      - name: Send info about discussion comment\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n          SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}}\n          SLACK_TITLE: 🆘 New comment under existing discussion that requires TSC Members attention 🆘\n          SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}}\n          MSG_MINIMAL: true\n      #########\n      # Handling Kit.com notifications\n      #########\n      - name: Install deps\n        run: npm install\n        working-directory: ./.github/workflows/scripts/kit\n      - name: Send email with Kit.com\n        uses: actions/github-script@v7\n        env:\n          KIT_API_KEY: ${{ secrets.KIT_API_KEY }}\n          KIT_TSC_TAG_ID: ${{ secrets.KIT_TSC_TAG_ID }}\n          TITLE: ${{ github.event.discussion.title }}\n          HTML_URL: ${{ github.event.comment.html_url }}\n        with:\n          script: |\n            const sendEmail = require('./.github/workflows/scripts/kit/index.js');\n            return sendEmail(process.env.HTML_URL, process.env.TITLE);\n"
  },
  {
    "path": ".github/workflows/please-take-a-look-command.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# It uses Github actions to listen for comments on issues and pull requests and \n# if the comment contains /please-take-a-look or /ptal it will add a comment pinging \n# the code-owners who are reviewers for PR\n\nname: Please take a Look\n\non:\n  issue_comment:\n    types: [created]\n\npermissions: {}\n\njobs:\n  ping-for-attention:\n    if: >\n      github.event.issue.pull_request &&\n      github.event.issue.state != 'closed' &&\n      github.actor != 'asyncapi-bot' &&\n      (\n        contains(github.event.comment.body, '/please-take-a-look') ||\n        contains(github.event.comment.body, '/ptal') ||\n        contains(github.event.comment.body, '/PTAL') \n      )\n    name: Ping code owners for attention\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check for Please Take a Look Command\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.GH_TOKEN }}\n          script: |\n            const prDetailsUrl = context.payload.issue.pull_request.url;\n            const { data: pull } = await github.request(prDetailsUrl);\n            const reviewers = (pull.requested_reviewers || []).map(reviewer => reviewer.login);\n\n            const { data: reviews } = await github.rest.pulls.listReviews({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              pull_number: context.issue.number\n            });\n\n            const reviewersWhoHaveReviewed = reviews.map(review => review.user.login);\n\n            const reviewersWhoHaveNotReviewed = reviewers.filter(reviewer => !reviewersWhoHaveReviewed.includes(reviewer));\n\n            if (reviewersWhoHaveNotReviewed.length > 0) {\n              const comment = reviewersWhoHaveNotReviewed.filter(reviewer => reviewer !== 'asyncapi-bot-eve' ).map(reviewer => `@${reviewer}`).join(' ');\n              await github.rest.issues.createComment({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                body: `${comment} Please take a look at this PR. Thanks! :wave:`\n              });\n            }\n"
  },
  {
    "path": ".github/workflows/release-announcements.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\nname: 'Announce releases in different channels'\n\non: \n  release:\n    types: \n      - published\n\npermissions:\n  contents: read            # To checkout code and read release information\n\njobs:\n\n  slack-announce:\n    name: Slack - notify on every release\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Convert markdown to slack markdown for issue\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR\n        id: markdown\n        env:\n          RELEASE_TAG: ${{github.event.release.tag_name}}\n          RELEASE_URL: ${{github.event.release.html_url}}\n          RELEASE_BODY: ${{ github.event.release.body }}\n        with:\n          markdown: \"[${{ env.RELEASE_TAG }}](${{ env.RELEASE_URL }}) \\n ${{ env.RELEASE_BODY }}\"\n      - name: Send info about release to Slack\n        uses: rtCamp/action-slack-notify@c33737706dea87cd7784c687dadc9adf1be59990 # Using v2.3.2\n        env:\n            SLACK_WEBHOOK: ${{ secrets.SLACK_RELEASES }}\n            SLACK_TITLE: Release ${{ env.RELEASE_TAG }} for ${{ env.REPO_NAME }} is out in the wild 😱💪🍾🎂\n            SLACK_MESSAGE: ${{steps.markdown.outputs.text}}\n            MSG_MINIMAL: true\n            RELEASE_TAG: ${{github.event.release.tag_name}}\n            REPO_NAME: ${{github.repository}}\n\n  twitter-announce:\n    name: Twitter - notify on minor and major releases\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n      - name: Get version of last and previous release\n        uses: actions/github-script@v7\n        id: versions\n        with:\n          github-token: ${{ github.token }}\n          script: |\n            const query = `query($owner:String!, $name:String!) {\n              repository(owner:$owner, name:$name){\n                releases(first: 2, orderBy: {field: CREATED_AT, direction: DESC}) {\n                  nodes {\n                    name\n                  }\n                }\n              }\n            }`;\n            const variables = {\n              owner: context.repo.owner,\n              name: context.repo.repo\n            };\n            const { repository: { releases: { nodes } } } = await github.graphql(query, variables);\n            core.setOutput('lastver', nodes[0].name);\n            // In case of first release in the package, there is no such thing as previous error, so we set info about previous version only once we have it\n            // We should tweet about the release no matter of the type as it is initial release\n            if (nodes.length != 1) core.setOutput('previousver', nodes[1].name);\n      - name: Identify release type\n        id: releasetype\n        # if previousver is not provided then this steps just logs information about missing version, no errors\n        env:\n          PREV_VERSION: ${{steps.versions.outputs.previousver}}\n          LAST_VERSION: ${{steps.versions.outputs.lastver}}\n        run: echo \"type=$(npx -q -p semver-diff-cli semver-diff \"$PREV_VERSION\" \"$LAST_VERSION\")\" >> \"$GITHUB_OUTPUT\"\n      - name: Get name of the person that is behind the newly released version\n        id: author\n        run: |\n          AUTHOR_NAME=$(git log -1 --pretty=format:'%an')\n          printf 'name=%s\\n' \"$AUTHOR_NAME\" >> \"$GITHUB_OUTPUT\"\n      - name: Publish information about the release to Twitter # tweet only if detected version change is not a patch\n        # tweet goes out even if the type is not major or minor but \"You need provide version number to compare.\"\n        # it is ok, it just means we did not identify previous version as we are tweeting out information about the release for the first time\n        if: steps.releasetype.outputs.type != 'null' && steps.releasetype.outputs.type != 'patch' # null means that versions are the same\n        uses: m1ner79/Github-Twittction@d1e508b6c2170145127138f93c49b7c46c6ff3a7   # using 2.0.0 https://github.com/m1ner79/Github-Twittction/releases/tag/v2.0.0\n        env:\n          RELEASE_TAG: ${{github.event.release.tag_name}}\n          REPO_NAME: ${{github.repository}}\n          AUTHOR_NAME: ${{ steps.author.outputs.name }}\n          RELEASE_URL: ${{github.event.release.html_url}}\n        with:\n          twitter_status: \"Release ${{ env.RELEASE_TAG }} for ${{ env.REPO_NAME }} is out in the wild 😱💪🍾🎂\\n\\nThank you for the contribution ${{ env.AUTHOR_NAME }} ${{ env.RELEASE_URL }}\"\n          twitter_consumer_key: ${{ secrets.TWITTER_CONSUMER_KEY }} \n          twitter_consumer_secret: ${{ secrets.TWITTER_CONSUMER_SECRET }} \n          twitter_access_token_key: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }} \n          twitter_access_token_secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}"
  },
  {
    "path": ".github/workflows/release-chocolatey.yml",
    "content": "name: Release Chocolatey Package\non:\n  # We cannot run chocolatey release continusly every time we release something as sometimes we release a couple of times a day and overload chocolatey pipelines\n  # More details https://github.com/asyncapi/cli/issues/503\n  schedule:\n    - cron: '0 23 * * *' # Run every day at 23:00 UTC\n  # Since now release depends on schedule, might be that there is a situation we cannot wait for schedule and trigger release manually\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Version to release (optional)'\n        required: false\n  \njobs:\n  release:\n    name: Publish to Chocolatey Community\n    runs-on: windows-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Set Version\n        id: release_version\n        run: |\n          if ( \"${{ github.event_name }}\" -eq \"workflow_dispatch\" -and \"${{ github.event.inputs.version }}\" -ne \"\" ) {\n            $version = \"${{ github.event.inputs.version }}\"\n          }\n          else {\n            $version = $(npm pkg get version)\n          }\n\n          $version = $version.Replace(\"`\"\", \"\")\n          echo \"Setting version to $version\"\n          echo \"version=$version\" >> $env:GITHUB_OUTPUT\n\n      - name: Check if this is a new version to release\n        id: check_new_version\n        run: |\n          $output = choco search asyncapi-cli --version=${{ steps.release_version.outputs.version }}\n          # Output is of the form:\n          # Chocolatey v2.2.2\n          # asyncapi-cli 0.0.1 [Approved]\n          # 1 packages found.\n          # If the version is not found, the output will of the form:\n          # Chocolatey v2.2.2\n          # 0 packages found.\n\n          if ($output -match \"0 packages found.\") {\n            echo \"This is a new version to release\"\n            echo \"new_version=true\" >> $env:GITHUB_OUTPUT\n          }\n          else {\n            echo \"This is not a new version to release\"\n            echo \"new_version=false\" >> $env:GITHUB_OUTPUT\n          }\n          \n      - name: Download release\n        if: steps.check_new_version.outputs.new_version == 'true'\n        run: |\n          echo \"Downloading release assets for version ${{ steps.release_version.outputs.version }}\"\n          mkdir -p ./dist/win32\n          curl -L \"https://github.com/asyncapi/cli/releases/download/v${{ steps.release_version.outputs.version }}/asyncapi.x64.exe\" -o \"./dist/win32/asyncapi.x64.exe\"\n          curl -L \"https://github.com/asyncapi/cli/releases/download/v${{ steps.release_version.outputs.version }}/asyncapi.x86.exe\" -o \"./dist/win32/asyncapi.x86.exe\"\n\n      - name: Get Checksum of the release\n        if: steps.check_new_version.outputs.new_version == 'true'\n        id: release_checksum\n        run: |\n          $checksum = (Get-FileHash -Path \"./dist/win32/asyncapi.x86.exe\" -Algorithm SHA256).Hash\n          $checksum64 = (Get-FileHash -Path \"./dist/win32/asyncapi.x64.exe\" -Algorithm SHA256).Hash\n          echo \"Setting checksum to $checksum\"\n          echo \"checksum=$checksum\" >> $env:GITHUB_OUTPUT\n          echo \"Setting checksum64 to $checksum64\"\n          echo \"checksum64=$checksum64\" >> $env:GITHUB_OUTPUT\n\n      - name: Make nuspec from the template\n        if: steps.check_new_version.outputs.new_version == 'true'\n        run: |\n          cd ./.github/workflows/deploy/chocolatey\n          pwsh -File ./replace.ps1 -version ${{ steps.release_version.outputs.version }} -checksum ${{ steps.release_checksum.outputs.checksum }} -checksum64 ${{ steps.release_checksum.outputs.checksum64 }}\n\n      - name: Run Chocolatey Pack\n        if: steps.check_new_version.outputs.new_version == 'true'\n        run: |\n          cd ./.github/workflows/deploy/chocolatey\n          choco pack ./asyncapi-cli.nuspec\n          choco apikey add --source \"'https://push.chocolatey.org/'\" --key ${{ secrets.CHOCOLATEY_API_KEY }}\n          choco push ./asyncapi.${{ steps.release_version.outputs.version }}.nupkg --source \"'https://push.chocolatey.org/'\"  \n          \n      - if: failure() # Only, on failure, send a message on the 94_bot-failing-ci slack channel\n        name: Report workflow run status to Slack\n        uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #using https://github.com/8398a7/action-slack/releases/tag/v3.15.1\n        with:\n          status: ${{ job.status }}\n          fields: repo,action,workflow\n          text: 'AsyncAPI CLI release to Chocolatey failed'\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}"
  },
  {
    "path": ".github/workflows/release-docker.yml",
    "content": "name: Release Docker Image\non:\n  release:\n    types:\n      - published\n\njobs:\n  publish-docker:\n    name: Generating Docker\n    runs-on: ubuntu-latest\n    steps:\n      - name: Get version without v character\n        id: version\n        env:\n          TAG_NAME: ${{ github.event.release.tag_name }}\n        run: |\n          VERSION_WITHOUT_V=${TAG_NAME:1}\n          echo \"value=${VERSION_WITHOUT_V}\" >> $GITHUB_OUTPUT\n\n      - name : Checkout repository\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ github.event.release.tag_name }}\n\n      - name: Set Up QEMU\n        uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0\n\n      - name: Set Up Docker Buildx\n        uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0\n\n      - name: login to Docker Hub\n        uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Build Image\n        uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0\n        with:\n          push: true\n          load: false\n          build-args: |\n            ASYNCAPI_CLI_VERSION=${{ steps.version.outputs.value }}\n          tags: |\n            asyncapi/cli:${{ steps.version.outputs.value }}\n            asyncapi/cli:latest\n          platforms: linux/amd64,linux/arm64\n          cache-from: type=gha\n          cache-to: type=gha\n\n      - name:  Sync README.md and Description to Docker Hub\n        uses: actions/checkout@v4\n\n      - uses: meeDamian/sync-readme@82715041300710d9be7c726c9d6c683b70451087 # v1.0.6\n        with:\n          user: ${{ secrets.DOCKER_USERNAME }}\n          pass: ${{ secrets.DOCKER_PASSWORD }}\n          slug: asyncapi/cli\n          description: CLI to work with your AsyncAPI files\n\n  publish-action-docker:\n    name: Release github action for cli and update version in action.yml\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ github.event.release.tag_name }}\n\n      - name: Get version without v character\n        id: version\n        env:\n          TAG_NAME: ${{ github.event.release.tag_name }}\n        run: |\n          VERSION_WITHOUT_V=${TAG_NAME:1}\n          echo \"value=${VERSION_WITHOUT_V}\" >> $GITHUB_OUTPUT\n\n      - name: Set Up QEMU\n        uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0\n\n      - name: Set Up Docker Buildx\n        uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0\n\n      - name: Login to Docker Hub\n        uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Build and push\n        uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0\n        with:\n          context: .\n          file: ./github-action/Dockerfile\n          push: true\n          tags: |\n            asyncapi/github-action-for-cli:${{ steps.version.outputs.value }}\n            asyncapi/github-action-for-cli:latest\n          platforms: linux/amd64,linux/arm64\n\n      - name: Change directory to github-action\n        run: |\n          cd github-action/\n          ls -la\n\n      - uses: meeDamian/sync-readme@82715041300710d9be7c726c9d6c683b70451087 # v1.0.6\n        with:\n          user: ${{ secrets.DOCKER_USERNAME }}\n          pass: ${{ secrets.DOCKER_PASSWORD }}\n          slug: asyncapi/github-action-for-cli\n          description: Github action for AsyncAPI CLI\n\n"
  },
  {
    "path": ".github/workflows/release-server-api.yml",
    "content": "name: Release Server API Image\non: \n  release:\n    types: \n      - published\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Version to release'\n        required: true\njobs:\n\n  publish-docker:\n    name: Generating Docker\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Get version without v character\n        id: version\n        run: |\n          VERSION=${{github.event.release.tag_name || github.event.inputs.version}}\n          VERSION_WITHOUT_V=${VERSION:1}\n          echo \"value=${VERSION_WITHOUT_V}\" >> $GITHUB_OUTPUT\n\n      - name: Set Up QEMU\n        uses: docker/setup-qemu-action@v3\n\n      - name: Set Up Docker Buildx\n        uses: docker/setup-buildx-action@v3\n\n      - name: Login to Docker Hub\n        uses: docker/login-action@v3\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Build and Push Docker Image\n        uses: docker/build-push-action@v6\n        id: push\n        with:\n          context: .\n          file: ./src/apps/api/Dockerfile\n          push: true\n          tags: |\n            asyncapi/server-api:${{ steps.version.outputs.value }}\n            asyncapi/server-api:latest\n          platforms: linux/amd64,linux/arm64\n          \n      - uses: meeDamian/sync-readme@82715041300710d9be7c726c9d6c683b70451087 #version 1.0.6 https://github.com/meeDamian/sync-readme/releases/tag/v1.0.6\n        with:\n          user: ${{secrets.DOCKER_USERNAME}}\n          pass: ${{ secrets.DOCKER_PASSWORD }}\n          slug: asyncapi/server-api\n          readme: ./src/apps/api/README.md\n          description: Server API providing official AsyncAPI tools\n\n    outputs:\n      digest: ${{ steps.push.outputs.digest }}\n\n  deploy-app:\n    name: Deploy to DigitalOcean App\n    needs: publish-docker\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        \n      - name: Deploy to DigitalOcean App\n        uses: digitalocean/app_action/deploy@190f99be3ce4cbbe5d1ca0a63a8ac9d0add205d0\n        env:\n          DOCKER_DIGEST: ${{ needs.publish-docker.outputs.digest }}\n        with:\n          token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}\n          app_spec_location: ./src/apps/api/.do/app.yaml\n          print_deploy_logs: true\n"
  },
  {
    "path": ".github/workflows/release-with-changesets.yml",
    "content": "# It does magic only if there is a package.json file in the root of the project\nname: Release\n\non:\n  push:\n    branches:\n      - master\n        # The below lines are not enough to have release supported for these branches\n      - next-spec\n      - next-major\n      - next-major-spec\n      - beta\n      - alpha\n      - next\n\njobs:\n  test-nodejs:\n    # We just check the message of the first commit as there is always just one commit because we squash into one before merging\n    # \"commits\" contains an array of objects where one of the properties is the commit \"message\"\n    # Release workflow will be skipped if release conventional commits are not used\n    if: |\n      (startsWith( github.event.commits[0].message , 'fix:' ) ||\n      startsWith( github.event.commits[0].message, 'fix!:' ) ||\n      startsWith( github.event.commits[0].message, 'feat:' ) ||\n      startsWith( github.event.commits[0].message, 'chore(release):' ) ||\n      startsWith( github.event.commits[0].message, 'feat!:' ))\n    name: Test NodeJS release on ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n    steps:\n      - name: Set git to use LF # To once and for all finish the never-ending fight between Unix and Windows\n        run: |\n          git config --global core.autocrlf false\n          git config --global core.eol lf\n        shell: bash\n      - name: Checkout repository\n        uses: actions/checkout@v4\n      - name: Check if Node.js project and has package.json\n        id: packagejson\n        run: test -e ./package.json && echo \"exists=true\" >> $GITHUB_OUTPUT || echo \"exists=false\" >> $GITHUB_OUTPUT\n        shell: bash\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Check package-lock version\n        uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master\n        id: lockversion\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: \"${{ steps.lockversion.outputs.version }}\"\n          registry-url: \"https://registry.npmjs.org\"\n      - if: steps.lockversion.outputs.version == '18' && matrix.os == 'windows-latest'\n        name: Install npm cli 8\n        shell: bash\n        # npm cli 10 is buggy because of some cache issues\n        run: npm install -g npm@8.19.4\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Install dependencies\n        shell: bash\n        run: npm ci\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Run test\n        run: npm test --if-present\n      - if: failure() # Only, on failure, send a message on the 94_bot-failing-ci slack channel\n        name: Report workflow run status to Slack\n        uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 # v3.15.1\n        with:\n          status: ${{ job.status }}\n          fields: repo,action,workflow\n          text: \"Release workflow failed in testing job\"\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}\n\n  release:\n    needs: [test-nodejs]\n    name: Publish to any of NPM, GitHub, or Docker Hub\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      id-token: write\n      pull-requests: write\n    steps:\n      - name: Set git to use LF # To once and for all finish the never-ending fight between Unix and Windows\n        run: |\n          git config --global core.autocrlf false\n          git config --global core.eol lf\n      - name: Checkout repository\n        uses: actions/checkout@v6\n      - name: Check if Node.js project and has package.json\n        id: packagejson\n        run: test -e ./package.json && echo \"exists=true\" >> $GITHUB_OUTPUT || echo \"exists=false\" >> $GITHUB_OUTPUT\n        shell: bash\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Check package-lock version\n        uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master\n        id: lockversion\n        with:\n          node-version: ${{ vars.NODE_VERSION }}\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: \"${{ steps.lockversion.outputs.version }}\"\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Install dependencies\n        shell: bash\n        run: npm ci\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Install changelog\n        shell: bash\n        # This step can be removed once the issue is fixed in the changeset package.\n        run: npm install @changesets/changelog-git@0.2.0\n\n      - if: steps.packagejson.outputs.exists == 'true'\n        name: Publish to any of NPM, Github, and Docker Hub\n        #this step has 2 goals, it is either identifying that there is changeset file created and then this action creates a PR with version bump that will trigger release - or if it sees there is no changeset, and there are versions changes in package.json files, it publish new versions to NPM is they are not there yet\n        # However, as currently changeset's publish isn't working well with OIDC next step will directly publish to NPM if there are version changes but no changesets\n        uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3\n        id: release\n        with:\n          version: npm run bump:version\n          commit: \"chore(release): release and bump versions of packages\"\n          title: \"chore(release): release and bump versions of packages\"\n          # Working around changesets action not supporting OIDC yet. Need to pass successful release output for triggering github release\n          publish: npm run publish:trusted\n          setupGitUser: false\n        env:\n          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n          GIT_AUTHOR_NAME: asyncapi-bot\n          GIT_AUTHOR_EMAIL: info@asyncapi.io\n          GIT_COMMITTER_NAME: asyncapi-bot\n          GIT_COMMITTER_EMAIL: info@asyncapi.io\n\n      - name: Publish to NPM directly (skip if version already published)\n        if: steps.packagejson.outputs.exists == 'true' && steps.release.outputs.hasChangesets == 'false'\n        shell: bash\n        run: |\n          # Check if the version in package.json is already published\n          VERSION=$(node -p \"require('./package.json').version\")\n          PACKAGE_NAME=$(node -p \"require('./package.json').name\")\n          if npm view \"$PACKAGE_NAME@$VERSION\" > /dev/null 2>&1; then\n            echo \"Version $VERSION of package $PACKAGE_NAME is already published. Skipping publish.\"\n          else\n            echo \"Publishing version $VERSION of package $PACKAGE_NAME to NPM.\"\n            npm publish --provenance\n          fi\n\n      - if: failure() # Only, on failure, send a message on the 94_bot-failing-ci Slack channel\n        name: Report workflow run status to Slack\n        uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 # v3.15.1\n        with:\n          status: ${{ job.status }}\n          fields: repo,action,workflow\n          text: \"Release workflow failed in release job\"\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}\n"
  },
  {
    "path": ".github/workflows/scripts/README.md",
    "content": "The entire `scripts` directory is centrally managed in [.github](https://github.com/asyncapi/.github/) repository. Any changes in this folder should be done in central repository."
  },
  {
    "path": ".github/workflows/scripts/kit/htmlContent.js",
    "content": "/**\n * This code is centrally managed in https://github.com/asyncapi/.github/\n * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n *\n * Kit.com version — greeting uses Kit Liquid (subscriber.first_name). Unsubscribe is provided by the Kit email template/footer.\n */\n\n/**\n * Escape HTML special characters to prevent XSS\n */\nfunction escapeHtml(text) {\n    if (!text) return '';\n    return text\n        .replace(/&/g, '&amp;')\n        .replace(/</g, '&lt;')\n        .replace(/>/g, '&gt;')\n        .replace(/\"/g, '&quot;')\n        .replace(/'/g, '&#039;');\n}\n\nmodule.exports = (link, title) => {\n    // Sanitize inputs to prevent XSS\n    const safeLink = escapeHtml(link);\n    const safeTitle = escapeHtml(title);\n\n    return `<!doctype html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n    <head>\n        <!-- NAME: SIMPLE TEXT -->\n        <!--[if gte mso 15]>\n        <xml>\n            <o:OfficeDocumentSettings>\n            <o:AllowPNG/>\n            <o:PixelsPerInch>96</o:PixelsPerInch>\n            </o:OfficeDocumentSettings>\n        </xml>\n        <![endif]-->\n        <meta charset=\"UTF-8\">\n        <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        <title>${safeTitle}</title>\n        \n    <style type=\"text/css\">\n\t\tp{\n\t\t\tmargin:10px 0;\n\t\t\tpadding:0;\n\t\t}\n\t\ttable{\n\t\t\tborder-collapse:collapse;\n\t\t}\n\t\th1,h2,h3,h4,h5,h6{\n\t\t\tdisplay:block;\n\t\t\tmargin:0;\n\t\t\tpadding:0;\n\t\t}\n\t\timg,a img{\n\t\t\tborder:0;\n\t\t\theight:auto;\n\t\t\toutline:none;\n\t\t\ttext-decoration:none;\n\t\t}\n\t\tbody,#bodyTable,#bodyCell{\n\t\t\theight:100%;\n\t\t\tmargin:0;\n\t\t\tpadding:0;\n\t\t\twidth:100%;\n\t\t}\n\t\t.mcnPreviewText{\n\t\t\tdisplay:none !important;\n\t\t}\n\t\t#outlook a{\n\t\t\tpadding:0;\n\t\t}\n\t\timg{\n\t\t\t-ms-interpolation-mode:bicubic;\n\t\t}\n\t\ttable{\n\t\t\tmso-table-lspace:0pt;\n\t\t\tmso-table-rspace:0pt;\n\t\t}\n\t\t.ReadMsgBody{\n\t\t\twidth:100%;\n\t\t}\n\t\t.ExternalClass{\n\t\t\twidth:100%;\n\t\t}\n\t\tp,a,li,td,blockquote{\n\t\t\tmso-line-height-rule:exactly;\n\t\t}\n\t\ta[href^=tel],a[href^=sms]{\n\t\t\tcolor:inherit;\n\t\t\tcursor:default;\n\t\t\ttext-decoration:none;\n\t\t}\n\t\tp,a,li,td,body,table,blockquote{\n\t\t\t-ms-text-size-adjust:100%;\n\t\t\t-webkit-text-size-adjust:100%;\n\t\t}\n\t\t.ExternalClass,.ExternalClass p,.ExternalClass td,.ExternalClass div,.ExternalClass span,.ExternalClass font{\n\t\t\tline-height:100%;\n\t\t}\n\t\ta[x-apple-data-detectors]{\n\t\t\tcolor:inherit !important;\n\t\t\ttext-decoration:none !important;\n\t\t\tfont-size:inherit !important;\n\t\t\tfont-family:inherit !important;\n\t\t\tfont-weight:inherit !important;\n\t\t\tline-height:inherit !important;\n\t\t}\n\t\t#bodyCell{\n\t\t\tpadding:10px;\n\t\t}\n\t\t.templateContainer{\n\t\t\tmax-width:600px !important;\n\t\t}\n\t\ta.mcnButton{\n\t\t\tdisplay:block;\n\t\t}\n\t\t.mcnImage,.mcnRetinaImage{\n\t\t\tvertical-align:bottom;\n\t\t}\n\t\t.mcnTextContent{\n\t\t\tword-break:break-word;\n\t\t}\n\t\t.mcnTextContent img{\n\t\t\theight:auto !important;\n\t\t}\n\t\t.mcnDividerBlock{\n\t\t\ttable-layout:fixed !important;\n\t\t}\n\t\tbody,#bodyTable{\n\t\t\tbackground-color:#FFFFFF;\n\t\t\tbackground-image:none;\n\t\t\tbackground-repeat:no-repeat;\n\t\t\tbackground-position:center;\n\t\t\tbackground-size:cover;\n\t\t}\n\t\t#bodyCell{\n\t\t\tborder-top:0;\n\t\t}\n\t\t.templateContainer{\n\t\t\tborder:0;\n\t\t}\n\t\th1{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:26px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\th2{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:22px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\th3{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:20px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\th4{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:18px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateHeader{\n\t\t\tborder-top:0;\n\t\t\tborder-bottom:0;\n\t\t}\n\t\t#templateHeader .mcnTextContent,#templateHeader .mcnTextContent p{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:16px;\n\t\t\tline-height:150%;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateHeader .mcnTextContent a,#templateHeader .mcnTextContent p a{\n\t\t\tcolor:#007C89;\n\t\t\tfont-weight:normal;\n\t\t\ttext-decoration:underline;\n\t\t}\n\t\t#templateBody{\n\t\t\tborder-top:0;\n\t\t\tborder-bottom:0;\n\t\t}\n\t\t#templateBody .mcnTextContent,#templateBody .mcnTextContent p{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:16px;\n\t\t\tline-height:150%;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateBody .mcnTextContent a,#templateBody .mcnTextContent p a{\n\t\t\tcolor:#007C89;\n\t\t\tfont-weight:normal;\n\t\t\ttext-decoration:underline;\n\t\t}\n\t\t#templateFooter{\n\t\t\tborder-top:0;\n\t\t\tborder-bottom:0;\n\t\t}\n\t\t#templateFooter .mcnTextContent,#templateFooter .mcnTextContent p{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:12px;\n\t\t\tline-height:150%;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateFooter .mcnTextContent a,#templateFooter .mcnTextContent p a{\n\t\t\tcolor:#202020;\n\t\t\tfont-weight:normal;\n\t\t\ttext-decoration:underline;\n\t\t}\n\t@media only screen and (min-width:768px){\n\t\t.templateContainer{\n\t\t\twidth:600px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\tbody,table,td,p,a,li,blockquote{\n\t\t\t-webkit-text-size-adjust:none !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\tbody{\n\t\t\twidth:100% !important;\n\t\t\tmin-width:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnRetinaImage{\n\t\t\tmax-width:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImage{\n\t\t\twidth:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnCartContainer,.mcnCaptionTopContent,.mcnRecContentContainer,.mcnCaptionBottomContent,.mcnTextContentContainer,.mcnBoxedTextContentContainer,.mcnImageGroupContentContainer,.mcnCaptionLeftTextContentContainer,.mcnCaptionRightTextContentContainer,.mcnCaptionLeftImageContentContainer,.mcnCaptionRightImageContentContainer,.mcnImageCardLeftTextContentContainer,.mcnImageCardRightTextContentContainer,.mcnImageCardLeftImageContentContainer,.mcnImageCardRightImageContentContainer{\n\t\t\tmax-width:100% !important;\n\t\t\twidth:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnBoxedTextContentContainer{\n\t\t\tmin-width:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageGroupContent{\n\t\t\tpadding:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnCaptionLeftContentOuter .mcnTextContent,.mcnCaptionRightContentOuter .mcnTextContent{\n\t\t\tpadding-top:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageCardTopImageContent,.mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,.mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent{\n\t\t\tpadding-top:18px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageCardBottomImageContent{\n\t\t\tpadding-bottom:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageGroupBlockInner{\n\t\t\tpadding-top:0 !important;\n\t\t\tpadding-bottom:0 !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageGroupBlockOuter{\n\t\t\tpadding-top:9px !important;\n\t\t\tpadding-bottom:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnTextContent,.mcnBoxedTextContentColumn{\n\t\t\tpadding-right:18px !important;\n\t\t\tpadding-left:18px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageCardLeftImageContent,.mcnImageCardRightImageContent{\n\t\t\tpadding-right:18px !important;\n\t\t\tpadding-bottom:0 !important;\n\t\t\tpadding-left:18px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcpreview-image-uploader{\n\t\t\tdisplay:none !important;\n\t\t\twidth:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th1{\n\t\t\tfont-size:22px !important;\n\t\t\tline-height:125% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th2{\n\t\t\tfont-size:20px !important;\n\t\t\tline-height:125% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th3{\n\t\t\tfont-size:18px !important;\n\t\t\tline-height:125% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th4{\n\t\t\tfont-size:16px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttable.mcnBoxedTextContentContainer td.mcnTextContent,td.mcnBoxedTextContentContainer td.mcnTextContent p{\n\t\t\tfont-size:14px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttd#templateHeader td.mcnTextContent,td#templateHeader td.mcnTextContent p{\n\t\t\tfont-size:16px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttd#templateBody td.mcnTextContent,td#templateBody td.mcnTextContent p{\n\t\t\tfont-size:16px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttd#templateFooter td.mcnTextContent,td#templateFooter td.mcnTextContent p{\n\t\t\tfont-size:14px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}</style></head>\n    <body style=\"background:#FFFFFF none no-repeat center/cover;height: 100%;margin: 0;padding: 0;width: 100%;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #FFFFFF;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;\">\n        <center>\n            <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" height=\"100%\" width=\"100%\" id=\"bodyTable\" style=\"background:#FFFFFF none no-repeat center/cover;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-color: #FFFFFF;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;\">\n                <tr>\n                    <td align=\"left\" valign=\"top\" id=\"bodyCell\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 10px;width: 100%;border-top: 0;\">\n                        <!-- BEGIN TEMPLATE // -->\n                        <!--[if (gte mso 9)|(IE)]>\n                        <table align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"600\" style=\"width:600px;\">\n                        <tr>\n                        <td align=\"center\" valign=\"top\" width=\"600\" style=\"width:600px;\">\n                        <![endif]-->\n                        <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" class=\"templateContainer\" style=\"border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border: 0;max-width: 600px !important;\">\n                            <tr>\n                                <td valign=\"top\" id=\"templateHeader\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border-top: 0;border-bottom: 0;\"><table class=\"mcnTextBlock\" style=\"min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n    <tbody class=\"mcnTextBlockOuter\">\n        <tr>\n            <td class=\"mcnTextBlockInner\" style=\"padding-top: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" valign=\"top\">\n              \t<!--[if mso]>\n\t\t\t\t<table align=\"left\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" style=\"width:100%;\">\n\t\t\t\t<tr>\n\t\t\t\t<![endif]-->\n\t\t\t    \n\t\t\t\t<!--[if mso]>\n\t\t\t\t<td valign=\"top\" width=\"600\" style=\"width:600px;\">\n\t\t\t\t<![endif]-->\n                <table style=\"max-width: 100%;min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" class=\"mcnTextContentContainer\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" align=\"left\">\n                    <tbody><tr>\n                        \n                        <td class=\"mcnTextContent\" style=\"padding-top: 0;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;\" valign=\"top\">\n                        \n                            Hey {{ subscriber.first_name | strip | default: \"TSC member\" }},<br>\n<br>\nThere is a new topic at AsyncAPI Initiative that requires Technical Steering Committee attention. \n<br>\nPlease have a look if it is just something you need to be aware of, or maybe your vote is needed.\n<br>\nTopic: <a href=\"${ safeLink }\" style=\"color:#007c89;font-weight:normal;text-decoration:underline\" target=\"_blank\">${ safeTitle }</a>.\n                        </td>\n                    </tr>\n                </tbody></table>\n\t\t\t\t<!--[if mso]>\n\t\t\t\t</td>\n\t\t\t\t<![endif]-->\n                \n\t\t\t\t<!--[if mso]>\n\t\t\t\t</tr>\n\t\t\t\t</table>\n\t\t\t\t<![endif]-->\n            </td>\n        </tr>\n    </tbody>\n</table></td>\n                            </tr>\n                            <tr>\n                                <td valign=\"top\" id=\"templateBody\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border-top: 0;border-bottom: 0;\">\n                                <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" class=\"mcnTextBlock\" style=\"min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n\n    <tbody class=\"mcnTextBlockOuter\">\n        <tr>\n            <td valign=\"top\" class=\"mcnTextBlockInner\" style=\"padding-top: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n              \t<!--[if mso]>\n\t\t\t\t<table align=\"left\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" style=\"width:100%;\">\n\t\t\t\t<tr>\n\t\t\t\t<![endif]-->\n\t\t\t    \n\t\t\t\t<!--[if mso]>\n\t\t\t\t<td valign=\"top\" width=\"600\" style=\"width:600px;\">\n\t\t\t\t<![endif]-->\n                <table align=\"left\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"max-width: 100%;min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" width=\"100%\" class=\"mcnTextContentContainer\">\n                    <tbody><tr>\n                        \n                        <td valign=\"top\" class=\"mcnTextContent\" style=\"padding-top: 0;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;\">\n                        \n                            Cheers,<br>\nAsyncAPI Initiative\n                        </td>\n                    </tr>\n                </tbody></table>\n\t\t\t\t<!--[if mso]>\n\t\t\t\t</td>\n\t\t\t\t<![endif]-->\n                \n\t\t\t\t<!--[if mso]>\n\t\t\t\t</tr>\n\t\t\t\t</table>\n\t\t\t\t<![endif]-->\n            </td>\n        </tr>\n    </tbody>\n</table></td>\n                            </tr>\n                            <tr>\n                                <td valign=\"top\" id=\"templateFooter\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border-top: 0;border-bottom: 0;\"><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" class=\"mcnTextBlock\" style=\"min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n    <tbody class=\"mcnTextBlockOuter\">\n        <tr>\n            <td valign=\"top\" class=\"mcnTextBlockInner\" style=\"padding-top: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n              \t<!--[if mso]>\n\t\t\t\t<table align=\"left\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" style=\"width:100%;\">\n\t\t\t\t<tr>\n\t\t\t\t<![endif]-->\n\t\t\t    \n\t\t\t\t<!--[if mso]>\n\t\t\t\t<td valign=\"top\" width=\"600\" style=\"width:600px;\">\n\t\t\t\t<![endif]-->\n                <table align=\"left\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"max-width: 100%;min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" width=\"100%\" class=\"mcnTextContentContainer\">\n                    <tbody><tr>\n                        \n                        <td valign=\"top\" class=\"mcnTextContent\" style=\"padding-top: 0;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #202020;font-family: Helvetica;font-size: 12px;line-height: 150%;text-align: left;\">\n                        \n                            You are receiving this email because you are subscribed to TSC Voting notifications.<br>\n&nbsp;\n                        </td>\n                    </tr>\n                </tbody></table>\n\t\t\t\t<!--[if mso]>\n\t\t\t\t</td>\n\t\t\t\t<![endif]-->\n                \n\t\t\t\t<!--[if mso]>\n\t\t\t\t</tr>\n\t\t\t\t</table>\n\t\t\t\t<![endif]-->\n            </td>\n        </tr>\n    </tbody>\n</table></td>\n                            </tr>\n                        </table>\n                        <!--[if (gte mso 9)|(IE)]>\n                        </td>\n                        </tr>\n                        </table>\n                        <![endif]-->\n                        <!-- // END TEMPLATE -->\n                    </td>\n                </tr>\n            </table>\n        </center>\n    </body>\n</html>\n`\n}"
  },
  {
    "path": ".github/workflows/scripts/kit/index.js",
    "content": "/**\n * This code is centrally managed in https://github.com/asyncapi/.github/\n * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n */\nconst core = require('@actions/core');\nconst htmlContent = require('./htmlContent.js');\n\nconst sanitizeLinkAndTitle = (link, title) => {\n  // Validate inputs to prevent injection attacks\n    if (!link || typeof link !== 'string' || link.length > 2000) {\n        return core.setFailed('Invalid link parameter');\n    }\n    if (!title || typeof title !== 'string' || title.length > 500) {\n        return core.setFailed('Invalid title parameter');\n    }\n\n    let parsedLink;\n    try {\n        parsedLink = new URL(link);\n    } catch (error) {\n        return core.setFailed('Invalid link parameter');\n    }\n\n    if (parsedLink.protocol !== 'https:') {\n        return core.setFailed('Link must use https protocol');\n    }\n\n    // Sanitize title by removing control characters and limiting length\n    const sanitizedTitle = title.replace(/[\\x00-\\x1F\\x7F]/g, '').substring(0, 250);\n    return { sanitizedLink: parsedLink.toString(), sanitizedTitle };\n};\n\nmodule.exports = async (link, title) => {\n    const KIT_BASE = 'https://api.kit.com/v4';\n    const TSC_TAG_ID = Number(process.env.KIT_TSC_TAG_ID);\n\n    // Schedule 1 minute ahead\n    const sendAt = new Date(Date.now() + 60 * 1000);\n\n    const { sanitizedLink, sanitizedTitle } = sanitizeLinkAndTitle(link, title);\n\n    const res = await fetch(`${KIT_BASE}/broadcasts`, {\n        method: 'POST',\n        headers: {\n            'X-Kit-Api-Key': process.env.KIT_API_KEY,\n            'Content-Type': 'application/json'\n        },\n        body: JSON.stringify({\n            subject: `TSC attention required: ${sanitizedTitle}`,\n            preview_text: 'Check out the latest topic that TSC members have to be aware of',\n            content: htmlContent(sanitizedLink, sanitizedTitle),\n            description: `TSC notification - ${new Date().toUTCString()}`,\n            public: false,\n            published_at: null,\n            send_at: sendAt.toISOString(),\n            subscriber_filter: [{ all: [{ type: 'tag', ids: [TSC_TAG_ID] }] }]\n        })\n    });\n\n    if (!res.ok) return core.setFailed(`Failed creating broadcast: ${await res.text()}`);\n    core.info(`Kit.com TSC broadcast scheduled for ${sendAt.toISOString()}`);\n};\n"
  },
  {
    "path": ".github/workflows/scripts/kit/package.json",
    "content": "{\n  \"name\": \"schedule-email\",\n  \"description\": \"Kit.com email broadcast script for TSC notifications. This file is centrally managed in https://github.com/asyncapi/.github/\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": {\n    \"@actions/core\": \"1.6.0\"\n  }\n}\n"
  },
  {
    "path": ".github/workflows/scripts/mailchimp/htmlContent.js",
    "content": "/**\n * This code is centrally managed in https://github.com/asyncapi/.github/\n * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n */\n\n/**\n * Escape HTML special characters to prevent XSS\n */\nfunction escapeHtml(text) {\n    if (!text) return '';\n    return text\n        .replace(/&/g, '&amp;')\n        .replace(/</g, '&lt;')\n        .replace(/>/g, '&gt;')\n        .replace(/\"/g, '&quot;')\n        .replace(/'/g, '&#039;');\n}\n\nmodule.exports = (link, title) => {\n    // Sanitize inputs to prevent XSS\n    const safeLink = escapeHtml(link);\n    const safeTitle = escapeHtml(title);\n\n    return `<!doctype html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n    <head>\n        <!-- NAME: SIMPLE TEXT -->\n        <!--[if gte mso 15]>\n        <xml>\n            <o:OfficeDocumentSettings>\n            <o:AllowPNG/>\n            <o:PixelsPerInch>96</o:PixelsPerInch>\n            </o:OfficeDocumentSettings>\n        </xml>\n        <![endif]-->\n        <meta charset=\"UTF-8\">\n        <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        <title>*|MC:SUBJECT|*</title>\n        \n    <style type=\"text/css\">\n\t\tp{\n\t\t\tmargin:10px 0;\n\t\t\tpadding:0;\n\t\t}\n\t\ttable{\n\t\t\tborder-collapse:collapse;\n\t\t}\n\t\th1,h2,h3,h4,h5,h6{\n\t\t\tdisplay:block;\n\t\t\tmargin:0;\n\t\t\tpadding:0;\n\t\t}\n\t\timg,a img{\n\t\t\tborder:0;\n\t\t\theight:auto;\n\t\t\toutline:none;\n\t\t\ttext-decoration:none;\n\t\t}\n\t\tbody,#bodyTable,#bodyCell{\n\t\t\theight:100%;\n\t\t\tmargin:0;\n\t\t\tpadding:0;\n\t\t\twidth:100%;\n\t\t}\n\t\t.mcnPreviewText{\n\t\t\tdisplay:none !important;\n\t\t}\n\t\t#outlook a{\n\t\t\tpadding:0;\n\t\t}\n\t\timg{\n\t\t\t-ms-interpolation-mode:bicubic;\n\t\t}\n\t\ttable{\n\t\t\tmso-table-lspace:0pt;\n\t\t\tmso-table-rspace:0pt;\n\t\t}\n\t\t.ReadMsgBody{\n\t\t\twidth:100%;\n\t\t}\n\t\t.ExternalClass{\n\t\t\twidth:100%;\n\t\t}\n\t\tp,a,li,td,blockquote{\n\t\t\tmso-line-height-rule:exactly;\n\t\t}\n\t\ta[href^=tel],a[href^=sms]{\n\t\t\tcolor:inherit;\n\t\t\tcursor:default;\n\t\t\ttext-decoration:none;\n\t\t}\n\t\tp,a,li,td,body,table,blockquote{\n\t\t\t-ms-text-size-adjust:100%;\n\t\t\t-webkit-text-size-adjust:100%;\n\t\t}\n\t\t.ExternalClass,.ExternalClass p,.ExternalClass td,.ExternalClass div,.ExternalClass span,.ExternalClass font{\n\t\t\tline-height:100%;\n\t\t}\n\t\ta[x-apple-data-detectors]{\n\t\t\tcolor:inherit !important;\n\t\t\ttext-decoration:none !important;\n\t\t\tfont-size:inherit !important;\n\t\t\tfont-family:inherit !important;\n\t\t\tfont-weight:inherit !important;\n\t\t\tline-height:inherit !important;\n\t\t}\n\t\t#bodyCell{\n\t\t\tpadding:10px;\n\t\t}\n\t\t.templateContainer{\n\t\t\tmax-width:600px !important;\n\t\t}\n\t\ta.mcnButton{\n\t\t\tdisplay:block;\n\t\t}\n\t\t.mcnImage,.mcnRetinaImage{\n\t\t\tvertical-align:bottom;\n\t\t}\n\t\t.mcnTextContent{\n\t\t\tword-break:break-word;\n\t\t}\n\t\t.mcnTextContent img{\n\t\t\theight:auto !important;\n\t\t}\n\t\t.mcnDividerBlock{\n\t\t\ttable-layout:fixed !important;\n\t\t}\n\t\tbody,#bodyTable{\n\t\t\tbackground-color:#FFFFFF;\n\t\t\tbackground-image:none;\n\t\t\tbackground-repeat:no-repeat;\n\t\t\tbackground-position:center;\n\t\t\tbackground-size:cover;\n\t\t}\n\t\t#bodyCell{\n\t\t\tborder-top:0;\n\t\t}\n\t\t.templateContainer{\n\t\t\tborder:0;\n\t\t}\n\t\th1{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:26px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\th2{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:22px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\th3{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:20px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\th4{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:18px;\n\t\t\tfont-style:normal;\n\t\t\tfont-weight:bold;\n\t\t\tline-height:125%;\n\t\t\tletter-spacing:normal;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateHeader{\n\t\t\tborder-top:0;\n\t\t\tborder-bottom:0;\n\t\t}\n\t\t#templateHeader .mcnTextContent,#templateHeader .mcnTextContent p{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:16px;\n\t\t\tline-height:150%;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateHeader .mcnTextContent a,#templateHeader .mcnTextContent p a{\n\t\t\tcolor:#007C89;\n\t\t\tfont-weight:normal;\n\t\t\ttext-decoration:underline;\n\t\t}\n\t\t#templateBody{\n\t\t\tborder-top:0;\n\t\t\tborder-bottom:0;\n\t\t}\n\t\t#templateBody .mcnTextContent,#templateBody .mcnTextContent p{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:16px;\n\t\t\tline-height:150%;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateBody .mcnTextContent a,#templateBody .mcnTextContent p a{\n\t\t\tcolor:#007C89;\n\t\t\tfont-weight:normal;\n\t\t\ttext-decoration:underline;\n\t\t}\n\t\t#templateFooter{\n\t\t\tborder-top:0;\n\t\t\tborder-bottom:0;\n\t\t}\n\t\t#templateFooter .mcnTextContent,#templateFooter .mcnTextContent p{\n\t\t\tcolor:#202020;\n\t\t\tfont-family:Helvetica;\n\t\t\tfont-size:12px;\n\t\t\tline-height:150%;\n\t\t\ttext-align:left;\n\t\t}\n\t\t#templateFooter .mcnTextContent a,#templateFooter .mcnTextContent p a{\n\t\t\tcolor:#202020;\n\t\t\tfont-weight:normal;\n\t\t\ttext-decoration:underline;\n\t\t}\n\t@media only screen and (min-width:768px){\n\t\t.templateContainer{\n\t\t\twidth:600px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\tbody,table,td,p,a,li,blockquote{\n\t\t\t-webkit-text-size-adjust:none !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\tbody{\n\t\t\twidth:100% !important;\n\t\t\tmin-width:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnRetinaImage{\n\t\t\tmax-width:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImage{\n\t\t\twidth:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnCartContainer,.mcnCaptionTopContent,.mcnRecContentContainer,.mcnCaptionBottomContent,.mcnTextContentContainer,.mcnBoxedTextContentContainer,.mcnImageGroupContentContainer,.mcnCaptionLeftTextContentContainer,.mcnCaptionRightTextContentContainer,.mcnCaptionLeftImageContentContainer,.mcnCaptionRightImageContentContainer,.mcnImageCardLeftTextContentContainer,.mcnImageCardRightTextContentContainer,.mcnImageCardLeftImageContentContainer,.mcnImageCardRightImageContentContainer{\n\t\t\tmax-width:100% !important;\n\t\t\twidth:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnBoxedTextContentContainer{\n\t\t\tmin-width:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageGroupContent{\n\t\t\tpadding:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnCaptionLeftContentOuter .mcnTextContent,.mcnCaptionRightContentOuter .mcnTextContent{\n\t\t\tpadding-top:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageCardTopImageContent,.mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,.mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent{\n\t\t\tpadding-top:18px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageCardBottomImageContent{\n\t\t\tpadding-bottom:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageGroupBlockInner{\n\t\t\tpadding-top:0 !important;\n\t\t\tpadding-bottom:0 !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageGroupBlockOuter{\n\t\t\tpadding-top:9px !important;\n\t\t\tpadding-bottom:9px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnTextContent,.mcnBoxedTextContentColumn{\n\t\t\tpadding-right:18px !important;\n\t\t\tpadding-left:18px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcnImageCardLeftImageContent,.mcnImageCardRightImageContent{\n\t\t\tpadding-right:18px !important;\n\t\t\tpadding-bottom:0 !important;\n\t\t\tpadding-left:18px !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\t.mcpreview-image-uploader{\n\t\t\tdisplay:none !important;\n\t\t\twidth:100% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th1{\n\t\t\tfont-size:22px !important;\n\t\t\tline-height:125% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th2{\n\t\t\tfont-size:20px !important;\n\t\t\tline-height:125% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th3{\n\t\t\tfont-size:18px !important;\n\t\t\tline-height:125% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\th4{\n\t\t\tfont-size:16px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttable.mcnBoxedTextContentContainer td.mcnTextContent,td.mcnBoxedTextContentContainer td.mcnTextContent p{\n\t\t\tfont-size:14px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttd#templateHeader td.mcnTextContent,td#templateHeader td.mcnTextContent p{\n\t\t\tfont-size:16px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttd#templateBody td.mcnTextContent,td#templateBody td.mcnTextContent p{\n\t\t\tfont-size:16px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}\t@media only screen and (max-width: 480px){\n\t\ttd#templateFooter td.mcnTextContent,td#templateFooter td.mcnTextContent p{\n\t\t\tfont-size:14px !important;\n\t\t\tline-height:150% !important;\n\t\t}\n\n}</style></head>\n    <body style=\"background:#FFFFFF none no-repeat center/cover;height: 100%;margin: 0;padding: 0;width: 100%;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #FFFFFF;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;\">\n        <!--*|IF:MC_PREVIEW_TEXT|*-->\n        <!--[if !gte mso 9]><!----><span class=\"mcnPreviewText\" style=\"display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;\">*|MC_PREVIEW_TEXT|*</span><!--<![endif]-->\n        <!--*|END:IF|*-->\n        <center>\n            <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" height=\"100%\" width=\"100%\" id=\"bodyTable\" style=\"background:#FFFFFF none no-repeat center/cover;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-color: #FFFFFF;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;\">\n                <tr>\n                    <td align=\"left\" valign=\"top\" id=\"bodyCell\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 10px;width: 100%;border-top: 0;\">\n                        <!-- BEGIN TEMPLATE // -->\n                        <!--[if (gte mso 9)|(IE)]>\n                        <table align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"600\" style=\"width:600px;\">\n                        <tr>\n                        <td align=\"center\" valign=\"top\" width=\"600\" style=\"width:600px;\">\n                        <![endif]-->\n                        <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" class=\"templateContainer\" style=\"border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border: 0;max-width: 600px !important;\">\n                            <tr>\n                                <td valign=\"top\" id=\"templateHeader\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border-top: 0;border-bottom: 0;\"><table class=\"mcnTextBlock\" style=\"min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n    <tbody class=\"mcnTextBlockOuter\">\n        <tr>\n            <td class=\"mcnTextBlockInner\" style=\"padding-top: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" valign=\"top\">\n              \t<!--[if mso]>\n\t\t\t\t<table align=\"left\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" style=\"width:100%;\">\n\t\t\t\t<tr>\n\t\t\t\t<![endif]-->\n\t\t\t    \n\t\t\t\t<!--[if mso]>\n\t\t\t\t<td valign=\"top\" width=\"600\" style=\"width:600px;\">\n\t\t\t\t<![endif]-->\n                <table style=\"max-width: 100%;min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" class=\"mcnTextContentContainer\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" align=\"left\">\n                    <tbody><tr>\n                        \n                        <td class=\"mcnTextContent\" style=\"padding-top: 0;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;\" valign=\"top\">\n                        \n                            Hey *|FNAME|*,<br>\n<br>\nThere is a new topic at AsyncAPI Initiative that requires Technical Steering Committee attention. \n<br>\nPlease have a look if it is just something you need to be aware of, or maybe your vote is needed.\n<br>\nTopic: <a href=\"${ safeLink }\" style=\"color:#007c89;font-weight:normal;text-decoration:underline\" target=\"_blank\">${ safeTitle }</a>.\n                        </td>\n                    </tr>\n                </tbody></table>\n\t\t\t\t<!--[if mso]>\n\t\t\t\t</td>\n\t\t\t\t<![endif]-->\n                \n\t\t\t\t<!--[if mso]>\n\t\t\t\t</tr>\n\t\t\t\t</table>\n\t\t\t\t<![endif]-->\n            </td>\n        </tr>\n    </tbody>\n</table></td>\n                            </tr>\n                            <tr>\n                                <td valign=\"top\" id=\"templateBody\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border-top: 0;border-bottom: 0;\">\n                                <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" class=\"mcnTextBlock\" style=\"min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n\n    <tbody class=\"mcnTextBlockOuter\">\n        <tr>\n            <td valign=\"top\" class=\"mcnTextBlockInner\" style=\"padding-top: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n              \t<!--[if mso]>\n\t\t\t\t<table align=\"left\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" style=\"width:100%;\">\n\t\t\t\t<tr>\n\t\t\t\t<![endif]-->\n\t\t\t    \n\t\t\t\t<!--[if mso]>\n\t\t\t\t<td valign=\"top\" width=\"600\" style=\"width:600px;\">\n\t\t\t\t<![endif]-->\n                <table align=\"left\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"max-width: 100%;min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" width=\"100%\" class=\"mcnTextContentContainer\">\n                    <tbody><tr>\n                        \n                        <td valign=\"top\" class=\"mcnTextContent\" style=\"padding-top: 0;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;\">\n                        \n                            Cheers,<br>\nAsyncAPI Initiative\n                        </td>\n                    </tr>\n                </tbody></table>\n\t\t\t\t<!--[if mso]>\n\t\t\t\t</td>\n\t\t\t\t<![endif]-->\n                \n\t\t\t\t<!--[if mso]>\n\t\t\t\t</tr>\n\t\t\t\t</table>\n\t\t\t\t<![endif]-->\n            </td>\n        </tr>\n    </tbody>\n</table></td>\n                            </tr>\n                            <tr>\n                                <td valign=\"top\" id=\"templateFooter\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border-top: 0;border-bottom: 0;\"><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" class=\"mcnTextBlock\" style=\"min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n    <tbody class=\"mcnTextBlockOuter\">\n        <tr>\n            <td valign=\"top\" class=\"mcnTextBlockInner\" style=\"padding-top: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\">\n              \t<!--[if mso]>\n\t\t\t\t<table align=\"left\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" style=\"width:100%;\">\n\t\t\t\t<tr>\n\t\t\t\t<![endif]-->\n\t\t\t    \n\t\t\t\t<!--[if mso]>\n\t\t\t\t<td valign=\"top\" width=\"600\" style=\"width:600px;\">\n\t\t\t\t<![endif]-->\n                <table align=\"left\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"max-width: 100%;min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;\" width=\"100%\" class=\"mcnTextContentContainer\">\n                    <tbody><tr>\n                        \n                        <td valign=\"top\" class=\"mcnTextContent\" style=\"padding-top: 0;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #202020;font-family: Helvetica;font-size: 12px;line-height: 150%;text-align: left;\">\n                        \n                            Want to change how you receive these emails?<br>\nYou can <a href=\"*|UPDATE_PROFILE|*\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #202020;font-weight: normal;text-decoration: underline;\">update your preferences</a> or <a href=\"*|UNSUB|*\" style=\"mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #202020;font-weight: normal;text-decoration: underline;\">unsubscribe from this list</a>.<br>\n&nbsp;\n                        </td>\n                    </tr>\n                </tbody></table>\n\t\t\t\t<!--[if mso]>\n\t\t\t\t</td>\n\t\t\t\t<![endif]-->\n                \n\t\t\t\t<!--[if mso]>\n\t\t\t\t</tr>\n\t\t\t\t</table>\n\t\t\t\t<![endif]-->\n            </td>\n        </tr>\n    </tbody>\n</table></td>\n                            </tr>\n                        </table>\n                        <!--[if (gte mso 9)|(IE)]>\n                        </td>\n                        </tr>\n                        </table>\n                        <![endif]-->\n                        <!-- // END TEMPLATE -->\n                    </td>\n                </tr>\n            </table>\n        </center>\n    </body>\n</html>\n`\n}"
  },
  {
    "path": ".github/workflows/stale-issues-prs.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\nname: Manage stale issues and PRs\n\non:\n  schedule:\n  - cron: \"0 0 * * *\"\n\npermissions: {}\n\njobs:\n  stale:\n    if: startsWith(github.repository, 'asyncapi/')\n    name: Mark issue or PR as stale\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read            # As delete-branch is not being used\n      issues: write             # To add comments and labels to issues\n      pull-requests: write      # To add comments and labels to PRs\n    steps:\n    - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 #v9.1.0 but pointing to commit for security reasons\n      with:\n        repo-token: ${{ github.token }}\n        stale-issue-message: |\n          This issue has been automatically marked as stale because it has not had recent activity :sleeping:\n\n          It will be closed in 120 days if no further activity occurs. To unstale this issue, add a comment with a detailed explanation. \n\n          There can be many reasons why some specific issue has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under [open governance model](https://github.com/asyncapi/community/blob/master/CHARTER.md). \n\n          Let us figure out together how to push this issue forward. Connect with us through [one of many communication channels](https://github.com/asyncapi/community/issues/1) we established here.\n\n          Thank you for your patience :heart:\n        stale-pr-message: |\n          This pull request has been automatically marked as stale because it has not had recent activity :sleeping:\n\n          It will be closed in 120 days if no further activity occurs. To unstale this pull request, add a comment with detailed explanation.\n\n          There can be many reasons why some specific pull request has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under [open governance model](https://github.com/asyncapi/community/blob/master/CHARTER.md). \n\n          Let us figure out together how to push this pull request forward. Connect with us through [one of many communication channels](https://github.com/asyncapi/community/issues/1) we established here.\n\n          Thank you for your patience :heart:\n        days-before-stale: 120\n        days-before-close: 120\n        stale-issue-label: stale\n        stale-pr-label: stale\n        exempt-issue-labels: keep-open\n        exempt-pr-labels: keep-open\n        close-issue-reason: not_planned\n"
  },
  {
    "path": ".github/workflows/test-action.yml",
    "content": "name: PR testing of CLI action\n\non:\n  pull_request:\n    types: [ opened, synchronize, reopened, ready_for_review ]\n\njobs:\n  should-workflow-run:\n    runs-on: ubuntu-latest\n    steps:\n      - if: >\n          !github.event.pull_request.draft && !(\n            (github.actor == 'asyncapi-bot' && (\n              startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') ||\n              startsWith(github.event.pull_request.title, 'chore(release):')\n            )) ||\n            (github.actor == 'asyncapi-bot-eve' && (\n              startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') ||\n              startsWith(github.event.pull_request.title, 'chore(release):')\n            )) ||\n            (github.actor == 'allcontributors[bot]' &&\n              startsWith(github.event.pull_request.title, 'docs: add')\n            )\n          )\n        id: should_run\n        name: Should Run\n        run: echo \"shouldrun=true\" >> $GITHUB_OUTPUT\n    outputs:\n      shouldrun: ${{ steps.should_run.outputs.shouldrun }}\n\n  build-docker:\n    needs: should-workflow-run\n    name: Build Docker image\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Get docker version\n        id: docker_version\n        run: >\n          ls -la;\n          action=$(cat action.yml);\n          regex='docker:\\/\\/asyncapi\\/github-action-for-cli:([0-9.]+)';\n          [[ $action =~ $regex ]];\n          action_version=${BASH_REMATCH[1]};\n          echo \"action_version=$action_version\" >> $GITHUB_OUTPUT\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v3\n      - name: Build Docker image and export\n        uses: docker/build-push-action@v5\n        with:\n          context: .\n          file: ./github-action/Dockerfile\n          tags: asyncapi/github-action-for-cli:${{ steps.docker_version.outputs.action_version }}\n          outputs: type=docker,dest=/tmp/asyncapi.tar\n      - name: Upload artifact\n        uses: actions/upload-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp/asyncapi.tar\n\n\n  test-defaults:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        uses: ./\n        with:\n          filepath: ./github-action/test/asyncapi.yml\n      - name: Assert GitHub Action\n        run: |\n          echo \"Listing all files\"\n          ls -R\n          echo \"Asserting GitHub Action\"\n          if [ -f \"./output/asyncapi.md\" ]; then\n            echo \"Files exist\"\n          else\n            echo \"Files do not exist:- ./output/asyncapi.md\"\n            echo \"Action failed\"\n            exit 1\n          fi\n\n  test-validate-success:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        uses: ./\n        with:\n          filepath: ./github-action/test/asyncapi.yml\n          command: validate\n\n  test-custom-command:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        uses: ./\n        with:\n          # Custom command to generate models\n          # Note: You can use command itself to generate models, but this is just an example for testing custom commands\n          custom_command: \"generate models typescript ./github-action/test/asyncapi.yml -o ./output\"\n      - name: Assert GitHub Action\n        run: |\n          echo \"Listing all files\"\n          ls -R\n          echo \"Asserting GitHub Action\"\n          if [ -f \"./output/AnonymousSchema_1.ts\" ]; then\n            echo \"Models have been generated\"\n          else\n            echo \"Models have not been generated\"\n            echo \"Action failed\"\n            exit 1\n          fi\n\n  test-custom-output:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        uses: ./\n        with:\n          filepath: ./github-action/test/asyncapi.yml\n          output: custom-output\n      - name: Assert GitHub Action\n        run: |\n          echo \"Listing all files\"\n          ls -R\n          echo \"Asserting GitHub Action\"\n          if [ -f \"./custom-output/asyncapi.md\" ]; then\n            echo \"Files exist\"\n          else\n            echo \"Files do not exist:- ./custom-output/asyncapi.md\"\n            echo \"Action failed\"\n            exit 1\n          fi\n\n  test-file-not-found:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        id: test\n        uses: ./\n        with:\n          filepath: non_existent_file.yml\n        continue-on-error: true\n      - name: Check for failure\n        run: |\n          if [ \"${{ steps.test.outcome }}\" == \"success\" ]; then\n            echo \"Test Failure: non_existent_file.yml should throw an error but did not\"\n            exit 1\n          else\n            echo \"Test Success: non_existent_file.yml threw an error as expected\"\n          fi\n\n  test-invalid-input:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        id: test\n        uses: ./\n        with:\n          filepath: github-action/test/asyncapi.yml\n          command: generate # No template or language specified\n          template: '' # Empty string\n        continue-on-error: true\n      - name: Check for failure\n        run: |\n          if [ \"${{ steps.test.outcome }}\" == \"success\" ]; then\n            echo \"Test Failure: generate command should throw an error as no template or language specified but did not\"\n            exit 1\n          else\n            echo \"Test Success: generate command threw an error as expected\"\n          fi\n\n  test-optimize:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        uses: ./\n        with:\n          filepath: github-action/test/unoptimized.yml\n          command: optimize\n          parameters: '-o new-file --no-tty'\n      - name: Assert GitHub Action\n        run: |\n          echo \"Listing all files\"\n          ls -R\n          echo \"Asserting GitHub Action\"\n          if [ -f \"./github-action/test/unoptimized_optimized.yml\" ]; then\n            echo \"The specified file has been optimized\"\n          else\n            echo \"The specified file has not been optimized\"\n            echo \"Action failed\"\n            exit 1\n          fi\n\n  test-bundle:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Make output directory\n        run: mkdir -p ./output/bundle\n      - name: Test GitHub Action\n        uses: ./\n        with:\n          custom_command: 'bundle ./github-action/test/bundle/asyncapi.yaml ./github-action/test/bundle/features.yaml --base ./github-action/test/bundle/asyncapi.yaml -o ./output/bundle/asyncapi.yaml'\n      - name: Assert GitHub Action\n        run: |\n          echo \"Listing all files\"\n          ls -R\n          echo \"Asserting GitHub Action\"\n          if [ -f \"./output/bundle/asyncapi.yaml\" ]; then\n            echo \"The specified files have been bundled\"\n          else\n            echo \"The specified files have not been bundled\"\n            echo \"Action failed\"\n            exit 1\n          fi\n\n  test-convert:\n    if: ${{ needs.should-workflow-run.outputs.shouldrun == 'true' }}\n    runs-on: ubuntu-latest\n    needs: [should-workflow-run, build-docker]\n    steps:\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: asyncapi\n          path: /tmp\n      - name: Load Docker image\n        run: |\n          docker load --input /tmp/asyncapi.tar\n          docker image ls -a\n      - uses: actions/checkout@v4\n      - name: Remove bin directory to disable developer mode\n        run: rm -rf ./bin\n      - name: Test GitHub Action\n        uses: ./\n        with:\n          command: convert\n          filepath: github-action/test/asyncapi.yml\n          output: output/convert/asyncapi.yaml\n      - name: Assert GitHub Action\n        run: |\n          echo \"Listing all files\"\n          ls -R\n          echo \"Asserting GitHub Action\"\n          if [ -f \"./output/convert/asyncapi.yaml\" ]; then\n            echo \"The specified file has been converted\"\n          else\n            echo \"The specified file has not been converted\"\n            echo \"Action failed\"\n            exit 1\n          fi\n"
  },
  {
    "path": ".github/workflows/update-docs-in-website.yml",
    "content": "name: Update latest CLI documentation in the website\n\non:\n  push:\n    branches:\n      - 'master'\n    paths:\n      - 'docs/*.md'\n  workflow_dispatch:\njobs:\n  Make-PR:\n    name: Make PR on website repository with updated latest CLI documentation\n    runs-on: ubuntu-latest\n    env:\n      GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n    steps:\n      - name: Checkout Current repository\n        uses: actions/checkout@v3\n        with:\n          path: cli\n      - name: Checkout Another repository\n        uses: actions/checkout@v3\n        with:\n          repository: asyncapi/website\n          path: website\n          token: ${{ env.GITHUB_TOKEN }}\n      - name: Config git\n        run: |\n          git config --global user.name asyncapi-bot\n          git config --global user.email info@asyncapi.io\n      - name: Create branch\n        working-directory: ./website\n        run: |\n          git checkout -b update-cli-docs-${{ github.sha }}\n      - name: Copy cli folder from Current Repo to Another\n        working-directory: ./website\n        run: |\n          mkdir -p ./markdown/docs/tools/cli\n          printf \"%s\\ntitle: CLI\\nweight: 10\\n%s\" \"---\" \"---\"> ../cli/docs/_section.md\n          mv ../cli/docs/*.md ./markdown/docs/tools/cli\n      - name: Commit and push\n        working-directory: ./website\n        run: |\n          git add .\n          git commit -m \"docs(cli): update latest cli docs\"\n          git push https://${{ env.GITHUB_TOKEN }}@github.com/asyncapi/website\n      - name: Create PR\n        working-directory: ./website\n        run: |\n          gh pr create --title \"docs(cli): update latest cli documentation\" --body \"Updated cli documentation is available and this PR introduces update to cli folder on the website\" --head \"update-cli-docs-${{ github.sha }}\"\n"
  },
  {
    "path": ".github/workflows/update-docs-on-docs-commits.yml",
    "content": "# This workflow is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# The given workflow is responsible for generating docs and creating PR with them when there is a commit with docs: prefix\n\n# This workflow will be updated in all repos with the topic get-global-docs-autoupdate\n\nname: 'Update generated parts of documentation on docs: commits'\n\non:\n  push:\n    branches:\n      - master\n\njobs:\n  docs-gen:\n    name: 'Generate docs and create PR'\n    runs-on: ubuntu-latest\n    # PR should be created within this GH action only if it is a docs: commit\n    # Otherwise it will conflict with release workflow\n    if: startsWith(github.event.commits[0].message, 'docs:')\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n      - name: Determine what node version to use\n        # This workflow is from our own org repo and safe to reference by 'master'.\n        uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master # //NOSONAR\n        with:\n          node-version: ${{ vars.NODE_VERSION }}\n        id: lockversion\n      - name: Use Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: \"${{ steps.lockversion.outputs.version }}\"\n          cache: 'npm'\n          cache-dependency-path: '**/package-lock.json'\n      - name: Install dependencies\n        run: npm ci\n      - name: Regenerate docs\n        run: npm run generate:assets --if-present\n      - name: Create Pull Request with updated docs\n        uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # uses 7.0.8 https://github.com/peter-evans/create-pull-request/releases/tag/v7.0.8\n        with:\n          token: ${{ secrets.GH_TOKEN }}\n          commit-message: 'chore: update generated docs'\n          committer: asyncapi-bot <info@asyncapi.io>\n          author: asyncapi-bot <info@asyncapi.io>\n          title: 'chore: update generated docs'\n          body: 'Update of docs that are generated and were forgotten on PR level.'\n          branch: gen-docs-update/${{ github.job }}\n      - name: Report workflow status to Slack\n        if: failure() # Only, on failure, send a message on the 94_bot-failing-ci slack channel\n        uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486 #using https://github.com/8398a7/action-slack/releases/tag/v3.16.2\n        with:\n          status: ${{ job.status }}\n          fields: repo,action,workflow\n          text: 'AsyncAPI docs generation workflow failed'\n          author_name: asyncapi-bot\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}     "
  },
  {
    "path": ".github/workflows/update-maintainers-trigger.yaml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\nname: Trigger MAINTAINERS.yaml file update\n\non:\n  push:\n    branches: [ master ]\n    paths:\n      # Check all valid CODEOWNERS locations: \n      # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-file-location\n      - 'CODEOWNERS'\n      - '.github/CODEOWNERS'\n      - '.docs/CODEOWNERS'\n\npermissions: \n  contents: read            # Just to limit GITHUB_TOKEN as we use GH_TOKEN only\n\njobs:\n  trigger-maintainers-update:\n    name: Trigger updating MAINTAINERS.yaml because of CODEOWNERS change\n    runs-on: ubuntu-latest\n    \n    steps:\n      - name: Repository Dispatch\n        uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # https://github.com/peter-evans/repository-dispatch/releases/tag/v3.0.0\n        with:\n          # The PAT with the 'public_repo' scope is required\n          token: ${{ secrets.GH_TOKEN }}\n          repository: ${{ github.repository_owner }}/community\n          event-type: trigger-maintainers-update\n"
  },
  {
    "path": ".github/workflows/update-pr.yml",
    "content": "# This workflow is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\n# This workflow will run on every comment with /update or /u. And will create merge-commits for the PR.\n# This also works with forks, not only with branches in the same repository/organization.\n# Currently, does not work with forks in different organizations.\n\n# This workflow will be distributed to all repositories in the AsyncAPI organization\n\nname: Update PR branches from fork\n\npermissions:\n  contents: read\n\non:\n  issue_comment: \n    types: [created]\n\njobs:\n  update-pr:\n    name: Update the fork PR with upstream changes\n    if: >\n      startsWith(github.repository, 'asyncapi/') &&\n      github.event.issue.pull_request && \n      github.event.issue.state != 'closed' && (\n        contains(github.event.comment.body, '/update') ||\n        contains(github.event.comment.body, '/u')\n      )\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write                     # Required to read PR details and post comments on the PR\n      pull-requests: write              # Required to update the PR branch \n      contents: read\n    steps:\n      - name: Get Pull Request Details\n        id: pr\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.GH_TOKEN || github.token }}\n          previews: 'merge-info-preview' # https://docs.github.com/en/graphql/overview/schema-previews#merge-info-preview-more-detailed-information-about-a-pull-requests-merge-state-preview\n          script: |\n            const prNumber = context.payload.issue.number;\n            core.debug(`PR Number: ${prNumber}`);\n            const { data: pr } = await github.rest.pulls.get({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              pull_number: prNumber\n            });\n\n            // If the PR has conflicts, we don't want to update it\n            const updateable = ['behind', 'blocked', 'unknown', 'draft', 'clean', 'unstable'].includes(pr.mergeable_state);\n            console.log(`PR #${prNumber} is ${pr.mergeable_state} and is ${updateable ? 'updateable' : 'not updateable'}`);\n            core.setOutput('updateable', updateable);\n\n            core.debug(`Updating PR #${prNumber} with head ${pr.head.sha}`);\n\n            return {\n              id: pr.node_id,\n              number: prNumber,\n              head: pr.head.sha,\n            }\n      - name: Update the Pull Request\n        if: steps.pr.outputs.updateable == 'true'\n        uses: actions/github-script@v7\n        env:\n          PR_DETAILS: ${{ steps.pr.outputs.result }}\n        with:\n          github-token: ${{ secrets.GH_TOKEN || github.token }}\n          script: |\n            const mutation = `mutation update($input: UpdatePullRequestBranchInput!) {\n              updatePullRequestBranch(input: $input) {\n                pullRequest {\n                  mergeable\n                }\n              }\n            }`;\n\n            const pr_details = JSON.parse(process.env.PR_DETAILS);\n\n            try {\n              const { data } = await github.graphql(mutation, {\n                input: {\n                  pullRequestId: pr_details.id,\n                  expectedHeadOid: pr_details.head,\n                }\n              });\n            } catch (GraphQLError) {\n              core.debug(GraphQLError);\n              if (\n                GraphQLError.name === 'GraphqlResponseError' &&\n                GraphQLError.errors.some(\n                  error => error.type === 'FORBIDDEN' || error.type === 'UNAUTHORIZED'\n                )\n              ) {\n                // Add comment to PR if the bot doesn't have permissions to update the PR\n                const comment = `Hi @${context.actor}. Update of PR has failed. It can be due to one of the following reasons:\n                -  I don't have permissions to update this PR. To update your fork with upstream using bot you need to enable [Allow edits from maintainers](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) option in the PR. \n                -  The fork is located in an organization, not under your personal profile. No solution for that. You are on your own with manual update.\n                -  There may be a conflict in the PR. Please resolve the conflict and try again.`;\n\n                await github.rest.issues.createComment({\n                  owner: context.repo.owner,\n                  repo: context.repo.repo,\n                  issue_number: context.issue.number,\n                  body: comment\n                });\n\n                core.setFailed('Bot does not have permissions to update the PR');\n              } else {\n                core.setFailed(GraphQLError.message);\n              }\n            }\n"
  },
  {
    "path": ".github/workflows/upload-release-assets.yml",
    "content": "name: Upload custom assets to GitHub release\n\non:\n  release:\n    types:\n      - published\n\nenv:\n  CI: true\n\njobs:\n  upload-assets:\n    name: Generate and upload assets\n    runs-on: ${{ matrix.os }}\n    continue-on-error: true\n    strategy:\n      matrix:\n        include:\n          - os: ubuntu-latest\n            npm_script: pack:linux\n            dist_folder: deb\n            extension: deb\n          - os: ubuntu-latest\n            npm_script: pack:tarballs\n            dist_folder: tar\n            extension: tar.gz\n          - os: ubuntu-latest\n            npm_script: pack:tarballs:alpine\n            dist_folder: tar\n            extension: alpine.tar.gz\n            container: 'node:20-alpine'\n            alpine: true\n          - os: ubuntu-latest\n            npm_script: pack:windows\n            dist_folder: win32\n            extension: x64.exe\n          - os: ubuntu-latest\n            npm_script: pack:windows\n            dist_folder: win32\n            extension: x86.exe\n          - os: macos-latest\n            npm_script: pack:macos\n            dist_folder: macos\n            extension: arm64.pkg\n          - os: macos-latest\n            npm_script: pack:macos\n            dist_folder: macos\n            extension: x64.pkg\n    \n    container: ${{ matrix.container }}\n    \n    steps:\n      - name: Install base tools for alpine container\n        if: matrix.container == 'node:20-alpine'\n        run: |\n          apk add --no-cache bash git python3 make g++ perl-utils xz\n\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      # Needed to avoid \"fatal: detected dubious ownership in repository\" error when using alpine container\n      - name: Mark GitHub workspace as safe\n        if: matrix.container == 'node:20-alpine'\n        run: |\n          git config --global --add safe.directory \"$GITHUB_WORKSPACE\"\n\n      \n      - name: Check package-lock version\n        if: matrix.container == ''\n        uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master\n        id: lockversion\n        with:\n          node-version: ${{ vars.NODE_VERSION }}\n      \n      - name: Setup Node.js\n        if: matrix.container == ''\n        uses: actions/setup-node@v6\n        with:\n          node-version: \"${{ steps.lockversion.outputs.version }}\"\n\n      - name: Get version from package.json\n        uses: actions/github-script@v6\n        id: extractver\n        with:\n          script: |\n            const packageJson = require('./package.json');\n            const packageJsonVersion = packageJson.version;\n            core.setOutput('version', packageJsonVersion);\n      - if: matrix.npm_script == 'pack:windows'\n        #fix for windows build issue #1433\n        name: Install p7zip-full nsis\n        run: sudo apt-get install -y p7zip-full nsis\n\n      - if: matrix.npm_script == 'pack:windows'\n        #npm cli 10 is buggy because of some cache issue\n        name: Install npm cli 10\n        shell: bash\n        run: npm install -g npm@latest\n      - name: Install dependencies\n        run: npm ci\n      - name: Build project\n        shell: bash\n        run: npm run prepublishOnly\n      - name: Assets generation\n        shell: bash\n        run: |\n          npm run ${{ matrix.npm_script }}\n          if [[ \"${{ matrix.alpine }}\" == \"true\" ]]; then\n            npm run pack:rename alpine\n          else\n            npm run pack:rename\n          fi\n      \n      - name: Update release\n        uses: softprops/action-gh-release@v1\n        with:\n          files: dist/${{ matrix.dist_folder }}/asyncapi.${{ matrix.extension }}\n          tag_name: v${{ steps.extractver.outputs.version }}\n          token: ${{ secrets.GH_TOKEN }}\n      - if: failure() # Only, on failure, send a message on the 94_bot-failing-ci slack channel\n        name: Report workflow run status to Slack\n        uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #using https://github.com/8398a7/action-slack/releases/tag/v3.15.1\n        with:\n          status: ${{ job.status }}\n          fields: repo,action,workflow\n          text: 'AsyncAPI CLI release build artifacts failed'\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}"
  },
  {
    "path": ".github/workflows/welcome-first-time-contrib.yml",
    "content": "# This action is centrally managed in https://github.com/asyncapi/.github/\n# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo\n\nname: Welcome first time contributors\n\non:\n  pull_request:\n    types: \n      - opened\n  issues:\n    types:\n      - opened\n\npermissions:\n  issues: read                  # Required to check if the issue is the user's first contribution\n  pull-requests: read           # Required to check if the pull request is the user's first contribution\n\njobs:\n  welcome:\n    name: Post welcome message\n    if: ${{ !contains(fromJson('[\"asyncapi-bot\", \"dependabot[bot]\", \"dependabot-preview[bot]\", \"allcontributors[bot]\"]'), github.actor) }} # zizmor: ignore[obfuscation]\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read            # Required to read repository data for checking if it's the user's first contribution\n      issues: write             # Required to post welcome message on issues\n      pull-requests: write      # Required to post welcome message on pull requests\n    steps:\n    - uses: actions/github-script@v7\n      with:\n        github-token: ${{ github.token }}\n        script: |\n          const issueMessage = `Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) and the instructions about a [basic recommended setup](https://github.com/asyncapi/community/blob/master/git-workflow.md) useful for opening a pull request.<br />Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`;\n          const prMessage = `Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) useful for opening a pull request.<br />Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`;\n          if (!issueMessage && !prMessage) {\n              throw new Error('Action must have at least one of issue-message or pr-message set');\n          }\n          const isIssue = !!context.payload.issue;\n          let isFirstContribution;\n          if (isIssue) {\n              const query = `query($owner:String!, $name:String!, $contributer:String!) {\n              repository(owner:$owner, name:$name){\n                issues(first: 1, filterBy: {createdBy:$contributer}){\n                  totalCount\n                }\n              }\n            }`;\n            const variables = {\n              owner: context.repo.owner,\n              name: context.repo.repo,\n              contributer: context.payload.sender.login\n            };\n            const { repository: { issues: { totalCount } } } = await github.graphql(query, variables);\n            isFirstContribution = totalCount === 1;\n          } else {\n              const query = `query($qstr: String!) {\n                search(query: $qstr, type: ISSUE, first: 1) {\n                   issueCount\n                 }\n              }`;\n            const variables = {\n              \"qstr\": `repo:${context.repo.owner}/${context.repo.repo} type:pr author:${context.payload.sender.login}`,\n            };\n            const { search: { issueCount } } = await github.graphql(query, variables);\n            isFirstContribution = issueCount === 1;\n          }\n          \n          if (!isFirstContribution) {\n              console.log(`Not the users first contribution.`);\n              return;\n          }\n          const message = isIssue ? issueMessage : prMessage;\n          // Add a comment to the appropriate place\n          if (isIssue) {\n              const issueNumber = context.payload.issue.number;\n              console.log(`Adding message: ${message} to issue #${issueNumber}`);\n              await github.rest.issues.createComment({\n                  owner: context.payload.repository.owner.login,\n                  repo: context.payload.repository.name,\n                  issue_number: issueNumber,\n                  body: message\n              });\n          }\n          else {\n            const pullNumber = context.payload.pull_request.number;\n              console.log(`Adding message: ${message} to pull request #${pullNumber}`);\n              await github.rest.pulls.createReview({\n                  owner: context.payload.repository.owner.login,\n                  repo: context.payload.repository.name,\n                  pull_number: pullNumber,\n                  body: message,\n                  event: 'COMMENT'\n              });\n          }\n"
  },
  {
    "path": ".gitignore",
    "content": "*-debug.log\n*-error.log\n.DS_Store\n.idea\n/.nyc_output\n/dist\n/dist/\n/lib\n/node_modules/\n/tmp\n/yarn.lock\n/assets/examples/**\n!assets/examples/default-example.yaml\n!assets/examples/tutorial.yml'\n!assets/examples/default-example.json\nnode_modules\n/test/integration/generate/models/\ntest.asyncapi-cli\nasyncapi.json\ntest/fixtures/minimaltemplate/__transpiled\ntest/fixtures/newtemplate/__transpiled\ntest/fixtures/specification-conv.yml\ntest/fixtures/specification-conv.yaml\ntest/fixtures/specification-conv.json\n.vscode\nsrc/domains/models/generate/ClientLanguages.ts\n\n/action/\n/github-action/output/\n\noclif.manifest.json\nspec-examples.zip\n\n# Coverage for testing\n\ncoverage\n.asyncapi-analytics\n\n# Analytics\n\n.asyncapi-analytics\n\n# Modelina\ntest/fixtures/generate/models\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n    \"singleQuote\": true\n}"
  },
  {
    "path": ".sonarcloud.properties",
    "content": "sonar.exclusions=test/**/*,Dockerfile,github-action/Dockerfile\nsonar.issue.ignore.multicriteria=e1,e2\n\n# Exclude copy recursively and root user issue in Dockerfile\nsonar.issue.ignore.multicriteria.e1.ruleKey=docker:S6470\nsonar.issue.ignore.multicriteria.e1.resourceKey=**/Dockerfile\n\nsonar.issue.ignore.multicriteria.e2.ruleKey=docker:S6471\nsonar.issue.ignore.multicriteria.e2.resourceKey=**/Dockerfile\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# @asyncapi/cli\n\n## 6.0.0\n\n### Major Changes\n\n- 7580cee: Removal of postman -> asyncapi conversion functionality\n\n  ## ⚠ BREAKING CHANGES\n\n  Remove postman conversion utilities due to unmaintained dependencies and compatibility issues.\n\n  **Why this change?**\n  - The `postman2openapi` dependency causes multiple issues due to its WASM involvement\n  - WASM file loading causes browser compatibility issues after webpack updates\n  - Alternative libraries like `postman-to-openapi` did not provide adequate functionality\n  - The underlying dependencies are unmaintained and pose long-term maintenance risks\n\n  **Impact:**\n  - The `convert` command no longer supports postman format conversion\n  - Users relying on postman conversion will need to find alternative solutions\n\n  **Future:**\n  We can consider re-adding this feature after community discussion and establishing a sustainable maintenance plan with actively maintained dependencies.\n\n  Related: https://github.com/asyncapi/converter-js/pull/311\n\n## 5.0.7\n\n### Patch Changes\n\n- 72fd21f: Bump @asyncapi/generator from v3.1.0 &rarr; v3.1.1\n\n## 5.0.6\n\n### Patch Changes\n\n- a414293: - Updated `@asyncapi/generator` from `3.0.1` → `3.1.0`\n\n## 5.0.5\n\n### Patch Changes\n\n- be7c41d: chore: bump Node.js version to 24 in remaining Dockerfiles\n\n## 5.0.4\n\n### Patch Changes\n\n- cacf566: Update server-api image to use Node 24.\n\n## 5.0.3\n\n### Patch Changes\n\n- 125e907: Update dependencies to the latest feasible ones thus eliminating vulnerabilities.\n\n## 5.0.2\n\n### Patch Changes\n\n- 4073175: - Alpine Releases have been fixed now.\n  - Smaller docker image sizes and pruned dependencies.\n\n## 5.0.1\n\n### Patch Changes\n\n- 394967f: fix: remove unnecessary await from startPreview call\n\n  The startPreview function returns void, not a Promise, so awaiting it\n  was incorrect and triggered a linter error. This matches the pattern\n  used in the studio command.\n\n## 5.0.0\n\n### Major Changes\n\n- dac7bb4: Removed support for AsyncAPI Generator v1 and v2. The CLI now exclusively uses Generator v3. The `--use-new-generator` flag has been removed from the `generate fromTemplate` command.\n\n- b90a9b7: ## Major release with important security updates\n  - Keeping in mind the recent Shai-Hulud attack, we have adopted trusted publishing with NPM.\n  - This requires us to use node >= 24 and npm >= 11\n  - Next.js version is in sync with Studio, and is currently 14.2.35 deemed safe by CVE. [For more details](https://nextjs.org/blog/CVE-2025-66478)\n\n  ### Breaking Changes\n  - Node.js version 24 or higher is now required.\n  - NPM version 11 or higher is now required.\n  - Next.js version is now 14.2.35 or higher.\n  - The CLI now exclusively uses Generator v3 only.\n  - The `--use-new-generator` flag has been removed from the `generate fromTemplate` command.\n  - Default template in action has been upgraded to `@asyncapi/markdown-template@2.0.0`\n\n  Please make sure to update your environment accordingly before upgrading to this version.\n\n### Minor Changes\n\n- c648614: Studio updated to 1.1.0 with Next.js 14.2.35 in https://github.com/asyncapi/cli/pull/1922/changes\n\n## 4.1.3\n\n### Patch Changes\n\n- a77940f: fix: show the correct path to the newly created optimized file\n  - e16ebf5: fix: show the correct path to the newly created optimized file\n\n## 4.1.2\n\n### Patch Changes\n\n- df62a63: fix: generate optimized output corresponding to the input format\n  - c756765: fix: generate optimized output corresponding to the input format\n\n## 4.1.1\n\n### Patch Changes\n\n- 8eca9ed: fix: update redoc\n  - adde5d4: chore: update redoc\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n\n## 4.1.0\n\n### Minor Changes\n\n- 70761f0: feat: add new custom resolver to fetch the reference from private repo\n  - 88ceb3d: add new custom resolver to fetch the reference from private repo\n  - 4974358: Update src/domains/services/validation.service.ts\n\n  Co-authored-by: Fran Méndez <fmvilas@gmail.com>\n  - c764ee6: update instruction in the test case for the validation success\n  - 2b9457f: fix test-cases\n  - 86bbfc4: fix lint issue across the project\n  - 6813ef0: Update 1875.md\n\n## 4.0.1\n\n### Patch Changes\n\n- 9b479fc: fix: lockfile fixed\n  - 92bb81b: fix:lockfile fixed\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n\n## 4.0.0\n\n### Major Changes\n\n- 9d05d49: feat!: tests, flags and glee command code removed\n  - 7df684f: chore: major tests flags and glee command code removed\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n  - 4a94f51: chore:glee removal complete\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n\n## 3.6.0\n\n### Minor Changes\n\n- ca8101f: feat: output flag renamed and writing to file functionality added to diff command\n  - 8c2e940: fix:parse flag output renamed to save-output and saving diff output to file functionality added to diff command\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n  - 59e1df9: chore:tests added for flags save-output in validate and diff command\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n  - 7228a36: chore:cleanup\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n  - ccb5388: chore:cleanup\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n\n## 3.5.2\n\n### Patch Changes\n\n- bbc9451: fix: server-api deploy\n  - 837be8a: fix: server-api release and deployment\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - 09af23e: chore: add app platform spec\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - a5eef9f: chore: install generator templates globally\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n\n## 3.5.1\n\n### Patch Changes\n\n- 5a99f6f: fix: modify api:build script to generate languages\n  - 9488a1b: fix: modify script to generate languages\n\n  Updated the 'api:build' script to include language generation.\n\n## 3.5.0\n\n### Minor Changes\n\n- 129f531: Introduced a new `asyncapi generate client` command to the CLI.\n\n  This command allows you to generate client libraries using the new **AsyncAPI Generator** baked-in templates. Read more about [baked-in templates in official documentation](https://www.asyncapi.com/docs/tools/generator/baked-in-templates).\n\n  This feature is based on a new concept introduced in [AsyncAPI Generator version 2.8.3](https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%402.8.3). The number of templates is limited and the solution is still in the experimental phase. It is not recommended to use them in production. Instead, join us in the [Generator project](https://github.com/asyncapi/generator) to help improve templates with your use cases and your AsyncAPI documents.\n\n## 3.4.2\n\n### Patch Changes\n\n- d832abc: Fixes the dependencies and package-lock.json\n\n## 3.4.2\n\n### Patch Changes\n\n- 80dcadd: fix: get server-api release ready\n  - 35248ba: chore: fix server-api release\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - 4dcdd02: fix: get server-api release ready\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n\n## 3.4.1\n\n### Patch Changes\n\n- e2a3583: fix: made studio start on different port if one is already in use\n  - 43a8cb8: fix: made studio start on differnt port if one is already in use\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n  - ce4b061: Merge remote-tracking branch 'remote/master' into fix-studio-multiple-instance\n  - 122812a: chore: new approach for port allocation done\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n  - 5946001: fix: removed comment and fix linting errors\n\n  Signed-off-by: Tushar Anand <tusharannand@gmail.com>\n\n## 3.4.0\n\n### Minor Changes\n\n- 6b00166: feat: asyncapi release for alpine distros\n  - 54ba750: feat: asyncapi on alpine distros\n  - 44d72c3: fix: rename the scripts to use rename instead of tarballs\n\n## 3.3.0\n\n### Minor Changes\n\n- c944268: feat: refactor CLI to be service based and initial migration of server-api\n  - ac95777: feat: refactor cli to be service based and migrate server-api\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - 6b925f4: chore: fix diff test\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - cfe6e8d: feat: add generator controller\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - b2d7bcc: chore: don't need to install everytime\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - dd71d22: Merge remote-tracking branch 'origin/master' into refactor\n  - 1fd1216: fix: linting\n\n  Signed-off-by: Ashish Padhy <ashishpadhy1729@gmail.com>\n  - 6bd3e73: chore: run prettier\n\n  Signed-off-by: Ashish Padhy <ashishpadhy1729@gmail.com>\n  - ca3fdee: chore: run linter\n\n  Signed-off-by: Ashish Padhy <ashishpadhy1729@gmail.com>\n  - 1d03d79: chore: quick startup of server-api\n\n  Signed-off-by: Shurtu-gal <ashishpadhy1729@gmail.com>\n  - 13729bb: chore: rename to apps\n\n  Signed-off-by: Ashish Padhy <ashishpadhy1729@gmail.com>\n  - cdd2a43: chore: rename npm commands\n\n  Signed-off-by: Ashish Padhy <ashishpadhy1729@gmail.com>\n  - 90aca02: Merge remote-tracking branch 'remote/master' into refactor\n\n## 3.2.0\n\n### Minor Changes\n\n- 0754d1d: feat: add new feature to supress the warnings\n  - ab94c15: Add a new flag that is --x-suppress-warnings to supress the warning in the validate command\n  - 55819cd: Allow to pass multiple warnings to supress and add check to verify the warning is correct or not\n  - 885fc71: Update src/core/parser.ts\n\n  Co-authored-by: Souvik De <souvikde.ns@gmail.com>\n  - 16b22de: Update src/core/flags/validate.flags.ts\n\n  Co-authored-by: Souvik De <souvikde.ns@gmail.com>\n  - de1caad: Add another flag to supressallwarnings and update test case\n\n## 3.1.1\n\n### Patch Changes\n\n- 152c272: feat: made the start studio command interactive along with addition of…\n  - 0e8e3c1: feat: made the start studio command inteactive along with addition of a flag to disable prompt.\n\n## 3.1.0\n\n### Minor Changes\n\n- d17aa54: feat: new command `asyncapi start preview` has been added\n\n## 3.0.1\n\n### Patch Changes\n\n- 1b62a66: - Fixes script detection issue in version 3.0.0\n  - Fixes testing by testing the github action with local CLI\n\n## 3.0.0\n\n### Major Changes\n\n- b6f8b82: feat: add autocomplete feature in cli\n\n## 2.17.0\n\n### Minor Changes\n\n- f0268d4: Remove `--renderer` flag and make `React` as the de-facto renderer, deprecating `Nunjucks`\n\n## 2.16.10\n\n### Patch Changes\n\n- e11fe05: fix: Wrong Error message in -h command #1725\n\n## 2.16.9\n\n### Patch Changes\n\n- 819b585: [Fix]: Json file supported in asyncapi new file command\n- 830fe82: Fix studio not opening in CLI studio command\n- 830fe82: fix: studio command not working\n\n## 2.16.8\n\n### Patch Changes\n\n- 460c99a: feat: use next.js version of studio\n- 460c99a: Upgrade studio to latest and allow use of next server\n\n## 2.16.7\n\n### Patch Changes\n\n- 6ca17b3: fix: update packages to latest stable version\n\n## 2.16.6\n\n### Patch Changes\n\n- 82b441f: fix: resolve error in AsyncAPI optimize\n\n## 2.16.5\n\n### Patch Changes\n\n- f873423: docs: update docs regarding asyncapi new command\n- 2deeb36: fix: print in cli asyncapi generate models without -o flag\n\n## 2.16.4\n\n### Patch Changes\n\n- 67d7e8f: fix: proxy implementation for optimize validate and convert fixed\n\n## 2.16.3\n\n### Patch Changes\n\n- cec8081: feat: added Proxy support for the generate commands.\n\n## 2.16.2\n\n### Patch Changes\n\n- 755339a: feat: change the implementation of new command\n\n## 2.16.1\n\n### Patch Changes\n\n- 3ab019f: chore(deps): bump jsonpath-plus and @stoplight/spectral-core\n- 07514e6: implemented new UI/UX improvements in config command\n- a774ae2: fix: starting of studio fixed when using example with new file\n\n## 2.16.0\n\n### Minor Changes\n\n- a37e124: Deprecate the --file flag in `start studio` command\n\n## 2.15.0\n\n### Minor Changes\n\n- dcfb8c7: fix: Remove unused package lodash.template\n\n## 2.14.1\n\n### Patch Changes\n\n- 08afb45: Prepare github action for release\n- da64c63: ci: bump artifact actions to v4\n\n## 2.14.0\n\n### Minor Changes\n\n- 6839c8f: - Changed docker build to a source code based build\n  - Changed name of github action to avoid clash\n  - Fixed Docker and Release Pipeline\n\n## 2.13.1\n\n### Patch Changes\n\n- 8ae33c4: Handle AsyncAPI v3 in diff command\n\n## 2.13.0\n\n### Minor Changes\n\n- a76b0fb: Add github-action to monorepo and set up changesets\n\n### Patch Changes\n\n- 81b925e: Updated README with Development.md file\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "# This file provides an overview of code owners in this repository.\n\n# Each line is a file pattern followed by one or more owners.\n# The last matching pattern has the most precedence.\n# For more details, read the following article on GitHub: https://help.github.com/articles/about-codeowners/.\n\n# The default owners are automatically added as reviewers when you open a pull request unless different owners are specified in the file.\n* @Souvikns @Amzani @Shurtu-gal @asyncapi-bot-eve @AayushSaini101\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "\n# Contributor Covenant 3.0 Code of Conduct\n\n## Our Pledge\n\nWe pledge to make our community welcoming, safe, and equitable for all.\n\nWe are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.\n\n## Encouraged Behaviors\n\nWhile acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.\n\nWith these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:\n\n1. Respecting the **purpose of our community**, our activities, and our ways of gathering.\n2. Engaging **kindly and honestly** with others.\n3. Respecting **different viewpoints** and experiences.\n4. **Taking responsibility** for our actions and contributions.\n5. Gracefully giving and accepting **constructive feedback**.\n6. Committing to **repairing harm** when it occurs.\n7. Behaving in other ways that promote and sustain the **well-being of our community**.\n\n\n## Restricted Behaviors\n\nWe agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.\n\n1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.\n2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.\n3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.\n4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.\n5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission.\n6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.\n7. Behaving in other ways that **threaten the well-being** of our community.\n\n### Other Restrictions\n\n1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions.\n2. **Failing to credit sources.** Not properly crediting the sources of content you contribute.\n3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community.\n4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors.\n\n\n## Reporting an Issue\n\nTensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm.\n\nWhen an incident does occur, it is important to report it promptly. To report a possible violation, here are a few simple ways to do it:  \n\n- Join our [AsyncAPI Slack](https://asyncapi.com/slack-invite) and share your report in the `#coc` channel.  \n- Reach out directly to any member of the [Code of Conduct Committee](https://github.com/orgs/asyncapi/teams/code_of_conduct).\n- Or, if you’d prefer, just send us an email at **conduct@asyncapi.com**.\n\nCommunity Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution.\n\n\n## Addressing and Repairing Harm\n\n****\n\nIf an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped.\n\n1) Warning\n   1) Event: A violation involving a single incident or series of incidents.\n   2) Consequence: A private, written warning from the Community Moderators.\n   3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations.\n2) Temporarily Limited Activities\n   1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation.\n   2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members.\n   3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over.\n3) Temporary Suspension\n   1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation.\n   2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions.\n   3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted.\n4) Permanent Ban\n   1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member.\n   2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior.\n   3) Repair: There is no possible repair in cases of this severity.\n\nThis enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community.\n\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.\n\n\n## Attribution\n\nThis Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/).\n\nContributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)\n\nFor answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion)."
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to AsyncAPI\nWe love your input! We want to make contributing to this project as easy and transparent as possible.\n\n## Contribution recogniton\n\nWe use [All Contributors](https://allcontributors.org/docs/en/specification) specification to handle recognitions. For more details read [this](https://www.asyncapi.com/docs/community/010-contribution-guidelines/recognize-contributors#main-content) document.\n\n\n\n\n## Summary of the contribution flow\n\nThe following is a summary of the ideal contribution flow. Please, note that Pull Requests can also be rejected by the maintainers when appropriate.\n\n```\n    ┌───────────────────────┐\n    │                       │\n    │    Open an issue      │\n    │  (a bug report or a   │\n    │   feature request)    │\n    │                       │\n    └───────────────────────┘\n               ⇩\n    ┌───────────────────────┐\n    │                       │\n    │  Open a Pull Request  │\n    │   (only after issue   │\n    │     is approved)      │\n    │                       │\n    └───────────────────────┘\n               ⇩\n    ┌───────────────────────┐\n    │                       │\n    │   Your changes will   │\n    │     be merged and     │\n    │ published on the next │\n    │        release        │\n    │                       │\n    └───────────────────────┘\n```\n\n## Code of Conduct\nAsyncAPI has adopted a [Code of Conduct](./CODE_OF_CONDUCT.md) that we expect project participants to adhere to. Please read it carefully to understand what sort of behaviour is expected.\n\n## Our Development Process\nWe use Github to host code, to track issues and feature requests, as well as accept pull requests.\n\n## Issues\n[Open an issue](https://github.com/asyncapi/asyncapi/issues/new) **only** if you want to report a bug or a feature. Don't open issues for questions or support, instead join our [Slack workspace](https://www.asyncapi.com/slack-invite) and ask there. Don't forget to follow our [Slack Etiquette](https://www.asyncapi.com/docs/community/060-meetings-and-communication/slack-etiquette) while interacting with community members! It's more likely you'll get help, and much faster!\n\n## Bug Reports and Feature Requests\n\nPlease use our issues templates that provide you with hints on what information we need from you to help you out.\n\n## Pull Requests\n\n**Please, make sure you open an issue before starting with a Pull Request, unless it's a typo or a really obvious error.** Pull requests are the best way to propose changes to the specification. Get familiar with our document that explains [Git workflow](https://www.asyncapi.com/docs/community/010-contribution-guidelines/git-workflow) used in our repositories.\n\n## Conventional commits\n\nOur repositories follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) specification. Releasing to GitHub and NPM is done with the support of [semantic-release](https://semantic-release.gitbook.io/semantic-release/).\n\nPull requests should have a title that follows the specification, otherwise, merging is blocked. If you are not familiar with the specification simply ask maintainers to modify. You can also use this cheatsheet if you want:\n\n- `fix: ` prefix in the title indicates that PR is a bug fix and PATCH release must be triggered.\n- `feat: ` prefix in the title indicates that PR is a feature and MINOR release must be triggered.\n- `docs: ` prefix in the title indicates that PR is only related to the documentation and there is no need to trigger release.\n- `chore: ` prefix in the title indicates that PR is only related to cleanup in the project and there is no need to trigger release.\n- `test: ` prefix in the title indicates that PR is only related to tests and there is no need to trigger release.\n- `refactor: ` prefix in the title indicates that PR is only related to refactoring and there is no need to trigger release.\n\nWhat about MAJOR release? just add `!` to the prefix, like `fix!: ` or `refactor!: `\n\nPrefix that follows specification is not enough though. Remember that the title must be clear and descriptive with usage of [imperative mood](https://chris.beams.io/posts/git-commit/#imperative).\n\nHappy contributing :heart:\n\n## License\nWhen you submit changes, your submissions are understood to be under the same [Apache 2.0 License](https://github.com/asyncapi/asyncapi/blob/master/LICENSE) that covers the project. Feel free to [contact the maintainers](https://www.asyncapi.com/slack-invite) if that's a concern.\n\n## References\nThis document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/master/CONTRIBUTING.md).\n"
  },
  {
    "path": "DEVELOPMENT.md",
    "content": "# Development guide\n\nThis guide will help you set up the `cli` locally, run tests, and use Docker for isolated testing.\n\n## Getting started\n\n1. Fork & Clone the repository:\n\nFirst fork the repository from github and then clone it,\n\n```bash\ngit clone https://github.com/{your_username}/cli.git\ncd cli\n```\n\nAfter cloning the repository, you should setup the fork properly and configure the `remote` repository as described [here](https://github.com/asyncapi/community/blob/master/git-workflow.md)\n\n2. Install dependencies:\n\n```bash\nnpm install\n```\n\n## Running tests\n\n### Local testing\n\nTo run all tests locally:\n\n- CLI tests: `npm run cli:test`\n- Unit tests: `npm run unit:test`\n- Github action tests: `npm run action:test`\n- Single test file: `npm run test:one -- <path-to-test-file>`\n\n### Adding tests\n\n1. Create new test files in the appropriate directory under `test/`:\n   - Unit tests: `test/unit/<category>/<name>.test.ts`\n   - Integration tests: `test/integration/<command>.test.ts`\n\n2. Follow the existing test patterns.\n\n3. Run your new tests using the commands mentioned above.\n\n> 📘 **For detailed debugging and testing guidelines**, see the [Debugging & Testing Guide](/docs/debugging-testing.md).\n\n## Release process\n\nTo release a major/minor/patch:\n\n### Conventional Commits:\n\nTo maintain a clear git history of commits and easily identify what each commit changed and whether it triggered a release, we use conventional commits. The feat and fix prefixes are particularly important as they are needed to trigger changesets. Using these prefixes ensures that the changes are correctly categorized and the versioning system functions as expected.\n\nFor Example:\n```\nfeat: add new feature\n```\n    \n#### Manual\n\n1.  Create a new release markdown file in the `.changeset` directory. The filename should indicate what the change is about.\n  \n2.  Add the following content to the file in this particular format:\n\n    ```markdown\n    ---\n    \"@package-name-1\": [type] (major/minor/patch)\n    \"@package-name-2\": [type]\n    ---\n\n    [Provide a brief description of the changes. For example: Added a new Release GitHub Flow to the Turborepo. No new features or bugfixes were introduced.]\n    ```\n\n    For Example:\n    \n    ```markdown\n    ---\n    \"@asyncapi/cli\": minor\n    ---\n\n    Adding new Release Github Flow to the Turborepo. No new features or bugfixes were introduced.\n\n    ```\n\n3. Include the file in your pull request.\n\n#### Using CLI\n\n1. Create a new release markdown file using changeset CLI. Below command will trigger an interactive prompt that you can use to specify release type and affected packages.\n    ```cli \n    npx -p @changesets/cli@2.27.7 changeset\n    ```\n\n2. Include the file in your pull request.\n\n> [!TIP]\n> For more detailed instructions, you can refer to the official documentation for creating a changeset:\n[Adding a changeset](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)\n\n### Release Flow:\n\n1. **Add a Changeset**:\n   - When you make changes that need to be released, create a markdown file in the `.changeset` directory stating the package name and level of change (major/minor/patch). \n\n2. **Open a Pull Request**:\n   - Push your changes and open a Pull Request (PR). After the PR is merged the changeset file helps communicate the type of changes (major, minor, patch).\n\n3. **CI Processes Changeset**:\n   - After PR is merged, a dedicated GitHub Actions release workflow runs using changeset action,\n\n   - This action reads the markdown files in the `.changeset` folder and creates a PR with the updated version of the package and removes the markdown file. For example:\n\n     Before:\n     ```json\n     \"name\": \"@asyncapi/cli\",\n     \"version\": \"2.0.1\",\n     ```\n\n     After:\n     ```json\n     \"name\": \"@asyncapi/cli\",\n     \"version\": \"3.0.1\",\n     ```\n\n   - The new PR will also contain the description from the markdown files,\n\n   - AsyncAPI bot automatically merge such release PR.\n\n4. **Release the Package**:\n\n   - After the PR is merged, the CI/CD pipeline triggers again. The `changesets/action` step identifies that the PR was created by itself. It then verifies if the current version of the package is greater than the previously released version. If a difference is detected, it executes the publish command to release the updated package.\n\n## Additional commands\n\n- Lint the code: `npm run lint`\n- Build Docker image: `npm run docker:build`\n\n## Troubleshooting\n\nIf you encounter any issues during development or testing, please check the following:\n\n1. Ensure you're using the correct Node.js version (24.0.0 or higher) and npm version (8.19.0 or higher).\n2. Clear the `node_modules` directory and reinstall dependencies if you encounter unexpected behavior.\n3. For Docker-related issues, make sure Docker is running and you have sufficient permissions.\n4. For permission errors, try: `sudo chown -R $(whoami) ./lib ./node_modules`\n5. For path alias issues, rebuild the project: `npm run build`\n\n> 📘 **For comprehensive debugging help**, see the [Debugging & Testing Guide](/docs/debugging-testing.md).\n\nIf problems persist, please open an issue on the GitHub repository.\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:24-alpine AS build\n\n# Copy the source code\nCOPY ./ /tmp/source_code\n\n# Install dependencies\nRUN cd /tmp/source_code && npm install --ignore-scripts\n\n# Build the source code\nRUN cd /tmp/source_code && npm run build\n\n# create libraries directory\nRUN mkdir -p /libraries\n\n# Copy the lib, bin, node_modules, and package.json files to the /libraries directory\nRUN cp -r /tmp/source_code/lib /libraries\nRUN cp -r /tmp/source_code/assets /libraries\nRUN cp /tmp/source_code/package.json /libraries\nRUN cp /tmp/source_code/package-lock.json /libraries\nRUN cp /tmp/source_code/oclif.manifest.json /libraries\n\n# Copy the bin directory to the /libraries directory\nRUN cp -r /tmp/source_code/bin /libraries\n\n# Remove everything inside /tmp\nRUN rm -rf /tmp/*\n\nFROM node:24-alpine\n\n# Set ARG to explicit value to build chosen version. Default is \"latest\"\nARG ASYNCAPI_CLI_VERSION=\n\n# Create a non-root user\nRUN addgroup -S myuser && adduser -S myuser -G myuser\n\nWORKDIR /app\n\n# Since 0.14.0 release of html-template chromium is needed for pdf generation\nENV PUPPETEER_EXECUTABLE_PATH /usr/bin/chromium-browser\nENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true\n# Since 0.30.0 release Git is supported and required as a dependency\n# Since 0.14.0 release of html-template chromium is needed for pdf generation.\n# More custom packages for specific template should not be added to this dockerfile. Instead, we should come up with some extensibility solution.\nRUN apk --update add git chromium && \\\n    apk add --no-cache --virtual .gyp python3 make g++ && \\\n    rm -rf /var/lib/apt/lists/* && \\\n    rm /var/cache/apk/*\n\n# Copy the libraries directory from the build stage\nCOPY --from=build /libraries /libraries\n\n# Install the dependencies\nRUN cd /libraries && npm install --omit=dev --ignore-scripts\n\n# Create a script that runs the desired command\nRUN ln -s /libraries/bin/run_bin /usr/local/bin/asyncapi\n\n# Make the script executable\nRUN chmod +x /usr/local/bin/asyncapi\n\n# Change ownership to non-root user\nRUN chown -R myuser:myuser /libraries /usr/local/bin/asyncapi || echo \"Failed to change ownership\"\n\nRUN chown -R myuser:myuser /usr/local/lib/node_modules && \\\nchown -R myuser:myuser /usr/local/bin\n\n# Switch to the non-root user\nUSER myuser\n\nENTRYPOINT [ \"asyncapi\" ]\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\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."
  },
  {
    "path": "NOTICE",
    "content": "Copyright 2016-2025 AsyncAPI Initiative\n\nThis product includes software developed at\nAsyncAPI Initiative (http://www.asyncapi.com/)."
  },
  {
    "path": "README.md",
    "content": "[![AsyncAPI CLI](./assets/logo.png)](https://www.asyncapi.com/tools/cli)\n\nCLI to work with your AsyncAPI files. Currently under development, we are working to bring more features. \n\n[![GitHub license](https://img.shields.io/github/license/asyncapi/cli)](https://github.com/asyncapi/cli/blob/master/LICENSE)\n[![PR testing - if Node project](https://github.com/asyncapi/cli/actions/workflows/if-nodejs-pr-testing.yml/badge.svg)](https://github.com/asyncapi/cli/actions/workflows/if-nodejs-pr-testing.yml)\n[![npm](https://img.shields.io/npm/dw/@asyncapi/cli)](https://www.npmjs.com/package/@asyncapi/cli)\n\n## Table of contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Architecture](#architecture)\n- [Debugging & Testing](#debugging--testing)\n- [Github Action](#github-action)\n- [Contributing](#contributing)\n  * [Set up development environment](#set-up-development-environment)\n  * [Command Structure and Patterns](#command-structure-and-patterns)\n- [Contributors](#contributors)\n\n<!-- tocstop -->\n\n## Installation\nLearn how to install the AsyncAPI CLI by following the instructions in the [installation guide](/docs/installation.md). \n\n## Usage\nThe [usage guide](/docs/usage.md) provides information about different ways to use the CLI.\n\n## Architecture\nThe [architecture guide](/docs/architecture.md) provides information about the architecture.\n\n## Debugging & Testing\nThe [debugging & testing guide](/docs/debugging-testing.md) provides step-by-step instructions for debugging CLI commands, API endpoints, and services, along with comprehensive testing guidelines.\n\n## Github Action\n\nThe AsyncAPI CLI can be used as a GitHub Action. You can find more information in the [GitHub Action guide](https://www.asyncapi.com/docs/tools/cli/github-action).\n\n## Contributing\n\nRead [CONTRIBUTING](https://github.com/asyncapi/.github/blob/master/CONTRIBUTING.md) guide.\n\n### Set up development environment\n\nRead [DEVELOPMENT.md](/DEVELOPMENT.md) file for development setup.\n\nAdditional steps:\n\n- Run `npm run test` to make sure everything is properly set up\n- Run `npm run build` and then `bin/run` to try new CLI locally\n\nThe UX developed for the CLI should comply with the [Command Line Interface Guideline](https://clig.dev/)\n\n### Command Structure and Patterns\n\nWe are following `verb + noun` and `namespace + noun + [verb]` pattern for making our commands and arguments. For example `asyncapi validate <spec-file-path>` and `asyncapi config context add <context-name> <spec-file-path>`.\n\n## Contributors\n\nThanks go to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tbody>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jotamusik\"><img src=\"https://avatars.githubusercontent.com/u/14940638?v=4?s=100\" width=\"100px;\" alt=\"Jorge Aguiar Martín\"/><br /><sub><b>Jorge Aguiar Martín</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=jotamusik\" title=\"Code\">💻</a> <a href=\"#ideas-jotamusik\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/cli/commits?author=jotamusik\" title=\"Tests\">⚠️</a> <a href=\"https://github.com/asyncapi/cli/commits?author=jotamusik\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://www.brainfart.dev/\"><img src=\"https://avatars.githubusercontent.com/u/6995927?v=4?s=100\" width=\"100px;\" alt=\"Lukasz Gornicki\"/><br /><sub><b>Lukasz Gornicki</b></sub></a><br /><a href=\"#ideas-derberg\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/cli/commits?author=derberg\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/cli/pulls?q=is%3Apr+reviewed-by%3Aderberg\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"#maintenance-derberg\" title=\"Maintenance\">🚧</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://souvik.vercel.app/\"><img src=\"https://avatars.githubusercontent.com/u/41781438?v=4?s=100\" width=\"100px;\" alt=\"souvik\"/><br /><sub><b>souvik</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=Souvikns\" title=\"Code\">💻</a> <a href=\"#ideas-Souvikns\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/cli/commits?author=Souvikns\" title=\"Tests\">⚠️</a> <a href=\"https://github.com/asyncapi/cli/pulls?q=is%3Apr+reviewed-by%3ASouvikns\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"#maintenance-Souvikns\" title=\"Maintenance\">🚧</a> <a href=\"https://github.com/asyncapi/cli/commits?author=Souvikns\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://boyney.io/\"><img src=\"https://avatars.githubusercontent.com/u/3268013?v=4?s=100\" width=\"100px;\" alt=\"David Boyne\"/><br /><sub><b>David Boyne</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=boyney123\" title=\"Code\">💻</a> <a href=\"#ideas-boyney123\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"#maintenance-boyney123\" title=\"Maintenance\">🚧</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.fmvilas.com/\"><img src=\"https://avatars.githubusercontent.com/u/242119?v=4?s=100\" width=\"100px;\" alt=\"Fran Méndez\"/><br /><sub><b>Fran Méndez</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=fmvilas\" title=\"Code\">💻</a> <a href=\"#ideas-fmvilas\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/cli/pulls?q=is%3Apr+reviewed-by%3Afmvilas\" title=\"Reviewed Pull Requests\">👀</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/magicmatatjahu\"><img src=\"https://avatars.githubusercontent.com/u/20404945?v=4?s=100\" width=\"100px;\" alt=\"Maciej Urbańczyk\"/><br /><sub><b>Maciej Urbańczyk</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/pulls?q=is%3Apr+reviewed-by%3Amagicmatatjahu\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"#maintenance-magicmatatjahu\" title=\"Maintenance\">🚧</a> <a href=\"#ideas-magicmatatjahu\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://aayushsahu.com/\"><img src=\"https://avatars.githubusercontent.com/u/54525741?v=4?s=100\" width=\"100px;\" alt=\"Aayush Kumar Sahu\"/><br /><sub><b>Aayush Kumar Sahu</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=aayushmau5\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/cli/commits?author=aayushmau5\" title=\"Tests\">⚠️</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/mihirterna\"><img src=\"https://avatars.githubusercontent.com/u/31316452?v=4?s=100\" width=\"100px;\" alt=\"Mihir Kulkarni\"/><br /><sub><b>Mihir Kulkarni</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=mihirterna\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://imabp.github.io/resume/\"><img src=\"https://avatars.githubusercontent.com/u/53480076?v=4?s=100\" width=\"100px;\" alt=\"Abir\"/><br /><sub><b>Abir</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=imabp\" title=\"Tests\">⚠️</a> <a href=\"https://github.com/asyncapi/cli/commits?author=imabp\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/peter-rr\"><img src=\"https://avatars.githubusercontent.com/u/81691177?v=4?s=100\" width=\"100px;\" alt=\"Peter Ramos\"/><br /><sub><b>Peter Ramos</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=peter-rr\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://samridhi-98.github.io/Portfolio\"><img src=\"https://avatars.githubusercontent.com/u/54466041?v=4?s=100\" width=\"100px;\" alt=\"Samriddhi\"/><br /><sub><b>Samriddhi</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=Samridhi-98\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://linktr.ee/KharabePranay\"><img src=\"https://avatars.githubusercontent.com/u/68046838?v=4?s=100\" width=\"100px;\" alt=\"Pranay Kharabe\"/><br /><sub><b>Pranay Kharabe</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=pranay202\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://d-m-oladele.netlify.app/\"><img src=\"https://avatars.githubusercontent.com/u/98895460?v=4?s=100\" width=\"100px;\" alt=\"Damilola Oladele\"/><br /><sub><b>Damilola Oladele</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=activus-d\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/prayutsu\"><img src=\"https://avatars.githubusercontent.com/u/54636525?v=4?s=100\" width=\"100px;\" alt=\"Abhay Garg\"/><br /><sub><b>Abhay Garg</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=prayutsu\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/cli/commits?author=prayutsu\" title=\"Tests\">⚠️</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/sambhavgupta0705\"><img src=\"https://avatars.githubusercontent.com/u/81870866?v=4?s=100\" width=\"100px;\" alt=\"Sambhav Gupta\"/><br /><sub><b>Sambhav Gupta</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=sambhavgupta0705\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/cli/commits?author=sambhavgupta0705\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/CyberHippo\"><img src=\"https://avatars.githubusercontent.com/u/18269437?v=4?s=100\" width=\"100px;\" alt=\"Hippolyte Vergnol\"/><br /><sub><b>Hippolyte Vergnol</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=CyberHippo\" title=\"Code\">💻</a> <a href=\"#infra-CyberHippo\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://www.jentevets.com\"><img src=\"https://avatars.githubusercontent.com/u/22449126?v=4?s=100\" width=\"100px;\" alt=\"Jente Vets\"/><br /><sub><b>Jente Vets</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=Vetsoo\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/kaushik-rishi\"><img src=\"https://avatars.githubusercontent.com/u/52498617?v=4?s=100\" width=\"100px;\" alt=\"Rishi\"/><br /><sub><b>Rishi</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=kaushik-rishi\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://ashishpadhy.live\"><img src=\"https://avatars.githubusercontent.com/u/100484401?v=4?s=100\" width=\"100px;\" alt=\"Ashish Padhy\"/><br /><sub><b>Ashish Padhy</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=Shurtu-gal\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/meetagrawal09\"><img src=\"https://avatars.githubusercontent.com/u/73902874?v=4?s=100\" width=\"100px;\" alt=\"Meet Agrawal\"/><br /><sub><b>Meet Agrawal</b></sub></a><br /><a href=\"#infra-meetagrawal09\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://www.chinmayyy.tech\"><img src=\"https://avatars.githubusercontent.com/u/112387862?v=4?s=100\" width=\"100px;\" alt=\"Chinmay Shewale\"/><br /><sub><b>Chinmay Shewale</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=chinma-yyy\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/cli/commits?author=chinma-yyy\" title=\"Tests\">⚠️</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/mhmohona\"><img src=\"https://avatars.githubusercontent.com/u/14244685?v=4?s=100\" width=\"100px;\" alt=\"Mahfuza Humayra Mohona\"/><br /><sub><b>Mahfuza Humayra Mohona</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=mhmohona\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/GreenRover\"><img src=\"https://avatars.githubusercontent.com/u/512850?v=4?s=100\" width=\"100px;\" alt=\"Heiko Henning\"/><br /><sub><b>Heiko Henning</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=GreenRover\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://www.linkedin.com/in/aayush-saini-0a25931b1/\"><img src=\"https://avatars.githubusercontent.com/u/60972989?v=4?s=100\" width=\"100px;\" alt=\"Zack_Aayush\"/><br /><sub><b>Zack_Aayush</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=AayushSaini101\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/ayushnau\"><img src=\"https://avatars.githubusercontent.com/u/78146753?v=4?s=100\" width=\"100px;\" alt=\"Ayush Nautiyal\"/><br /><sub><b>Ayush Nautiyal</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=ayushnau\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/AnishKacham\"><img src=\"https://avatars.githubusercontent.com/u/79566582?v=4?s=100\" width=\"100px;\" alt=\"AnishKacham\"/><br /><sub><b>AnishKacham</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=Anish Kacham\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/aeworxet\"><img src=\"https://avatars.githubusercontent.com/u/16149591?v=4?s=100\" width=\"100px;\" alt=\"Viacheslav Turovskyi\"/><br /><sub><b>Viacheslav Turovskyi</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=aeworxet\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/amanbedi1\"><img src=\"https://avatars.githubusercontent.com/u/82234871?v=4?s=100\" width=\"100px;\" alt=\"Amanpreet Singh Bedi \"/><br /><sub><b>Amanpreet Singh Bedi </b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=amanbedi1\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/ron-debajyoti\"><img src=\"https://avatars.githubusercontent.com/u/22571664?v=4?s=100\" width=\"100px;\" alt=\"Debajyoti Halder\"/><br /><sub><b>Debajyoti Halder</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=ron-debajyoti\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Savio629\"><img src=\"https://avatars.githubusercontent.com/u/91362589?v=4?s=100\" width=\"100px;\" alt=\"Savio Dias\"/><br /><sub><b>Savio Dias</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=Savio629\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jonaslagoni\"><img src=\"https://avatars.githubusercontent.com/u/13396189?v=4?s=100\" width=\"100px;\" alt=\"Jonas Lagoni\"/><br /><sub><b>Jonas Lagoni</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=jonaslagoni\" title=\"Code\">💻</a> <a href=\"#ideas-jonaslagoni\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/cli/pulls?q=is%3Apr+reviewed-by%3Ajonaslagoni\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"https://github.com/asyncapi/cli/commits?author=jonaslagoni\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/KhudaDad414\"><img src=\"https://avatars.githubusercontent.com/u/32505158?v=4?s=100\" width=\"100px;\" alt=\"Khuda Dad Nomani\"/><br /><sub><b>Khuda Dad Nomani</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=KhudaDad414\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/cli/commits?author=KhudaDad414\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/smoya\"><img src=\"https://avatars.githubusercontent.com/u/1083296?v=4?s=100\" width=\"100px;\" alt=\"Sergio Moya \"/><br /><sub><b>Sergio Moya </b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=smoya\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Vishal2002\"><img src=\"https://avatars.githubusercontent.com/u/35897449?v=4?s=100\" width=\"100px;\" alt=\"Vishal Sharma\"/><br /><sub><b>Vishal Sharma</b></sub></a><br /><a href=\"https://github.com/asyncapi/cli/commits?author=Vishal2002\" title=\"Code\">💻</a></td>\n    </tr>\n  </tbody>\n</table>\n\n<!-- markdownlint-restore -->\n<!-- prettier-ignore-end -->\n\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n"
  },
  {
    "path": "action-template.yml",
    "content": "name: 'AsyncAPI CLI Action'\ndescription: 'One stop solution for all your AsyncAPI Specification needs in github actions.'\ninputs:\n  cli_version:\n    description: 'Version of AsyncAPI CLI to be used. This is only needed if you want to test with a specific version of AsyncAPI CLI. Default is latest which is also the recommended option.'\n    required: false\n    default: ''\n  command:\n    description: 'Command to run. Available commands in action :- generate, validate, convert, optimize and custom. Default is generate. For custom command, provide the whole command as input. List of available commands can be found in https://www.asyncapi.com/docs/tools/cli/usage.'\n    required: false\n    default: 'generate'\n  filepath:\n    description: 'Path to AsyncAPI document. This input is required if command is set to generate, validate, convert or optimize. Default is ./asyncapi.yaml'\n    required: false\n    default: 'asyncapi.yml'\n  template:\n    description: 'Template for the generator. Official templates are listed here https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate. You can pass template as npm package, url to git repository, link to tar file or local template.'\n    default: '@asyncapi/markdown-template@2.0.0'\n    required: false\n  language:\n    description: 'Language of the generated code. This input is required if you want to generate models. List of available languages can be found in https://www.asyncapi.com/docs/tools/cli/usage#asyncapi-generate-models-language-file'\n    required: false\n    default: ''\n  output:\n    description: 'Directory where to put the generated files. Can be used only with generate or convert commands. Default is output.'\n    required: false\n    default: 'output'\n  parameters:\n    description: 'The command that you use might support and even require specific parameters to be passed to the CLI for the generation. Template parameters should be preceded by -p'\n    required: false\n    default: ''\n  custom_command:\n    description: 'Custom command to be run. This input is required if command is set to custom.'\n    required: false\n    default: ''\n\nruns:\n  using: 'docker'\n  # This is the image that will be used to run the action.\n  # IMPORTANT: The version has to be changed manually in your PRs.\n  image: 'docker://asyncapi/github-action-for-cli:${ version }'\n  args:\n    - ${{ inputs.cli_version }}\n    - ${{ inputs.command }}\n    - ${{ inputs.filepath }}\n    - ${{ inputs.template }}\n    - ${{ inputs.language }}\n    - ${{ inputs.output }}\n    - ${{ inputs.parameters }}\n    - ${{ inputs.custom_command }}\n\nbranding:\n  icon: 'file-text'\n  color: purple\n"
  },
  {
    "path": "action.yml",
    "content": "name: 'AsyncAPI CLI Action'\ndescription: 'One stop solution for all your AsyncAPI Specification needs in github actions.'\ninputs:\n  cli_version:\n    description: 'Version of AsyncAPI CLI to be used. This is only needed if you want to test with a specific version of AsyncAPI CLI. Default is latest which is also the recommended option.'\n    required: false\n    default: ''\n  command:\n    description: 'Command to run. Available commands in action :- generate, validate, convert, optimize and custom. Default is generate. For custom command, provide the whole command as input. List of available commands can be found in https://www.asyncapi.com/docs/tools/cli/usage.'\n    required: false\n    default: 'generate'\n  filepath:\n    description: 'Path to AsyncAPI document. This input is required if command is set to generate, validate, convert or optimize. Default is ./asyncapi.yaml'\n    required: false\n    default: 'asyncapi.yml'\n  template:\n    description: 'Template for the generator. Official templates are listed here https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate. You can pass template as npm package, url to git repository, link to tar file or local template.'\n    default: '@asyncapi/markdown-template@2.0.0'\n    required: false\n  language:\n    description: 'Language of the generated code. This input is required if you want to generate models. List of available languages can be found in https://www.asyncapi.com/docs/tools/cli/usage#asyncapi-generate-models-language-file'\n    required: false\n    default: ''\n  output:\n    description: 'Directory where to put the generated files. Can be used only with generate or convert commands. Default is output.'\n    required: false\n    default: 'output'\n  parameters:\n    description: 'The command that you use might support and even require specific parameters to be passed to the CLI for the generation. Template parameters should be preceded by -p'\n    required: false\n    default: ''\n  custom_command:\n    description: 'Custom command to be run. This input is required if command is set to custom.'\n    required: false\n    default: ''\n\nruns:\n  using: 'docker'\n  # This is the image that will be used to run the action.\n  # IMPORTANT: The version has to be changed manually in your PRs.\n  image: 'docker://asyncapi/github-action-for-cli:6.0.0'\n  args:\n    - ${{ inputs.cli_version }}\n    - ${{ inputs.command }}\n    - ${{ inputs.filepath }}\n    - ${{ inputs.template }}\n    - ${{ inputs.language }}\n    - ${{ inputs.output }}\n    - ${{ inputs.parameters }}\n    - ${{ inputs.custom_command }}\n\nbranding:\n  icon: 'file-text'\n  color: purple\n"
  },
  {
    "path": "assets/create-template/templates/default/asyncapi.yaml",
    "content": "asyncapi: 3.1.0\ninfo:\n  title: Temperature Service\n  version: 1.0.0\n  description: This service is in charge of processing all the events related to temperature.\n\nservers:\n  dev:\n    url: test.mosquitto.org\n    protocol: mqtt\n\nchannels:\n  temperature/changed:\n    description: Updates the bedroom temperature in the database when the temperature drops or goes up.\n    publish:\n      operationId: temperatureChange\n      message:\n        description: Message that is being sent when the temperature in the bedroom changes.\n        contentType: application/json\n        payload:\n          type: object\n          additionalProperties: false\n          properties:\n            temperatureId:\n              type: string\n\ncomponents:\n  schemas:\n    temperatureId:\n      type: object\n      additionalProperties: false\n      properties:\n        temperatureId:\n          type: string\n"
  },
  {
    "path": "assets/create-template/templates/default/package.json",
    "content": "{\n  \"name\": \"myTemplate\",\n  \"generator\": {\n    \"renderer\": \"react\",\n    \"supportedProtocols\": []\n  },\n  \"dependencies\": {\n    \"@asyncapi/generator-react-sdk\": \"^1.1.2\"\n  }\n}\n"
  },
  {
    "path": "assets/create-template/templates/default/readme.md",
    "content": "### First install all the dependencies for template using below command:\nnpm install \n### Run the template using for a specific asyncapi document \n asyncapi generate fromTemplate <templateName> ../asyncapi-template "
  },
  {
    "path": "assets/create-template/templates/default/template/index.js",
    "content": "import { File, Text } from '@asyncapi/generator-react-sdk';\n\n// Pass the others parameters to get the specificatin of the asyncapi document\nexport default function ({ asyncapi }) {\n  return (\n    <File name=\"asyncapi.md\">\n      <Text>My application's markdown file.</Text>\n      <Text>App name: **{asyncapi.info().title()}**</Text>\n    </File>\n  );\n}\n"
  },
  {
    "path": "assets/examples/default-example.json",
    "content": "{\n    \"asyncapi\": \"3.1.0\",\n    \"info\": {\n      \"title\": \"Account Service\",\n      \"version\": \"1.0.0\",\n      \"description\": \"This service is in charge of processing user signups\"\n    },\n    \"channels\": {\n      \"userSignedUp\": {\n        \"address\": \"user/signedup\",\n        \"messages\": {\n          \"UserSignedUp\": {\n            \"$ref\": \"#/components/messages/UserSignedUp\"\n          }\n        }\n      }\n    },\n    \"operations\": {\n      \"onUserSignUp\": {\n        \"action\": \"receive\",\n        \"channel\": {\n          \"$ref\": \"#/channels/userSignedUp\"\n        },\n        \"messages\": [\n          {\n            \"$ref\": \"#/channels/userSignedUp/messages/UserSignedUp\"\n          }\n        ]\n      }\n    },\n    \"components\": {\n      \"messages\": {\n        \"UserSignedUp\": {\n          \"payload\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"displayName\": {\n                \"type\": \"string\",\n                \"description\": \"Name of the user\"\n              },\n              \"email\": {\n                \"type\": \"string\",\n                \"format\": \"email\",\n                \"description\": \"Email of the user\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }"
  },
  {
    "path": "assets/examples/default-example.yaml",
    "content": "asyncapi: 3.1.0\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\nchannels:\n  userSignedUp:\n    address: user/signedup\n    messages:\n      UserSignedUp:\n        $ref: '#/components/messages/UserSignedUp'\noperations:\n  onUserSignUp:\n    action: receive\n    channel:\n      $ref: '#/channels/userSignedUp'\n    messages:\n      - $ref: '#/channels/userSignedUp/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user"
  },
  {
    "path": "bin/dev",
    "content": "#!/usr/bin/env node\n\nconst oclif = require('@oclif/core')\n\nconst path = require('path')\nconst project = path.join(__dirname, '..', 'tsconfig.json')\n\n// In dev mode -> use ts-node and dev plugins\nprocess.env.NODE_ENV = 'development'\n\nrequire('ts-node').register({project})\n\n// In dev mode, always show stack traces\noclif.settings.debug = true;\n\n// Start the CLI\noclif.run().then(oclif.flush).catch(oclif.Errors.handle)\n"
  },
  {
    "path": "bin/dev.cmd",
    "content": "@echo off\n\nnode \"%~dp0\\dev\" %*"
  },
  {
    "path": "bin/run",
    "content": "#!/usr/bin/env node\n\nprocess.env.NODE_ENV = 'development';\n\nconst oclif = require('@oclif/core');\n\noclif.run()\n  .then(require('@oclif/core/flush'))\n  .catch((err) => {\n    const { handle } = require('@oclif/core/handle');\n    return handle(err);\n  });\n"
  },
  {
    "path": "bin/run.cmd",
    "content": "@echo off\n\nnode \"%~dp0\\run\" %*\n"
  },
  {
    "path": "bin/run_bin",
    "content": "#!/usr/bin/env node\n\n// Only the binary installed through NPM is considered production environment. See \"bin\" in package.json.\nprocess.env.NODE_ENV = 'production';\n\nconst oclif = require('@oclif/core');\n\noclif.run()\n  .then(require('@oclif/core/flush'))\n  .catch((err) => {\n    const { handle } = require('@oclif/core/handle');\n    return handle(err);\n  });\n"
  },
  {
    "path": "bin/run_bin.cmd",
    "content": "@echo off\n\nnode \"%~dp0\\run_bin\" %*\n"
  },
  {
    "path": "docs/architecture.md",
    "content": "---\ntitle: 'CLI Architecture'\nweight: 40\n---\n\n# CLI Architecture\n\n## Overview\n\nThe AsyncAPI CLI is built with [oclif](https://oclif.io/) and provides both command-line operations and a REST API server for working with AsyncAPI specifications.\n\n---\n\n## Architecture Diagram\n\n```\n┌─────────────────────────────────────────────────┐\n│  Entry Points                                   │\n│  ┌──────────┐              ┌──────────┐         │\n│  │   CLI    │              │   API    │         │\n│  │  (oclif) │              │ (Express)│         │\n│  └────┬─────┘              └────┬─────┘         │\n└───────┼─────────────────────────┼───────────────┘\n        └───────────┬─────────────┘\n                    ▼\n        ┌───────────────────────┐\n        │   Domain Services     │\n        │  Validation, Generator│\n        │  Convert, Config      │\n        └───────────┬───────────┘\n                    ▼\n        ┌───────────────────────┐\n        │   Domain Models       │\n        │  Specification,Context│\n        └───────────┬───────────┘\n                    ▼\n        ┌───────────────────────┐\n        │   Utilities           │\n        │  Logger, Helpers      │\n        └───────────────────────┘\n```\n\n---\n\n## Directory Structure\n\n```\nsrc/\n├── apps/\n│   ├── cli/              # CLI commands & internals\n│   └── api/              # REST API (Express)\n├── domains/\n│   ├── models/           # Specification, Context\n│   └── services/         # Business logic\n├── errors/               # Custom errors\n├── interfaces/           # TypeScript types\n└── utils/                # Utilities\n```\n\n---\n\n## Core Components\n\n### CLI Application\n\n| Component | Description |\n|-----------|-------------|\n| **Entry Points** | `bin/run` (dev), `bin/run_bin` (prod) |\n| **Base Command** | Metrics, parser integration, error handling |\n\n**Commands:**\n- **Core:** `validate`, `convert`, `format`, `optimize`, `diff`, `bundle`\n- **Generation:** `generate client`, `generate models`, `generate fromTemplate`\n- **Config:** `config context`, `config analytics`, `config versions`\n- **Utility:** `new file`, `new template`, `start api|studio|preview`, `pretty`\n\n### API Server\n\n**Endpoints:** `/v1/validate`, `/v1/parse`, `/v1/generate`, `/v1/convert`, `/v1/bundle`, `/v1/diff`, `/v1/docs`, `/v1/help`, `/v1/version`\n\n**Features:** Express with Helmet security, CORS, compression, RFC 7807 error responses\n\n### Domain Services\n\nAll services extend `BaseService` and return `ServiceResult<T>`:\n\n| Service | Purpose |\n|---------|---------|\n| `ValidationService` | Validates specs with Spectral, calculates scores |\n| `GeneratorService` | Generates code/models |\n| `ConvertService` | Converts between AsyncAPI/OpenAPI formats |\n| `ConfigService` | Manages CLI config and contexts |\n| `ArchiverService` | Creates ZIP archives |\n\n### Domain Models\n\n| Model | Purpose |\n|-------|---------|\n| **Specification** | Loads from file, URL, or context; auto-detects `asyncapi.json\\|yml\\|yaml` |\n| **Context** | Manages multiple AsyncAPI contexts; stored in `~/.asyncapi/` |\n\n### Error Classes\n\n`ContextError`, `SpecificationFileError`, `ValidationError`, `GeneratorError`, `DiffError`\n\n---\n\n## Execution Flow\n\n**CLI Command:**\n```\nUser Command → oclif → Base Command → Domain Service → ServiceResult\n```\n\n**API Request:**\n```\nHTTP Request → Express → Controller → Domain Service → HTTP Response\n```\n\n---\n\n## Extension Points\n\n| Add | Steps |\n|-----|-------|\n| **New Command** | Create in `src/apps/cli/commands/`, extend `Command`, implement `run()` |\n| **New API Endpoint** | Create controller in `src/apps/api/controllers/`, register in `index.ts` |\n| **New Service** | Create in `src/domains/services/`, extend `BaseService`, return `ServiceResult<T>` |\n\n---\n\n## Configuration\n\n| Config | Location |\n|--------|----------|\n| CLI Context | `~/.asyncapi/contexts.json`, `~/.asyncapi/.current` |\n| Analytics | `~/.asyncapi-analytics` |\n\n**Environment Variables:**\n- `NODE_ENV` — `development` | `production` | `test`\n- `PORT` — API server port (default: 3000)\n- `ASYNCAPI_METRICS_*` — Metrics configuration\n\n---\n\n## Technology Stack\n\n| Category | Technologies |\n|----------|--------------|\n| **Core** | oclif, TypeScript, Express |\n| **AsyncAPI** | @asyncapi/parser, generator, converter, bundler, diff, optimizer |\n| **Supporting** | winston, ajv, chalk, @clack/prompts |\n"
  },
  {
    "path": "docs/autocompleteEnabled.md",
    "content": "---\ntitle: 'Auto-complete setup'\nweight: 30\n---\n\n# AsyncAPI CLI Autocomplete Setup\n\nThis guide provides steps to enable autocomplete for the AsyncAPI CLI. The setup supports `zsh`, `bash`, and `PowerShell` (manual setup only).\n\n## Automatic Setup (Post-Install Script)\nThe AsyncAPI CLI includes a post-install script that automatically configures autocomplete for supported shells (`zsh` and `bash`). No additional steps are required.\n\n### Steps:\n1. Ensure that AsyncAPI CLI is installed. You can verify by running:\n   ```sh\n   asyncapi --version\n   ```\n   If the command fails, install it using:\n   ```sh\n   npm install -g @asyncapi/cli\n   ```\n\n\n2. Apply the changes by running:\n   ```sh\n   source ~/.bashrc   # For bash\n   source ~/.zshrc    # For zsh\n   ```\n\n3. Verify autocomplete by typing:\n   ```sh\n   asyncapi <TAB>\n   ```\n   You should see command suggestions.\n\n## Manual Setup (For PowerShell and Troubleshooting)\nIf the automatic setup does not work or if you need to enable autocomplete manually (especially for PowerShell), follow these steps.\n\n### Steps:\n1. **Build the AsyncAPI CLI manually:**\n   If you are working with the CLI project locally, you need to build it first:\n   ```sh\n   npm install\n   npm run build\n   ```\n\n2. **Run the autocomplete command manually:**\n   ```sh\n   ./bin/run autocomplete  # Run this from the project root folder\n   ```\n\n3. **Locate the AsyncAPI CLI executable:**\n   Run the following command to find the executable path:\n   ```sh\n   which asyncapi   # For bash/zsh\n   Get-Command asyncapi | Select-Object -ExpandProperty Definition   # For PowerShell\n   ```\n   If the command does not return a path, ensure AsyncAPI CLI is installed.\n\n4. **Generate and apply the autocomplete script:**\n   Run the following command based on your shell:\n   ```sh\n   printf \"$(./bin/run autocomplete script bash)\" >> ~/.bashrc; source ~/.bashrc   # For bash\n   printf \"$(./bin/run autocomplete script zsh)\" >> ~/.zshrc; source ~/.zshrc       # For zsh\n   printf \"$(./bin/run autocomplete script powershell)\" >> $PROFILE; . $PROFILE    # For PowerShell\n   ```\n\n5. **Test autocomplete:**\n   ```sh\n   asyncapi <TAB>\n   ```\n   If it works, autocomplete is successfully enabled!\n\n---\n\nIf you encounter any issues, ensure that your shell configuration file is correctly updated and sourced. Restart your terminal if necessary.\n\n"
  },
  {
    "path": "docs/context.md",
    "content": "---\ntitle: 'Context concept'\nweight: 60\n---\n\n## Overview\n\nAsyncAPI CLI provides functionality called `context`. It's purpose is to help to work with AsyncAPI CLI in large projects where you do not have just one service exposing AsyncAPI document, but multiple.\n\nEvent driven architecture involves multiple actors, subscribers and publishers. One time you want to validate document **A** and the other time you want to generate models from document **B**. Every time you do it, you need to provide to AsyncAPI CLI the location of the AsyncAPI document, which might be time consuming. You can workaround it with aliases in bash profiles or with other solutions but it is better to use `context` feature, as you can then store it in your repository and share with other team members.\n\nIn short it means that for example instead of writing `asyncapi validate /some/folder/my-asyncapi.yml` you can create a context called `myasync` that will be an alias for and point to `/some/folder/my-asyncapi.yml`. This way next time you use the CLI you can do `asyncapi validate myasync`.\n\n## Context File location\n\nYou can have a global context for your workstation, and a project specific context.\n\nIf your use case is that you work with multiple repositories, you might want to use a global context. The `.asyncapi-cli` context file is then located in your home directory. You can also store your custom `.asyncapi-cli` file in your project with custom configuration. This way when you run `asyncapi config context add` inside your project, the new context is added to the context file under your project.\n\n## How to add context to a project\n\n### Manually\n  - Create file `.asyncapi-cli` containing [minimal empty context file](#minimalEmptyContextFile) in:\n    - current directory\n    - root of current repository\n    - user's home directory\n\n### Using CLI's `init` command\n\n`asyncapi config context init [CONTEXT-FILE-PATH]`\n\nWhere `[CONTEXT-FILE-PATH]` instructs CLI what directory should the file `.asyncapi-cli` containing [minimal empty context file](#minimalEmptyContextFile) be created in:\n  - current directory: `asyncapi config context init .` (default)\n  - root of current repository: `asyncapi config context init ./`\n  - user's home directory: `asyncapi config context init ~`\n  \n(if `[CONTEXT-FILE-PATH]` is omitted, empty context file is created in current directory)\n\nMake use of newly created `.asyncapi-cli` by executing command:\n\n`asyncapi config context add [CONTEXT-NAME] [SPEC-FILE-PATH]`\n\n### Setup example in a real project\n\nBelow you can see an example of context setup for [Event Driven Flight status notification service](https://github.com/amadeus4dev-examples/amadeus-async-flight-status/tree/ff433b6d320a3a6a2499976cbf0782353bc57c16) of the [Amadeus Airline Platform](https://amadeus.com/en/industries/airlines/airline-platform), with multiple microservices and their AsyncAPI documents.\n\n```bash\n# One-time initialization of '.asyncapi-cli' file\n(main)$ asyncapi config context init\nInitialized context /amadeus-async-flight-status/.asyncapi-cli\n\n# Adding first context\n(main)$ asyncapi config context add subscriber subscriber/asyncapi.yaml\nAdded context \"subscriber\".\nYou can set it as your current context: asyncapi config context use subscriber\nYou can use this context when needed by passing subscriber as a parameter: asyncapi validate subscriber\n\n# Adding more contexts\n(main)$ asyncapi config context add notifier notifier/asyncapi.yaml\nAdded context \"notifier\".\nYou can set it as your current context: asyncapi config context use notifier\nYou can use this context when needed by passing notifier as a parameter: asyncapi validate notifier\n\n(main)$ asyncapi config context add monitor monitor/asyncapi.yaml\nAdded context \"monitor\".\nYou can set it as your current context: asyncapi config context use monitor\nYou can use this context when needed by passing monitor as a parameter: asyncapi validate monitor\n\n# Setting monitor as default context\n(main)$ asyncapi config context use monitor\nmonitor is set as current\n\n# Now you do not even have to remember the context name, and default 'monitor/asyncapi.yaml' will be validated\n(main)$ asyncapi validate\nFile monitor/asyncapi.yaml is valid but has (itself and/or referenced documents) governance issues.\nmonitor/asyncapi.yaml\n  1:1       warning  asyncapi-defaultContentType      AsyncAPI document should have \"defaultContentType\" field.\n  1:1       warning  asyncapi-id                      AsyncAPI document should have \"id\" field.\n  1:1       warning  asyncapi2-tags                   AsyncAPI object should have non-empty \"tags\" array.\n  1:11  information  asyncapi-latest-version          The latest version of AsyncAPi is not used. It is recommended update to the \"2.6.0\" version.  asyncapi\n  2:6       warning  asyncapi-info-contact            Info object should have \"contact\" object.                                                     info\n 19:15      warning  asyncapi2-operation-operationId  Operation should have an \"operationId\" field defined.                                         channels.flight/update.subscribe\n 26:13      warning  asyncapi2-operation-operationId  Operation should have an \"operationId\" field defined.                                         channels.flight/queue.publish\n✖ 7 problems (0 errors, 6 warnings, 1 info, 0 hints)\n\n# You can now use context name when running AsyncAPI commands, no need to remember file location like 'notifier/asyncapi.yaml'\n(main)$ asyncapi validate notifier\nFile notifier/asyncapi.yaml is valid but has (itself and/or referenced documents) governance issues.\nnotifier/asyncapi.yaml\n  1:1       warning  asyncapi-defaultContentType      AsyncAPI document should have \"defaultContentType\" field.\n  1:1       warning  asyncapi-id                      AsyncAPI document should have \"id\" field.\n  1:1       warning  asyncapi2-tags                   AsyncAPI object should have non-empty \"tags\" array.\n  1:11  information  asyncapi-latest-version          The latest version of AsyncAPi is not used. It is recommended update to the \"2.6.0\" version.  asyncapi\n  2:6       warning  asyncapi-info-contact            Info object should have \"contact\" object.                                                     info\n 18:13      warning  asyncapi2-operation-operationId  Operation should have an \"operationId\" field defined.                                         channels.flight/update.publish\n✖ 6 problems (0 errors, 5 warnings, 1 info, 0 hints)\n\n# Switch default context \n(main)$ asyncapi config context use notifier\nnotifier is set as current\n\n# List all contexts\n(main)$ asyncapi config context list\nmonitor: monitor/asyncapi.yaml\nnotifier: notifier/asyncapi.yaml\nsubscriber: subscriber/asyncapi.yaml\n```\n\n## Context File structure\n\n### Fixed Fields\n\nField Name | Type | Description\n---|:---:|---\ncurrent | `string` | An optional string value representing one of context names, which is used as default in CLI. Default means you can run CLI commands without providing context name, like `asyncapi validate`, and it will run against the default - `current` - context.\nstore | [Store Object](#storeObject) | **REQUIRED**. Map of filesystem paths to target AsyncAPI documents.\n\n### <a name=\"storeObject\"></a>Store Object\n\nMap of filesystem paths to target AsyncAPI documents.\n\n**Patterned Fields**\n\nField Pattern | Type | Description\n---|:---:|---\n\\{contextName\\} | `string` | An optional string value representing filesystem path to the target AsyncAPI document.\n\n### <a name=\"minimalEmptyContextFile\"></a>Minimal Empty Context File\nRaw JSON:\n```\n{\n  \"store\": {}\n}\n```\nStringified JSON:\n```\n{\"store\":{}}\n```\n\n### Context File Example\n\nExample of a context file for [Event Driven Flight status notification service](https://github.com/amadeus4dev-examples/amadeus-async-flight-status/tree/ff433b6d320a3a6a2499976cbf0782353bc57c16) of the [Amadeus Airline Platform](https://amadeus.com/en/industries/airlines/airline-platform), with multiple microservices and their AsyncAPI documents:\n```\n{\n  \"current\": \"monitor\",\n  \"store\": {\n    \"monitor\": \"monitor/asyncapi.yaml\",\n    \"notifier\": \"notifier/asyncapi.yaml\",\n    \"subscriber\": \"subscriber/asyncapi.yaml\"\n  }\n}\n```\n\n## More context related CLI options\n\nAll commands for managing contexts are available under `asyncapi config context` [CLI commands group](usage#asyncapi-config-context).\n"
  },
  {
    "path": "docs/contributing-prs.md",
    "content": "---\ntitle: 'Contributing via Pull Requests'\nweight: 50\n---\n\n# Contributing via Pull Requests\n\n## Getting Started\n\n1. **Open an issue first** (unless it's a trivial fix)\n2. **Set up environment** — Follow [DEVELOPMENT.md](../DEVELOPMENT.md)\n3. **Create a branch** — Use prefixes: `feat/`, `fix/`, `docs/`, `refactor/`, `test/`, `chore/`\n\n---\n\n## PR Title Format\n\nFollow [Conventional Commits](https://www.conventionalcommits.org/):\n\n| Type | Description | Release |\n|------|-------------|---------|\n| `feat:` | New feature | MINOR |\n| `fix:` | Bug fix | PATCH |\n| `docs:` | Documentation | None |\n| `chore:` | Maintenance | None |\n| `test:` | Tests only | None |\n| `refactor:` | Code refactoring | None |\n\n**Breaking changes:** Add `!` → `feat!:`, `fix!:`\n\n**Examples:**\n- ✅ `feat: add AsyncAPI 3.0 validation support`\n- ✅ `fix: resolve context loading with special characters`\n- ❌ `Added new feature`\n- ❌ `fix bug`\n\n---\n\n## PR Checklist\n\n**Before submitting:**\n- [ ] Branch synced with `main`\n- [ ] `npm run build` passes\n- [ ] `npm run cli:test` passes\n- [ ] `npm run lint` passes (max 5 warnings)\n- [ ] Documentation updated (if needed)\n\n**Code quality:**\n- [ ] Follows existing code patterns\n- [ ] TypeScript types used (avoid `any`)\n- [ ] Error handling implemented\n- [ ] Tests added for new functionality\n- [ ] No `console.log` or commented code\n\n---\n\n## Code Standards\n\n| Area | Guideline |\n|------|-----------|\n| **TypeScript** | Explicit types, interfaces for objects, prefer `const` |\n| **Organization** | Follow existing structure, use path aliases (`@/`, `@cli/`, `@domains/`) |\n| **Errors** | Use custom errors from `src/errors/`, return `ServiceResult<T>` from services |\n| **Commands** | Extend base `Command`, use domain services for business logic |\n\n---\n\n## Testing\n\n**Add tests for:**\n- New commands or API endpoints\n- Bug fixes (regression tests)\n- New domain services\n- Complex business logic\n\n```bash\nnpm run cli:test      # All tests\nnpm run unit:test     # Unit tests only\n```\n\n---\n\n## Best Practices\n\n| ❌ Avoid | ✅ Do |\n|----------|-------|\n| Large PRs (>500 lines) | Small, focused PRs |\n| Multiple concerns in one PR | One issue per PR |\n| Skipping tests | Comprehensive tests |\n| Hardcoded values | Externalize configuration |\n| Force push to main | Rebase instead of merge |\n\n---\n\n## Review Process\n\n1. CI runs automated checks\n2. Maintainers review code\n3. Address feedback promptly\n4. PR merged when approved\n\n---\n\n## Quick Reference\n\n```bash\n# Setup\nnpm install && npx lefthook install\n\n# Before PR\nnpm run build && npm run lint && npm run cli:test\n```\n\n**Quality over speed** — Write good code, tests, and documentation.\n"
  },
  {
    "path": "docs/debugging-testing.md",
    "content": "---\ntitle: 'Debugging & Testing Guide'\nweight: 60\n---\n\n# Debugging & Testing Guide\n\nThis guide provides step-by-step instructions for debugging and testing the AsyncAPI CLI. Whether you're fixing a bug, adding a new feature, or understanding existing code, this document will help you navigate the debugging process effectively.\n\n## Table of Contents\n\n- [Project Structure Overview](#project-structure-overview)\n- [Setting Up Your Environment](#setting-up-your-environment)\n- [Debugging CLI Commands](#debugging-cli-commands)\n- [Debugging the API Server](#debugging-the-api-server)\n- [Debugging Services](#debugging-services)\n- [Writing Tests](#writing-tests)\n- [Running Tests](#running-tests)\n- [Common Issues & Solutions](#common-issues--solutions)\n- [Debugging Tools & Tips](#debugging-tools--tips)\n\n---\n\n## Project Structure Overview\n\nUnderstanding the codebase structure is essential for effective debugging:\n\n```\nsrc/\n├── apps/\n│   ├── api/           # REST API server (Express.js)\n│   │   ├── controllers/   # API endpoint handlers\n│   │   ├── middlewares/   # Request/response middleware\n│   │   └── exceptions/    # API error types\n│   └── cli/           # CLI application (oclif)\n│       ├── commands/      # CLI command implementations\n│       └── internal/      # Shared CLI utilities, flags, base classes\n├── domains/\n│   ├── models/        # Domain models (SpecificationFile, Context, etc.)\n│   └── services/      # Business logic services\n├── errors/            # Custom error classes\n├── utils/             # Utility functions\n└── interfaces/        # TypeScript interfaces\n\ntest/\n├── fixtures/          # Test data files (AsyncAPI specs, etc.)\n├── helpers/           # Test utilities\n├── integration/       # Integration tests for CLI commands\n└── unit/              # Unit tests for services and controllers\n    ├── controllers/   # API controller tests\n    ├── services/      # Service layer tests\n    └── utils/         # Utility function tests\n```\n\n---\n\n## Setting Up Your Environment\n\n### 1. Install Dependencies\n\n```bash\nnpm install\n```\n\n### 2. Build the Project\n\n```bash\nnpm run build\n```\n\n### 3. Set Up Environment Variables for Debugging\n\nCreate a `.env` file or export variables:\n\n```bash\n# Enable development mode (verbose logging)\nexport NODE_ENV=development\n\n# Disable analytics during testing\nexport TEST=1\n\n# Set custom context file for testing\nexport CUSTOM_CONTEXT_FILENAME=\"test.asyncapi-cli\"\nexport CUSTOM_CONTEXT_FILE_LOCATION=\"\"\n```\n\n---\n\n## Debugging CLI Commands\n\n### Method 1: Using `bin/run` (Development Mode)\n\nThe `bin/run` script runs the CLI in development mode with TypeScript directly:\n\n```bash\n# Run any command with debugging\n./bin/run validate ./path/to/asyncapi.yml\n\n# With verbose output\nDEBUG=* ./bin/run validate ./path/to/asyncapi.yml\n```\n\n### Method 2: Using Node.js Inspector\n\n```bash\n# Start with Node inspector\nnode --inspect-brk ./bin/run validate ./path/to/asyncapi.yml\n\n# Then open Chrome DevTools at: chrome://inspect\n```\n\n### Method 3: VS Code Debugging\n\nCreate `.vscode/launch.json`:\n\n```json\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Debug CLI Command\",\n      \"program\": \"${workspaceFolder}/bin/run\",\n      \"args\": [\"validate\", \"./test/fixtures/specification.yml\"],\n      \"env\": {\n        \"NODE_ENV\": \"development\",\n        \"TEST\": \"1\"\n      },\n      \"sourceMaps\": true,\n      \"outFiles\": [\"${workspaceFolder}/lib/**/*.js\"]\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Debug Current Test File\",\n      \"program\": \"${workspaceFolder}/node_modules/mocha/bin/_mocha\",\n      \"args\": [\n        \"--require\", \"ts-node/register\",\n        \"--require\", \"tsconfig-paths/register\",\n        \"--timeout\", \"100000\",\n        \"${file}\"\n      ],\n      \"env\": {\n        \"NODE_ENV\": \"development\",\n        \"TEST\": \"1\",\n        \"CUSTOM_CONTEXT_FILENAME\": \"test.asyncapi-cli\"\n      },\n      \"sourceMaps\": true\n    }\n  ]\n}\n```\n\n### Method 4: Adding Console Logs\n\nFor quick debugging, add logs in command files:\n\n```typescript\n// In src/apps/cli/commands/validate.ts\nasync run() {\n  const { args, flags } = await this.parse(Validate);\n  \n  // Debug: Log parsed arguments\n  console.log('DEBUG - Args:', JSON.stringify(args, null, 2));\n  console.log('DEBUG - Flags:', JSON.stringify(flags, null, 2));\n  \n  // ... rest of the command\n}\n```\n\n---\n\n## Debugging the API Server\n\n### Starting the API in Development Mode\n\n```bash\n# Start with hot-reload\nnpm run api:dev\n\n# Or manually with debugging\nNODE_ENV=development DEBUG=* node ./lib/apps/api/server.js\n```\n\n### Testing API Endpoints\n\n```bash\n# Validate endpoint\ncurl -X POST http://localhost:3000/v1/validate \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"asyncapi\": \"asyncapi: 3.1.0\\ninfo:\\n  title: Test\\n  version: 1.0.0\\nchannels: {}\"}'\n\n# Parse endpoint\ncurl -X POST http://localhost:3000/v1/parse \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"asyncapi\": \"...\"}'\n```\n\n### Debugging API Controllers\n\nAdd middleware logging:\n\n```typescript\n// In src/apps/api/middlewares/logger.middleware.ts\n// Logs are automatically enabled in development mode\n```\n\n---\n\n## Debugging Services\n\n### ValidationService\n\nThe `ValidationService` handles document validation. To debug:\n\n```typescript\n// Test the service directly\nimport { ValidationService } from '@services/validation.service';\nimport { load } from '@models/SpecificationFile';\n\nasync function debugValidation() {\n  const service = new ValidationService();\n  const specFile = await load('./path/to/spec.yml');\n  \n  const result = await service.validateDocument(specFile, {\n    'fail-severity': 'error',\n    'log-diagnostics': true,\n  });\n  \n  console.log('Validation Result:', JSON.stringify(result, null, 2));\n}\n```\n\n### ConversionService\n\n```typescript\nimport { ConversionService } from '@services/convert.service';\nimport { load } from '@models/SpecificationFile';\n\nasync function debugConversion() {\n  const service = new ConversionService();\n  const specFile = await load('./path/to/spec.yml');\n  \n  const result = await service.convertDocument(specFile, {\n    format: 'asyncapi',\n    'target-version': '3.0.0',\n    perspective: 'server',\n  });\n  \n  console.log('Conversion Result:', JSON.stringify(result, null, 2));\n}\n```\n\n### GeneratorService\n\n```typescript\nimport { GeneratorService } from '@services/generator.service';\n\nasync function debugGenerator() {\n  const service = new GeneratorService();\n  // Add your debugging logic\n}\n```\n\n---\n\n## Writing Tests\n\n### Test File Naming Convention\n\n- Unit tests: `test/unit/<category>/<name>.test.ts`\n- Integration tests: `test/integration/<command>.test.ts`\n\n### Unit Test Structure\n\n```typescript\n// test/unit/services/my-service.test.ts\nimport { expect } from 'chai';\nimport { MyService } from '../../../src/domains/services/my.service';\n\ndescribe('MyService', () => {\n  let service: MyService;\n\n  beforeEach(() => {\n    service = new MyService();\n  });\n\n  describe('methodName', () => {\n    it('should do something when given valid input', async () => {\n      // Arrange\n      const input = { /* test data */ };\n      \n      // Act\n      const result = await service.methodName(input);\n      \n      // Assert\n      expect(result.success).to.be.true;\n      expect(result.data).to.exist;\n    });\n\n    it('should handle errors gracefully', async () => {\n      // Arrange\n      const invalidInput = null;\n      \n      // Act\n      const result = await service.methodName(invalidInput);\n      \n      // Assert\n      expect(result.success).to.be.false;\n      expect(result.error).to.include('error message');\n    });\n  });\n});\n```\n\n### Integration Test Structure (CLI Commands)\n\n```typescript\n// test/integration/mycommand.test.ts\nimport { expect, test } from '@oclif/test';\nimport path from 'path';\n\nconst validSpec = path.resolve(__dirname, '../fixtures/specification.yml');\nconst invalidSpec = path.resolve(__dirname, '../fixtures/specification-invalid.yml');\n\ndescribe('mycommand', () => {\n  describe('with valid input', () => {\n    test\n      .stdout()\n      .command(['mycommand', validSpec])\n      .it('should succeed with valid specification', (ctx) => {\n        expect(ctx.stdout).to.contain('Success');\n      });\n  });\n\n  describe('with invalid input', () => {\n    test\n      .stderr()\n      .command(['mycommand', invalidSpec])\n      .exit(1)\n      .it('should fail with invalid specification', (ctx) => {\n        expect(ctx.stderr).to.contain('Error');\n      });\n  });\n\n  describe('with flags', () => {\n    test\n      .stdout()\n      .command(['mycommand', validSpec, '--output', 'result.json'])\n      .it('should handle output flag', (ctx) => {\n        expect(ctx.stdout).to.contain('saved');\n      });\n  });\n});\n```\n\n### API Controller Test Structure\n\n```typescript\n// test/unit/controllers/my.controller.test.ts\nimport request from 'supertest';\nimport { App } from '../../../src/apps/api/app';\nimport { MyController } from '../../../src/apps/api/controllers/my.controller';\n\ndescribe('MyController', () => {\n  let app: App;\n\n  beforeEach(async () => {\n    app = new App([new MyController()]);\n    await app.init();\n  });\n\n  describe('[POST] /v1/myendpoint', () => {\n    it('should return 200 with valid input', async () => {\n      return request(app.getServer())\n        .post('/v1/myendpoint')\n        .send({ data: 'valid' })\n        .expect(200)\n        .then((response) => {\n          expect(response.body).to.have.property('result');\n        });\n    });\n\n    it('should return 422 with invalid input', async () => {\n      return request(app.getServer())\n        .post('/v1/myendpoint')\n        .send({})\n        .expect(422);\n    });\n  });\n});\n```\n\n### Using Test Fixtures\n\n```typescript\nimport path from 'path';\nimport { load } from '@models/SpecificationFile';\n\n// Load test fixtures\nconst fixturesPath = path.resolve(__dirname, '../../fixtures');\n\nasync function loadTestSpec(filename: string) {\n  return load(path.join(fixturesPath, filename));\n}\n\n// Usage in tests\ndescribe('MyTest', () => {\n  it('should handle v3 spec', async () => {\n    const spec = await loadTestSpec('specification-v3.yml');\n    // ... test logic\n  });\n});\n```\n\n---\n\n## Running Tests\n\n### Run All Tests\n\n```bash\nnpm test\n```\n\n### Run Only CLI Tests\n\n```bash\nnpm run cli:test\n```\n\n### Run Only Unit Tests\n\n```bash\nnpm run unit:test\n```\n\n### Run a Single Test File\n\n```bash\nnpm run test:one -- test/integration/validate.test.ts\n```\n\n### Run Tests with Coverage\n\n```bash\nnpm run cli:test\n# Coverage report is generated in ./coverage/\n```\n\n### Run Tests in Watch Mode (Development)\n\n```bash\n# Using nodemon for file watching\nnpm run dev\n\n# Then run tests manually when needed\nnpm run unit:test\n```\n\n---\n\n## Common Issues & Solutions\n\n### Issue 1: \"Cannot find module '@utils/proxy'\"\n\n**Cause:** TypeScript path aliases not resolved.\n\n**Solution:**\n```bash\n# Rebuild the project\nnpm run build\n```\n\n### Issue 2: Permission Denied Errors\n\n**Cause:** Files created by root or different user.\n\n**Solution:**\n```bash\n# Fix permissions\nsudo chown -R $(whoami) ./lib ./node_modules ./.nyc_output\n```\n\n### Issue 3: Test Context File Conflicts\n\n**Cause:** Tests using the same context file as development.\n\n**Solution:**\n```bash\n# Set test-specific context file\nexport CUSTOM_CONTEXT_FILENAME=\"test.asyncapi-cli\"\nexport CUSTOM_CONTEXT_FILE_LOCATION=\"\"\n```\n\n### Issue 4: \"ECONNREFUSED\" in API Tests\n\n**Cause:** API server not started or wrong port.\n\n**Solution:**\n```bash\n# Ensure the app is initialized in tests\nconst app = new App([new MyController()]);\nawait app.init();  // Don't forget this!\n```\n\n### Issue 5: Async Test Timeouts\n\n**Cause:** Default timeout too short for async operations.\n\n**Solution:**\n```typescript\n// Increase timeout for specific tests\nit('should handle slow operation', async function() {\n  this.timeout(30000);  // 30 seconds\n  // ... test logic\n});\n```\n\n### Issue 6: ESLint Errors in Tests\n\n**Cause:** Using testing patterns that trigger lint rules.\n\n**Solution:**\n```typescript\n// Add eslint disable for specific patterns\n/* eslint-disable @typescript-eslint/no-unused-expressions */\nexpect(result).to.be.true;  // Chai assertions\n```\n\n---\n\n## Debugging Tools & Tips\n\n### 1. Enable Verbose Logging\n\n```bash\nDEBUG=* ./bin/run validate spec.yml\n```\n\n### 2. Use Node.js Inspector\n\n```bash\nnode --inspect-brk ./bin/run validate spec.yml\n# Open chrome://inspect in Chrome\n```\n\n### 3. Print Stack Traces\n\n```typescript\ntry {\n  // risky operation\n} catch (error) {\n  console.error('Stack trace:', error.stack);\n  throw error;\n}\n```\n\n### 4. Use TypeScript Source Maps\n\nEnsure `tsconfig.json` has:\n```json\n{\n  \"compilerOptions\": {\n    \"sourceMap\": true\n  }\n}\n```\n\n### 5. Debug Parser Output\n\n```typescript\nimport { Parser } from '@asyncapi/parser';\n\nconst parser = new Parser();\nconst { document, diagnostics } = await parser.parse(specContent);\n\nconsole.log('Parsed document:', JSON.stringify(document?.json(), null, 2));\nconsole.log('Diagnostics:', JSON.stringify(diagnostics, null, 2));\n```\n\n### 6. Inspect Service Results\n\nAll services return a `ServiceResult` type:\n```typescript\ninterface ServiceResult<T> {\n  success: boolean;\n  data?: T;\n  error?: string;\n}\n\n// Always check both success and data\nif (result.success && result.data) {\n  console.log('Success:', result.data);\n} else {\n  console.log('Error:', result.error);\n}\n```\n\n### 7. Test Commands Interactively\n\n```bash\n# Build and run immediately\nnpm run build && ./bin/run validate ./test/fixtures/specification.yml\n```\n\n---\n\n## Quick Reference\n\n| Task | Command |\n|------|---------|\n| Build project | `npm run build` |\n| Run all tests | `npm test` |\n| Run CLI tests | `npm run cli:test` |\n| Run unit tests | `npm run unit:test` |\n| Run single test | `npm run test:one -- <path>` |\n| Lint code | `npm run lint` |\n| Fix lint issues | `npm run lint:fix` |\n| Start API dev server | `npm run api:dev` |\n| Debug CLI command | `./bin/run <command> <args>` |\n| Debug with inspector | `node --inspect-brk ./bin/run <command>` |\n\n---\n\n## Getting Help\n\nIf you're still stuck:\n\n1. Check existing tests for similar functionality\n2. Look at the error messages and stack traces\n3. Search for similar issues in the [GitHub Issues](https://github.com/asyncapi/cli/issues)\n4. Ask in the [AsyncAPI Slack](https://asyncapi.com/slack-invite) `#tooling` channel\n\n"
  },
  {
    "path": "docs/github-action.md",
    "content": "---\ntitle: GitHub Action for CLI\nweight: 50\n---\n\nThis action exposes the [AsyncAPI CLI](https://github.com/asyncapi/cli). It allows you to generate documentation, validate AsyncAPI documents, convert between different AsyncAPI versions and much more. The source code of the action can be found [here](https://github.com/asyncapi/cli/tree/master/github-action)\n\n## Inputs\n\n### `cli_version`\n\nVersion of the AsyncAPI CLI you wish to use. You can find all available versions [here](https://github.com/asyncapi/cli/releases). Recommended leave it out of the inputs and use the default value.\n\n**Default** points to the`latest` version.\n\n> [!TIP]\n> We recommend to default to `latest` version. This way there is no overhead with the script updating the CLI version. As it takes a lot of time to update the CLI version, we recommend to update it only when you need to use another one for compatibility reasons.\n\n### `command`\n\nCommand that you wish to run. You can find all available commands Available commands are:\n- `generate` - generates documentation from AsyncAPI document\n- `validate` - validates AsyncAPI document\n- `optimize` - optimizes AsyncAPI document\n- `convert` - converts AsyncAPI document to another version\n- `custom` - allows you to run any command that is available in the AsyncAPI CLI. You can find all available commands [here](https://www.asyncapi.com/docs/tools/cli/usage).\n\n**Default** points to `generate` command.\n\n> [!IMPORTANT]\n> In case you want to use `custom` command, you need to pass an array of commands to the [`custom_command`](#custom_command) input. Although passing command is not required, it is recommended to pass it to avoid any issues later on.\n> For example, if you want to run `asyncapi bundle ./asyncapi.yaml --output final-asyncapi.yaml` you need to pass `\"bundle ./asyncapi.yaml --output final-asyncapi.yaml\" to the `custom_command` input.\n\n### `custom_command`\n\nIn case you want to use `custom` command you need to pass the command that you want to run in this input. You can find all available commands [here](https://www.asyncapi.com/docs/tools/cli/usage). \n\n**Default** points to '' (empty string).\n\nSample usage:\n\n```yaml\n- name: Generating HTML from my AsyncAPI document\n  uses: asyncapi/cli@v2.16.0# You can use any version you want\n  with:\n    custom_command: bundle ./asyncapi.yaml --output final-asyncapi.yaml\n```\n\n> [!CAUTION]\n> You have to pass the whole command as a string including the parameters and the command itself.\n> It will run like this: `asyncapi <custom_command>`\n\n\n### `filepath`\n\nPath to the AsyncAPI document that you want to process.   \n\n**Default** expects the AsyncAPI document to be in the root of the repository and named `asyncapi.yaml`.\n\n### `template`\n\nTemplate for the generator. Official templates are listed here https://github.com/asyncapi/generator#list-of-official-generator-templates. You can pass template as npm package, url to git repository, link to tar file or local template.\n\n**Default** points to `@asyncapi/markdown-template@2.0.0` template.\n\n> [!TIP]\n> We recommend to always specify the version of the template to not encounter any issues with the action in case of release of the template that is not compatible with given version of the generator.\n\n### `language`\n\nSpecifies the language to be used for the generated models. The value must be a valid language name supported by [modelina](https://github.com/asyncapi/modelina). \n\n**Default** is not set.\n\n> [!WARNING]\n> Either `language` or `template` must be set else an error will be thrown. \n> The action will return an error if the language is not supported by [modelina](https://github.com/asyncapi/modelina).\n\n### `output`\n\nPath to the output directory. Can be used for `generate` and `convert` commands.\n\n**Default** points to `output` directory in the root of the repository.\n\n### `parameters`\n\nThe command that you use might support and even require specific parameters to be passed to the CLI for the generation. You can find all available parameters [here](https://www.asyncapi.com/docs/tools/cli/usage).\n\n**Default** points to '' (empty string).\n\n> [!NOTE]\n> For template parameters, you need to pass them as `-p <template_parameters>` as can be seen in CLI documentation.\n\n\n## Example usage\n\n> [!WARNING]\n> Using `docker://asyncapi/github-action-for-cli` will not work as expected. This is because the GitHub Actions runner does not pass params to the docker image correctly. This is why we recommend to use `asyncapi/cli` instead.\n> However, you don't need to worry as it won't build the image every time. It will pull it from Docker Hub as it is already built there.\n\n### Basic\n\nIn case all defaults are fine for you, just add such step:\n\n```yaml\n- name: Generating Markdown from my AsyncAPI document\n  uses: asyncapi/cli@v2.16.0 # You can use any version you want\n```\n\n### Using all possible inputs\n\nIn case you do not want to use defaults, you for example want to use different template:\n\n```yaml\n- name: Generating HTML from my AsyncAPI document\n  uses: asyncapi/cli@v2.16.0 # You can use any version you want\n  with:\n    command: generate\n    filepath: ./docs/api/asyncapi.yaml\n    template: \"@asyncapi/html-template@0.9.0\" #In case of template from npm. Or can use a link.\n    output: ./generated-html\n    parameters: \"-p baseHref=/test-experiment/ sidebarOrganization=byTags\"\n```\n> [!IMPORTANT]\n> Note the usage of `-p` in `parameters` input. This is required for template parameters, unlike previous versions of this action as the action includes other commands than just `generate`.\n\n### Example workflow with publishing generated HTML to GitHub Pages\n\nIn case you want to validate your asyncapi file first, and also send generated HTML to GitHub Pages this is how full workflow could look like:\n\n```yaml\nname: AsyncAPI documents processing\n\non:\n  push:\n    branches: [ master ]\n\njobs:\n  generate:\n    runs-on: ubuntu-latest\n    steps:\n    #\"standard step\" where repo needs to be checked-out first\n    - name: Checkout repo\n      uses: actions/checkout@v2\n      \n    #In case you do not want to use defaults, you for example want to use different template\n    - name: Generating HTML from my AsyncAPI document\n      uses: asyncapi/cli@v2.16.0 # You can use any version you want\n      with:\n        template: '@asyncapi/html-template@0.9.0'  #In case of template from npm, because of @ it must be in quotes\n        filepath: docs/api/my-asyncapi.yml\n        parameters: -p baseHref=/test-experiment/ sidebarOrganization=byTags #space separated list of key/values\n        output: generated-html\n      \n    #Using another action that takes generated HTML and pushes it to GH Pages\n    - name: Deploy GH page\n      uses: JamesIves/github-pages-deploy-action@3.4.2\n      with:\n        ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        BRANCH: gh-pages\n        FOLDER: generated-html\n```\n\n### Example workflow for generating models\n\nIn case you want to use models generated from your AsyncAPI document, you can use this action to generate them and then use them in your workflow. This is how full workflow could look like:\n\n```yaml\n\nname: AsyncAPI documents processing\n\non:\n  push:\n    branches: [ master ]\n\njobs:\n  generate-models:\n    runs-on: ubuntu-latest\n    steps:\n    #\"standard step\" where repo needs to be checked-out first\n    - name: Checkout repo\n      uses: actions/checkout@v2\n      \n    - name: Generating models from my AsyncAPI document\n      uses: asyncapi/cli@v2.16.0 # You can use any version you want\n      with:\n        command: generate\n        filepath: docs/api/my-asyncapi.yml\n        language: typescript\n        output: generated-models\n```\n\n### Example workflow for validating AsyncAPI document changes\n\nIn case you want to validate your AsyncAPI document changes, you can use this action to validate them and then use them in your workflow. This is how full workflow could look like:\n\n```yaml\nname: Validate AsyncAPI document\n\non:\n  pull_request:\n    branches: [ master ]\n\njobs:\n  validate:\n    runs-on: ubuntu-latest\n    steps:\n    #\"standard step\" where repo needs to be checked-out first\n    - name: Checkout repo\n      uses: actions/checkout@v2\n      \n    - name: Validating AsyncAPI document\n      uses: asyncapi/cli@v2.16.0 # You can use any version you want\n      with:\n        command: validate\n        filepath: docs/api/my-asyncapi.yml\n```\n\n## Local dry run\n\nUse following commands to run and test github action locally:\n\n1. Build docker image of github action for cli\n\n  ```bash\n    npm run action:docker:build\n  ```\n\n2. Execute docker image with proper arguments\n\n  ```bash\n    docker run -e GITHUB_WORKSPACE=\"\" --workdir /action  -v \"/home/{user}/path/to/repo\":\"/action\" asyncapi/github-action-for-cli  \"\" \"generate\" \"github-action/test/asyncapi.yml\" \"@asyncapi/markdown-template@2.0.0\" \"\" \"output\" \"\" \"\"\n  ```\n\nMake sure to change the path of the repo and user in the command.\n\n## Troubleshooting\n\nYou can enable more log information in GitHub Action by adding `ACTIONS_STEP_DEBUG` secret to repository where you want to use this action. Set the value of this secret to `true` and you''ll notice more debug logs from this action."
  },
  {
    "path": "docs/index.md",
    "content": "---\ntitle: 'Introduction'\nweight: 20\n---\n\n\nThe AsyncAPI CLI is a command-line tool that provides a set of commands for working with AsyncAPI documents. AsyncAPI is a specification for describing asynchronous APIs, which allows developers to define the structure of messages exchanged between different parts of their applications. The AsyncAPI CLI simplifies creating, validating, bundling, and manipulating AsyncAPI documents, making it easier to work with asynchronous APIs.\n\n## Features\n\nThe AsyncAPI CLI offers the following key features:\n\n* Creation: New AsyncAPI documents can be created from scratch using the CLI, which is useful when starting a new project or creating a new version of an existing API.\n\n* Validation: AsyncAPI documents can be quickly and easily validated using the [AsyncAPI Parser](https://github.com/asyncapi/parser-js), which ensures that the documents conform to the AsyncAPI specification and catches errors early in the development process.\n\n* Conversion: The AsyncAPI CLI can convert AsyncAPI documents from one version to another, which is helpful for migrating APIs to a newer version of the AsyncAPI specification.\n\n* Difference: The AsyncAPI CLI can be used to find the differences between two AsyncAPI documents, which helps compare different versions of an API or identify changes made to an API.\n  \n* Generation: The AsyncAPI CLI leverages AsyncAPI libraries like [Generator](https://github.com/asyncapi/generator) and [Modelina](https://github.com/asyncapi/modelina), which allow you to generate various types of documentation, applications, and models in different programming languages. This feature can save significant time and effort when creating new APIs.\n\n* Optimize: Using [Optimizer](https://github.com/asyncapi/optimizer/), the AsyncAPI CLI can be used to optimize an AsyncAPI specification file which can optimize the structure of the AsyncAPI document to make it smaller and without repetition.\n\n* Start: The AsyncAPI CLI can be used to start [AsyncAPI Studio](https://studio.asyncapi.com/) locally, which the user can use to view, edit, and test AsyncAPI documents.\n  \nTo summarize, the AsyncAPI CLI offers the following features and process flow, as shown in the diagram below:\n\n```mermaid\ngraph TD;\nA[AsyncAPI Document]\nB[Creation]\nJ[Studio - Editor]\nI[Optimization]\nD[Validation]\nC[Generation]\nF[Apps/Docs]\nG[Models]\nH[Diff]\nK[Bundling]\nE[Conversion]\nA-->B;\nA-->D;\nA-->C;\nC-->F\nC-->G\nA-->H;\nA-->I;\nA-->J;\nA-->E;\nA-->K;\n```\n\n## CLI flow\n\nThe following flowchart illustrates the process flow of the AsyncAPI CLI:\n\n```mermaid\ngraph TD;\nA[Start] --> B[User runs the AsyncAPI CLI]\nB --> C[User issues a command]\nC --> D[CLI processes the command and runs the corresponding operation]\nD --> |Is the operation successful?| E{Yes}\nD --> |Is the operation recoverable?| F{Yes}\nE --> G[CLI returns the results of the operation to the user]\nF --> |Operation Error| H[CLI displays an error message and suggests possible next steps]\nG --> J[User receives the results]\nH --> I[User follows suggested steps to recover]\nI --> C[User reissues the corrected command]\nJ[User terminates the AsyncAPI CLI] --> K[End]\n```\n\nThis flowchart shows the high-level process that occurs when using the AsyncAPI CLI. The user starts by running a command (such as `validate`, `generate`, or `start`), which the CLI processes. The CLI then performs the corresponding operation (such as validating or generating an AsyncAPI document) and returns the results to the user. If an error occurs, the CLI displays an error message and suggests possible next steps for the user.\n"
  },
  {
    "path": "docs/installation.md",
    "content": "---\ntitle: 'Installation guide'\nweight: 20\n---\n\n## Node and npm\n\nTo use the AsyncAPI CLI tool, you must install NPM and Node.js version 16 or higher. To check if you already have both installed, run the following commands in your terminal:\n\n```sh\n# check if node is installed\nnode -v\n# or\nnode --version\n\n# check if NPM is installed\nnpm -v\n# or\nnpm --version\n```\n\nIf you don’t have Node.js or NPM installed, you can install both with this [Node.js package manager](https://nodejs.org/en/download/package-manager/).\n\nAfter installing Node.js and NPM, run the following command to install the AsyncAPI ClI globally:\n```sh\nnpm install -g @asyncapi/cli\n```\nTo enable the autocomplete feature in the CLI for the shells **bash and zshrc**, there is a script that will run automatically and autocomplete is only support for **bash and zshrc** for the **powershell** refer to manually enabling  [autocomplete](https://www.asyncapi.com/docs/tools/cli/autocompleteEnabled) guide in ClI:\n\nAfter the ClI installation :\n\nif the configuration is not present logs will be:\n```sh\n✅ Autocomplete configuration added to .zshrc.\n```\nIf the configuration is present for autocomplete logs:\n```sh\n✅ Autocomplete is already configured. Skipping addition.\n```\n\nTo refresh the variables:\n\n```sh\n source ~/.bashrc   # For bash\n source ~/.zshrc    # For zsh\n```\n\n## Docker\n\nInstall [Docker](https://docs.docker.com/get-docker/) first, then use docker to build the image using the following command :\n``` \ndocker build -t asyncapi/cli:latest . \n``` \nand run the image using the following command :\n\n```bash\ndocker run --rm -it \\\n--user=root \\\n-v [ASYNCAPI SPEC FILE LOCATION]:/app/asyncapi.yml \\\n-v [GENERATED FILES LOCATION]:/app/output \\\nasyncapi/cli [COMMAND HERE]\n\n# Example that you can run inside the cli directory after cloning this repository. First, you specify the mount in the location of your AsyncAPI specification file and then you mount it in the directory where the generation result should be saved.\ndocker run --rm -it \\\n   --user=root \\\n   -v ${PWD}/test/integration/fixtures/asyncapi_v1.yml:/app/asyncapi.yml \\\n   -v ${PWD}/output:/app/output \\\n   asyncapi/cli generate fromTemplate -o /app/output /app/asyncapi.yml @asyncapi/html-template --force-write\n```\nNote: Use ``` ` ``` instead of `\\` for Windows.\n\n\n## Mac\nThere are two ways to install the AsyncAPI CLI on your macOS: using the `brew` package manager or `pkg` files.\n\n### brew\n\nTo install the AsyncAPI CLI using the `brew` package manager, run the following commands in your terminal:\n```sh\n# Install brew\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n\n# Install AsyncAPI CLI\nbrew install asyncapi\n```\n\n### pkg\n\nEvery release of the AsyncAPI CLI has two macOS dedicated `pkg` file that enables you to install the CLI tool as a macOS application for x64 as well as arm64 architecture.\nTo download the latest CLI release, run this command in your terminal:\n```sh\n# For x64\ncurl -OL https://github.com/asyncapi/cli/releases/latest/download/asyncapi.x64.pkg\n\n# For arm64\ncurl -OL https://github.com/asyncapi/cli/releases/latest/download/asyncapi.arm64.pkg\n```\n\nTo download a specific CLI release, run this command in your terminal:\n```sh\ncurl -OL https://github.com/asyncapi/cli/releases/download/<replace this with the specific CLI version e.g v0.13.0>/asyncapi.pkg\n```\n\n<Remember>\nFollow this link for all <a href=\"https://github.com/asyncapi/cli/releases\">AsyncAPI CLI releases</a>.\n</Remember>\n\nAfter downloading the AsyncAPI CLI, install it via the following command:\n\n```sh\nsudo installer -pkg asyncapi.pkg -target /\n```\n\n## Windows \n\nThere are two ways to install the AsyncAPI CLI on your Windows operating system: using the `chocolatey` package manager or executable files.\n\n### Chocolatey\n\nPrerequisites:\n[Chocolatey](https://chocolatey.org/install) must be installed on your Windows operating system. The installation instructions can be found [here](https://docs.chocolatey.org/en-us/choco/setup#installing-chocolatey-cli).\n\nTo install the AsyncAPI CLI using the `chocolatey` package manager, run the following command in your terminal with administrator privileges:\n\n```sh\n# Install AsyncAPI CLI\nchoco install asyncapi\n```\n\nTo upgrade run this command:- \n```sh\n# Upgrade AsyncAPI CLI\nchoco upgrade asyncapi\n```\nTo install a specific version run this command:\n```sh\n# Install AsyncAPI CLI version xx.xx.xx\nchoco install asyncapi --version xx.xx.xx\n```\nAll the AsyncAPI CLI versions can be found [here](https://chocolatey.org/packages/asyncapi).\n\n### Executable files\n\nJust install the appropriate installer and simply follow the default installation steps to complete the installation process.\n\nDownload [asyncapi.x64.exe](https://github.com/asyncapi/cli/releases/latest/download/asyncapi.x64.exe) for 64-bit Windows and download [asyncapi.x86.exe](https://github.com/asyncapi/cli/releases/latest/download/asyncapi.x86.exe) for 32-bit Windows.\n\n\n## Linux\nSelecting the appropriate AsyncAPI CLI installation method on a Linux operating system depends on your Linux distro.\n\n### Debian based distros\n\nFor Debian based distros, you can install the AsycAPI CLI using the `dpkg` package manager for Debian.\n```sh\ncurl -OL https://github.com/asyncapi/cli/releases/latest/download/asyncapi.deb\n```\n\nTo download a specific release of the CLI, run this command in your terminal:\n```sh\ncurl -OL https://github.com/asyncapi/cli/releases/download/<replace this with the specific CLI version e.g v0.13.0>/asyncapi.deb\n```\n\n### Other distros\nYou can install the AsyncAPI CLI for other Linux distros using the archive `tar.gz` file. \n\n#### For Alpine Linux / musl-based systems:\nTo download the latest Alpine-compatible release, run this command in your terminal:\n```sh\ncurl -OL https://github.com/asyncapi/cli/releases/latest/download/asyncapi-alpine.tar.gz\n```\n\nTo download a specific Alpine-compatible release, run this command in your terminal:\n```sh\ncurl -OL https://github.com/asyncapi/cli/releases/download/<replace this with the specific CLI version e.g v0.13.0>/asyncapi-alpine.tar.gz\n```\n\nOnce downloaded, untar the file:\n```sh\ntar -xzf asyncapi-alpine.tar.gz\n```\n\n#### For other Linux distributions (glibc-based):\n\nTo download the latest release of the CLI, run this command in your terminal:\n```sh\ncurl -OL https://github.com/asyncapi/cli/releases/latest/download/asyncapi.tar.gz\n```\n\nTo download a specific release of the CLI, run this command in your terminal:\n```sh\ncurl -OL https://github.com/asyncapi/cli/releases/download/<replace this with the specific CLI version e.g v0.13.0>/asyncapi.tar.gz\n```\n\nOnce you have downloaded the archived file, untar it by running this command in your terminal:\n```sh\ntar -xzf asyncapi.tar.gz\n```\n\n### Setting up the symlink (for both Alpine and glibc versions):\n\nThe step above will create an `AsyncAPI` directory in the current path. To run the CLI from anywhere, you must create a `symlink`. If the current path you are on is `/user/local/bin`, for example, you must create the `symlink` in the `/user/local/bin` directory by following these steps:\n```sh\n# cd into the unarchived directory\ncd asyncapi\n\n# get the absolute path\npwd\n\n# Create a symlink\nln -s <absolute-path>/bin/asyncapi /user/local/bin/asyncapi\n\n# The \"asyncapi\" command should be available to be used\nasyncapi\n```\n> [!NOTE]\n> If youare using Alpine Linux or any musl-based distribution, make sure to download the `-alpine.tar.gz` version to avoid glibc compatibility issues. The regular `asyncapi.tar.gz` file is compiled for glibc-based systems and will not work on Alpine."
  },
  {
    "path": "docs/metrics_collection.md",
    "content": "---\ntitle: 'Metrics Collection'\nweight: 70\n---\n\n# Metrics collection guideline\n\nAsyncAPI **anonymously** tracks command executions to improve the specification and tools, ensuring no sensitive data reaches our servers. It aids in comprehending how AsyncAPI tools are used and adopted, facilitating ongoing improvements to our specifications and tools.\n\nEven though metrics collection is enabled by default, you can always [disable tracking](#how-to-disable-tracking) if you want to.\n\n## What we collect\nWe are collecting the following metrics:\n\n- `asyncapi_adoption.action.invoked`:\nWith this metric we are tracking the command executed on the CLI as soon as the command is invoked, so it has already been executed but not finished yet. We just want to know which commands are used, regardless they have failed or succeeded.\n\nExample of the data collected by this metric when the `validate` command has been executed:\n```\nasyncapi_adoption.action.invoked        COUNTER { action: 'validate' }  1\n```\n\n- `asyncapi_adoption.action.finished`:\nThis metric tracks the command executed once it has already finished, carrying the result of the execution and some metadata based on the AsyncAPI document in place.\n\nExample for `validate` command successfully executed and finished:\n```\nasyncapi_adoption.action.finished       COUNTER {\n  validation_result: 'valid',\n  success: true,\n  asyncapi_version: '2.6.0',\n  asyncapi_servers: 2,\n  asyncapi_channels: 4,\n  asyncapi_messages: 3,\n  asyncapi_operations_send: 3,\n  asyncapi_operations_receive: 1,\n  asyncapi_schemas: 52,\n  action: 'validate'\n  }       1\n```\n\n## Where the data is stored\nWe are making use of [New Relic API](https://docs.newrelic.com/docs/apis/intro-apis/introduction-new-relic-apis/#rest-api) to send the metrics collected to _New Relic_ servers, where they are stored, and finally visualized on the AsyncAPI website.\n\nMetrics won't be collected in CI environments, or when the \"CI\" env variable is set up to \"true\".\n\nThe analytics config file will be store by default at your home directory. In case you prefer to change the file path then you should set the `ASYNCAPI_METRICS_CONFIG_PATH` env var to any specific path value when running any command. For instance:\n````\nASYNCAPI_METRICS_CONFIG_PATH=/tmp/.asyncapi-analytics asyncapi config analytics --status\n````\n\n## How to disable tracking\nTo disable tracking, please run the following command:  \n`asyncapi config analytics --disable`\n\nOnce disabled, if you want to enable tracking back again then run:  \n`asyncapi config analytics --enable`\n\nIn case you do not know the current status of analytics, then you can append the \"--status\" flag to be aware of it:\n`asyncapi config analytics --status`\n\nRemember that keeping this tracking enabled will help AsyncAPI community to provide better specifications and tools in the future."
  },
  {
    "path": "docs/usage.md",
    "content": "---\ntitle: 'Usage'\nweight: 40\n---\n\n<!-- \n\nThis file is automatically generated from updateUsageDocs.js script. In package.json in line 158-161 lines the following steps has been executed in order to run this script successfully - \n\n* generate:readme:create: It creates the initial content for the README file by printing the usage and commands tags using printf and redirects the output to scripts/README.md file.\n* generate:readme:commands: It changes the directory to the scripts folder and executes the oclif readme command. This command generates the usage and commands sections based on the CLI commands and updates the content in the scripts/README.md file.\n* generate:assets: This script combines the two previously mentioned scripts (generate:readme:toc and generate:commands) to generate the necessary assets, such as the README file and usage documentation.\n* generate:commands: This script executes the following steps:\n - Runs the generate:readme:create script to create the initial content for the README file.\n - Executes the generate:readme:commands script to generate the usage and commands sections based on the CLI commands.\n - Runs the updateUsageDocs.js script using Node.js to update the usage documentation file with the contents of the generated README file.\n - Deletes the scripts/README.md file using the rimraf command.\n\n-->\n\nThe AsyncAPI CLI makes it easier to work with AsyncAPI documents.\n# Usage\n\n<!-- usage -->\n```sh-session\n$ npm install -g @asyncapi/cli\n$ asyncapi COMMAND\nrunning command...\n$ asyncapi (--version|--v)\n@asyncapi/cli/5.0.7 darwin-arm64 node-v24.7.0\n$ asyncapi --help [COMMAND]\nUSAGE\n  $ asyncapi COMMAND\n...\n```\n<!-- usagestop -->\n\n# Commands\n\n<!-- commands -->\n* [`asyncapi autocomplete [SHELL]`](#asyncapi-autocomplete-shell)\n* [`asyncapi bundle`](#asyncapi-bundle)\n* [`asyncapi config`](#asyncapi-config)\n* [`asyncapi config analytics`](#asyncapi-config-analytics)\n* [`asyncapi config auth add PATTERN TOKEN`](#asyncapi-config-auth-add-pattern-token)\n* [`asyncapi config context`](#asyncapi-config-context)\n* [`asyncapi config context add CONTEXT-NAME SPEC-FILE-PATH`](#asyncapi-config-context-add-context-name-spec-file-path)\n* [`asyncapi config context current`](#asyncapi-config-context-current)\n* [`asyncapi config context edit CONTEXT-NAME NEW-SPEC-FILE-PATH`](#asyncapi-config-context-edit-context-name-new-spec-file-path)\n* [`asyncapi config context init [CONTEXT-FILE-PATH]`](#asyncapi-config-context-init-context-file-path)\n* [`asyncapi config context list`](#asyncapi-config-context-list)\n* [`asyncapi config context remove CONTEXT-NAME`](#asyncapi-config-context-remove-context-name)\n* [`asyncapi config context use CONTEXT-NAME`](#asyncapi-config-context-use-context-name)\n* [`asyncapi config versions`](#asyncapi-config-versions)\n* [`asyncapi convert [SPEC-FILE]`](#asyncapi-convert-spec-file)\n* [`asyncapi diff OLD NEW`](#asyncapi-diff-old-new)\n* [`asyncapi format [SPEC-FILE]`](#asyncapi-format-spec-file)\n* [`asyncapi generate`](#asyncapi-generate)\n* [`asyncapi generate client LANGUAGE [ASYNCAPI]`](#asyncapi-generate-client-language-asyncapi)\n* [`asyncapi generate fromTemplate [ASYNCAPI] [TEMPLATE]`](#asyncapi-generate-fromtemplate-asyncapi-template)\n* [`asyncapi generate models LANGUAGE FILE`](#asyncapi-generate-models-language-file)\n* [`asyncapi new`](#asyncapi-new)\n* [`asyncapi new file`](#asyncapi-new-file)\n* [`asyncapi new template`](#asyncapi-new-template)\n* [`asyncapi optimize [SPEC-FILE]`](#asyncapi-optimize-spec-file)\n* [`asyncapi pretty SPEC-FILE`](#asyncapi-pretty-spec-file)\n* [`asyncapi start`](#asyncapi-start)\n* [`asyncapi start api`](#asyncapi-start-api)\n* [`asyncapi start preview SPEC-FILE`](#asyncapi-start-preview-spec-file)\n* [`asyncapi start studio [SPEC-FILE]`](#asyncapi-start-studio-spec-file)\n* [`asyncapi validate [SPEC-FILE]`](#asyncapi-validate-spec-file)\n\n## `asyncapi autocomplete [SHELL]`\n\nDisplay autocomplete installation instructions.\n\n```\nUSAGE\n  $ asyncapi autocomplete [SHELL] [-r]\n\nARGUMENTS\n  [SHELL]  (zsh|bash|powershell) Shell type\n\nFLAGS\n  -r, --refresh-cache  Refresh cache (ignores displaying instructions)\n\nDESCRIPTION\n  Display autocomplete installation instructions.\n\nEXAMPLES\n  $ asyncapi autocomplete\n\n  $ asyncapi autocomplete bash\n\n  $ asyncapi autocomplete zsh\n\n  $ asyncapi autocomplete powershell\n\n  $ asyncapi autocomplete --refresh-cache\n```\n\n_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.2.40/src/commands/autocomplete/index.ts)_\n\n## `asyncapi bundle`\n\nBundle one or multiple AsyncAPI Documents and their references together.\n\n```\nUSAGE\n  $ asyncapi bundle [-h] [-o <value>] [-b <value>] [-d <value>] [-x]\n\nFLAGS\n  -b, --base=<value>     Path to the file which will act as a base. This is required when some properties need to be\n                         overwritten.\n  -d, --baseDir=<value>  One relative/absolute path to directory relative to which paths to AsyncAPI Documents that\n                         should be bundled will be resolved.\n  -h, --help             Show CLI help.\n  -o, --output=<value>   The output file name. Omitting this flag the result will be printed in the console.\n  -x, --xOrigin          Pass this switch to generate properties \"x-origin\" that will contain historical values of\n                         dereferenced \"$ref\"s.\n\nDESCRIPTION\n  Bundle one or multiple AsyncAPI Documents and their references together.\n\nEXAMPLES\n  $ asyncapi bundle ./asyncapi.yaml > final-asyncapi.yaml\n\n  $ asyncapi bundle ./asyncapi.yaml --output final-asyncapi.yaml\n\n  $ asyncapi bundle ./asyncapi.yaml ./features.yaml\n\n  $ asyncapi bundle ./asyncapi.yaml ./features.yaml --base ./main.yaml\n\n  $ asyncapi bundle ./asyncapi.yaml ./features.yaml --base ./main.yaml --xOrigin\n\n  $ asyncapi bundle ./asyncapi.yaml -o final-asyncapi.yaml --base ../public-api/main.yaml --baseDir ./social-media/comments-service\n```\n\n_See code: [src/commands/bundle.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/bundle.ts)_\n\n## `asyncapi config`\n\nCLI config settings\n\n```\nUSAGE\n  $ asyncapi config\n\nDESCRIPTION\n  CLI config settings\n```\n\n_See code: [src/commands/config/index.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/index.ts)_\n\n## `asyncapi config analytics`\n\nEnable or disable analytics for metrics collection\n\n```\nUSAGE\n  $ asyncapi config analytics [-h] [-d] [-e] [-s]\n\nFLAGS\n  -d, --disable  disable analytics\n  -e, --enable   enable analytics\n  -h, --help     Show CLI help.\n  -s, --status   show current status of analytics\n\nDESCRIPTION\n  Enable or disable analytics for metrics collection\n```\n\n_See code: [src/commands/config/analytics.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/analytics.ts)_\n\n## `asyncapi config auth add PATTERN TOKEN`\n\nAdd an authentication config for resolving $ref files requiring HTTP Authorization.\n\n```\nUSAGE\n  $ asyncapi config auth add PATTERN TOKEN [-a <value>] [-h <value>...]\n\nARGUMENTS\n  PATTERN  Glob pattern for matching protected URLs (e.g. github.com/org/repo/**/*.*)\n  TOKEN    Authentication token or environment variable reference (prefix with $, e.g. $GITHUB_TOKEN)\n\nFLAGS\n  -a, --auth-type=<value>  Authentication type (default is \"Bearer\")\n  -h, --header=<value>...  Additional header in key=value format; can be used multiple times\n\nDESCRIPTION\n  Add an authentication config for resolving $ref files requiring HTTP Authorization.\n```\n\n_See code: [src/commands/config/auth/add.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/auth/add.ts)_\n\n## `asyncapi config context`\n\nManage short aliases for full paths to AsyncAPI documents\n\n```\nUSAGE\n  $ asyncapi config context\n\nDESCRIPTION\n  Manage short aliases for full paths to AsyncAPI documents\n```\n\n_See code: [src/commands/config/context/index.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/index.ts)_\n\n## `asyncapi config context add CONTEXT-NAME SPEC-FILE-PATH`\n\nAdd a context to the store\n\n```\nUSAGE\n  $ asyncapi config context add CONTEXT-NAME SPEC-FILE-PATH [-h] [-s]\n\nARGUMENTS\n  CONTEXT-NAME    context name\n  SPEC-FILE-PATH  file path of the spec file\n\nFLAGS\n  -h, --help         Show CLI help.\n  -s, --set-current  Set context being added as the current context\n\nDESCRIPTION\n  Add a context to the store\n```\n\n_See code: [src/commands/config/context/add.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/add.ts)_\n\n## `asyncapi config context current`\n\nShows the current context that is being used\n\n```\nUSAGE\n  $ asyncapi config context current [-h]\n\nFLAGS\n  -h, --help  Show CLI help.\n\nDESCRIPTION\n  Shows the current context that is being used\n```\n\n_See code: [src/commands/config/context/current.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/current.ts)_\n\n## `asyncapi config context edit CONTEXT-NAME NEW-SPEC-FILE-PATH`\n\nEdit a context in the store\n\n```\nUSAGE\n  $ asyncapi config context edit CONTEXT-NAME NEW-SPEC-FILE-PATH [-h]\n\nARGUMENTS\n  CONTEXT-NAME        context name\n  NEW-SPEC-FILE-PATH  file path of the spec file\n\nFLAGS\n  -h, --help  Show CLI help.\n\nDESCRIPTION\n  Edit a context in the store\n```\n\n_See code: [src/commands/config/context/edit.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/edit.ts)_\n\n## `asyncapi config context init [CONTEXT-FILE-PATH]`\n\nInitialize context\n\n```\nUSAGE\n  $ asyncapi config context init [CONTEXT-FILE-PATH] [-h]\n\nARGUMENTS\n  [CONTEXT-FILE-PATH]  Specify directory in which context file should be created:\n                       - current directory          : asyncapi config context init .(default)\n                       - root of current repository : asyncapi config context init ./\n                       - user's home directory      : asyncapi config context init ~`\n\nFLAGS\n  -h, --help  Show CLI help.\n\nDESCRIPTION\n  Initialize context\n```\n\n_See code: [src/commands/config/context/init.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/init.ts)_\n\n## `asyncapi config context list`\n\nList all the stored contexts in the store\n\n```\nUSAGE\n  $ asyncapi config context list [-h]\n\nFLAGS\n  -h, --help  Show CLI help.\n\nDESCRIPTION\n  List all the stored contexts in the store\n```\n\n_See code: [src/commands/config/context/list.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/list.ts)_\n\n## `asyncapi config context remove CONTEXT-NAME`\n\nDelete a context from the store\n\n```\nUSAGE\n  $ asyncapi config context remove CONTEXT-NAME [-h]\n\nARGUMENTS\n  CONTEXT-NAME  Name of the context to delete\n\nFLAGS\n  -h, --help  Show CLI help.\n\nDESCRIPTION\n  Delete a context from the store\n```\n\n_See code: [src/commands/config/context/remove.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/remove.ts)_\n\n## `asyncapi config context use CONTEXT-NAME`\n\nSet a context as current\n\n```\nUSAGE\n  $ asyncapi config context use CONTEXT-NAME [-h]\n\nARGUMENTS\n  CONTEXT-NAME  name of the saved context\n\nFLAGS\n  -h, --help  Show CLI help.\n\nDESCRIPTION\n  Set a context as current\n```\n\n_See code: [src/commands/config/context/use.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/context/use.ts)_\n\n## `asyncapi config versions`\n\nShow versions of AsyncAPI tools used\n\n```\nUSAGE\n  $ asyncapi config versions [-h]\n\nFLAGS\n  -h, --help  Show CLI help.\n\nDESCRIPTION\n  Show versions of AsyncAPI tools used\n```\n\n_See code: [src/commands/config/versions.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/config/versions.ts)_\n\n## `asyncapi convert [SPEC-FILE]`\n\nConvert asyncapi documents older to newer versions or OpenAPI documents to AsyncAPI\n\n```\nUSAGE\n  $ asyncapi convert [SPEC-FILE] -f openapi|asyncapi [-h] [-o <value>] [-t <value>] [-p client|server]\n    [--proxyHost <value>] [--proxyPort <value>]\n\nARGUMENTS\n  [SPEC-FILE]  spec path, url, or context-name\n\nFLAGS\n  -f, --format=<option>         (required) [default: asyncapi] Specify the format to convert from (openapi or asyncapi)\n                                <options: openapi|asyncapi>\n  -h, --help                    Show CLI help.\n  -o, --output=<value>          path to the file where the result is saved\n  -p, --perspective=<option>    [default: server] Perspective to use when converting OpenAPI to AsyncAPI (client or\n                                server). Note: This option is only applicable for OpenAPI to AsyncAPI conversions.\n                                <options: client|server>\n  -t, --target-version=<value>  [default: 3.1.0] asyncapi version to convert to\n      --proxyHost=<value>       Name of the ProxyHost\n      --proxyPort=<value>       Port number number for the proxyHost.\n\nDESCRIPTION\n  Convert asyncapi documents older to newer versions or OpenAPI documents to AsyncAPI\n```\n\n_See code: [src/commands/convert.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/convert.ts)_\n\n## `asyncapi diff OLD NEW`\n\nFind diff between two asyncapi files\n\n```\nUSAGE\n  $ asyncapi diff OLD NEW [-h] [-f json|yaml|yml|md] [-t breaking|non-breaking|unclassified|all]\n    [--markdownSubtype json|yaml|yml] [-o <value>] [--no-error] [-w] [--log-diagnostics] [--diagnostics-format\n    json|stylish|junit|html|text|teamcity|pretty|github-actions|sarif|code-climate|gitlab|markdown] [--fail-severity\n    error|warn|info|hint] [-s <value>]\n\nARGUMENTS\n  OLD  old spec path, URL or context-name\n  NEW  new spec path, URL or context-name\n\nFLAGS\n  -f, --format=<option>              [default: yaml] format of the output\n                                     <options: json|yaml|yml|md>\n  -h, --help                         Show CLI help.\n  -o, --overrides=<value>            path to JSON file containing the override properties\n  -s, --save-output=<value>          The output file name. Omitting this flag the result will be printed in the console.\n  -t, --type=<option>                [default: all] type of the output\n                                     <options: breaking|non-breaking|unclassified|all>\n  -w, --watch                        Enable watch mode\n      --diagnostics-format=<option>  [default: stylish] format to use for validation diagnostics\n                                     <options: json|stylish|junit|html|text|teamcity|pretty|github-actions|sarif|code-cl\n                                     imate|gitlab|markdown>\n      --fail-severity=<option>       [default: error] diagnostics of this level or above will trigger a failure exit\n                                     code\n                                     <options: error|warn|info|hint>\n      --[no-]log-diagnostics         log validation diagnostics or not\n      --markdownSubtype=<option>     the format of changes made to AsyncAPI document. It works only when diff is\n                                     generated using md type. For example, when you specify subtype as json, then diff\n                                     information in markdown is dumped as json structure.\n                                     <options: json|yaml|yml>\n      --no-error                     don't show error on breaking changes\n\nDESCRIPTION\n  Find diff between two asyncapi files\n```\n\n_See code: [src/commands/diff.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/diff.ts)_\n\n## `asyncapi format [SPEC-FILE]`\n\nConvert asyncapi documents from any format to yaml, yml or JSON\n\n```\nUSAGE\n  $ asyncapi format [SPEC-FILE] -f yaml|yml|json [-h] [-o <value>]\n\nARGUMENTS\n  [SPEC-FILE]  spec path, url, or context-name\n\nFLAGS\n  -f, --format=<option>  (required) [default: json] Specify the format to convert to\n                         <options: yaml|yml|json>\n  -h, --help             Show CLI help.\n  -o, --output=<value>   path to the file where the result is saved\n\nDESCRIPTION\n  Convert asyncapi documents from any format to yaml, yml or JSON\n```\n\n_See code: [src/commands/format.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/format.ts)_\n\n## `asyncapi generate`\n\nGenerate typed models or other things like clients, applications or docs using AsyncAPI Generator templates.\n\n```\nUSAGE\n  $ asyncapi generate\n\nDESCRIPTION\n  Generate typed models or other things like clients, applications or docs using AsyncAPI Generator templates.\n```\n\n_See code: [src/commands/generate/index.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/generate/index.ts)_\n\n## `asyncapi generate client LANGUAGE [ASYNCAPI]`\n\nGenerates clients baked-in AsyncAPI Generator. Available for: dart, java, javascript, python. If some language is not supported or you want to improve existing client, join us at https://github.com/asyncapi/generator\n\n```\nUSAGE\n  $ asyncapi generate client LANGUAGE [ASYNCAPI] [-h] [-d <value>...] [--no-interactive] [-i] [--debug] [-n\n    <value>...] [-o <value>] [--force-write] [-w] [-p <value>...] [--map-base-url <value>] [--registry-url <value>]\n    [--registry-auth <value>] [--registry-token <value>] [--proxyHost <value>] [--proxyPort <value>]\n\nARGUMENTS\n  LANGUAGE    The language you want the client generated for. Available target languages: dart, java, javascript, python\n  [ASYNCAPI]  - Local path, url or context-name pointing to AsyncAPI file\n\nFLAGS\n  -d, --disable-hook=<value>...  Disable a specific hook type or hooks from a given hook type\n  -h, --help                     Show CLI help.\n  -i, --install                  Installs the template and its dependencies (defaults to false)\n  -n, --no-overwrite=<value>...  Glob or path of the file(s) to skip when regenerating\n  -o, --output=<value>           Directory where to put the generated files (defaults to current directory)\n  -p, --param=<value>...         Additional param to pass to templates\n  -w, --watch                    Watches the template directory and the AsyncAPI document, and re-generate the files\n                                 when changes occur. Ignores the output directory.\n      --debug                    Enable more specific errors in the console\n      --force-write              Force writing of the generated files to given directory even if it is a git repo with\n                                 unstaged files or not empty dir (defaults to false)\n      --map-base-url=<value>     Maps all schema references from base url to local folder\n      --no-interactive           Disable interactive mode and run with the provided flags.\n      --proxyHost=<value>        Name of the ProxyHost\n      --proxyPort=<value>        Port number number for the proxyHost.\n      --registry-auth=<value>    The registry username and password encoded with base64, formatted as username:password\n      --registry-token=<value>   The npm registry authentication token, that can be passed instead of base64 encoded\n                                 username and password\n      --registry-url=<value>     [default: https://registry.npmjs.org] Specifies the URL of the private registry for\n                                 fetching templates and dependencies\n\nDESCRIPTION\n  Generates clients baked-in AsyncAPI Generator. Available for: dart, java, javascript, python. If some language is not\n  supported or you want to improve existing client, join us at https://github.com/asyncapi/generator\n\nEXAMPLES\n  $ asyncapi generate client javascript asyncapi.yaml --param version=1.0.0 singleFile=true --output ./docs --force-write\n```\n\n_See code: [src/commands/generate/client.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/generate/client.ts)_\n\n## `asyncapi generate fromTemplate [ASYNCAPI] [TEMPLATE]`\n\nGenerates whatever you want using templates compatible with AsyncAPI Generator.\n\n```\nUSAGE\n  $ asyncapi generate fromTemplate [ASYNCAPI] [TEMPLATE] [-h] [-d <value>...] [--no-interactive] [-i] [--debug] [-n\n    <value>...] [-o <value>] [--force-write] [-w] [-p <value>...] [--map-base-url <value>] [--registry-url <value>]\n    [--registry-auth <value>] [--registry-token <value>] [--proxyHost <value>] [--proxyPort <value>]\n\nARGUMENTS\n  [ASYNCAPI]  - Local path, url or context-name pointing to AsyncAPI file\n  [TEMPLATE]  - Name of the generator template like for example @asyncapi/html-template or\n              https://github.com/asyncapi/html-template\n\nFLAGS\n  -d, --disable-hook=<value>...  Disable a specific hook type or hooks from a given hook type\n  -h, --help                     Show CLI help.\n  -i, --install                  Installs the template and its dependencies (defaults to false)\n  -n, --no-overwrite=<value>...  Glob or path of the file(s) to skip when regenerating\n  -o, --output=<value>           Directory where to put the generated files (defaults to current directory)\n  -p, --param=<value>...         Additional param to pass to templates\n  -w, --watch                    Watches the template directory and the AsyncAPI document, and re-generate the files\n                                 when changes occur. Ignores the output directory.\n      --debug                    Enable more specific errors in the console\n      --force-write              Force writing of the generated files to given directory even if it is a git repo with\n                                 unstaged files or not empty dir (defaults to false)\n      --map-base-url=<value>     Maps all schema references from base url to local folder\n      --no-interactive           Disable interactive mode and run with the provided flags.\n      --proxyHost=<value>        Name of the ProxyHost\n      --proxyPort=<value>        Port number number for the proxyHost.\n      --registry-auth=<value>    The registry username and password encoded with base64, formatted as username:password\n      --registry-token=<value>   The npm registry authentication token, that can be passed instead of base64 encoded\n                                 username and password\n      --registry-url=<value>     [default: https://registry.npmjs.org] Specifies the URL of the private registry for\n                                 fetching templates and dependencies\n\nDESCRIPTION\n  Generates whatever you want using templates compatible with AsyncAPI Generator.\n\nEXAMPLES\n  $ asyncapi generate fromTemplate asyncapi.yaml @asyncapi/html-template --param version=1.0.0 singleFile=true --output ./docs --force-write\n```\n\n_See code: [src/commands/generate/fromTemplate.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/generate/fromTemplate.ts)_\n\n## `asyncapi generate models LANGUAGE FILE`\n\nGenerates typed models\n\n```\nUSAGE\n  $ asyncapi generate models LANGUAGE FILE [-h] [-o <value>] [--packageName <value>] [--namespace <value>]\n    [--tsModelType class|interface] [--tsEnumType enum|union] [--tsModuleSystem ESM|CJS] [--tsIncludeComments]\n    [--tsExportType default|named] [--tsJsonBinPack] [--tsMarshalling] [--tsExampleInstance] [--tsRawPropertyNames]\n    [--csharpAutoImplement] [--csharpNewtonsoft] [--csharpArrayType Array|List] [--csharpHashcode] [--csharpEqual]\n    [--csharpSystemJson] [--goIncludeComments] [--goIncludeTags] [--javaIncludeComments] [--javaJackson]\n    [--javaConstraints] [--javaArrayType Array|List] [--pyDantic] [--no-interactive] [--log-diagnostics]\n    [--diagnostics-format\n    json|stylish|junit|html|text|teamcity|pretty|github-actions|sarif|code-climate|gitlab|markdown] [--fail-severity\n    error|warn|info|hint] [-s <value>] [--proxyHost <value>] [--proxyPort <value>]\n\nARGUMENTS\n  LANGUAGE  (typescript|csharp|golang|java|javascript|dart|python|rust|kotlin|php|cplusplus|scala) The language you want\n            the typed models generated for.\n  FILE      Path or URL to the AsyncAPI document, or context-name\n\nFLAGS\n  -h, --help                         Show CLI help.\n  -o, --output=<value>               The output directory where the models should be written to. Omitting this flag will\n                                     write the models to `stdout`.\n  -s, --save-output=<value>          The output file name. Omitting this flag the result will be printed in the console.\n      --csharpArrayType=<option>     [default: Array] C# specific, define which type of array needs to be generated.\n                                     <options: Array|List>\n      --csharpAutoImplement          C# specific, define whether to generate auto-implemented properties or not.\n      --csharpEqual                  C# specific, generate the models with the Equal method overwritten\n      --csharpHashcode               C# specific, generate the models with the GetHashCode method overwritten\n      --csharpNewtonsoft             C# specific, generate the models with newtonsoft serialization support\n      --csharpSystemJson             C# specific, generate the models with System.Text.Json serialization support\n      --diagnostics-format=<option>  [default: stylish] format to use for validation diagnostics\n                                     <options: json|stylish|junit|html|text|teamcity|pretty|github-actions|sarif|code-cl\n                                     imate|gitlab|markdown>\n      --fail-severity=<option>       [default: error] diagnostics of this level or above will trigger a failure exit\n                                     code\n                                     <options: error|warn|info|hint>\n      --goIncludeComments            Golang specific, if enabled add comments while generating models.\n      --goIncludeTags                Golang specific, if enabled add tags while generating models.\n      --javaArrayType=<option>       [default: Array] Java specific, define which type of array needs to be generated.\n                                     <options: Array|List>\n      --javaConstraints              Java specific, generate the models with constraints\n      --javaIncludeComments          Java specific, if enabled add comments while generating models.\n      --javaJackson                  Java specific, generate the models with Jackson serialization support\n      --[no-]log-diagnostics         log validation diagnostics or not\n      --namespace=<value>            C#, C++ and PHP specific, define the namespace to use for the generated models.\n                                     This is required when language is `csharp`,`c++` or `php`.\n      --no-interactive               Disable interactive mode and run with the provided flags.\n      --packageName=<value>          Go, Java and Kotlin specific, define the package to use for the generated models.\n                                     This is required when language is `go`, `java` or `kotlin`.\n      --proxyHost=<value>            Name of the ProxyHost\n      --proxyPort=<value>            Port number number for the proxyHost.\n      --pyDantic                     Python specific, generate the Pydantic models.\n      --tsEnumType=<option>          [default: enum] TypeScript specific, define which type of enums needs to be\n                                     generated.\n                                     <options: enum|union>\n      --tsExampleInstance            Typescript specific, generate example of the model.\n      --tsExportType=<option>        [default: default] TypeScript specific, define which type of export needs to be\n                                     generated.\n                                     <options: default|named>\n      --tsIncludeComments            TypeScript specific, if enabled add comments while generating models.\n      --tsJsonBinPack                TypeScript specific, define basic support for serializing to and from binary with\n                                     jsonbinpack.\n      --tsMarshalling                TypeScript specific, generate the models with marshalling functions.\n      --tsModelType=<option>         [default: class] TypeScript specific, define which type of model needs to be\n                                     generated.\n                                     <options: class|interface>\n      --tsModuleSystem=<option>      [default: ESM] TypeScript specific, define the module system to be used.\n                                     <options: ESM|CJS>\n      --tsRawPropertyNames           Typescript specific, generate the models using raw property names.\n\nDESCRIPTION\n  Generates typed models\n```\n\n_See code: [src/commands/generate/models.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/generate/models.ts)_\n\n## `asyncapi new`\n\nCreate a new AsyncAPI project, specification files, or templates for clients and applications.\n\n```\nUSAGE\n  $ asyncapi new\n\nDESCRIPTION\n  Create a new AsyncAPI project, specification files, or templates for clients and applications.\n```\n\n_See code: [src/commands/new/index.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/new/index.ts)_\n\n## `asyncapi new file`\n\nCreates a new asyncapi file\n\n```\nUSAGE\n  $ asyncapi new file [-h] [-n <value>] [-e <value>] [-s] [-p <value>] [--no-tty]\n\nFLAGS\n  -e, --example=<value>\n      name of the example to use. Available examples are:\n      - simple-asyncapi.yml\n      - adeo-kafka-request-reply-asyncapi.yml\n      - anyof-asyncapi.yml\n      - application-headers-asyncapi.yml\n      - correlation-id-asyncapi.yml\n      - websocket-gemini-asyncapi.yml\n      - gitter-streaming-asyncapi.yml\n      - kraken-websocket-request-reply-message-filter-in-reply-asyncapi.yml\n      - kraken-websocket-request-reply-multiple-channels-asyncapi.yml\n      - mercure-asyncapi.yml\n      - not-asyncapi.yml\n      - operation-security-asyncapi.yml\n      - oneof-asyncapi.yml\n      - rpc-client-asyncapi.yml\n      - rpc-server-asyncapi.yml\n      - slack-rtm-asyncapi.yml\n      - tutorial.yml\n      - streetlights-kafka-asyncapi.yml\n      - streetlights-operation-security-asyncapi.yml\n      - streetlights-mqtt-asyncapi.yml\n\n  -h, --help\n      Show CLI help.\n\n  -n, --file-name=<value>\n      name of the file\n\n  -p, --port=<value>\n      port in which to start Studio\n\n  -s, --studio\n      open in Studio\n\n  --no-tty\n      do not use an interactive terminal\n\nDESCRIPTION\n  Creates a new asyncapi file\n\nEXAMPLES\n  $ asyncapi new\t - start creation of a file in interactive mode\n\n  $ asyncapi new --file-name=my-asyncapi.yaml --example=default-example.yaml --no-tty\t - create a new file with a specific name, using one of the examples and without interactive mode\n```\n\n_See code: [src/commands/new/file.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/new/file.ts)_\n\n## `asyncapi new template`\n\nCreates a new template\n\n```\nUSAGE\n  $ asyncapi new template [-h] [-n <value>] [-t <value>] [-f <value>] [--force-write]\n\nFLAGS\n  -f, --file=<value>      The path to the AsyncAPI file for generating a template.\n  -h, --help              Show CLI help.\n  -n, --name=<value>      [default: project] Name of the Project\n  -t, --template=<value>  [default: default] Name of the Template\n      --force-write       Force writing of the generated files to given directory even if it is a git repo with unstaged\n                          files or not empty dir (defaults to false)\n\nDESCRIPTION\n  Creates a new template\n```\n\n_See code: [src/commands/new/template.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/new/template.ts)_\n\n## `asyncapi optimize [SPEC-FILE]`\n\noptimize asyncapi specification file\n\n```\nUSAGE\n  $ asyncapi optimize [SPEC-FILE] [-h] [-p\n    remove-components|reuse-components|move-duplicates-to-components|move-all-to-components...] [-i schema...] [-o\n    terminal|new-file|overwrite] [--no-tty] [--proxyHost <value>] [--proxyPort <value>]\n\nARGUMENTS\n  [SPEC-FILE]  spec path, url, or context-name\n\nFLAGS\n  -h, --help                      Show CLI help.\n  -i, --ignore=<option>...        [default: ] list of components to be ignored from the optimization process\n                                  <options: schema>\n  -o, --output=<option>           [default: terminal] select where you want the output.\n                                  <options: terminal|new-file|overwrite>\n  -p, --optimization=<option>...  [default: remove-components,reuse-components,move-duplicates-to-components,move-all-to\n                                  -components] select the type of optimizations that you want to apply.\n                                  <options: remove-components|reuse-components|move-duplicates-to-components|move-all-to\n                                  -components>\n      --no-tty                    do not use an interactive terminal\n      --proxyHost=<value>         Name of the ProxyHost\n      --proxyPort=<value>         Port number number for the proxyHost.\n\nDESCRIPTION\n  optimize asyncapi specification file\n\nEXAMPLES\n  $ asyncapi optimize ./asyncapi.yaml\n\n  $ asyncapi optimize ./asyncapi.yaml --no-tty\n\n  $ asyncapi optimize ./asyncapi.yaml --optimization=remove-components --optimization=reuse-components --optimization=move-all-to-components --no-tty\n\n  $ asyncapi optimize ./asyncapi.yaml --optimization=remove-components --output=terminal --no-tty\n\n  $ asyncapi optimize ./asyncapi.yaml --ignore=schema\n```\n\n_See code: [src/commands/optimize.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/optimize.ts)_\n\n## `asyncapi pretty SPEC-FILE`\n\nBeautify the AsyncAPI spec file (indentation, styling) in place or output the formatted spec to a new file.\n\n```\nUSAGE\n  $ asyncapi pretty SPEC-FILE [-o <value>]\n\nARGUMENTS\n  SPEC-FILE  spec path, url, or context-name\n\nFLAGS\n  -o, --output=<value>  Output file path\n\nDESCRIPTION\n  Beautify the AsyncAPI spec file (indentation, styling) in place or output the formatted spec to a new file.\n\nEXAMPLES\n  $ asyncapi pretty ./asyncapi.yaml\n\n  $ asyncapi pretty ./asyncapi.yaml --output formatted-asyncapi.yaml\n```\n\n_See code: [src/commands/pretty.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/pretty.ts)_\n\n## `asyncapi start`\n\nStarts AsyncAPI-related services. Currently, it supports launching the AsyncAPI Studio\n\n```\nUSAGE\n  $ asyncapi start\n\nDESCRIPTION\n  Starts AsyncAPI-related services. Currently, it supports launching the AsyncAPI Studio\n```\n\n_See code: [src/commands/start/index.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/start/index.ts)_\n\n## `asyncapi start api`\n\nstarts the AsyncAPI server API.\n\n```\nUSAGE\n  $ asyncapi start api [-h] [-m development|production|test] [-p <value>]\n\nFLAGS\n  -h, --help           Show CLI help.\n  -m, --mode=<option>  [default: production] mode in which to start the API\n                       <options: development|production|test>\n  -p, --port=<value>   port in which to start the API\n\nDESCRIPTION\n  starts the AsyncAPI server API.\n```\n\n_See code: [src/commands/start/api.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/start/api.ts)_\n\n## `asyncapi start preview SPEC-FILE`\n\nstarts a new local instance of Studio in minimal state bundling all the refs of the schema file and with no editing allowed.\n\n```\nUSAGE\n  $ asyncapi start preview SPEC-FILE [-h] [-p <value>] [-b <value>] [-d <value>] [-x] [-l] [-B]\n\nARGUMENTS\n  SPEC-FILE  the path to the file to be opened with studio or context name\n\nFLAGS\n  -B, --noBrowser        Pass this to not open browser automatically upon running the command\n  -b, --base=<value>     Path to the file which will act as a base. This is required when some properties need to be\n                         overwritten while bundling with the file.\n  -d, --baseDir=<value>  One relative/absolute path to directory relative to which paths to AsyncAPI Documents that\n                         should be bundled will be resolved.\n  -h, --help             Show CLI help.\n  -l, --suppressLogs     Pass this to suppress the detiled error logs.\n  -p, --port=<value>     port in which to start Studio in the preview mode\n  -x, --xOrigin          Pass this switch to generate properties \"x-origin\" that will contain historical values of\n                         dereferenced \"$ref\"s.\n\nDESCRIPTION\n  starts a new local instance of Studio in minimal state bundling all the refs of the schema file and with no editing\n  allowed.\n```\n\n_See code: [src/commands/start/preview.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/start/preview.ts)_\n\n## `asyncapi start studio [SPEC-FILE]`\n\nstarts a new local instance of Studio\n\n```\nUSAGE\n  $ asyncapi start studio [SPEC-FILE] [-h] [-f <value>] [-p <value>] [--no-interactive] [-B]\n\nARGUMENTS\n  [SPEC-FILE]  spec path, url, or context-name\n\nFLAGS\n  -B, --noBrowser       Pass this to not open browser automatically upon running the command\n  -f, --file=<value>    path to the AsyncAPI file to link with Studio\n  -h, --help            Show CLI help.\n  -p, --port=<value>    port in which to start Studio\n      --no-interactive  disable prompts for this command which asks for file path if not passed via the arguments.\n\nDESCRIPTION\n  starts a new local instance of Studio\n```\n\n_See code: [src/commands/start/studio.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/start/studio.ts)_\n\n## `asyncapi validate [SPEC-FILE]`\n\nvalidate asyncapi file\n\n```\nUSAGE\n  $ asyncapi validate [SPEC-FILE] [-h] [-w] [--log-diagnostics] [--diagnostics-format\n    json|stylish|junit|html|text|teamcity|pretty|github-actions|sarif|code-climate|gitlab|markdown] [--fail-severity\n    error|warn|info|hint] [-s <value>] [--score] [--suppressWarnings <value>...] [--suppressAllWarnings] [--proxyHost\n    <value>] [--proxyPort <value>]\n\nARGUMENTS\n  [SPEC-FILE]  spec path, url, or context-name\n\nFLAGS\n  -h, --help                         Show CLI help.\n  -s, --save-output=<value>          The output file name. Omitting this flag the result will be printed in the console.\n  -w, --watch                        Enable watch mode\n      --diagnostics-format=<option>  [default: stylish] format to use for validation diagnostics\n                                     <options: json|stylish|junit|html|text|teamcity|pretty|github-actions|sarif|code-cl\n                                     imate|gitlab|markdown>\n      --fail-severity=<option>       [default: error] diagnostics of this level or above will trigger a failure exit\n                                     code\n                                     <options: error|warn|info|hint>\n      --[no-]log-diagnostics         log validation diagnostics or not\n      --proxyHost=<value>            Name of the ProxyHost\n      --proxyPort=<value>            Port number number for the proxyHost.\n      --score                        Compute the score of the AsyncAPI document. Scoring is based on whether the\n                                     document has description, license, server and/or channels.\n      --suppressAllWarnings          Suppress all warnings from the validation output.\n      --suppressWarnings=<value>...  List of warning codes to suppress from the validation output.\n\nDESCRIPTION\n  validate asyncapi file\n```\n\n_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v5.0.7/src/commands/validate.ts)_\n<!-- commandsstop -->\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import js from '@eslint/js';\nimport tseslint from 'typescript-eslint';\nimport sonarjs from 'eslint-plugin-sonarjs';\nimport security from 'eslint-plugin-security';\nimport github from 'eslint-plugin-github';\n\nexport default [\n  {\n    ignores: [\n      'node_modules',\n      'dist',\n      'lib',\n      'test/commands/generate/models/',\n      'test/helpers',\n      'test/fixtures/minimaltemplate',\n      'test/fixtures/newtemplate',\n      '.github/workflows/scripts',\n      'github-action',\n      'scripts/**/*.js',\n    ],\n  },\n  js.configs.recommended,\n  ...tseslint.configs.recommended,\n  {\n    files: ['**/*.ts', '**/*.tsx', '**/*.js'],\n    plugins: {\n      '@typescript-eslint': tseslint.plugin,\n      sonarjs,\n      security,\n      github,\n    },\n    languageOptions: {\n      parser: tseslint.parser,\n      parserOptions: {\n        ecmaVersion: 'latest',\n        sourceType: 'module',\n      },\n      globals: {\n        process: 'readonly',\n        console: 'readonly',\n        module: 'readonly',\n        require: 'readonly',\n        __dirname: 'readonly',\n        __filename: 'readonly',\n        exports: 'readonly',\n        global: 'readonly',\n        Buffer: 'readonly',\n        setTimeout: 'readonly',\n        clearTimeout: 'readonly',\n        setInterval: 'readonly',\n        clearInterval: 'readonly',\n      },\n    },\n    rules: {\n      strict: 0,\n      'github/array-foreach': 'error',\n      'eol-last': ['error', 'always'],\n      '@typescript-eslint/no-explicit-any': 'off',\n      '@typescript-eslint/explicit-module-boundary-types': 'off',\n      'sonarjs/no-small-switch': 'off',\n      'security/detect-non-literal-fs-filename': 'off',\n      'no-process-exit': 'error',\n      'no-warning-comments': 'error',\n      'no-loop-func': 'error',\n      curly: ['error'],\n      'no-multi-spaces': 'error',\n      'consistent-return': 'off',\n      'consistent-this': ['off', 'self'],\n      'func-style': 'off',\n      'max-nested-callbacks': ['error', 3],\n      camelcase: 'off',\n      'no-debugger': 'warn',\n      'no-empty': 'warn',\n      'no-invalid-regexp': 'warn',\n      'no-unused-expressions': 'warn',\n      'no-fallthrough': 'warn',\n      'sonarjs/cognitive-complexity': 'warn',\n      eqeqeq: 'error',\n      'no-undef': 'off',\n      'no-dupe-keys': 'error',\n      'no-empty-character-class': 'error',\n      'no-self-compare': 'error',\n      'valid-typeof': 'error',\n      'no-unused-vars': 'off',\n      'handle-callback-err': 'error',\n      'no-shadow-restricted-names': 'error',\n      'no-new-require': 'error',\n      'no-mixed-spaces-and-tabs': 'error',\n      'block-scoped-var': 'error',\n      'no-else-return': 'error',\n      'no-throw-literal': 'error',\n      'no-void': 'error',\n      radix: 'error',\n      'wrap-iife': ['error', 'outside'],\n      'no-shadow': 'off',\n      'no-path-concat': 'error',\n      'valid-jsdoc': [\n        'off',\n        {\n          requireReturn: false,\n          requireParamDescription: false,\n          requireReturnDescription: false,\n        },\n      ],\n      'no-spaced-func': 'error',\n      'semi-spacing': 'error',\n      quotes: ['error', 'single'],\n      'key-spacing': [\n        'error',\n        {\n          beforeColon: false,\n          afterColon: true,\n        },\n      ],\n      indent: ['error', 2],\n      'no-lonely-if': 'error',\n      'no-floating-decimal': 'error',\n      'brace-style': [\n        'error',\n        '1tbs',\n        {\n          allowSingleLine: true,\n        },\n      ],\n      'comma-style': ['error', 'last'],\n      'no-multiple-empty-lines': [\n        'error',\n        {\n          max: 1,\n        },\n      ],\n      'no-nested-ternary': 'error',\n      'operator-assignment': ['error', 'always'],\n      'padded-blocks': ['error', 'never'],\n      'quote-props': ['error', 'as-needed'],\n      'keyword-spacing': [\n        'error',\n        {\n          before: true,\n          after: true,\n          overrides: {},\n        },\n      ],\n      'space-before-blocks': ['error', 'always'],\n      'array-bracket-spacing': ['error', 'never'],\n      'computed-property-spacing': ['error', 'never'],\n      'space-in-parens': ['error', 'never'],\n      'space-unary-ops': [\n        'error',\n        {\n          words: true,\n          nonwords: false,\n        },\n      ],\n      'wrap-regex': 'error',\n      'linebreak-style': 'off',\n      semi: ['error', 'always'],\n      'arrow-spacing': [\n        'error',\n        {\n          before: true,\n          after: true,\n        },\n      ],\n      'no-class-assign': 'error',\n      'no-const-assign': 'error',\n      'no-this-before-super': 'error',\n      'no-var': 'error',\n      'object-shorthand': ['error', 'always'],\n      'prefer-arrow-callback': 'error',\n      'prefer-const': 'error',\n      'prefer-spread': 'error',\n      'prefer-template': 'error',\n      '@typescript-eslint/no-unused-vars': 'error',\n    },\n  },\n  {\n    files: ['**/*.spec.ts', '**/*.spec.tsx', 'src/help/command.ts', '**/*.test.ts'],\n    rules: {\n      'no-undef': 'off',\n      'security/detect-non-literal-fs-filename': 'off',\n      'sonarjs/no-duplicate-string': 'off',\n      'security/detect-object-injection': 'off',\n      'max-nested-callbacks': 'off',\n      'sonarjs/no-identical-functions': 'off',\n      '@typescript-eslint/no-non-null-assertion': 'off',\n      '@typescript-eslint/no-unused-vars': 'off',\n      'no-use-before-define': 'off',\n      'sonarjs/cognitive-complexity': 'off',\n      '@typescript-eslint/no-unused-expressions': 'off',\n    },\n  },\n  {\n    files: ['src/apps/cli/internal/base.ts'],\n    rules: {\n      '@typescript-eslint/ban-ts-comment': 'off',\n    },\n  },\n  {\n    files: ['**/*.js'],\n    rules: {\n      '@typescript-eslint/no-var-requires': 'off',\n    },\n  },\n  {\n    files: ['src/domains/models/Preview.ts', 'src/domains/models/Studio.ts', 'test/jest.setup.ts'],\n    rules: {\n      '@typescript-eslint/no-require-imports': 'off',\n      '@typescript-eslint/no-var-requires': 'off',\n    },\n  },\n];\n"
  },
  {
    "path": "github-action/.asyncapi-tool",
    "content": "title: GitHub Action for Generator\nfilters:\n    technology:\n        - AsyncAPI Generator\n    categories:\n        - github-actions\n"
  },
  {
    "path": "github-action/Dockerfile",
    "content": "# Stage 1: Build stage with Node setup\nFROM node:24-alpine as build\n\n# Copy the source code\nCOPY ./ /tmp/source_code\n\n# Install dependencies\nRUN cd /tmp/source_code && npm install --ignore-scripts\n\n# Build the source code\nRUN cd /tmp/source_code && npm run build\n\n# create libraries directory\nRUN mkdir -p /libraries\n\n# Copy the lib, bin, node_modules, and package.json files to the /libraries directory\nRUN cp -r /tmp/source_code/lib /libraries\nRUN cp -r /tmp/source_code/assets /libraries\nRUN cp /tmp/source_code/package.json /libraries\nRUN cp /tmp/source_code/package-lock.json /libraries\nRUN cp /tmp/source_code/oclif.manifest.json /libraries\n\n# Copy the bin directory to the /libraries directory\nRUN cp -r /tmp/source_code/bin /libraries\n\n# Remove everything inside /tmp\nRUN rm -rf /tmp/*\n\nFROM node:24-alpine\n\n# Install necessary packages\nRUN apk add --no-cache bash git\n\n# Copy the libraries directory from the build stage\nCOPY --from=build /libraries /libraries\n\nRUN cd /libraries && npm install --production --ignore-scripts\n\n# Create a non-root user\n# RUN addgroup -S myuser && adduser -S myuser -G myuser\n\n# Create a script that runs the desired command\nRUN ln -s /libraries/bin/run_bin /usr/local/bin/asyncapi\n\n# Make the script executable\nRUN chmod +x /usr/local/bin/asyncapi\n\n# Change ownership to non-root user\n# RUN chown -R myuser:myuser /libraries /usr/local/bin/asyncapi || echo \"Failed to change ownership\"\n\n# Copy the entrypoint script\nCOPY github-action/entrypoint.sh /entrypoint.sh\n\n# Make the entrypoint script executable\nRUN chmod +x /entrypoint.sh\n\n# Set the entrypoint\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": "github-action/Makefile",
    "content": "DEFAULT_VERSION = 'latest'\nDEFAULT_COMMAND = 'generate'\nTEST_FILEPATH = 'test/asyncapi.yml'\nDEFAULT_TEMPLATE = '@asyncapi/markdown-template@2.0.0'\nDEFAULT_LANGUAGE = ''\nDEFAULT_OUTPUT = 'output'\nDEFAULT_PARAMETERS = ''\nDEFAULT_CUSTOM_COMMANDS = ''\nCUSTOM_COMMANDS = 'validate test/asyncapi.yml'\n\n# Add env variables to the shell\nexport GITHUB_WORKSPACE = $(shell pwd)\n\nrun:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) $(DEFAULT_COMMAND) $(TEST_FILEPATH) $(DEFAULT_TEMPLATE) $(DEFAULT_LANGUAGE) $(DEFAULT_OUTPUT) $(DEFAULT_PARAMETERS) $(DEFAULT_CUSTOM_COMMANDS) \n\ntest: test-default test-validate-success test-custom-output test-custom-commands test-optimize test-bundle test-convert\n\n# Test cases\n\n# Tests if the action has been bumped greater than the latest release\ntest-action-bump:\n\t@bash bump-test.sh\n\n# Tests the default configuration without any inputs\ntest-default:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) $(DEFAULT_COMMAND) $(TEST_FILEPATH) $(DEFAULT_TEMPLATE) $(DEFAULT_LANGUAGE) $(DEFAULT_OUTPUT) $(DEFAULT_PARAMETERS) $(DEFAULT_CUSTOM_COMMANDS) \n\n# Tests the validate command with a valid specification\ntest-validate-success:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) 'validate' $(TEST_FILEPATH) $(DEFAULT_TEMPLATE) $(DEFAULT_LANGUAGE) $(DEFAULT_OUTPUT) $(DEFAULT_PARAMETERS) $(DEFAULT_CUSTOM_COMMANDS) \n\n# Tests the validate command with an invalid specification\ntest-validate-fail:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) 'validate' './test/specification-invalid.yml' $(DEFAULT_TEMPLATE) $(DEFAULT_LANGUAGE) $(DEFAULT_OUTPUT) $(DEFAULT_PARAMETERS) $(DEFAULT_CUSTOM_COMMANDS) \n\n# Tests if the generator can output to a custom directory\ntest-custom-output:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) $(DEFAULT_COMMAND) $(TEST_FILEPATH) $(DEFAULT_TEMPLATE) 'typescript' './output/custom-output' $(DEFAULT_PARAMETERS) $(DEFAULT_CUSTOM_COMMANDS) \n\n# Tests if the action prefers custom commands over the default command\ntest-custom-commands:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) $(DEFAULT_COMMAND) $(TEST_FILEPATH) $(DEFAULT_TEMPLATE) 'typescript' './output/custom-output' $(DEFAULT_PARAMETERS) $(CUSTOM_COMMANDS) \n\n# Tests if the action fails when the input is invalid (e.g. invalid template as is the case here) \nfail-test:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) $(DEFAULT_COMMAND) $(TEST_FILEPATH) '' $(DEFAULT_LANGUAGE) $(DEFAULT_OUTPUT) $(DEFAULT_PARAMETERS) $(DEFAULT_CUSTOM_COMMANDS) \n\n# Tests if the action optimizes the specification\ntest-optimize:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) 'optimize' 'test/unoptimized.yml' $(DEFAULT_TEMPLATE) $(DEFAULT_LANGUAGE) $(DEFAULT_OUTPUT) '-o new-file --no-tty' $(DEFAULT_CUSTOM_COMMANDS)\n\n# Tests if the action can bundle the specification with custom commands\nBUNDLE_COMMAND='bundle ./test/bundle/asyncapi.yaml ./test/bundle/features.yaml --base ./test/bundle/asyncapi.yaml -o ./output/bundle/asyncapi.yaml'\ntest-bundle:\n\tmkdir -p ./output/bundle\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) 'bundle' 'test/bundle/asyncapi.yaml' $(DEFAULT_TEMPLATE) $(DEFAULT_LANGUAGE) $(DEFAULT_OUTPUT) '-o output/bundle/asyncapi.yaml' $(BUNDLE_COMMAND)\n\n# Tests if the action can convert the specification with custom commands\ntest-convert:\n\t@bash ./entrypoint.sh $(DEFAULT_VERSION) 'convert' 'test/asyncapi.yml' $(DEFAULT_TEMPLATE) $(DEFAULT_LANGUAGE) 'output/convert/asyncapi.yaml' '' $(DEFAULT_CUSTOM_COMMANDS)\n"
  },
  {
    "path": "github-action/README.md",
    "content": "# GitHub Action for CLI\n\nThis action exposes the [AsyncAPI CLI](https://github.com/asyncapi/cli). It allows you to generate documentation, validate AsyncAPI documents, convert between different AsyncAPI versions and much more.\n\nThe detailed docs can be found [here](../docs/github-action.md)."
  },
  {
    "path": "github-action/bump-test.sh",
    "content": "#!/bin/bash\n\nversion=$(cat package.json | jq -r '.version');\naction=$(cat action.yml);\nregex='docker:\\/\\/asyncapi\\/github-action-for-cli:([0-9.]+)'\n\n[[ $action =~ $regex ]]\n\naction_version=${BASH_REMATCH[1]};\n\necho \"Action version: $action_version\";\necho \"Package version: $version\";\n\nif [[ $action_version > $version ]]; then\n  echo \"Action version is greater than package version\"; \nelse \\\n  echo \"Action version has not been bumped. Please bump the action version to the semantically correct version after $version\"; \\\n  exit 1; \\\nfi"
  },
  {
    "path": "github-action/entrypoint.sh",
    "content": "#!/usr/bin/env bash\n\n# Exit immediately if a command exits with a non-zero status.\n# Treat unset variables as an error when substituting.\nset -eu\n\nNC='\\033[0m' # No Color\nGREEN='\\033[0;32m'\nYELLOW='\\033[0;33m'\nBLUE='\\033[0;34m'\nRED='\\033[0;31m'\n\nif [[ -z \"$GITHUB_WORKSPACE\" ]]; then\n  workdir=$(pwd)\nelse\n  workdir=\"$GITHUB_WORKSPACE\"\nfi\n\nCLI_VERSION=\"$1\"\nCOMMAND=\"$2\"\nFILEPATH=\"$workdir/$3\" # Absolute path to the AsyncAPI file\nTEMPLATE=\"$4\"\nLANGUAGE=\"$5\"\nOUTPUT=\"$workdir/$6\" # Absolute path to the output directory\nPARAMETERS=\"$7\"\nCUSTOM_COMMAND=\"$8\"\n\necho \"::group::Debug information\"\n# Check if currently running in Developer environment by checking for presence of ./bin/run or ../bin/run and set alias asyncapi= PATH_TO_BIN/RUN\nif [[ -f \"$workdir/bin/run\" ]]; then\n  echo -e \"${BLUE}Running in developer environment...${NC}\"\n  echo -e \"${BLUE}Setting alias for asyncapi:${NC}\" \"$workdir/bin/run\"\n  shopt -s expand_aliases\n  alias asyncapi=\"$workdir/bin/run\"\nelif [[ -f \"$workdir/../bin/run\" ]]; then\n  echo -e \"${BLUE}Running in developer environment...${NC}\"\n  echo -e \"${BLUE}Setting alias for asyncapi:${NC}\" \"$workdir/../bin/run\"\n  shopt -s expand_aliases\n  alias asyncapi=\"$workdir/../bin/run\"\nelif [[ -n \"$CLI_VERSION\" && ! \"$CLI_VERSION\" == \"latest\" ]]; then\n  echo -e \"${BLUE}CLI version:${NC}\" \"$CLI_VERSION\"\n  # Check if the CLI version is already installed or not\n  if [[ -z $(command -v -- asyncapi) ]]; then\n    output=''\n  else\n    output=$(asyncapi --version >/dev/null 2>&1)\n  fi\n  # output @asyncapi/cli/1.1.1 linux-x64 node-v20.8.1\n  version=$(echo \"$output\" | cut -d' ' -f1 | cut -d '/' -f3)\n  if [[ \"$version\" == \"$CLI_VERSION\" ]]; then\n    echo -e \"${BLUE}AsyncAPI CLI already installed:${NC}\" \"$CLI_VERSION\" \"...skipping\"\n  else \n    echo -e \"${BLUE}Installing AsyncAPI CLI:${NC}\" \"$CLI_VERSION\"\n    npm install -g @asyncapi/cli@$CLI_VERSION\n  fi\nelse\n  if [[ -z $(command -v -- asyncapi) ]]; then\n    echo -e \"${RED}No CLI installation found. Installing the latest one\"\n    npm install -g @asyncapi/cli\n  fi\n  echo -e \"${BLUE}CLI version:${NC}\" \"latest\"\nfi\necho \"::endgroup::\"\n\necho -e \"${BLUE}AsyncAPI CLI version:${NC}\" \"$(asyncapi --version)\"\n\necho -e \"${GREEN}Executing AsyncAPI CLI...${NC}\"\n\ngit config --global --add safe.directory \"$GITHUB_WORKSPACE\"\n\nif [[ -n \"$CUSTOM_COMMAND\" ]]; then\n  echo \"::group::Debug information\" \n  echo -e \"${BLUE}Executing custom command:${NC} asyncapi\" \"$CUSTOM_COMMAND\"\n  eval \"asyncapi $CUSTOM_COMMAND\"\n  echo \"::endgroup::\"\n  exit 0\nfi \n\necho \"::group::Debug information\"\necho -e \"${BLUE}Command:${NC}\" \"$COMMAND\"\necho -e \"${BLUE}File:${NC}\" \"$FILEPATH\"\necho -e \"${BLUE}Template:${NC}\" \"$TEMPLATE\"\necho -e \"${BLUE}Language:${NC}\" \"$LANGUAGE\"\necho -e \"${BLUE}Output:${NC}\" \"$OUTPUT\"\necho -e \"${BLUE}Parameters:${NC}\" \"$PARAMETERS\"\necho \"::endgroup::\"\n\nhandle_file_error () {\n  echo -e \"${RED}Validation error: File not found:${NC}\" \"$1\"\n  echo -e \"skipping...\\n\"\n}\n\nhandle_validate () {\n  echo -e \"${BLUE}Validating AsyncAPI file...${NC}\"\n  echo \"::group::Debug information\"\n\n  if [[ ! -f \"$FILEPATH\" ]]; then \n    handle_file_error \"$FILEPATH\"\n    exit 1\n  fi\n\n  echo -e \"${BLUE}Executing command:${NC}\" \"asyncapi validate $FILEPATH $PARAMETERS\"\n  eval \"asyncapi validate $FILEPATH $PARAMETERS\"\n  echo \"::endgroup::\"\n}\n\nhandle_optimize () {\n  echo -e \"${BLUE}Optimising AsyncAPI file...${NC}\"\n  echo \"::group::Debug information\"\n  \n  if [[ ! -f \"$FILEPATH\" ]]; then \n    handle_file_error \"$FILEPATH\"\n    exit 1\n  fi\n\n  echo -e \"${BLUE}Executing command:${NC}\" \"asyncapi optimize $FILEPATH $PARAMETERS\"\n  eval \"asyncapi optimize $FILEPATH $PARAMETERS\"\n  echo \"::endgroup::\"\n}\n\nhandle_generate () {\n  echo -e \"${BLUE}Generating from AsyncAPI file...${NC}\"\n\n  if [[ ! -f \"$FILEPATH\" ]]; then \n    handle_file_error \"$FILEPATH\"\n    exit 1\n  fi\n\n  # Check if the output directory exists or not and create it if it doesn't\n  output_dir=$(dirname \"$OUTPUT\")\n\n  if [[ ! -d \"$output_dir\" ]]; then\n    mkdir -p \"$output_dir\"\n    echo -e \"${BLUE}Created output directory:${NC}\" \"$output_dir\"\n  fi\n\n  echo \"::group::Debug information\"\n  if [[ -n \"$LANGUAGE\" ]]; then\n    echo -e \"${BLUE}Executing command:${NC}\" \"asyncapi generate models $LANGUAGE $FILEPATH -o $OUTPUT $PARAMETERS\"\n    eval \"asyncapi generate models $LANGUAGE $FILEPATH -o $OUTPUT $PARAMETERS\"\n  elif [[ -n \"$TEMPLATE\" ]]; then\n    echo -e \"${BLUE}Executing command:${NC}\" \"asyncapi generate fromTemplate $FILEPATH $TEMPLATE -o $OUTPUT $PARAMETERS\"\n    eval \"asyncapi generate fromTemplate $FILEPATH $TEMPLATE -o $OUTPUT $PARAMETERS\"  \n  else\n    echo -e \"${RED}Invalid configuration:${NC} Either language or template must be specified.\"\n    exit 1\n  fi\n  echo \"::endgroup::\"\n}\n\nhandle_convert () {\n  echo -e \"${BLUE}Converting AsyncAPI file...${NC}\"\n  echo \"::group::Debug information\"\n\n  if [[ ! -f \"$FILEPATH\" ]]; then \n    handle_file_error \"$FILEPATH\"\n    exit 1\n  fi\n\n  if [[ -z \"$OUTPUT\" ]]; then\n    echo -e \"${BLUE}Executing command:${NC}\" \"asyncapi convert $FILEPATH $PARAMETERS\"\n    eval \"asyncapi convert $FILEPATH $PARAMETERS\"\n  else\n    # Create the output directory if it doesn't exist\n    output_dir=$(dirname \"$OUTPUT\")\n\n    if [[ ! -d \"$output_dir\" ]]; then\n      mkdir -p \"$output_dir\"\n      echo -e \"${BLUE}Created output directory:${NC}\" \"$output_dir\"\n    fi\n\n    echo -e \"${BLUE}Executing command:${NC}\" \"asyncapi convert $FILEPATH -o $OUTPUT $PARAMETERS\"\n    eval \"asyncapi convert $FILEPATH -o $OUTPUT $PARAMETERS\"\n  fi\n}\n\nif [[ $COMMAND == \"validate\" ]]; then\n  handle_validate\nelif [[ $COMMAND == \"optimize\" ]]; then\n  handle_optimize\nelif [[ $COMMAND == \"generate\" ]]; then\n  handle_generate\nelif [[ $COMMAND == \"convert\" ]]; then\n  handle_convert\nelse\n  echo -e \"${RED}Invalid command:${NC}\" \"$COMMAND\"\n  echo -e \"${YELLOW}NOTE: Command can be either validate, optimize or generate.${NC}\"\nfi"
  },
  {
    "path": "github-action/lib/bump-action-version.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n\nfunction bumpActionVersion() {\n  const packageJsonPath = path.join(__dirname, '../../', 'package.json');\n  const packageJsonVersion = require(packageJsonPath).version;\n\n  const templatePath = path.join(__dirname, '../../', 'action-template.yml');\n  const outputPath = path.join(__dirname, '../../', 'action.yml');\n\n  const templateContent = fs.readFileSync(templatePath, 'utf8');\n  const updatedContent = templateContent.replace(/\\${ version }/g, packageJsonVersion);\n\n  fs.writeFileSync(outputPath, updatedContent, 'utf8');\n  console.log(`Updated action.yml with version ${packageJsonVersion}`);\n}\n\nbumpActionVersion();\n"
  },
  {
    "path": "github-action/test/asyncapi.yml",
    "content": "asyncapi: '2.0.0'\ninfo:\n  title: Streetlights API\n  version: '1.0.0'\n  description: |\n    The Smartylighting Streetlights API allows you\n    to remotely manage the city lights.\n  license:\n    name: Apache 2.0\n    url: 'https://www.apache.org/licenses/LICENSE-2.0'\n\nservers:\n  mosquitto:\n    url: mqtt://test.mosquitto.org\n    protocol: mqtt\n\nchannels:\n  light/measured:\n    publish:\n      summary: Inform about environmental lighting conditions for a particular streetlight.\n      operationId: onLightMeasured\n      message:\n        payload:\n          type: object\n          properties:\n            id:\n              type: integer\n              minimum: 0\n              description: Id of the streetlight.\n            lumens:\n              type: integer\n              minimum: 0\n              description: Light intensity measured in lumens.\n            sentAt:\n              type: string\n              format: date-time\n              description: Date and time when the message was sent.\n"
  },
  {
    "path": "github-action/test/bundle/asyncapi.yaml",
    "content": "asyncapi: \"2.5.0\"\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: \"./messages.yaml#/messages/UserSignedUp\""
  },
  {
    "path": "github-action/test/bundle/features.yaml",
    "content": "asyncapi: \"2.5.0\"\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user logouts\nchannels:\n  user/loggedOut:\n    subscribe:\n      message:\n        $ref: \"./messages.yaml#/messages/UserLoggedOut\""
  },
  {
    "path": "github-action/test/bundle/messages.yaml",
    "content": "messages:\n  UserSignedUp:\n    payload:\n      type: object\n      properties:\n        displayName:\n          type: string\n          description: Name of the user\n        email:\n          type: string\n          format: email\n          description: Email of the user\n  UserLoggedOut:\n    payload:\n      type: object\n      properties:\n        displayName:\n          type: string\n          description: Name of the user\n        userId:\n          type: string\n          description: Id the user\n        timestamp:\n          type: number\n          description: Time stamp when the user logged out\n"
  },
  {
    "path": "github-action/test/dummy.yml",
    "content": "asyncapi: '2.3.0'\n\nexternalDocs:\n  description: Find more info here\n  url: https://www.asyncapi.com\n\ninfo:\n  title: Dummy example with all spec features included\n  version: '0.0.1'\n  description: |\n    This is an example of AsyncAPI specification file that is suppose to include all possible features of the AsyncAPI specification. Do not use it on production.\n\n    It's goal is to support development of documentation and code generation with the [AsyncAPI Generator](https://github.com/asyncapi/generator/) and [Template projects](https://github.com/search?q=topic%3Aasyncapi+topic%3Agenerator+topic%3Atemplate)\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0\n  contact:\n    name: API Support\n    url: http://www.asyncapi.com/support\n    email: info@asyncapi.io\n  x-twitter: '@AsyncAPISpec'\n\ntags:\n  - name: root-tag1\n    externalDocs:\n      description: External docs description 1\n      url: https://www.asyncapi.com/\n  - name: root-tag2\n    description: Description 2\n    externalDocs:\n      url: \"https://www.asyncapi.com/\"\n  - name: root-tag3\n  - name: root-tag4\n    description: Description 4\n  - name: root-tag5\n    externalDocs:\n      url: \"https://www.asyncapi.com/\"\n\nservers:\n  dummy-mqtt:\n    $ref: '#/components/servers/dummyMQTT'\n  dummy-amqp:\n    url: amqp://localhost:{port}\n    protocol: amqp\n    description: dummy AMQP broker\n    protocolVersion: \"0.9.1\"\n    variables:\n      port:\n        enum:\n          - '15672'\n          - '5672'\n    security:\n      - user-password: []\n  dummy-kafka:\n    url: http://localhost:{port}\n    protocol: kafka\n    description: dummy Kafka broker\n    variables:\n      port:\n        default: '9092'\n\ndefaultContentType: application/json\n\nchannels:\n  dummy/channel/with/{dummy}/parameter/create:\n    x-dummy-security:\n      $ref: '#/components/securitySchemes/supportedOauthFlows/flows/clientCredentials'\n    description: Dummy channel description.\n    parameters:\n      dummy:\n        $ref: '#/components/parameters/dummy'\n    publish:\n      summary: Inform whenever something dummy is created.\n      description: |\n        Longer description.\n\n        Still dummy though.\n      operationId: receiveNewDummyInfo\n      tags:\n        - name: oparation-tag1\n          externalDocs:\n            description: External docs description 1\n            url: https://www.asyncapi.com/\n        - name: oparation-tag2\n          description: Description 2\n          externalDocs:\n            url: \"https://www.asyncapi.com/\"\n        - name: oparation-tag3\n        - name: oparation-tag4\n          description: Description 4\n        - name: oparation-tag5\n          externalDocs:\n            url: \"https://www.asyncapi.com/\"\n      traits:\n        - $ref: '#/components/operationTraits/kafka'\n      message:\n        $ref: '#/components/messages/dummyCreated'\n\n  dummy/channel/without/parameter:\n    $ref: '#/components/channels/dummyChannel'\ncomponents:\n  servers: \n    dummyMQTT:\n      url: mqtt://localhost\n      protocol: mqtt\n      description: dummy MQTT broker\n      bindings:\n          mqtt:\n            clientId: guest\n            cleanSession: true\n  channels:\n    dummyChannel:\n      bindings:\n        amqp:\n          is: routingKey\n      subscribe:\n        operationId: receiveSystemInfo\n        bindings:\n          amqp:\n            expiration: 100000\n            userId: guest\n            cc: [ 'user.logs' ]\n            priority: 10\n            deliveryMode: 2\n            mandatory: false\n            bcc: [ 'external.audit' ]\n            replyTo: user.signedup\n            timestamp: true\n            ack: false\n            bindingVersion: 0.1.0\n        message:\n          $ref: '#/components/messages/dummyInfo'\n  messages:\n    dummyCreated:\n      name: dummyCreated\n      title: Dummy created message\n      summary: This is just a dummy create message\n      correlationId:\n        description: This is a dummy correlation ID.\n        location: $message.header#/correlationId\n      tags:\n        - name: message-tag1\n          externalDocs:\n            description: External docs description 1\n            url: https://www.asyncapi.com/\n        - name: message-tag2\n          description: Description 2\n          externalDocs:\n            url: \"https://www.asyncapi.com/\"\n        - name: message-tag3\n        - name: message-tag4\n          description: Description 4\n        - name: message-tag5\n          externalDocs:\n            url: \"https://www.asyncapi.com/\"\n      headers:\n        type: object\n        properties:\n          my-custom-app-header:\n            type: string\n          correlationId:\n            type: string\n      payload:\n        $ref: \"#/components/schemas/dummyCreated\"\n      bindings:\n        kafka:\n          key:\n            type: object\n            properties:\n              id:\n                type: string\n                format: uuid\n              type:\n                type: string\n                enum: [ 'type1', \"type2\" ]\n          bindingVersion: '0.1.0'\n        amqp:\n          contentEncoding: gzip\n          messageType: 'user.signup'\n          bindingVersion: 0.1.0\n      x-response:\n        $ref: \"#/components/messages/dummyInfo\"\n    dummyInfo:\n      name: dummyInfo\n      title: Dummy system info\n      summary: This is just a dummy info message\n      correlationId:\n        location: $message.header#/correlationId\n      description: |\n        More description for a dummy message.\n\n        It is a dummy system info message.\n      traits:\n        - $ref: '#/components/messageTraits/commonHeaders'\n      payload:\n        $ref: \"#/components/schemas/dummyInfo\"\n      examples:\n        - name: option1example\n          summary: this is dummy summary for option1example\n          headers:\n            my-app-header: 12\n          payload:\n            prop1: option1\n            sentAt: 2020-01-31T13:24:53Z\n        - name: headerExample\n          headers:\n            my-app-header: 13\n        - payload:\n            prop1: option2\n            sentAt: 2020-01-31T13:24:53Z\n\n\n  schemas:\n    dummyCreated:\n      type: object\n      required:\n        - prop2\n      x-schema-extensions-as-object:\n            type: object\n            properties:\n              prop1:\n                type: string\n              prop2:\n                type: integer\n                minimum: 0\n      x-schema-extensions-as-primitive: dummy\n      x-schema-extensions-as-array:\n        - \"item1\"\n        - \"item2\"\n      properties:\n        prop1:\n          type: integer\n          minimum: 0\n          description: Dummy prop1\n          x-prop1-dummy: dummy extension\n        prop2:\n          type: string\n          description: Dummy prop2\n        sentAt:\n          $ref: \"#/components/schemas/sentAt\"\n        dummyArrayWithObject:\n          $ref: \"#/components/schemas/dummyArrayWithObject\"\n        dummyArrayWithArray:\n          $ref: \"#/components/schemas/dummyArrayWithArray\"\n        dummyObject:\n          $ref: \"#/components/schemas/dummyObject\"\n        dummyArrayRank:\n          $ref: '#/components/schemas/dummyArrayRank'\n    dummyInfo:\n      type: object\n      required:\n        - prop1\n      properties:\n        prop1:\n          type: string\n          enum:\n            - option1\n            - option2\n          description: Dummy prop1\n        sentAt:\n          $ref: \"#/components/schemas/sentAt\"\n    dummyArrayWithObject:\n      type: array\n      items:\n        $ref: \"#/components/schemas/dummyInfo\"\n    dummyArrayWithArray:\n      type: array\n      items:\n        - $ref: \"#/components/schemas/dummyInfo\"\n        - type: string\n        - type: number\n    dummyObject:\n      type: object\n      properties:\n        dummyObjectProp1:\n          $ref: \"#/components/schemas/sentAt\"\n        dummyObjectProp2:\n          $ref: \"#/components/schemas/dummyRecursiveObject\"\n        dummyObjectProp3:\n          type: object\n          additionalProperties: true\n        dummyObjectProp4:\n          type: object\n          additionalProperties: false\n    dummyRecursiveObject:\n      type: object\n      properties:\n        dummyRecursiveProp1:\n          $ref: \"#/components/schemas/dummyObject\"\n        dummyRecursiveProp2:\n          type: string\n    sentAt:\n      type: string\n      format: date-time\n      description: Date and time when the message was sent.\n    dummyArrayRank:\n      type: object\n      properties:\n        dummyArrayValueRank: \n          $ref: '#/components/schemas/dummyArrayValueRank'\n        dummyArrayDimensions: \n          $ref: '#/components/schemas/dummyArrayArrayDimensions'\n    dummyArrayValueRank:\n      description: >\n        This Attribute indicates whether the val Attribute of the datapoint is an\n        array and how many dimensions the array has.\n      type: integer\n      default: -1\n      examples:\n        - 2\n      oneOf:\n        - const: -1\n          description: 'Scalar: The value is not an array.'\n        - const: 0\n          description: 'OneOrMoreDimensions: The value is an array with one or more dimensions.'\n        - const: 1\n          description: 'OneDimension: The value is an array with one dimension.'\n        - const: 2\n          description: 'The value is an array with two dimensions.'\n    dummyArrayArrayDimensions:\n      type: array\n      items:\n        type: integer\n        minimum: 0\n      examples:\n        - [3, 5]\n\n  securitySchemes:\n    user-password:\n      type: userPassword\n    apiKey:\n      type: apiKey\n      in: user\n      description: Provide your API key as the user and leave the password empty.\n    supportedOauthFlows:\n      type: oauth2\n      description: Flows to support OAuth 2.0\n      flows:\n        implicit:\n          authorizationUrl: 'https://authserver.example/auth'\n          scopes:\n            'dummy:created': Ability to create dummy message\n            'dymmy:read': Ability to read dummy info\n        password:\n          tokenUrl: 'https://authserver.example/token'\n          scopes:\n            'dummy:created': Ability to create dummy message\n            'dymmy:read': Ability to read dummy info\n        clientCredentials:\n          tokenUrl: 'https://authserver.example/token'\n          scopes:\n            'dummy:created': Ability to create dummy message\n            'dymmy:read': Ability to read dummy info\n        authorizationCode:\n          authorizationUrl: 'https://authserver.example/auth'\n          tokenUrl: 'https://authserver.example/token'\n          refreshUrl: 'https://authserver.example/refresh'\n          scopes:\n            'dummy:created': Ability to create dummy message\n            'dymmy:read': Ability to read dummy info\n    openIdConnectWellKnown:\n      type: openIdConnect\n      openIdConnectUrl: 'https://authserver.example/.well-known'\n\n  parameters:\n    dummy:\n      description: The ID of the new dummy message.\n      schema:\n        type: string\n        description: Description that not be rendered, as parameter has explicit description.\n\n  messageTraits:\n    commonHeaders:\n      headers:\n        type: object\n        properties:\n          my-app-header:\n            type: integer\n            minimum: 0\n            maximum: 100\n          correlationId:\n            type: string\n\n  operationTraits:\n    kafka:\n      bindings:\n        kafka:\n          groupId: my-app-group-id\n          clientId: my-app-client-id\n          bindingVersion: '0.1.0'"
  },
  {
    "path": "github-action/test/specification-invalid.yml",
    "content": "asyncapi: 2.2.0\ninfo:\n  title: Account Service\nversion: 1.0.0\ndescription: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: '#/components/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n"
  },
  {
    "path": "github-action/test/unoptimized.yml",
    "content": "asyncapi: 2.0.0\ninfo:\n  title: Streetlights API\n  version: '1.0.0'\nchannels:\n  smartylighting/event/{streetlightId}/lighting/measured:\n    parameters:\n      #this parameter is duplicated. it can be moved to components and ref-ed from here.\n      streetlightId:\n        schema:\n          type: string\n    subscribe:\n      operationId: receiveLightMeasurement\n      traits:\n        - bindings:\n            kafka:\n              clientId: my-app-id\n      message:\n        name: lightMeasured\n        title: Light measured\n        contentType: application/json\n        traits:\n          - headers:\n              type: object\n              properties:\n                my-app-header:\n                  type: integer\n                  minimum: 0\n                  maximum: 100\n        payload:\n          type: object\n          properties:\n            lumens:\n              type: integer\n              minimum: 0\n            #full form is used, we can ref it to: #/components/schemas/sentAt\n            sentAt:\n              type: string\n              format: date-time\n  smartylighting/action/{streetlightId}/turn/on:\n    parameters:\n      streetlightId:\n        schema:\n          type: string\n    publish:\n      operationId: turnOn\n      traits:\n        - bindings:\n            kafka:\n              clientId: my-app-id\n      message:\n        name: turnOnOff\n        title: Turn on/off\n        traits:\n          - headers:\n              type: object\n              properties:\n                my-app-header:\n                  type: integer\n                  minimum: 0\n                  maximum: 100\n        payload:\n          type: object\n          properties:\n            sentAt:\n              $ref: \"#/components/schemas/sentAt\"\ncomponents:\n  messages:\n    #libarary should be able to find and delete this message, because it is not used anywhere.\n    unusedMessage:\n      name: unusedMessage\n      title: This message is not used in any channel.\n      \n  schemas:\n    #this schema is ref-ed in one channel and used full form in another. library should be able to identify and ref the second channel as well.\n    sentAt:\n      type: string\n      format: date-time"
  },
  {
    "path": "github-action/test/unoptimized_optimized.yml",
    "content": "asyncapi: 2.0.0\ninfo:\n  title: Streetlights API\n  version: 1.0.0\nchannels:\n  smartylighting/event/{streetlightId}/lighting/measured:\n    $ref: >-\n      #/components/channels/smartylighting/event/{streetlightId}/lighting/measured\n    parameters:\n      streetlightId:\n        schema:\n          $ref: '#/components/schemas/schema'\n  smartylighting/action/{streetlightId}/turn/on:\n    $ref: '#/components/channels/smartylighting/action/{streetlightId}/turn/on'\ncomponents:\n  schemas:\n    sentAt:\n      type: string\n      format: date-time\n    lumens:\n      type: integer\n      minimum: 0\n    payload:\n      type: object\n      properties:\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n  messages:\n    message:\n      name: turnOnOff\n      title: Turn on/off\n      traits:\n        - headers:\n            type: object\n            properties:\n              my-app-header:\n                type: integer\n                minimum: 0\n                maximum: 100\n      payload:\n        $ref: '#/components/schemas/payload'\n  operations:\n    subscribe:\n      operationId: receiveLightMeasurement\n      traits:\n        - bindings:\n            kafka:\n              clientId: my-app-id\n      message:\n        $ref: '#/components/messages/message'\n    publish:\n      operationId: turnOn\n      traits:\n        - bindings:\n            kafka:\n              clientId: my-app-id\n      message:\n        $ref: '#/components/messages/message'\n  channels:\n    smartylighting/event/{streetlightId}/lighting/measured:\n      parameters:\n        streetlightId:\n          schema:\n            $ref: '#/components/schemas/schema'\n      subscribe:\n        $ref: '#/components/operations/subscribe'\n    smartylighting/action/{streetlightId}/turn/on:\n      parameters:\n        streetlightId:\n          schema:\n            $ref: '#/components/schemas/schema'\n      publish:\n        $ref: '#/components/operations/publish'\n"
  },
  {
    "path": "jest.config.ts",
    "content": "import type { Config } from '@jest/types';\n\nconst config: Config.InitialOptions = {\n  coverageReporters: [\n    'text',\n    'html'\n  ],\n  transform: {\n    '^.+\\\\.(t|j)sx?$': '@swc/jest',\n  },\n  roots: ['<rootDir>'],\n  testRegex: '(/__tests__/.*|(\\\\.|/)(test|spec))\\\\.[jt]sx?$',\n  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],\n  testTimeout: 100000,\n  setupFiles: ['./test/jest.setup.ts'],\n  collectCoverageFrom: [\n    'src/**'\n  ],\n};\n\nexport default config;\n"
  },
  {
    "path": "nodemon.json",
    "content": "{\n  \"watch\": [\n    \"src\",\n    \".env\"\n  ],\n  \"ext\": \"js,ts,json\",\n  \"ignore\": [\n    \"src/**/*.spec.ts\",\n    \"src/**/*.test.ts\"\n  ],\n  \"exec\": \"ts-node --require tsconfig-paths/register --transpile-only src/apps/api/server.ts\"\n}"
  },
  {
    "path": "openapi.yaml",
    "content": "openapi: 3.1.0\ninfo:\n  version: 0.2.0\n  title: AsyncAPI Server API\n  description: Server API providing official AsyncAPI tools\n  contact:\n    name: AsyncAPI Initiative\n    email: info@asyncapi.io\n    url: https://asyncapi.com/\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n\nservers:\n  - url: https://api.asyncapi.com/v1\n\npaths:\n  /version:\n    get:\n      summary: Get the current version of the AsyncAPI CLI.\n      operationId: getVersion\n      tags:\n        - version\n      responses:\n        \"200\":\n          description: Successfully retrieved the version.\n          content:\n            application/json:\n              schema:\n                ref: \"#/components/schemas/VersionResponse\"\n        \"500\":\n          description: Internal server error.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n  /validate:\n    post:\n      summary: Validate the given AsyncAPI document.\n      operationId: validate\n      tags:\n        - validate\n        - parser\n      externalDocs:\n        name: Github Repository for the AsyncAPI Parser\n        url: https://github.com/asyncapi/parser-js\n      requestBody:\n        description: Validate the given AsyncAPI document.\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/ValidateRequest'\n      responses:\n        \"204\":\n          description: The given AsyncAPI document is valid.\n        \"400\":\n          description: The given AsyncAPI document is not valid.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        \"422\":\n          description: The given AsyncAPI document is not valid due to invalid parameters in the request.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n\n  /parse:\n    post:\n      summary: Parse the given AsyncAPI document.\n      operationId: parse\n      tags:\n        - parse\n        - parser\n      externalDocs:\n        name: Github Repository for the AsyncAPI Parser\n        url: https://github.com/asyncapi/parser-js\n      requestBody:\n        description: Parse the given AsyncAPI document.\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/ParseRequest'\n      responses:\n        \"200\":\n          description: AsyncAPI document successfully parsed.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/ParseResponse\"\n        \"400\":\n          description: The given AsyncAPI document is not valid.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        \"422\":\n          description: The given AsyncAPI document is not valid due to invalid parameters in the request.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n\n  /generate:\n    post:\n      summary: Generate the given AsyncAPI template.\n      operationId: generate\n      tags:\n        - generate\n        - generator\n      externalDocs:\n        name: Github Repository for the AsyncAPI Generator\n        url: https://github.com/asyncapi/generator\n      requestBody:\n        description: Template details to be generated.\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/GenerateRequest'\n      responses:\n        \"200\":\n          description: Template successfully generated.\n          content:\n            application/zip:\n              schema:\n                $ref: '#/components/schemas/GenerateResponse'\n        '400':\n          description: Failed to generate the given template due to invalid AsyncAPI document.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        \"422\":\n          description: Failed to generate the given template due to invalid parameters.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n\n  /convert:\n    post:\n      summary: Convert the given AsyncAPI/OpenAPI document to AsyncAPI document with the specified version.\n      operationId: convert\n      tags:\n        - convert\n        - converter\n      externalDocs:\n        name: Github Repository for the AsyncAPI Converter\n        url: https://github.com/asyncapi/converter-js\n      requestBody:\n        description: Parameters to convert the spec.\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/ConvertRequest'\n      responses:\n        '200':\n          description: AsyncAPI document successfully converted.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/ConvertResponse'\n        '400':\n          description: Failed to convert due to invalid AsyncAPI document.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n        '422':\n          description: Failed to convert the given document due to invalid parameters.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n\n  /bundle:\n    post:\n      summary: Bundle the given AsyncAPI document(s).\n      operationId: bundle\n      tags:\n        - bundle\n        - bundler\n      externalDocs:\n        name: Github Repository for the AsyncAPI Bundler\n        url: https://github.com/asyncapi/bundler\n      requestBody:\n        description: Bundle the given AsyncAPI document(s) to single one.\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/BundleRequest\"\n      responses:\n        \"200\":\n          description: AsyncAPI document(s) successfully bundled.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/BundleResponse\"\n        \"400\":\n          description: The given AsyncAPI document(s) is/are not valid.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        \"422\":\n          description: The given AsyncAPI document(s) is/are not valid due to invalid parameters in the request.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n\n  /help:\n    get:\n      summary: Retrieve help information for the given command.\n      operationId: help\n      tags:\n        - help\n      parameters:\n        - name: command\n          in: query\n          style: form\n          explode: true\n          description: The command for which help information is needed.\n          required: true\n          schema:\n            type: string\n      responses:\n        \"200\":\n          description: Help information retrieved successfully.\n          content:\n            application/json:\n              schema:\n                oneOf:\n                  - $ref: '#/components/schemas/HelpListResponse'\n                  - $ref: '#/components/schemas/HelpCommandResponse'\n        \"400\":\n          description: Bad request\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n        \"404\":\n          description: Command not found\n          content:\n            application/problem+json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Problem\"\n\n  /diff:\n    post:\n      summary: Compare the given AsyncAPI documents.\n      operationId: diff\n      tags:\n        - diff\n        - differ\n      externalDocs:\n        name: Github Repository for the AsyncAPI Diff\n        url: https://github.com/asyncapi/diff\n      requestBody:\n        description: Compare the given AsyncAPI documents.\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/DiffRequest'\n      responses:\n        '200':\n          description: Successfully received changes between two AsyncAPI documents.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/DiffResponse'\n        '400':\n          description: One of the AsyncAPI documents is not valid.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n        '422':\n          description: Failed to retrieve changes between two AsyncAPI documents due to invalid parameters.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n        default:\n          description: Unexpected problem.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Problem'\n\ncomponents:\n  schemas:\n    AsyncAPIDocument:\n      description: AsyncAPI document in JSON or YAML.\n      oneOf:\n        - type: string # can be in YAML format\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.0.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.1.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.2.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.3.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.4.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.5.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.6.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/3.0.0.json\n        - $ref: https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/3.1.0.json\n    AsyncAPIDocuments:\n      type: array\n      description: AsyncAPI documents in JSON or YAML.\n      minItems: 1\n      items:\n        $ref: '#/components/schemas/AsyncAPIDocument'\n    OpenAPIDocument:\n      type: string\n      description: OpenAPI document in JSON or YAML.\n      oneOf:\n        - type: string # can be in YAML format\n        - $ref: https://spec.openapis.org/oas/3.0/schema/2024-10-18\n    SpecVersions:\n      type: string\n      description: Valid specification versions for the AsyncAPI document.\n      default: latest\n      enum:\n        - '2.0.0'\n        - '2.1.0'\n        - '2.2.0'\n        - '2.3.0'\n        - '2.4.0'\n        - '2.5.0'\n        - '2.6.0'\n        - '3.0.0'\n        - '3.1.0'\n        - 'latest'\n\n    ValidateRequest:\n      type: object\n      required:\n        - asyncapi\n      properties:\n        asyncapi:\n          $ref: '#/components/schemas/AsyncAPIDocument'\n\n    ValidateResponse:\n      type: object\n      required:\n        - status\n      properties:\n        status:\n          type: string\n          description: Validation status of the AsyncAPI document.\n          enum:\n            - valid\n            - invalid\n        diagnostics:\n          type: array\n          description: List of validation diagnostics.\n          items:\n            type: object\n        asyncapi:\n          $ref: '#/components/schemas/AsyncAPIDocument'\n        score:\n          type: number\n          description: Score of the AsyncAPI document based on validation.\n          minimum: 0\n          maximum: 100\n    ParseRequest:\n      type: object\n      required:\n        - asyncapi\n      properties:\n        asyncapi:\n          $ref: '#/components/schemas/AsyncAPIDocument'\n    ParseResponse:\n      type: object\n      required:\n        - parsed\n      properties:\n        parsed:\n          type: string\n\n    GenerateRequest:\n      type: object\n      required:\n        - asyncapi\n        - template\n      properties:\n        asyncapi:\n          $ref: \"#/components/schemas/AsyncAPIDocument\"\n        template:\n          type: string\n          description: Template name to be generated.\n          enum:\n            # TODO add more templates\n            - \"@asyncapi/dotnet-nats-template\"\n            - \"@asyncapi/go-watermill-template\"\n            - \"@asyncapi/html-template\"\n            - \"@asyncapi/java-spring-cloud-stream-template\"\n            - \"@asyncapi/java-spring-template\"\n            - \"@asyncapi/java-template\"\n            - \"@asyncapi/markdown-template\"\n            - \"@asyncapi/nodejs-template\"\n            - \"@asyncapi/nodejs-ws-template\"\n            - \"@asyncapi/python-paho-template\"\n            - \"@asyncapi/ts-nats-template\"\n            - \"@asyncapi/minimaltemplate\"\n            - \"@asyncapi/dotnet-rabbitmq-template\"\n        use-fallback-generator:\n          type: boolean\n          description: Use generator version 1.17.25\n          default: false\n        # TODO parameter validation\n        parameters:\n          type: object\n          description: |\n            Template parameters to be generated. Each template has different parameters that you should check in the documentation, \n            which is usually located in the template's repository.\n            This field is optional but may be required for some templates.\n          additionalProperties: true\n    GenerateResponse:\n      type: string\n      format: binary\n\n    ConvertRequest:\n      type: object\n      required:\n        - source\n        - format\n      properties:\n        source:\n          description: The source document to be converted.\n          oneOf:\n            - $ref: '#/components/schemas/AsyncAPIDocument'\n            - $ref: '#/components/schemas/OpenAPIDocument'\n        target-version:\n          description: The version of the AsyncAPI document to be converted to (only applicable for AsyncAPI documents).\n          $ref: '#/components/schemas/SpecVersions'\n        perspective:\n          type: string\n          description: The perspective of the conversion, e.g. \"server\", \"client\".\n          default: server\n          enum:\n            - server\n            - client\n        format:\n          type: string\n          description: The format of the source document to be converted.\n          enum:\n            - asyncapi\n            - openapi\n    ConvertResponse:\n      type: object\n      properties:\n        converted:\n          $ref: '#/components/schemas/AsyncAPIDocument'\n\n    BundleRequest:\n      type: object\n      required:\n        - asyncapis \n      properties:\n        asyncapis:\n          $ref: \"#/components/schemas/AsyncAPIDocuments\"\n        base:\n          $ref: \"#/components/schemas/AsyncAPIDocument\"\n    BundleResponse:\n      type: object\n      required:\n        - bundled \n      properties:\n        bundled:\n          $ref: \"#/components/schemas/AsyncAPIDocument\"\n\n    DiffRequest:\n      type: object\n      required:\n        - asyncapis\n      properties:\n        asyncapis:\n          type: array\n          minItems: 2\n          maxItems: 2\n          items:\n            $ref: '#/components/schemas/AsyncAPIDocument'\n    DiffResponse:\n      type: object\n      properties:\n        diff:\n          type: [object, string]\n          description: The diff between the two AsyncAPI documents.\n\n    HelpListResponse:\n      type: object\n      properties:\n        commands:\n          type: array\n          items:\n            type: string\n          description: A list of all available commands.        \n    HelpCommandResponse:\n      type: object\n      description: Detailed help information for a specific command.\n      properties:\n        command:\n          type: string\n          description: The name of the command.\n        description:\n          type: string\n          description: Detailed description of the command.\n    \n    Problem:\n      type: object\n      properties:\n        type:\n          type: string\n          format: uri\n          description: |\n            An absolute URI that identifies the problem type. When dereferenced,\n            it SHOULD provide human-readable documentation for the problem type\n            (e.g., using HTML).\n          default: \"about:blank\"\n          example: \"https://api.asyncapi.com/problem/invalid-template-parameters\"\n        title:\n          type: string\n          description: |\n            A short, summary of the problem type. Written in english and readable.\n          example: Invalid AsyncAPI document.\n        status:\n          type: integer\n          format: int32\n          description: |\n            The HTTP status code generated by the origin server for this occurrence\n            of the problem.\n          minimum: 100\n          maximum: 600\n          exclusiveMaximum: true\n          example: 422\n        detail:\n          type: string\n          description: |\n            A human readable explanation specific to this occurrence of the problem.\n          example: Connection to database timed out\n        instance:\n          type: string\n          format: uri\n          description: |\n            An absolute URI that identifies the specific occurrence of the problem.\n            It may or may not yield further information if dereferenced.\n      required:\n        - type\n        - title\n        - status\n      additionalProperties: true\n    VersionResponse:\n      type: object\n      description: Version information for the AsyncAPI CLI API\n      properties:\n        version:\n          type: string\n          description: The current version of the AsyncAPI CLI\n          example: \"3.5.1\"\n        name:\n          type: string\n          description: The name of the application\n          example: \"@asyncapi/cli\"\n        description:\n          type: string\n          description: Description of the application\n          example: \"All in one CLI for all AsyncAPI tools\"\n        runtime:\n          type: object\n          description: Runtime information\n          properties:\n            node:\n              type: string\n              description: Node.js version\n              example: \"v24.7.0\"\n            environment:\n              type: string\n              description: Build environment\n              example: \"development\"\n              enum: [\"development\", \"staging\", \"production\"]\n            platform:\n              type: string\n              description: Operating system platform\n              example: \"darwin\"\n            arch:\n              type: string\n              description: System architecture\n              example: \"arm64\"\n            uptime:\n              type: string\n              description: Application uptime\n              example: \"12 seconds\"\n            startTime:\n              type: string\n              format: date-time\n              description: Application start time\n              example: \"2025-09-12T05:08:25.171Z\"\n          required:\n            - node\n            - environment\n            - platform\n            - arch\n            - uptime\n            - startTime\n        repository:\n          type: object\n          description: Repository information\n          properties:\n            url:\n              type: string\n              format: uri\n              description: Repository URL\n              example: \"https://github.com/asyncapi/cli\"\n            bugs:\n              type: string\n              format: uri\n              description: Bug tracker URL\n              example: \"https://github.com/asyncapi/cli/issues\"\n            license:\n              type: string\n              description: License type\n              example: \"Apache-2.0\"\n          required:\n            - url\n            - bugs\n            - license\n        api:\n          type: object\n          description: API metadata\n          properties:\n            basePath:\n              type: string\n              description: API base path\n              example: \"/version\"\n            timestamp:\n              type: string\n              format: date-time\n              description: Current timestamp\n              example: \"2025-09-12T05:08:37.979Z\"\n            health:\n              type: string\n              description: API health status\n              example: \"ok\"\n              enum: [\"ok\", \"degraded\", \"error\"]\n          required:\n            - basePath\n            - timestamp\n            - health\n      required:\n        - version\n        - name\n        - description\n        - build\n        - runtime\n        - repository\n        - api\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@asyncapi/cli\",\n  \"description\": \"All in one CLI for all AsyncAPI tools\",\n  \"version\": \"6.0.0\",\n  \"author\": \"@asyncapi\",\n  \"bin\": {\n    \"asyncapi\": \"./bin/run_bin\"\n  },\n  \"repository\": {\n    \"url\": \"git+https://github.com/asyncapi/cli.git\",\n    \"type\": \"git\"\n  },\n  \"bugs\": \"https://github.com/asyncapi/cli/issues\",\n  \"dependencies\": {\n    \"@asyncapi/avro-schema-parser\": \"^3.0.24\",\n    \"@asyncapi/bundler\": \"^1.0.1\",\n    \"@asyncapi/converter\": \"^2.0.1\",\n    \"@asyncapi/diff\": \"^0.5.0\",\n    \"@asyncapi/generator\": \"^3.2.0\",\n    \"@asyncapi/modelina-cli\": \"^5.10.1\",\n    \"@asyncapi/openapi-schema-parser\": \"^3.0.24\",\n    \"@asyncapi/optimizer\": \"^1.0.4\",\n    \"@asyncapi/parser\": \"^3.6.0\",\n    \"@asyncapi/problem\": \"^1.0.0\",\n    \"@asyncapi/protobuf-schema-parser\": \"^3.6.0\",\n    \"@asyncapi/raml-dt-schema-parser\": \"^4.0.24\",\n    \"@asyncapi/studio\": \"^1.2.0\",\n    \"@clack/prompts\": \"^0.11.0\",\n    \"@oclif/core\": \"^4.8.0\",\n    \"@oclif/plugin-autocomplete\": \"^3.2.39\",\n    \"@smoya/asyncapi-adoption-metrics\": \"^2.4.9\",\n    \"@stoplight/spectral-cli\": \"6.15.0\",\n    \"archiver\": \"^7.0.1\",\n    \"body-parser\": \"^2.2.1\",\n    \"chalk\": \"^4.1.2\",\n    \"chokidar\": \"^4.0.3\",\n    \"compression\": \"^1.8.1\",\n    \"config\": \"^4.1.1\",\n    \"cors\": \"^2.8.5\",\n    \"express\": \"^5.2.1\",\n    \"fast-levenshtein\": \"^3.0.0\",\n    \"fs-extra\": \"^11.3.2\",\n    \"generator-v2\": \"npm:@asyncapi/generator@3.0.1\",\n    \"helmet\": \"^8.1.0\",\n    \"https-proxy-agent\": \"^7.0.6\",\n    \"inquirer\": \"^8.2.6\",\n    \"js-yaml\": \"^4.1.1\",\n    \"oclif\": \"^4.22.57\",\n    \"open\": \"^8.4.2\",\n    \"picocolors\": \"^1.1.1\",\n    \"redoc-express\": \"^2.1.3\",\n    \"unzipper\": \"^0.12.3\",\n    \"uuid\": \"^14.0.0\",\n    \"winston\": \"^3.19.0\",\n    \"ws\": \"^8.18.3\",\n    \"yaml\": \"^2.8.2\"\n  },\n  \"devDependencies\": {\n    \"@asyncapi/minimaltemplate\": \"./test/fixtures/minimaltemplate\",\n    \"@asyncapi/newtemplate\": \"./test/fixtures/newtemplate\",\n    \"@eslint/js\": \"^9.39.2\",\n    \"@oclif/test\": \"^3.2.15\",\n    \"@types/archiver\": \"^7.0.0\",\n    \"@types/body-parser\": \"^1.19.6\",\n    \"@types/chai\": \"^5.2.3\",\n    \"@types/compression\": \"^1.8.1\",\n    \"@types/config\": \"^3.3.5\",\n    \"@types/cors\": \"^2.8.19\",\n    \"@types/express\": \"^5.0.6\",\n    \"@types/fast-levenshtein\": \"^0.0.4\",\n    \"@types/fs-extra\": \"^11.0.4\",\n    \"@types/inquirer\": \"^9.0.9\",\n    \"@types/js-yaml\": \"^4.0.9\",\n    \"@types/mocha\": \"^10.0.10\",\n    \"@types/node\": \"^25.0.3\",\n    \"@types/supertest\": \"^6.0.3\",\n    \"@types/ws\": \"^8.18.1\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.50.0\",\n    \"@typescript-eslint/parser\": \"^8.50.0\",\n    \"c8\": \"^10.1.3\",\n    \"chai\": \"^6.2.1\",\n    \"cross-env\": \"^10.1.0\",\n    \"eslint\": \"^9.39.2\",\n    \"eslint-config-oclif\": \"^6\",\n    \"eslint-plugin-github\": \"^6.0.0\",\n    \"eslint-plugin-security\": \"^3.0.1\",\n    \"eslint-plugin-sonarjs\": \"^3.0.5\",\n    \"markdown-toc\": \"^1.2.0\",\n    \"mocha\": \"^11.7.5\",\n    \"nodemon\": \"^3.1.11\",\n    \"puppeteer\": \"^24.33.0\",\n    \"rimraf\": \"^6.1.2\",\n    \"simple-git\": \"^3.30.0\",\n    \"supertest\": \"^7.1.4\",\n    \"ts-node\": \"^10.9.2\",\n    \"tsc-alias\": \"^1.8.16\",\n    \"tsconfig-paths\": \"^4.2.0\",\n    \"typescript\": \"^5.9.3\"\n  },\n  \"engines\": {\n    \"node\": \">=24.0.0\"\n  },\n  \"files\": [\n    \"/bin\",\n    \"/lib\",\n    \"/assets\",\n    \"/scripts\",\n    \"/npm-shrinkwrap.json\",\n    \"/openapi.yaml\",\n    \"/oclif.manifest.json\"\n  ],\n  \"homepage\": \"https://github.com/asyncapi/cli\",\n  \"keywords\": [\n    \"oclif\"\n  ],\n  \"license\": \"Apache-2.0\",\n  \"main\": \"lib/index.js\",\n  \"oclif\": {\n    \"commands\": \"./lib/apps/cli/commands\",\n    \"bin\": \"asyncapi\",\n    \"additionalVersionFlags\": [\n      \"--v\"\n    ],\n    \"hooks\": {\n      \"command_not_found\": [\n        \"./lib/apps/cli/internal/hooks/command_not_found/myhook\"\n      ]\n    },\n    \"macos\": {\n      \"identifier\": \"com.asyncapi.cli\"\n    },\n    \"topicSeparator\": \" \",\n    \"topics\": {\n      \"config:context\": {},\n      \"config\": {\n        \"description\": \"CLI config settings\"\n      },\n      \"generate\": {\n        \"description\": \"Generate models and template\"\n      }\n    },\n    \"plugins\": [\n      \"@oclif/plugin-warn-if-update-available\",\n      \"@oclif/plugin-autocomplete\"\n    ],\n    \"warn-if-update-available\": {\n      \"frequency\": 24,\n      \"message\": \"\\n<%= chalk.yellow('╭────────────────────────────────────────────────────────────────╮') %>\\n<%= chalk.yellow('│                                                                │') %>\\n<%= chalk.yellow('│') %>               Update available! <%= chalk.red(config.version) %> → <%= chalk.greenBright(latest) %>.                 <%= chalk.yellow('│') %>\\n<%= chalk.yellow('│') %> <%= chalk.magentaBright('Changelog:') %> https://github.com/asyncapi/cli/releases/tag/v<%= latest %> <%= chalk.yellow('│') %>\\n<%= chalk.yellow('│') %>      Run \\\"<%= chalk.magentaBright('npm install -g @asyncapi/cli@latest') %>\\\" to update.      <%= chalk.yellow('│') %>\\n<%= chalk.yellow('│') %>                                                                <%= chalk.yellow('│') %>\\n<%= chalk.yellow('╰────────────────────────────────────────────────────────────────╯') %>\",\n      \"frequencyUnit\": \"hours\"\n    }\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"optionalDependencies\": {\n    \"fsevents\": \"^2.3.3\"\n  },\n  \"scripts\": {\n    \"build\": \"rimraf lib && node scripts/fetch-asyncapi-example.js && npm run generate:languages && tsc && tsc-alias --project tsconfig.json && oclif manifest && echo \\\"Build Completed\\\"\",\n    \"bump:github-action\": \"cd github-action/lib/ && node bump-action-version.js\",\n    \"bump:version\": \"npx -p @changesets/cli@2.27.7 changeset version && npm run bump:github-action\",\n    \"dev\": \"tsc --watch\",\n    \"docker:build\": \"docker build -t asyncapi/cli:latest .\",\n    \"generate:readme:create\": \"node -e \\\"const fs = require('fs'); fs.writeFileSync('scripts/README.md', '# Usage\\\\n\\\\n<!-- usage -->\\\\n\\\\n# Commands\\\\n\\\\n<!-- commands -->\\\\n');\\\"\",\n    \"generate:readme:commands\": \"npm run build && cd scripts && cross-env DEBUG=* oclif readme\",\n    \"generate:assets\": \"npm run generate:readme:toc && npm run generate:commands\",\n    \"generate:commands\": \"npm run generate:readme:create && npm run generate:readme:commands && node ./scripts/updateUsageDocs.js && rimraf ./scripts/README.md\",\n    \"generate:readme:toc\": \"markdown-toc -i README.md\",\n    \"generate:languages\": \"node scripts/generateTypesForGenerateCommand.js\",\n    \"lint\": \"eslint --max-warnings 5 .\",\n    \"lint:fix\": \"eslint --max-warnings 5 . --fix\",\n    \"pack:macos\": \"oclif pack macos && npm run pack:rename\",\n    \"pack:linux\": \"oclif pack deb && npm run pack:rename\",\n    \"pack:tarballs\": \"oclif pack tarballs -t linux-x64 && npm run pack:rename\",\n    \"pack:tarballs:alpine\": \"oclif pack tarballs -t linux-x64 && npm run pack:rename alpine\",\n    \"pack:windows\": \"oclif pack win && npm run pack:rename\",\n    \"pack:rename\": \"node scripts/releasePackagesRename.js\",\n    \"publish:trusted\": \"echo \\\"Working around changesets action not supporting OIDC yet. Need to pass successful release output for triggering github release\\\";VERSION=$(node -p \\\"require('./package.json').version\\\"); PACKAGE_NAME=$(node -p \\\"require('./package.json').name\\\"); if npm view $PACKAGE_NAME@$VERSION > /dev/null 2>&1; then echo \\\"Version $VERSION of package $PACKAGE_NAME already published to NPM. Skipping GitHub release.\\\"; else git tag v$VERSION -m v$VERSION; echo \\\"New tag: $PACKAGE_NAME@$VERSION.\\\"; fi\",\n    \"prepublishOnly\": \"npm run build\",\n    \"pretest\": \"npm run build\",\n    \"pretest:coverage\": \"npm run build\",\n    \"posttest\": \"rimraf ./test.asyncapi-cli\",\n    \"postinstall\": \"node ./scripts/enableAutoComplete.js\",\n    \"test\": \"npm run cli:test && npm run action:test\",\n    \"cli:test\": \"cross-env NODE_ENV=development TEST=1 CUSTOM_CONTEXT_FILENAME=\\\"test.asyncapi-cli\\\" CUSTOM_CONTEXT_FILE_LOCATION=\\\"\\\" NODE_CONFIG_DIR=\\\"./src/apps/api/configs\\\" c8 mocha --require ts-node/register --require tsconfig-paths/register --require test/helpers/init.js --reporter spec --timeout 100000 \\\"test/**/*.test.ts\\\"\",\n    \"unit:test\": \"cross-env NODE_ENV=development TEST=1 CUSTOM_CONTEXT_FILENAME=\\\"test.asyncapi-cli\\\" CUSTOM_CONTEXT_FILE_LOCATION=\\\"\\\" NODE_CONFIG_DIR=\\\"./src/apps/api/configs\\\" c8 mocha --require ts-node/register --require tsconfig-paths/register --require test/helpers/init.js --reporter spec --timeout 100000 \\\"test/unit/**/*.test.ts\\\"\",\n    \"test:one\": \"cross-env NODE_ENV=development TEST=1 CUSTOM_CONTEXT_FILENAME=\\\"test.asyncapi-cli\\\" CUSTOM_CONTEXT_FILE_LOCATION=\\\"\\\"  NODE_CONFIG_DIR=\\\"./src/apps/api/configs\\\" c8 mocha --require ts-node/register --require tsconfig-paths/register --require test/helpers/init.js --reporter spec --timeout 100000\",\n    \"get-version\": \"echo $npm_package_version\",\n    \"createhook\": \"oclif generate hook myhook --event=command_not_found\",\n    \"createhookinit\": \"oclif generate hook inithook --event=init\",\n    \"action:docker:build\": \"docker build -f github-action/Dockerfile -t asyncapi/github-action-for-cli:latest .\",\n    \"action:test\": \"npm run build && cd github-action && make test\",\n    \"api:dev\": \"cross-env NODE_ENV=development PORT=3000 nodemon\",\n    \"api:prod\": \"npm run api:build && cross-env NODE_ENV=production PORT=80 node lib/apps/api/server.js\",\n    \"api:docker\": \"docker run -it -p 80:80 asyncapi/server-api\",\n    \"api:build:docker\": \"docker build -t asyncapi/server-api -f ./src/apps/api/Dockerfile .\",\n    \"api:build\": \"rimraf lib && npm run generate:languages && tsc && tsc-alias --project tsconfig.json\",\n    \"api:test\": \"\"\n  },\n  \"types\": \"lib/index.d.ts\"\n}\n"
  },
  {
    "path": "scripts/enableAutoComplete.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\nconst { spawnSync } = require('child_process');\nconst os = require('os');\nconst fs = require('fs');\nconst path = require('path');\n\nconst allowedShells = ['zsh', 'bash'];\n\n// Helper function to find the first existing file among a list of paths\nfunction findExistingFile(possibleFiles) {\n  for (const file of possibleFiles) {\n    const fullPath = path.join(os.homedir(), file);\n    if (fs.existsSync(fullPath)) {\n      return fullPath;\n    }\n  }\n  return null;\n}\n\nconst shellConfigs = {\n  zsh: {\n    rcFile: path.join(os.homedir(), '.zshrc'),\n    detectFile: path.join(os.homedir(), '.zshrc'),\n    postMessage: 'Run: source ~/.zshrc',\n    action: (output, rcFile) => {\n      const configContent = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf-8') : '';\n\n      if (configContent.includes(output.trim())) {\n        console.log(`✅ Autocomplete is already configured in ${rcFile}. Skipping addition.`);\n      } else {\n        fs.appendFileSync(rcFile, `\\n# AsyncAPI CLI Autocomplete\\n${output}\\n`);\n        console.log(`✅ Autocomplete configuration added to ${rcFile}.`);\n      }\n    },\n  },\n  bash: {\n    rcFile: findExistingFile(['.bashrc', '.bash_profile', '.profile']) || path.join(os.homedir(), '.bashrc'),\n    detectFile: findExistingFile(['.bashrc', '.bash_profile', '.profile']),\n    postMessage: '', // This will be set dynamically later\n    action: (output, rcFile) => {\n      const configContent = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf-8') : '';\n\n      if (configContent.includes(output.trim())) {\n        console.log(`✅ Autocomplete is already configured in ${rcFile}. Skipping addition.`);\n      } else {\n        fs.appendFileSync(rcFile, `\\n# AsyncAPI CLI Autocomplete\\n${output}\\n`);\n        console.log(`✅ Autocomplete configuration added to ${rcFile}.`);\n      }\n    },\n  },\n};\n\n// Set correct postMessage dynamically\nif (shellConfigs.bash.detectFile) {\n  shellConfigs.bash.postMessage = `Run: source ${shellConfigs.bash.detectFile}`;\n} else {\n  shellConfigs.bash.postMessage = 'Run: source ~/.bashrc';\n}\n\nfunction getShellConfig(shell) {\n  if (!allowedShells.includes(shell)) {\n    throw new Error(`Unsupported shell: ${shell}. Autocomplete only supports zsh and bash.`);\n  }\n  return shellConfigs[shell];\n}\n\nfunction detectShell() {\n  const detectedShells = [];\n  for (const [shell, config] of Object.entries(shellConfigs)) {\n    if (config.detectFile && fs.existsSync(config.detectFile)) {\n      detectedShells.push(shell);\n    }\n  }\n  return detectedShells;\n}\n\nfunction checkPotentialPath(potentialPath) {\n  if (potentialPath.includes(path.sep)) {\n    if (fs.existsSync(potentialPath)) {\n      return potentialPath;\n    }\n  } else {\n    const result = spawnSync('/bin/sh', ['-c', `command -v ${potentialPath}`], {\n      encoding: 'utf-8',\n      stdio: 'pipe',\n    });\n    if (result.status === 0 && result.stdout) {\n      return result.stdout.trim().split('\\n')[0];\n    }\n  }\n  return null;\n}\n\nfunction findCliExecutable() {\n  const possiblePaths = [\n    path.resolve('./bin/run'),\n    path.resolve('../bin/run'),\n    path.resolve('./node_modules/.bin/asyncapi'),\n    'asyncapi',\n  ];\n\n  for (const potentialPath of possiblePaths) {\n    try {\n      const foundPath = checkPotentialPath(potentialPath);\n      if (foundPath) {\n        console.log(`Found CLI executable at: ${foundPath}`);\n        return foundPath;\n      }\n    } catch (error) {\n      console.warn(`⚠️ Ignored error while checking path ${potentialPath}: ${error.message}`);\n    }\n  }\n\n  throw new Error('CLI executable not found. Ensure AsyncAPI CLI is installed.');\n}\n\nfunction generateAutocompleteScript(shell) {\n  const executablePath = findCliExecutable();\n  const result = spawnSync(executablePath, ['autocomplete', 'script', shell], {\n    encoding: 'utf-8',\n    stdio: 'pipe',\n  });\n  if (result.status !== 0 || result.error) {\n    throw new Error(\n      `Autocomplete setup for ${shell} failed: ${result.stderr || result.error?.message || 'Unknown error'}`\n    );\n  }\n  const output = result.stdout;\n  if (!output || output.trim() === '') {\n    throw new Error(`No autocomplete script generated for ${shell}.`);\n  }\n  return output;\n}\n\nfunction setupAutocomplete(shell) {\n  if (!allowedShells.includes(shell)) {\n    console.error(`❌ Autocomplete only supports zsh and bash. Skipping setup for ${shell}.`);\n    return;\n  }\n\n  try {\n    const config = getShellConfig(shell);\n    console.log(`🔧 Generating autocomplete script for ${shell}...`);\n    const output = generateAutocompleteScript(shell);\n    config.action(output, config.rcFile);\n    console.log(`✅ Autocomplete configured for ${shell}. ${config.postMessage}`);\n  } catch (error) {\n    console.error(`❌ Failed to setup autocomplete for ${shell}: ${error.message}`);\n  }\n}\n\n// Start\nconst shells = detectShell();\nif (shells.length) {\n  for (const shell of shells) {\n    setupAutocomplete(shell);\n  }\n} else {\n  console.log('⚠️ Shell not detected or unsupported. Autocomplete setup skipped.');\n}\n"
  },
  {
    "path": "scripts/fetch-asyncapi-example.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\n\nconst fs = require('fs');\nconst unzipper = require('unzipper');\nconst path = require('path');\n\nconst { Parser } = require('@asyncapi/parser/cjs');\nconst { AvroSchemaParser } = require('@asyncapi/avro-schema-parser');\nconst { OpenAPISchemaParser } = require('@asyncapi/openapi-schema-parser');\nconst { RamlDTSchemaParser } = require('@asyncapi/raml-dt-schema-parser');\nconst { pipeline } = require('stream');\nconst { promisify } = require('util');\n\nconst streamPipeline = promisify(pipeline);\n\nconst parser = new Parser({\n  schemaParsers: [\n    AvroSchemaParser(),\n    OpenAPISchemaParser(),\n    RamlDTSchemaParser(),\n  ]\n});\n\nconst SPEC_EXAMPLES_ZIP_URL = 'https://github.com/asyncapi/spec/archive/refs/heads/master.zip';\nconst EXAMPLE_DIRECTORY = path.join(__dirname, '../assets/examples');\nconst TEMP_ZIP_NAME = 'spec-examples.zip';\n\nconst fetchAsyncAPIExamplesFromExternalURL = () => {\n  try {\n    return new Promise((resolve, reject) => {\n      fetch(SPEC_EXAMPLES_ZIP_URL)\n        .then(async (res) => {\n          if (res.status !== 200) {\n            return reject(new Error(`Failed to fetch examples from ${SPEC_EXAMPLES_ZIP_URL}`));\n          }\n\n          const file = fs.createWriteStream(TEMP_ZIP_NAME);\n          await streamPipeline(res.body, file);\n\n          console.log('Fetched ZIP file');\n          resolve();\n        })\n        .catch(reject);\n    });\n  } catch (error) {\n    console.error(error);\n  }\n};\n\nconst unzipAsyncAPIExamples = async () => {\n  return new Promise((resolve, reject) => {\n    if (!fs.existsSync(EXAMPLE_DIRECTORY)) {\n      fs.mkdirSync(EXAMPLE_DIRECTORY);\n    }\n\n    fs.createReadStream(TEMP_ZIP_NAME)\n      .pipe(unzipper.Parse())\n      .on('entry', async (entry) => {\n        const fileName = entry.path;\n        if (fileName.includes('examples/') && fileName.includes('.yml') && entry.type === 'File') {\n          const fileContent = await entry.buffer();\n          const fileNameWithExtension = fileName.split('examples/')[1];\n          fs.writeFileSync(path.join(EXAMPLE_DIRECTORY, fileNameWithExtension), fileContent.toString());\n        } else {\n          entry.autodrain();\n        }\n      }).on('close', () => {\n        console.log('Unzipped all examples from ZIP');\n        resolve();\n      }).on('error', (error) => {\n        reject(new Error(`Error in unzipping from ZIP: ${error.message}`));\n      });\n  });\n};\n\nconst buildCLIListFromExamples = async () => {\n  const files = fs.readdirSync(EXAMPLE_DIRECTORY);\n  const examples = files.filter(file => file.includes('.yml')).sort();\n\n  const buildExampleList = examples.map(async example => {\n    const examplePath = path.join(EXAMPLE_DIRECTORY, example);\n    const exampleContent = fs.readFileSync(examplePath, { encoding: 'utf-8' });\n\n    try {\n      const { document } = await parser.parse(exampleContent);\n      // Failed for some reason to parse this spec file (document is undefined), ignore for now\n      if (!document) {\n        return;\n      }\n\n      const title = document.info().title();\n      const protocols = listAllProtocolsForFile(document);\n      return {\n        name: protocols ? `${title} - (protocols: ${protocols})` : title,\n        value: example\n      };\n    } catch (error) {\n      console.error(error);\n    }\n  });\n\n  const exampleList = (await Promise.all(buildExampleList)).filter(item => !!item);\n  const orderedExampleList = exampleList.sort((a, b) => a.name.localeCompare(b.name));\n\n  fs.writeFileSync(path.join(EXAMPLE_DIRECTORY, 'examples.json'), JSON.stringify(orderedExampleList, null, 4));\n};\n\nconst listAllProtocolsForFile = (document) => {\n  const servers = document.servers();\n  if (servers.length === 0) {\n    return '';\n  }\n\n  return servers.all().map(server => server.protocol()).join(',');\n};\n\nconst tidyUp = async () => {\n  fs.unlinkSync(TEMP_ZIP_NAME);\n};\n\n(async () => {\n  await fetchAsyncAPIExamplesFromExternalURL();\n  await unzipAsyncAPIExamples();\n  await buildCLIListFromExamples();\n  await tidyUp();\n})();\n"
  },
  {
    "path": "scripts/generateTypesForGenerateCommand.js",
    "content": "const { writeFile } = require('fs/promises');\nconst path = require('path');\nconst { listBakedInTemplates } = require('@asyncapi/generator');\n\nasync function generateClientLanguages() {\n  const bakedInClients = listBakedInTemplates({ type: 'client' });\n\n  const targets = Array.from(new Set(bakedInClients.map(t => t.target))).sort();\n\n  const enumEntries = targets\n    .map(t => `  ${capitalize(t)} = '${t}',`)\n    .join('\\n');\n\n  const fileContent = `// Auto-generated. Do not edit manually.\nexport enum AvailableLanguage {\n${enumEntries}\n}\n\nexport const availableLanguages = [${targets.map(t => `'${t}'`).join(', ')}] as const;\nexport type AvailableLanguageType = typeof availableLanguages[number];\n\n/**\n * Returns the first available language as the default option.\n */\nexport const getDefaultLanguage = (): AvailableLanguageType => availableLanguages[0];\n`;\n\n  const outputPath = path.join(__dirname, '../src/domains/models/generate/ClientLanguages.ts');\n  await writeFile(outputPath, fileContent);\n\n  console.log('✅ ClientLanguages.ts generated');\n}\n\nfunction capitalize(str) {\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\ngenerateClientLanguages().catch(err => {\n  throw new Error(`Failed to generate ClientLanguages.ts: ${err.message}`);\n});\n"
  },
  {
    "path": "scripts/releasePackagesRename.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\n\nconst { rename, access, mkdir } = require('fs').promises;\nconst packageJson = require('../package.json');\nconst path = require('path');\nconst simpleGit = require('simple-git');\nconst git = simpleGit({baseDir: process.cwd()});\n\nasync function fileExists(checkPath) {\n  try {\n    await access(checkPath);\n    return true;\n  } catch (e) {\n    return false;\n  }\n}\n\nasync function checkAndRenameFile(generatedPath, newPath) {\n  if (await fileExists(generatedPath)) {\n    await rename(generatedPath, newPath);\n  }\n}\n\nasync function createDirectory(directoryPath) {\n  const exists = await fileExists(directoryPath);\n  if (!exists) {\n    await mkdir(directoryPath);\n  }\n}\n\nasync function renameDeb({version, name, sha}) {\n  const dist = 'dist/deb';\n\n  // deb package naming convention: https://github.com/oclif/oclif/blob/fb5da961f925fa0eba5c5b05c8cee0c9bd156c00/src/upload-util.ts#L51\n  const generatedPath = path.resolve(dist, `${name}_${version}.${sha}-1_amd64.deb`);\n  const newPath = path.resolve(dist, 'asyncapi.deb');\n  await checkAndRenameFile(generatedPath, newPath);\n}\n\nasync function renameTar({version, name, sha, isAlpine}) {\n  const dist = 'dist';\n\n  const generatedPath = path.resolve(dist, `${name}-v${version}-${sha}-linux-x64.tar.gz`);\n  // for tarballs, the files are generated in `dist/` directory.\n  // Creates a new `tar` directory(`dist/tar`), and moves the generated tarball inside that directory.\n  const tarDirectory = path.resolve(dist, 'tar');\n  await createDirectory(tarDirectory);\n\n  const fileName = isAlpine ? 'asyncapi.alpine.tar.gz' : 'asyncapi.tar.gz';\n  const newPath = path.resolve(tarDirectory, fileName);\n  await checkAndRenameFile(generatedPath, newPath);\n}\n\nasync function renameWindows({version, name, sha, arch}) {\n  const dist = 'dist/win32';\n\n  const generatedPath = path.resolve(dist, `${name}-v${version}-${sha}-${arch}.exe`);\n  const newPath = path.resolve(dist, `asyncapi.${arch}.exe`);\n  await checkAndRenameFile(generatedPath, newPath);\n}\n\nasync function renamePkg({version, name, sha, arch}) {\n  const dist = 'dist/macos';\n\n  const generatedPath = path.resolve(dist, `${name}-v${version}-${sha}-${arch}.pkg`);\n  const newPath = path.resolve(dist, `asyncapi.${arch}.pkg`);\n  await checkAndRenameFile(generatedPath, newPath);\n}\n\nasync function renamePackages() {\n  const version = packageJson.version;\n  const name = 'asyncapi';\n  const sha = await git.revparse(['--short', 'HEAD']);\n  const isAlpine = process.argv.includes('alpine');\n\n  if (isAlpine) {\n    await renameTar({version, name, sha, isAlpine: true});\n  } else {\n    await renameDeb({version: version.split('-')[0], name, sha});\n    await renamePkg({version, name, sha, arch: 'x64'});\n    await renamePkg({version, name, sha, arch: 'arm64'});\n    await renameWindows({version, name, sha, arch: 'x64'});\n    await renameWindows({version, name, sha, arch: 'x86'});\n    await renameTar({version, name, sha, isAlpine: false});\n  }\n}\n\nrenamePackages();\n"
  },
  {
    "path": "scripts/updateUsageDocs.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\nconst {writeFile, readFile} = require('fs').promises;\n\n// Define the paths to the README and usage files\nconst README_PATH = './scripts/README.md'; // File path for the generated README file\nconst USAGE_PATH = './docs/usage.md'; // File path for the usage documentation file\n\nconst header = `---\ntitle: 'Usage'\nweight: 40\n---\n\n<!-- \n\nThis file is automatically generated from updateUsageDocs.js script. In package.json in line 158-161 lines the following steps has been executed in order to run this script successfully - \n\n* generate:readme:create: It creates the initial content for the README file by printing the usage and commands tags using printf and redirects the output to scripts/README.md file.\n* generate:readme:commands: It changes the directory to the scripts folder and executes the oclif readme command. This command generates the usage and commands sections based on the CLI commands and updates the content in the scripts/README.md file.\n* generate:assets: This script combines the two previously mentioned scripts (generate:readme:toc and generate:commands) to generate the necessary assets, such as the README file and usage documentation.\n* generate:commands: This script executes the following steps:\n - Runs the generate:readme:create script to create the initial content for the README file.\n - Executes the generate:readme:commands script to generate the usage and commands sections based on the CLI commands.\n - Runs the updateUsageDocs.js script using Node.js to update the usage documentation file with the contents of the generated README file.\n - Deletes the scripts/README.md file using the rimraf command.\n\n-->\n\nThe AsyncAPI CLI makes it easier to work with AsyncAPI documents.\n`;\n\n// Define an async function to write the header and the README contents to the usage documentation file\nasync function run() {\n  try {\n    await writeFile(USAGE_PATH, header);\n    const readmeContents = await readContents();\n    // Append the contents of the README file to the usage documentation file\n    await writeFile(USAGE_PATH, readmeContents, { flag: 'a' });\n  } catch (e) {\n    console.error(e);\n  }\n}\n\nrun();\n\nasync function readContents() {\n  let readmeContents;\n  let commandsContent = '';\n\n  while (commandsContent.length === 0) {\n    readmeContents = await readFile(README_PATH, 'utf8');\n    \n    // Check if the content between <!-- commands --> and <!-- commandsstop --> is empty\n    const commandsStartText = '<!-- commands -->';\n    const commandStartIndex = readmeContents.indexOf(commandsStartText);\n    const commandStopIndex = readmeContents.indexOf('<!-- commandsstop -->');\n    //cutting the content between the above mentioned tags, removing white spaces and checking if there is some text as it will mean text was added by oclif\n    commandsContent = readmeContents.slice(commandStartIndex + commandsStartText.length, commandStopIndex).trim();\n\n    if (commandsContent.length === 0) {\n      console.log('No content between <!-- commands --> and <!-- commandsstop -->. Trying again...');\n    } else {\n      console.log('Content found!');\n    }\n\n    await delay(3000); // 3-second delay\n  }\n\n  return readmeContents;\n}\n\nfunction delay(ms) {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n"
  },
  {
    "path": "src/apps/api/.do/app.yaml",
    "content": "name: server-api\nregion: sfo3\n\ndomains:\n  - domain: api.asyncapi.com\n    type: PRIMARY\n\ningress:\n  rules:\n    - component:\n        name: asyncapi-server-api\n      match:\n        path:\n          prefix: /\n      cors:\n        allow_origins:\n          - exact: \"*\"\n        allow_methods: [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"]\n        allow_headers: [\"*\"]\n\nservices:\n  - name: asyncapi-server-api\n    image:\n      registry_type: DOCKER_HUB\n      registry: asyncapi\n      repository: server-api\n      digest: ${DOCKER_DIGEST}\n    http_port: 80\n    health_check:\n      http_path: /v1/help\n      port: 80\n    envs:\n      - key: PORT\n        value: \"80\"\n    instance_count: 1\n    instance_size_slug: basic-xs\n    alerts:\n      - rule: CPU_UTILIZATION\n        operator: GREATER_THAN\n        value: 80\n        window: TEN_MINUTES\n\nalerts:\n  - rule: DEPLOYMENT_FAILED\n"
  },
  {
    "path": "src/apps/api/.do/apps/.gitignore",
    "content": ".terraform\nterraform.tfstate\nterraform.tfstate.backup\n"
  },
  {
    "path": "src/apps/api/.do/apps/.terraform.lock.hcl",
    "content": "# This file is maintained automatically by \"terraform init\".\n# Manual edits may be lost in future updates.\n\nprovider \"registry.terraform.io/digitalocean/digitalocean\" {\n  version     = \"2.34.1\"\n  constraints = \">= 2.0.0\"\n  hashes = [\n    \"h1:iRyhFUfKnDRWx75+alSOEtdS0BtNUkrvLusC15s34eo=\",\n    \"zh:022d4c97af3d022d4e3735a81c6a7297aa43c3b28a8cecaa0ff58273a5677e2e\",\n    \"zh:1922f86d5710707eb497fbebcb1a1c5584c843a7e95c3900d750d81bd2785204\",\n    \"zh:1b7ab7c67a26c399eb5aa8a7a695cb59279c6a1a562ead3064e4a6b17cdacabe\",\n    \"zh:1dc666faa2ec0efc32329b4c8ff79813b54741ef1741bc42d90513e5ba904048\",\n    \"zh:220dec61ffd9448a91cca92f2bc6642df10db57b25d3d27036c3a370e9870cb7\",\n    \"zh:262301545057e654bd6193dc04b01666531fccfcf722f730827695098d93afa7\",\n    \"zh:63677684a14e6b7790833982d203fb2f84b105ad6b9b490b3a4ecc7043cdba81\",\n    \"zh:67a2932227623073aa9431a12916b52ce1ccddb96f9a2d6cdae2aaf7558ccbf8\",\n    \"zh:70dfc6ac33ee140dcb29a971df7eeb15117741b5a75b9f8486c5468c9dd28f24\",\n    \"zh:7e3b3b62754e86442048b4b1284e10807e3e58f417e1d59a4575dd29ac6ba518\",\n    \"zh:7e6fe662b1e283ad498eb2549d0c2260b908ab5b848e05f84fa4acdca5b4d5ca\",\n    \"zh:9c554170f20e659222896533a3a91954fb1d210eea60de05aea803b36d5ccd5d\",\n    \"zh:ad2f64d758bd718eb39171f1c31219900fd2bfb552a14f6a90b18cfd178a74b4\",\n    \"zh:cfce070000e95dfe56a901340ac256f9d2f84a73bf62391cba8a8e9bf1f857e0\",\n    \"zh:d5ae30eccd53ca7314157e62d8ec53151697ed124e43b24b2d16c565054730c6\",\n    \"zh:fbe5edf5337adb7360f9ffef57d02b397555b6a89bba68d1b60edfec6e23f02c\",\n  ]\n}\n"
  },
  {
    "path": "src/apps/api/.do/apps/main.tf",
    "content": "terraform {\n  required_version = \">= 1.0.0\"\n\n  required_providers {\n    digitalocean = {\n      source  = \"digitalocean/digitalocean\"\n      version = \">= 2.0.0\"\n    }\n  }\n}\n\nprovider \"digitalocean\" {}\n\nresource \"digitalocean_app\" \"server-api\" {\n  spec {\n    name = \"server-api\"\n    region = \"sfo3\"\n\n    domain {\n      name = \"api.asyncapi.com\"\n      type = \"PRIMARY\"\n    }\n\n    ingress {\n      rule {\n        component {\n          name = \"asyncapi-server-api\"\n        }\n        match {\n          path {\n            prefix = \"/\"\n          }\n        }\n        cors {\n          allow_origins {\n           exact = \"*\"\n          }\n          allow_methods = [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"]\n          allow_headers = [\"*\"]\n        }\n      }\n    }\n\n    service {\n      name = \"asyncapi-server-api\"\n      http_port = 80\n      health_check {\n        http_path = \"/v1/help/validate\"\n        port = 80\n      }\n      env {\n        key = \"PORT\"\n        value = \"80\"\n      }\n\n      image {\n        registry_type = \"DOCKER_HUB\"\n        registry = \"asyncapi\"\n        repository = \"server-api\"\n        tag = \"latest\"\n      }\n\n      instance_count = 1\n      instance_size_slug = \"basic-xs\" // $10/month\n\n      alert {\n        rule = \"CPU_UTILIZATION\"\n        value = 80\n        operator = \"GREATER_THAN\"\n        window = \"TEN_MINUTES\"\n      }\n    }\n\n    alert {\n      rule = \"DEPLOYMENT_FAILED\"\n    }\n  }\n}\n\noutput \"live_url\" {\n  value = digitalocean_app.server-api.default_ingress\n}\n"
  },
  {
    "path": "src/apps/api/Dockerfile",
    "content": "# ---- Base Alpine with Node ----\nFROM node:24-alpine AS base\n\n# Install required system packages\nRUN apk add --update nghttp2\n\n# ---- Build stage ----\nFROM base AS build\n\nWORKDIR /app\n\n# 1. Copy only package files first (for better caching)\nCOPY package*.json ./\n\n# 2. Install dependencies (cached unless package*.json changes)\nRUN npm install --ignore-scripts\n\n# 3. Copy the rest of the application code\nCOPY ./assets ./assets\nCOPY ./src ./src\nCOPY ./bin ./bin\nCOPY ./scripts ./scripts\nCOPY openapi.yaml ./\nCOPY *.json ./\n\n# 4. Build TypeScript or production-ready JS\nRUN npm run api:build\n\n# ---- Templates stage ----\nFROM base AS templates\n\n# Copy templates list\nCOPY ./src/apps/api/templates.json /tmp/templates.json\n\n# Install generator templates globally using the templates.json array\n# This layer will be cached unless template versions change\nRUN TEMPLATES=$(node -e \"console.log(JSON.parse(require('fs').readFileSync('/tmp/templates.json', 'utf8')).join(' '))\") && \\\n    npm install -g $TEMPLATES\n\n# ---- Release stage ----\nFROM base AS release\n\nWORKDIR /app\n\n# Create a non-root user\nRUN addgroup -g 1001 -S nodejs \\\n  && adduser -S asyncapi -u 1001\n\n# Copy built code\nCOPY --from=build /app/lib ./lib\n\n# Copy package files\nCOPY --from=build /app/package*.json ./\n\n# Install production deps, then remove heavy ones\nRUN npm ci --omit=dev --ignore-scripts \\\n  && npm uninstall @asyncapi/studio next \\\n  && npm cache clean --force \\\n  && npm prune --production\n\n# Copy globally installed templates from templates stage\nCOPY --from=templates /usr/local/lib/node_modules /usr/local/lib/node_modules\nCOPY --from=templates /usr/local/bin /usr/local/bin\n\n# Copy OpenAPI document\nCOPY openapi.yaml ./\n\n# Change ownership of the app directory to the non-root user\nRUN chown -R asyncapi:nodejs /app\n\n# Change ownership of node_modules to the non-root user\nRUN chown -R asyncapi:nodejs /usr/local/lib/node_modules\n\n# Switch to non-root user\nUSER asyncapi\n\n# Expose the application port\nEXPOSE 80\n\nENV NODE_ENV=production\nENV PORT=80\n\nCMD [\"node\", \"lib/apps/api/server.js\"]\n"
  },
  {
    "path": "src/apps/api/README.md",
    "content": "[![AsyncAPI Server API](../../../assets/server-api.png)](https://www.asyncapi.com)\n\nServer API providing official AsyncAPI tools.\n\n## :loudspeaker: ATTENTION:\n\nThis package is still under development and has not published and reached version 1.0.0 yet. This means that its API may contain breaking changes until we're able to deploy the first stable version and begin semantic versioning.\n\n---\n\n<!-- toc is generated with GitHub Actions do not remove toc markers -->\n\n<!-- toc -->\n\n- [Requirements](#requirements)\n- [Using it locally](#using-it-locally)\n- [Using it via Docker](#using-it-via-docker)\n- [Development](#development)\n- [Deployment](#deployment)\n  * [How the GitHub workflow works](#how-the-github-workflow-works)\n- [Contribution](#contribution)\n- [Supported by](#supported-by)\n- [Contributors](#contributors)\n\n<!-- tocstop -->\n\n## Requirements\n\n- [NodeJS](https://nodejs.org/en/) >= 14\n\n## Using it locally\n\nRun:\n\n```bash\nnpm install\nnpm run api:prod\n```\n\nserver is ready to use on [http://localhost:80](http://localhost:80).\n\n## Using it via Docker\n\nRun:\n\n```bash\ndocker run -it -p 80:80 asyncapi/server-api\n```\n\nserver is ready to use on [http://localhost:80](http://localhost:80).\n\n## Development\n\n1. Setup project by installing dependencies `npm install`\n2. Write code and tests.\n3. Make sure all tests pass `npm test`\n\n## Deployment\n\nThis project is deployed to [DigitalOcean App Platform](https://www.digitalocean.com/products/app-platform/) using [Terraform](https://www.terraform.io/) and [GitHub Actions](https://www.github.com/digitalocean/app_action/). To deploy it to your own account, follow these steps:\n\n1. Fork this repository.\n2. Create a [DigitalOcean Personal Access Token](https://cloud.digitalocean.com/account/api/tokens) with `read` and `write` permissions. For more information, see [DigitalOcean's documentation](https://docs.digitalocean.com/reference/api/create-personal-access-token/).\n3. Run `terraform init` to initialize the Terraform project as can be seen [here](./deployments/apps/main.tf). This should be run being located at ./deployments/apps directory preferably.\n4. Run `terraform apply` to create the necessary infrastructure. \n\n> [!NOTE]\n> You need to export the following environment variables before running `terraform apply`:\n> - `DIGITALOCEAN_ACCESS_TOKEN`: Your DigitalOcean Personal Access Token.\n\n\n### How the GitHub workflow works\n\nThe [GitHub workflow](./.github/workflows/release-docker.yml) is triggered when a new tag is pushed to the repository. It will build a new Docker image and push it to the [Docker Hub](https://hub.docker.com/r/asyncapi/server-api) repository. Then the [DigitalOcean App Platform GitHub Action](https://www.github.com/digitalocean/app_action/) updates the application with the new image.\n\n## Contribution\n\nRead [CONTRIBUTING](https://github.com/asyncapi/.github/blob/master/CONTRIBUTING.md) guide.\n\n## Supported by\n\n<p>\n  <a href=\"https://www.digitalocean.com/\">\n    <img src=\"https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg\" width=\"201px\">\n  </a>\n</p>\n\n## Contributors\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tbody>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/magicmatatjahu\"><img src=\"https://avatars.githubusercontent.com/u/20404945?v=4?s=100\" width=\"100px;\" alt=\"Maciej Urbańczyk\"/><br /><sub><b>Maciej Urbańczyk</b></sub></a><br /><a href=\"#maintenance-magicmatatjahu\" title=\"Maintenance\">🚧</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=magicmatatjahu\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=magicmatatjahu\" title=\"Documentation\">📖</a> <a href=\"https://github.com/asyncapi/server-api/issues?q=author%3Amagicmatatjahu\" title=\"Bug reports\">🐛</a> <a href=\"#ideas-magicmatatjahu\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/server-api/pulls?q=is%3Apr+reviewed-by%3Amagicmatatjahu\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=magicmatatjahu\" title=\"Tests\">⚠️</a> <a href=\"#infra-magicmatatjahu\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a> <a href=\"#mentoring-magicmatatjahu\" title=\"Mentoring\">🧑‍🏫</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://bolt04.github.io/react-ultimate-resume/\"><img src=\"https://avatars.githubusercontent.com/u/18630253?v=4?s=100\" width=\"100px;\" alt=\"David Pereira\"/><br /><sub><b>David Pereira</b></sub></a><br /><a href=\"#maintenance-BOLT04\" title=\"Maintenance\">🚧</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=BOLT04\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=BOLT04\" title=\"Documentation\">📖</a> <a href=\"https://github.com/asyncapi/server-api/issues?q=author%3ABOLT04\" title=\"Bug reports\">🐛</a> <a href=\"#ideas-BOLT04\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/server-api/pulls?q=is%3Apr+reviewed-by%3ABOLT04\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=BOLT04\" title=\"Tests\">⚠️</a> <a href=\"#infra-BOLT04\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a> <a href=\"#mentoring-BOLT04\" title=\"Mentoring\">🧑‍🏫</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/smoya\"><img src=\"https://avatars.githubusercontent.com/u/1083296?v=4?s=100\" width=\"100px;\" alt=\"Sergio Moya\"/><br /><sub><b>Sergio Moya</b></sub></a><br /><a href=\"#maintenance-smoya\" title=\"Maintenance\">🚧</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=smoya\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=smoya\" title=\"Documentation\">📖</a> <a href=\"https://github.com/asyncapi/server-api/issues?q=author%3Asmoya\" title=\"Bug reports\">🐛</a> <a href=\"#ideas-smoya\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/asyncapi/server-api/pulls?q=is%3Apr+reviewed-by%3Asmoya\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=smoya\" title=\"Tests\">⚠️</a> <a href=\"#infra-smoya\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a> <a href=\"#mentoring-smoya\" title=\"Mentoring\">🧑‍🏫</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://ritik307.github.io/portfolio/\"><img src=\"https://avatars.githubusercontent.com/u/22374829?v=4?s=100\" width=\"100px;\" alt=\"Ritik Rawal\"/><br /><sub><b>Ritik Rawal</b></sub></a><br /><a href=\"https://github.com/asyncapi/server-api/commits?author=ritik307\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=ritik307\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://everly-precia.netlify.app/\"><img src=\"https://avatars.githubusercontent.com/u/77877486?v=4?s=100\" width=\"100px;\" alt=\"Everly Precia Suresh\"/><br /><sub><b>Everly Precia Suresh</b></sub></a><br /><a href=\"https://github.com/asyncapi/server-api/commits?author=everly-gif\" title=\"Code\">💻</a> <a href=\"https://github.com/asyncapi/server-api/commits?author=everly-gif\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://ashishpadhy.live\"><img src=\"https://avatars.githubusercontent.com/u/100484401?v=4?s=100\" width=\"100px;\" alt=\"Ashish Padhy\"/><br /><sub><b>Ashish Padhy</b></sub></a><br /><a href=\"https://github.com/asyncapi/server-api/commits?author=Shurtu-gal\" title=\"Documentation\">📖</a> <a href=\"#infra-Shurtu-gal\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a></td>\n    </tr>\n  </tbody>\n</table>\n\n<!-- markdownlint-restore -->\n<!-- prettier-ignore-end -->\n\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n"
  },
  {
    "path": "src/apps/api/app.ts",
    "content": "import bodyParser from 'body-parser';\nimport compression from 'compression';\nimport config from 'config';\nimport cors from 'cors';\nimport express from 'express';\nimport helmet from 'helmet';\n\nimport { Controller } from '@/interfaces';\n\n// import { problemMiddleware } from './middlewares/problem.middleware';\n\nimport { logger } from '../../utils/logger';\nimport { API_VERSION } from './constants';\nimport { problemMiddleware } from './middlewares/problem.middleware';\nimport { loggerMiddleware } from './middlewares/logger.middleware';\n\nexport class App {\n  private app: express.Application;\n\n  constructor(\n    private readonly controllers: Controller[],\n    private readonly port: number | string = process.env.PORT || 80,\n    private readonly env: string = process.env.NODE_ENV || 'development',\n  ) {\n    this.app = express();\n  }\n\n  public async init() {\n    // initialize core middlewares\n    await this.initializeMiddlewares();\n    // initialize controllers\n    await this.initializeControllers();\n    // initialize error handling\n    await this.initializeErrorHandling();\n  }\n\n  public listen() {\n    this.app.listen(this.port, () => {\n      logger.info('=================================');\n      logger.info(`= ENV: ${this.env}`);\n      logger.info(\n        `= 🚀 AsyncAPI Server API listening on the port ${this.port}`,\n      );\n      logger.info('=================================');\n    });\n  }\n\n  public getServer() {\n    return this.app;\n  }\n\n  private async initializeMiddlewares() {\n    const requestBodyLimit = config.get<string>('request.body.limit');\n\n    this.app.use(\n      cors({\n        origin: config.get('cors.origin'),\n        credentials: config.get('cors.credentials'),\n      }),\n    );\n    this.app.use(compression());\n    this.app.use(\n      bodyParser.text({ type: ['text/*'], limit: requestBodyLimit }),\n    );\n    this.app.use(\n      bodyParser.urlencoded({ extended: true, limit: requestBodyLimit }),\n    );\n    this.app.use(\n      bodyParser.json({\n        type: ['json', '*/json', '+json'],\n        limit: requestBodyLimit,\n      }),\n    );\n    this.app.use(\n      helmet({\n        contentSecurityPolicy: {\n          directives: {\n            // for `/docs` path - we need to fetch redoc component from unpkg.com domain and hash for inline script\n            'script-src': [\n              '\\'self\\'',\n              'unpkg.com',\n              '\\'sha256-HoDNcNPq7PEOgc6zsff39t5lZ/S65RY7Zl4hI67unp0=\\'',\n            ],\n            // for schemas\n            'connect-src': ['\\'self\\'', 'http:'],\n            'worker-src': ['\\'self\\' blob:'],\n          },\n        },\n        // for `/docs` path\n        crossOriginEmbedderPolicy: false,\n      }),\n    );\n    this.app.use(loggerMiddleware);\n  }\n\n  private async initializeControllers() {\n    for (const controller of this.controllers) {\n      this.app.use(`/${API_VERSION}/`, await controller.boot());\n    }\n  }\n\n  private async initializeErrorHandling() {\n    this.app.use(problemMiddleware);\n  }\n}\n"
  },
  {
    "path": "src/apps/api/configs/development.json",
    "content": "{\n  \"env\": \"development\",\n  \"log\": {\n    \"format\": \"dev\",\n    \"dir\": \"../logs\"\n  },\n  \"cors\": {\n    \"origin\": true,\n    \"credentials\": false\n  },\n  \"request\": {\n    \"body\": {\n      \"limit\": \"5mb\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/api/configs/production.json",
    "content": "{\n  \"env\": \"production\",\n  \"log\": {\n    \"format\": \"combined\",\n    \"dir\": \"../logs\"\n  },\n  \"cors\": {\n    \"origin\": true,\n    \"credentials\": false\n  },\n  \"request\": {\n    \"body\": {\n      \"limit\": \"5mb\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/api/configs/test.json",
    "content": "{\n  \"env\": \"test\",\n  \"log\": {\n    \"format\": \"dev\",\n    \"dir\": \"../logs\"\n  },\n  \"cors\": {\n    \"origin\": true,\n    \"credentials\": false\n  },\n  \"request\": {\n    \"body\": {\n      \"limit\": \"5mb\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/api/constants.ts",
    "content": "export const API_VERSION = 'v1';\n"
  },
  {
    "path": "src/apps/api/controllers/bundle.controller.ts",
    "content": "import { NextFunction, Request, Response, Router } from 'express';\nimport bundler from '@asyncapi/bundler';\n\nimport { validationMiddleware } from '../middlewares/validation.middleware';\n\nimport { ProblemException } from '../exceptions/problem.exception';\nimport { Controller } from '@/interfaces';\n\nexport class BundleController implements Controller {\n  public basepath = '/bundle';\n\n  private async bundle(req: Request, res: Response, next: NextFunction) {\n    const asyncapis: Array<string> = req.body.asyncapis;\n    const base = req.body.base;\n\n    try {\n      const document = await bundler(asyncapis, { base });\n      const bundled = document.json();\n      res.status(200).json({ bundled });\n    } catch (err) {\n      return next(\n        new ProblemException({\n          type: 'internal-bundler-error',\n          title: 'Internal Bundler error',\n          status: 500,\n          detail: (err as Error).message,\n        }),\n      );\n    }\n  }\n\n  public async boot(): Promise<Router> {\n    const router = Router();\n\n    router.post(\n      this.basepath,\n      await validationMiddleware({\n        path: this.basepath,\n        method: 'post',\n        documents: ['asyncapis', 'base'],\n      }),\n      this.bundle.bind(this),\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/convert.controller.ts",
    "content": "import { NextFunction, Request, Response, Router } from 'express';\nimport { Controller, AsyncAPIDocument, ConversionOptions } from '@/interfaces';\nimport { validationMiddleware } from '../middlewares/validation.middleware';\nimport { ConversionService } from '@services/convert.service';\nimport { ProblemException } from '../exceptions/problem.exception';\nimport { Specification } from '@/domains/models/SpecificationFile';\n\ninterface ConvertDTO extends ConversionOptions {\n  source: AsyncAPIDocument | string;\n}\n\n/**\n * Controller which exposes the Convert functionality\n */\nexport class ConvertController implements Controller {\n  public basepath = '/convert';\n  private conversionService = new ConversionService();\n\n  private async convert(req: Request, res: Response, next: NextFunction) {\n    try {\n      const options = req.body as ConvertDTO;\n      const specFile = new Specification(\n        typeof options.source === 'string'\n          ? options.source\n          : JSON.stringify(options.source),\n      );\n      const result = await this.conversionService.convertDocument(\n        specFile,\n        options,\n      );\n\n      if (!result.success) {\n        return next(\n          new ProblemException({\n            type: 'conversion-error',\n            title: 'Conversion failed',\n            status: 400,\n            detail: result.error,\n          }),\n        );\n      }\n\n      res.status(200).json({\n        converted: result.data?.convertedDocument,\n        sourceFormat: result.data?.originalFormat,\n      });\n    } catch (err: unknown) {\n      if (err instanceof ProblemException) {\n        return next(err);\n      }\n\n      return next(\n        new ProblemException({\n          type: 'internal-server-error',\n          title: 'Internal server error',\n          status: 500,\n          detail: (err as Error).message,\n        }),\n      );\n    }\n  }\n\n  public async boot(): Promise<Router> {\n    const router = Router();\n\n    router.post(\n      this.basepath,\n      await validationMiddleware({\n        path: this.basepath,\n        method: 'post',\n        documents: ['source'],\n        condition: (req: Request) => {\n          const body = req.body as ConvertDTO;\n          return body.format === 'asyncapi';\n        },\n      }),\n      this.convert.bind(this),\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/diff.controller.ts",
    "content": "import { NextFunction, Request, Response, Router } from 'express';\nimport { diff } from '@asyncapi/diff';\n\nimport { validationMiddleware } from '../middlewares/validation.middleware';\n\nimport { ProblemException } from '../exceptions/problem.exception';\nimport { Controller } from '@/interfaces';\n\nexport class DiffController implements Controller {\n  public basepath = '/diff';\n\n  private async diff(req: Request, res: Response, next: NextFunction) {\n    const { asyncapis } = req.body;\n\n    try {\n      const output = diff(asyncapis[0], asyncapis[1]).getOutput();\n      res.status(200).json({ diff: output });\n    } catch (err) {\n      return next(\n        new ProblemException({\n          type: 'internal-diff-error',\n          title: 'Internal Diff error',\n          status: 500,\n          detail: (err as Error).message,\n        }),\n      );\n    }\n  }\n\n  public async boot(): Promise<Router> {\n    const router = Router();\n\n    router.post(\n      this.basepath,\n      await validationMiddleware({\n        path: this.basepath,\n        method: 'post',\n        documents: ['asyncapis'],\n      }),\n      this.diff.bind(this),\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/docs.controller.ts",
    "content": "import { Router } from 'express';\nimport redoc from 'redoc-express';\n\nimport { Controller } from '@/interfaces';\n\nimport { API_VERSION } from '../constants';\n\nexport class DocsController implements Controller {\n  public basepath = '/docs';\n\n  public boot(): Router {\n    const router = Router();\n\n    router.get(`${this.basepath}/openapi.yaml`, (_, res) => {\n      res.sendFile('openapi.yaml', { root: '.' });\n    });\n\n    router.get(\n      this.basepath,\n      redoc({\n        title: 'OpenAPI Documentation',\n        specUrl: `/${API_VERSION}${this.basepath}/openapi.yaml`,\n      }),\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/generate.controller.ts",
    "content": "import fs from 'fs';\nimport path from 'path';\nimport { NextFunction, Request, Response, Router } from 'express';\nimport Ajv from 'ajv';\n\nimport { Controller, GenerationOptions } from '@/interfaces';\n\nimport { validationMiddleware } from '../middlewares/validation.middleware';\n\nimport { ArchiverService } from '@services/archiver.service';\nimport { GeneratorService } from '@services/generator.service';\n\nimport { ProblemException } from '../exceptions/problem.exception';\nimport { Specification } from '@/domains/models/SpecificationFile';\n\n/**\n * Controller which exposes the Generator functionality\n */\nexport class GenerateController implements Controller {\n  public basepath = '/generate';\n\n  private archiverService = new ArchiverService();\n  private generatorService = new GeneratorService();\n  private ajv: Ajv | undefined;\n\n  private async generate(req: Request, res: Response, next: NextFunction) {\n    // try {\n    //   await this.validateTemplateParameters(req);\n    // } catch (err) {\n    //   return next(err);\n    // }\n\n    const zip = this.archiverService.createZip(res);\n\n    let tmpDir: string | undefined;\n    try {\n      tmpDir = await this.archiverService.createTempDirectory();\n      const { asyncapi, template, parameters } = req.body;\n      const options: GenerationOptions = {\n        forceWrite: true,\n        templateParams: parameters,\n      };\n\n      const generateFunc = this.generatorService.generate.bind(this.generatorService);\n\n      try {\n        const result = await generateFunc(\n          new Specification(\n            typeof asyncapi === 'object' ? JSON.stringify(asyncapi) : asyncapi,\n          ),\n          template,\n          tmpDir,\n          options,\n          {},\n          true,\n        );\n\n        if (!result.success) {\n          return next(\n            new ProblemException({\n              type: 'generation-error',\n              title: 'Generation Error',\n              status: 500,\n              detail: result.error || 'An error occurred during generation.',\n            }),\n          );\n        }\n      } catch (genErr: unknown) {\n        return next(\n          new ProblemException({\n            type: 'internal-generator-error',\n            title: 'Internal Generator error',\n            status: 500,\n            detail: (genErr as Error).message,\n          }),\n        );\n      }\n\n      this.archiverService.appendDirectory(zip, tmpDir, 'template');\n      this.archiverService.appendAsyncAPIDocument(zip, asyncapi);\n\n      res.status(200);\n      return await this.archiverService.finalize(zip);\n    } catch (err: unknown) {\n      return next(\n        new ProblemException({\n          type: 'internal-server-error',\n          title: 'Internal server error',\n          status: 500,\n          detail: (err as Error).message,\n        }),\n      );\n    } finally {\n      if (tmpDir) {\n        // Remove the temporary directory after the response is sent\n        // to avoid leaving temporary files on the server.\n        await this.archiverService.removeTempDirectory(tmpDir);\n      }\n    }\n  }\n\n  private async validateTemplateParameters(req: Request) {\n    const { template, parameters } = req.body;\n\n    const validate = await this.getAjvValidator(template);\n\n    if (!validate) {\n      throw new ProblemException({\n        type: 'invalid-template',\n        title: 'Invalid Generator Template',\n        status: 422,\n        detail: `Template ${template} is not valid or does not exist.`,\n      });\n    }\n\n    const valid = validate(parameters || {});\n    const errors = validate.errors && [...validate.errors];\n\n    if (valid === false) {\n      throw new ProblemException({\n        type: 'invalid-template-parameters',\n        title: 'Invalid Generator Template parameters',\n        status: 422,\n        validationErrors: errors as any,\n      });\n    }\n  }\n\n  /**\n   * Retrieve proper AJV's validator function, create or reuse it.\n   */\n  public async getAjvValidator(templateName: string) {\n    if (!this.ajv) {\n      throw new Error('AJV instance is not initialized');\n    }\n\n    let validate = this.ajv.getSchema(templateName);\n    if (!validate) {\n      this.ajv.addSchema(\n        (await this.serializeTemplateParameters(templateName)) || {},\n        templateName,\n      );\n      validate = this.ajv.getSchema(templateName);\n    }\n    return validate;\n  }\n\n  /**\n   * Serialize template parameters. Read all parameters from template's package.json and create a proper JSON Schema for validating parameters.\n   */\n  public async serializeTemplateParameters(\n    templateName: string,\n  ): Promise<object | undefined> {\n    const pathToPackageJSON = path.join(\n      __dirname,\n      `../../../../node_modules/${templateName}/package.json`,\n    );\n    const packageJSONContent = await fs.promises.readFile(\n      pathToPackageJSON,\n      'utf-8',\n    );\n    const packageJSON = JSON.parse(packageJSONContent);\n    if (!packageJSON) {\n      return;\n    }\n\n    const generator = packageJSON.generator;\n    if (!generator || !generator.parameters) {\n      return;\n    }\n\n    const parameters = generator.parameters || {};\n    const required: string[] = [];\n    for (const parameter in parameters) {\n      // at the moment all parameters have to be passed to the Generator instance as string\n      parameters[String(parameter)].type = 'string';\n      if (parameters[String(parameter)].required) {\n        required.push(parameter);\n      }\n      delete parameters[String(parameter)].required;\n    }\n\n    return {\n      $schema: 'http://json-schema.org/draft-07/schema#',\n      type: 'object',\n      properties: parameters,\n      required,\n      // don't allow non supported properties\n      additionalProperties: false,\n    };\n  }\n\n  public async boot(): Promise<Router> {\n    this.ajv = new Ajv({\n      inlineRefs: true,\n      allErrors: true,\n      schemaId: 'id',\n      logger: false,\n    });\n    const router = Router();\n\n    router.post(\n      `${this.basepath}`,\n      await validationMiddleware({\n        path: this.basepath,\n        method: 'post',\n        documents: ['asyncapi'],\n      }),\n      this.generate.bind(this),\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/help.controller.ts",
    "content": "import { Router, Request, Response, NextFunction } from 'express';\nimport { Controller } from '@/interfaces';\nimport { ProblemException } from '../exceptions/problem.exception';\nimport { getAppOpenAPI } from '../../../utils/app-openapi';\n\nconst getCommandsFromRequest = (req: Request): string[] => {\n  const param = req.params.command ?? req.params[0] ?? '';\n  if (Array.isArray(param)) {\n    return param.filter((cmd: string) => cmd.trim());\n  }\n  return param.split('/').filter((cmd: string) => cmd.trim());\n};\n\nconst getPathKeysMatchingCommands = (\n  commands: string[],\n  pathKeys: string[],\n): string | undefined => {\n  const exactPath = `/${commands.join('/')}`;\n  if (pathKeys.includes(exactPath)) {\n    return exactPath;\n  }\n\n  return pathKeys.find((pathKey) => {\n    const pathParts = pathKey.split('/').filter((part) => part !== '');\n    if (pathParts.length !== commands.length) {\n      return false;\n    }\n    return pathParts.every((pathPart, i) =>\n      pathPart === commands[i] || pathPart.startsWith('{') || pathPart.startsWith(':')\n    );\n  });\n};\n\nconst getFullRequestBodySpec = (operationDetails: any) => {\n  const schema = operationDetails?.requestBody?.content?.['application/json']?.schema ?? null;\n  if (!schema) {\n    return null;\n  }\n  const seen = new WeakSet();\n  return JSON.parse(JSON.stringify(schema, (_key, value) => {\n    if (typeof value === 'object' && value !== null) {\n      if (seen.has(value)) {\n        return '[Circular]';\n      }\n      seen.add(value);\n    }\n    return value;\n  }));\n};\n\nconst buildResponseObject = (\n  matchedPathKey: string,\n  method: string,\n  operationDetails: any,\n  requestBodySchema: any,\n) => {\n  return {\n    command: matchedPathKey,\n    method: method.toUpperCase(),\n    summary: operationDetails.summary || '',\n    requestBody: requestBodySchema,\n  };\n};\n\nexport class HelpController implements Controller {\n  public basepath = '/help';\n\n  public async boot(): Promise<Router> {\n    const router: Router = Router();\n\n    const helpHandler = async (req: Request, res: Response, next: NextFunction) => {\n      try {\n        const openapiSpec = await getAppOpenAPI();\n        const paths = openapiSpec?.paths ?? {};\n        const pathKeys = Object.keys(paths);\n        const commands = getCommandsFromRequest(req);\n\n        if (commands.length === 0) {\n          return res.json(pathKeys.map((path) => ({\n            command: path.replace(/^\\//, ''),\n            url: `${this.basepath}${path}`,\n          })));\n        }\n\n        const matchedPathKey = getPathKeysMatchingCommands(commands, pathKeys);\n\n        if (!matchedPathKey) {\n          return next(new ProblemException({\n            type: 'invalid-asyncapi-command',\n            title: 'Invalid AsyncAPI Command',\n            status: 404,\n            detail: 'The given AsyncAPI command is not valid.',\n          }));\n        }\n\n        const pathInfo = paths[matchedPathKey];\n        const method = pathInfo.get ? 'get' : 'post';\n        const operationDetails = pathInfo[method];\n\n        if (!operationDetails) {\n          return next(new ProblemException({\n            type: 'invalid-asyncapi-command',\n            title: 'Invalid AsyncAPI Command',\n            status: 404,\n            detail: 'The given AsyncAPI command is not valid.',\n          }));\n        }\n\n        const requestBodySchema = getFullRequestBodySpec(operationDetails);\n\n        return res.json(buildResponseObject(matchedPathKey, method, operationDetails, requestBodySchema));\n      } catch (err) {\n        return next(err);\n      }\n    };\n    \n    router.get(`${this.basepath}{/*command}`, helpHandler);\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/parse.controller.ts",
    "content": "import { Request, Response, Router } from 'express';\nimport { validationMiddleware } from '../middlewares/validation.middleware';\nimport { Controller } from '@/interfaces';\n\n/**\n * Controller which exposes the Parser functionality, to parse the AsyncAPI document.\n */\nexport class ParseController implements Controller {\n  public basepath = '/parse';\n\n  private async parse(req: Request, res: Response) {\n    const stringified = JSON.stringify(req.asyncapi?.parsedDocument);\n    res.status(200).json({\n      parsed: stringified,\n    });\n  }\n\n  public async boot(): Promise<Router> {\n    const router = Router();\n\n    router.post(\n      `${this.basepath}`,\n      await validationMiddleware({\n        path: this.basepath,\n        method: 'post',\n        documents: ['asyncapi'],\n      }),\n      this.parse.bind(this),\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/validate.controller.ts",
    "content": "import { Request, Response, Router } from 'express';\n\nimport { validationMiddleware } from '../middlewares/validation.middleware';\n\nimport { Controller } from '@/interfaces';\n\n/**\n * Controller which exposes the Parser functionality, to validate the AsyncAPI document.\n */\nexport class ValidateController implements Controller {\n  public basepath = '/validate';\n\n  private async validate(req: Request, res: Response) {\n    if (req.asyncapi?.parsedDocuments?.length) {\n      const results =\n        req.asyncapi.validationResults?.map((result) => ({\n          status: result.status,\n          asyncapi: result.document,\n          diagnostics: result.diagnostics,\n          score: result.score,\n        })) || [];\n      res.status(200).json({\n        results,\n      });\n    } else {\n      res.status(200).json({\n        status: req.asyncapi?.validationResult?.status,\n        asyncapi: req.asyncapi?.validationResult?.document,\n        diagnostics: req.asyncapi?.validationResult?.diagnostics,\n        score: req.asyncapi?.validationResult?.score,\n      });\n    }\n  }\n\n  public async boot(): Promise<Router> {\n    const router = Router();\n\n    router.post(\n      `${this.basepath}`,\n      await validationMiddleware({\n        path: this.basepath,\n        method: 'post',\n        documents: ['asyncapi'],\n      }),\n      this.validate.bind(this),\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/controllers/version.controller.ts",
    "content": "import * as os from 'os';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { Router, Request, Response, NextFunction } from 'express';\nimport { Controller } from '@/interfaces';\n\nexport class VersionController implements Controller {\n  public basepath = '/version';\n  private startTime = new Date();\n\n  private async getPackageInfo() {\n    try {\n      const packagePath = path.join(process.cwd(), 'package.json');\n      return JSON.parse(fs.readFileSync(packagePath, 'utf8'));\n    } catch {\n      return {};\n    }\n  }\n\n  public async boot(): Promise<Router> {\n    const router: Router = Router();\n\n    router.get(\n      `${this.basepath}`, async (req: Request, res: Response, next: NextFunction) => {\n        try {\n          const packageJson = await this.getPackageInfo();\n          \n          const versionInfo = {\n            // Core version information\n            version: packageJson.version || process.env.npm_package_version || 'unknown',\n            name: packageJson.name || 'AsyncAPI CLI API',\n            description: packageJson.description || 'All in one CLI for all AsyncAPI tools',\n            \n            // Runtime information\n            runtime: {\n              node: process.version,\n              environment: process.env.NODE_ENV || 'development',\n              platform: os.platform(),\n              arch: os.arch(),\n              uptime: `${Math.floor((Date.now() - this.startTime.getTime()) / 1000)} seconds`,\n              startTime: this.startTime.toISOString()\n            },\n\n            // Repository information\n            repository: {\n              url: packageJson.homepage || packageJson.repository?.url || 'https://github.com/asyncapi/cli',\n              bugs: packageJson.bugs || 'https://github.com/asyncapi/cli/issues',\n              license: packageJson.license || 'Apache-2.0'\n            },\n\n            // API metadata\n            api: {\n              basePath: this.basepath,\n              timestamp: new Date().toISOString(),\n              health: 'ok'\n            }\n          };\n\n          res.json(versionInfo);\n        } catch (err) {\n          return next(err);\n        }\n      }\n    );\n\n    return router;\n  }\n}\n"
  },
  {
    "path": "src/apps/api/exceptions/problem.exception.ts",
    "content": "import { ProblemMixin } from '@asyncapi/problem';\n\nexport interface ProblemExceptionProps {\n  status: number;\n  [key: string]: any;\n}\n\nconst typePrefix = 'https://api.asyncapi.com/problem';\n\nexport class ProblemException extends ProblemMixin<ProblemExceptionProps>({\n  typePrefix,\n}) {}\n"
  },
  {
    "path": "src/apps/api/index.ts",
    "content": "import { ValidateController } from './controllers/validate.controller';\nimport { ParseController } from './controllers/parse.controller';\nimport { GenerateController } from './controllers/generate.controller';\nimport { ConvertController } from './controllers/convert.controller';\nimport { BundleController } from './controllers/bundle.controller';\nimport { DiffController } from './controllers/diff.controller';\nimport { DocsController } from './controllers/docs.controller';\nimport { HelpController } from './controllers/help.controller';\nimport { VersionController } from './controllers/version.controller';\n\nexport const CONTROLLERS = [\n  new ValidateController(),\n  new ParseController(),\n  new GenerateController(),\n  new ConvertController(),\n  new BundleController(),\n  new DiffController(),\n  new DocsController(),\n  new HelpController(),\n  new VersionController(),\n];\n"
  },
  {
    "path": "src/apps/api/middlewares/logger.middleware.ts",
    "content": "import { NextFunction, Request, Response } from 'express';\nimport { logger } from '@utils/logger';\n\nexport function loggerMiddleware(req: Request, res: Response, next: NextFunction) {\n  const start = Date.now();\n  res.on('finish', () => {\n    const duration = Date.now() - start;\n    logger.info(\n      `[${req.method}] ${req.originalUrl} ${res.statusCode} - ${duration}ms`,\n    );\n  });\n  next();\n}\n"
  },
  {
    "path": "src/apps/api/middlewares/problem.middleware.ts",
    "content": "import { NextFunction, Request, Response } from 'express';\n\nimport { ProblemException } from '../exceptions/problem.exception';\nimport { logger } from '@utils/logger';\n\n/**\n * Catch problem exception, log it and serialize error to human readable form.\n */\nexport function problemMiddleware(\n  error: ProblemException,\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) {\n  if (res.headersSent) {\n    return next(error);\n  }\n\n  try {\n    const problemShape = error.get();\n    const status = (problemShape.status = problemShape.status || 500);\n    problemShape.title = problemShape.title || 'Internal server error';\n\n    logger.error(\n      `[${req.method}] ${req.path} >> Status:: ${status}, Type:: ${problemShape.type.replace('https://api.asyncapi.com/problem/', '')}, Title:: ${problemShape.title}, Detail:: ${problemShape.detail}`,\n    );\n\n    const isError = status >= 500;\n    const problem = error.toObject({\n      includeStack: isError,\n      includeCause: isError,\n    });\n    res.status(status).json(problem);\n  } catch (err: unknown) {\n    next(err);\n  }\n}\n"
  },
  {
    "path": "src/apps/api/middlewares/validation.middleware.ts",
    "content": "import { Request, Response, NextFunction } from 'express';\n\nimport { ProblemException } from '../exceptions/problem.exception';\nimport { createAjvInstance } from '../../../utils/ajv';\nimport { getAppOpenAPI } from '../../../utils/app-openapi';\n\nimport type { ValidateFunction } from 'ajv';\nimport { AsyncAPIDocument } from '@asyncapi/converter';\nimport { ValidationService } from '@services/validation.service';\nimport { Specification } from '@/domains/models/SpecificationFile';\nimport { ParserOptions } from '@asyncapi/parser/cjs/parser';\nimport { ValidationResult } from '@/interfaces';\n\nexport interface ValidationMiddlewareOptions {\n  path: string;\n  method:\n    | 'all'\n    | 'get'\n    | 'post'\n    | 'put'\n    | 'delete'\n    | 'patch'\n    | 'options'\n    | 'head';\n  documents?: Array<string>;\n  version?: 'v1';\n  condition?: (req: Request) => boolean;\n}\n\nconst ajvInstance = createAjvInstance();\n\n/**\n * Create AJV's validator function for given path in the OpenAPI document.\n */\nasync function compileAjv(options: ValidationMiddlewareOptions) {\n  const appOpenAPI = await getAppOpenAPI();\n  const paths = appOpenAPI.paths || {};\n\n  const pathName = options.path;\n  const path = paths[String(pathName)];\n  if (!path) {\n    throw new Error(\n      `Path \"${pathName}\" doesn't exist in the OpenAPI document.`,\n    );\n  }\n\n  const methodName = options.method;\n  const method = path[String(methodName)];\n  if (!method) {\n    throw new Error(\n      `Method \"${methodName}\" for \"${pathName}\" path doesn't exist in the OpenAPI document.`,\n    );\n  }\n\n  const requestBody = method.requestBody;\n  if (!requestBody) {\n    return;\n  }\n\n  let schema = requestBody.content['application/json'].schema;\n  if (!schema) {\n    return;\n  }\n\n  schema = { ...schema };\n  schema['$schema'] = 'http://json-schema.org/draft-07/schema';\n\n  if (options.documents && schema.properties) {\n    schema.properties = { ...schema.properties };\n    for (const field of options.documents) {\n      if (schema.properties[String(field)].items) {\n        schema.properties[String(field)] = {\n          ...schema.properties[String(field)],\n        };\n        schema.properties[String(field)].items = true;\n      } else {\n        schema.properties[String(field)] = true;\n      }\n    }\n  }\n\n  return ajvInstance.compile(schema);\n}\n\nasync function validateRequestBody(validate: ValidateFunction, body: any) {\n  const valid = validate(body);\n  const errors = validate.errors && [...validate.errors];\n\n  if (valid === false) {\n    throw new ProblemException({\n      type: 'invalid-request-body',\n      title: 'Invalid Request Body',\n      status: 422,\n      validationErrors: errors as any,\n    });\n  }\n}\n\nasync function validateSingleDocument(\n  asyncapi: string | AsyncAPIDocument,\n  path: string,\n  validationService: ValidationService,\n) {\n  if (typeof asyncapi === 'object') {\n    asyncapi = JSON.stringify(asyncapi);\n  }\n\n  const specFile = new Specification(asyncapi, { fileURL: path });\n\n  return validationService\n    .validateDocument(specFile, {\n      'fail-severity': 'error',\n      suppressAllWarnings: false,\n    })\n    .then((result) => {\n      if (!result.success || result.data?.status !== 'valid') {\n        throw new ProblemException({\n          type: 'invalid-asyncapi-document',\n          title: 'Invalid AsyncAPI Document',\n          status: 422,\n          detail: result.error || 'The provided AsyncAPI document is invalid.',\n          diagnostics: result.diagnostics || result.data?.diagnostics,\n        });\n      }\n      return result.data;\n    });\n}\n\nasync function validateListDocuments(\n  asyncapis: Array<string | AsyncAPIDocument>,\n  path: string,\n  validationService: ValidationService,\n) {\n  const validationResults: Array<ValidationResult> = [];\n  for (const asyncapi of asyncapis) {\n    const parsed = await validateSingleDocument(\n      asyncapi,\n      path,\n      validationService,\n    );\n    if (parsed) {\n      validationResults.push(parsed);\n    } else {\n      throw new ProblemException({\n        type: 'invalid-asyncapi-document',\n        title: 'Invalid AsyncAPI Document',\n        status: 422,\n        detail: 'One or more provided AsyncAPI documents are invalid.',\n      });\n    }\n  }\n  return validationResults;\n}\n\n/**\n * Validate RequestBody and sent AsyncAPI document(s) for given path and method based on the OpenAPI Document.\n */\nexport async function validationMiddleware(\n  options: ValidationMiddlewareOptions,\n) {\n  options.version = options.version || 'v1';\n  const validate = await compileAjv(options);\n  const documents = options.documents || [];\n\n  return async (\n    req: Request,\n    res: Response,\n    next: NextFunction,\n  ): Promise<void> => {\n    // Check if the condition is met\n    if (options.condition && !options.condition(req)) {\n      return next();\n    }\n\n    try {\n      if (!validate) {\n        throw new ProblemException({\n          type: 'invalid-request-body',\n          title: 'Invalid Request Body',\n          status: 422,\n          detail: `Request body validation is not supported for \"${options.path}\" path with \"${options.method}\" method.`,\n        });\n      }\n\n      await validateRequestBody(validate, req.body);\n    } catch (error: unknown) {\n      if (error instanceof ProblemException) {\n        return next(error);\n      }\n\n      // Handle unexpected errors\n      return next(\n        new ProblemException({\n          type: 'internal-server-error',\n          title: 'Internal Server Error',\n          status: 500,\n          detail: `An unexpected error occurred during request validation: ${(error as Error).message ?? 'Unknown error'}`,\n        }),\n      );\n    }\n\n    const parserConfig: ParserOptions = {\n      __unstable: {\n        resolver: {\n          resolvers: [\n            // @TODO: Add Cookie Based Resolvers after migration and understanding some\n            // details about how to use them in the new parser-js version.\n          ],\n        },\n      },\n    };\n\n    const validationService = new ValidationService(parserConfig);\n    const resolveURL =\n      req.header('x-asyncapi-resolve-url') ||\n      req.header('referer') ||\n      req.header('origin') ||\n      '';\n\n    try {\n      req.asyncapi = req.asyncapi || {};\n      for (const field of documents) {\n        const body = req.body[String(field)];\n        if (Array.isArray(body)) {\n          const results = await validateListDocuments(\n            body,\n            resolveURL,\n            validationService,\n          );\n          const parsedDocuments = results.map((result) => result.document);\n\n          if (!parsedDocuments.every(doc => doc !== undefined)) {\n            throw new ProblemException({\n              type: 'invalid-asyncapi-document-parse',\n              title: 'Invalid AsyncAPI Document (Parse Error)',\n              status: 422,\n              detail: 'One or more provided AsyncAPI documents are invalid.',\n              diagnostics: results.flatMap(result => result.diagnostics || []),\n            });\n          }\n\n          req.asyncapi.parsedDocuments = parsedDocuments;\n          req.asyncapi.validationResults = results;\n        } else {\n          const result = await validateSingleDocument(\n            body,\n            resolveURL,\n            validationService,\n          );\n          req.asyncapi.parsedDocument = result?.document;\n          req.asyncapi.validationResult = result;\n        }\n      }\n\n      next();\n    } catch (err: unknown) {\n      return next(err);\n    }\n  };\n}\n\nconst TYPES_400 = [\n  'null-or-falsey-document',\n  'impossible-to-convert-to-json',\n  'invalid-document-type',\n  'invalid-json',\n  'invalid-yaml',\n];\n\n/**\n * Some error types have to be treated as 400 HTTP Status Code, another as 422.\n */\nfunction retrieveStatusCode(type: string): number {\n  if (TYPES_400.includes(type)) {\n    return 400;\n  }\n  return 422;\n}\n\n/**\n * Merges fields from ParserError to ProblemException.\n */\nfunction mergeParserError(\n  error: ProblemException,\n  parserError: any,\n): ProblemException {\n  if (parserError.detail) {\n    error.set('detail', parserError.detail);\n  }\n  if (parserError.validationErrors) {\n    error.set('validationErrors', parserError.validationErrors);\n  }\n  if (parserError.parsedJSON) {\n    error.set('parsedJSON', parserError.parsedJSON);\n  }\n  if (parserError.location) {\n    error.set('location', parserError.location);\n  }\n  if (parserError.refs) {\n    error.set('refs', parserError.refs);\n  }\n  return error;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction tryConvertToProblemException(err: any) {\n  const typeName = err.type.replace(\n    'https://github.com/asyncapi/parser-js/',\n    '',\n  );\n  const error = new ProblemException({\n    type: typeName,\n    title: err.title,\n    status: retrieveStatusCode(typeName),\n  });\n  mergeParserError(error, err);\n\n  return error;\n}\n"
  },
  {
    "path": "src/apps/api/server-api.d.ts",
    "content": "import { AsyncAPIDocument, ValidationResult } from '@interfaces/index';\n\ndeclare module 'express' {\n  export interface Request {\n    asyncapi?: {\n      parsedDocument?: AsyncAPIDocument;\n      parsedDocuments?: Array<AsyncAPIDocument>;\n      validationResults?: Array<ValidationResult>;\n      validationResult?: ValidationResult;\n    };\n  }\n}\n"
  },
  {
    "path": "src/apps/api/server.ts",
    "content": "// include configs in the `dist` folder\nimport './configs/production.json';\nimport './configs/development.json';\nimport './configs/test.json';\n\n// for `config` module\nprocess.env['NODE_CONFIG_DIR'] = `${__dirname}/configs`;\n\nimport { App } from './app';\nimport { CONTROLLERS } from '.';\n\nasync function main() {\n  const app = new App(CONTROLLERS);\n  await app.init();\n  app.listen();\n}\nmain();\n"
  },
  {
    "path": "src/apps/api/templates.json",
    "content": "[\n  \"@asyncapi/html-template\",\n  \"@asyncapi/markdown-template\",\n  \"@asyncapi/nodejs-template\",\n  \"@asyncapi/nodejs-ws-template\",\n  \"@asyncapi/java-template\",\n  \"@asyncapi/java-spring-template\",\n  \"@asyncapi/java-spring-cloud-stream-template\",\n  \"@asyncapi/python-paho-template\",\n  \"@asyncapi/ts-nats-template\",\n  \"@asyncapi/go-watermill-template\",\n  \"@asyncapi/dotnet-nats-template\",\n  \"@asyncapi/php-template\",\n  \"@asyncapi/dotnet-rabbitmq-template\"\n]\n"
  },
  {
    "path": "src/apps/cli/commands/bundle.ts",
    "content": "import Command from '@cli/internal/base';\nimport bundle from '@asyncapi/bundler';\nimport { promises } from 'fs';\nimport path from 'path';\nimport { Specification } from '@models/SpecificationFile';\nimport { Document } from '@asyncapi/bundler/lib/document';\nimport { bundleFlags } from '@cli/internal/flags/bundle.flags';\n\nconst { writeFile } = promises;\n\nexport default class Bundle extends Command {\n  static readonly description =\n    'Bundle one or multiple AsyncAPI Documents and their references together.';\n  static strict = false;\n\n  static examples = [\n    'asyncapi bundle ./asyncapi.yaml > final-asyncapi.yaml',\n    'asyncapi bundle ./asyncapi.yaml --output final-asyncapi.yaml',\n    'asyncapi bundle ./asyncapi.yaml ./features.yaml',\n    'asyncapi bundle ./asyncapi.yaml ./features.yaml --base ./main.yaml',\n    'asyncapi bundle ./asyncapi.yaml ./features.yaml --base ./main.yaml --xOrigin',\n    'asyncapi bundle ./asyncapi.yaml -o final-asyncapi.yaml --base ../public-api/main.yaml --baseDir ./social-media/comments-service',\n  ];\n\n  static flags = bundleFlags();\n\n  async run() {\n    const { argv, flags } = await this.parse(Bundle);\n    const output = flags.output;\n    const outputFormat = path.extname(argv[0] as string);\n    const AsyncAPIFiles = argv as string[];\n\n    this.metricsMetadata.files = AsyncAPIFiles.length;\n\n    const document = await bundle(AsyncAPIFiles, {\n      base: flags.base,\n      baseDir: flags.baseDir,\n      xOrigin: flags.xOrigin,\n    });\n\n    await this.collectMetricsData(document);\n\n    if (!output) {\n      if (outputFormat === '.yaml' || outputFormat === '.yml') {\n        this.log(document.yml());\n      } else {\n        this.log(JSON.stringify(document.json()));\n      }\n    } else {\n      const format = path.extname(output);\n\n      if (format === '.yml' || format === '.yaml') {\n        await writeFile(\n          path.resolve(process.cwd(), output),\n          document.yml() || '',\n          {\n            encoding: 'utf-8',\n          },\n        );\n      }\n\n      if (format === '.json') {\n        await writeFile(\n          path.resolve(process.cwd(), output),\n          document.string() || '',\n          {\n            encoding: 'utf-8',\n          },\n        );\n      }\n      this.log(`Check out your shiny new bundled files at ${output}`);\n    }\n  }\n\n  private async collectMetricsData(document: Document) {\n    try {\n      // We collect the metadata from the final output so it contains all the files\n      this.specFile = new Specification(document.string() ?? '');\n    } catch (e: any) {\n      if (e instanceof Error) {\n        this.log(\n          `Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`,\n        );\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/analytics.ts",
    "content": "import { join, resolve } from 'path';\nimport Command from '@cli/internal/base';\nimport { promises as fPromises } from 'fs';\nimport { homedir } from 'os';\nimport { analyticsFlags } from '@cli/internal/flags/config/analytics.flags';\nimport { blueBright, redBright } from 'picocolors';\n\nconst { readFile, writeFile } = fPromises;\n\nexport default class Analytics extends Command {\n  static readonly description =\n    'Enable or disable analytics for metrics collection';\n  static readonly flags = analyticsFlags();\n\n  async run() {\n    const { flags } = await this.parse(Analytics);\n    const analyticsConfigFile =\n      process.env.ASYNCAPI_METRICS_CONFIG_PATH ||\n      join(homedir(), '.asyncapi-analytics');\n\n    try {\n      const analyticsConfigFileContent = JSON.parse(\n        await readFile(resolve(analyticsConfigFile), { encoding: 'utf8' }),\n      );\n\n      if (flags.disable) {\n        analyticsConfigFileContent.analyticsEnabled = 'false';\n        this.log('\\nAnalytics disabled.\\n');\n        this.metricsMetadata.analytics_disabled = flags.disable;\n      } else if (flags.enable) {\n        analyticsConfigFileContent.analyticsEnabled = 'true';\n        this.log('\\nAnalytics enabled.\\n');\n        this.metricsMetadata.analytics_enabled = flags.enable;\n      } else if (!flags.status) {\n        this.log(\n          `\\nPlease append the ${blueBright('--disable')} flag to the command if you prefer to disable analytics, or use the ${blueBright('--enable')} flag if you want to enable analytics again. To check the current analytics status, use the ${blueBright('--status')} flag.\\n`,\n        );\n        return;\n      }\n      await writeFile(\n        analyticsConfigFile,\n        JSON.stringify(analyticsConfigFileContent),\n        { encoding: 'utf8' },\n      );\n\n      if (flags.status) {\n        if (analyticsConfigFileContent.analyticsEnabled === 'true') {\n          this.log('\\nAnalytics are enabled.\\n');\n        } else {\n          this.log(\n            `\\n${redBright('Analytics are disabled.')} To enable analytics, use the ${blueBright('--enable')} flag.\\n`,\n          );\n        }\n        this.metricsMetadata.analytics_status_checked = flags.status;\n      }\n    } catch (e: any) {\n      switch (e.code) {\n      case 'ENOENT':\n        this.error(\n          `Unable to access the analytics configuration file. We tried to access the ${blueBright('.asyncapi-analytics')} file in the path \"${blueBright(analyticsConfigFile)}\" but the file could not be found.`,\n        );\n        break;\n      case 'EEXIST':\n        this.error(\n          `Unable to update the analytics configuration file. We tried to update your \".asyncapi-analytics\" file in the path \"${blueBright(analyticsConfigFile)}\" but the file does not exist.`,\n        );\n        break;\n      default:\n        this.error(\n          `Unable to change your analytics configuration. Please check the following message for further info about the error:\\n\\n${redBright(e)}`,\n        );\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/auth/add.ts",
    "content": "import { Args, Flags } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { blueBright } from 'picocolors';\nimport { ConfigService, AuthEntry } from '@/domains/services/config.service';\n\nexport default class AuthAdd extends Command {\n  static description =\n    'Add an authentication config for resolving $ref files requiring HTTP Authorization.';\n\n  static args = {\n    pattern: Args.string({\n      required: true,\n      description:\n        'Glob pattern for matching protected URLs (e.g. github.com/org/repo/**/*.*)',\n    }),\n    token: Args.string({\n      required: true,\n      description:\n        'Authentication token or environment variable reference (prefix with $, e.g. $GITHUB_TOKEN)',\n    }),\n  };\n\n  static flags = {\n    'auth-type': Flags.string({\n      char: 'a',\n      description: 'Authentication type (default is \"Bearer\")',\n    }),\n    header: Flags.string({\n      char: 'h',\n      description:\n        'Additional header in key=value format; can be used multiple times',\n      multiple: true,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(AuthAdd);\n\n    const isEnvVar = args.token.startsWith('$');\n    const tokenValue = isEnvVar ? args.token.slice(1) : args.token;\n\n    // Parse headers into an object\n    const headers: Record<string, string> = {};\n    if (flags.header) {\n      for (const headerEntry of flags.header) {\n        const [key, value] = headerEntry.split('=');\n        if (key && value) {\n          headers[key.trim()] = value.trim();\n        } else {\n          this.warn(`⚠️ Ignored invalid header format: ${headerEntry}`);\n        }\n      }\n    }\n\n    const entry: AuthEntry = {\n      pattern: args.pattern,\n      token: tokenValue,\n      authType: flags['auth-type'] || 'Bearer',\n      headers: Object.keys(headers).length ? headers : undefined,\n    };\n\n    try {\n      await ConfigService.addAuthEntry(entry);\n      this.log(\n        `✅ Auth config added for ${blueBright(args.pattern)} using ${\n          isEnvVar ? `env var (${tokenValue})` : 'raw token'\n        } with auth type ${blueBright(entry.authType || 'Bearer')}`\n      );\n      if (entry.headers) {\n        this.log(`Headers: ${JSON.stringify(entry.headers, null, 2)}`);\n      }\n    } catch (err) {\n      this.error(`❌ Failed to add auth config: ${(err as Error).message}`);\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/add.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { addContext, setCurrentContext } from '@models/Context';\nimport {\n  MissingContextFileError,\n  ContextFileWrongFormatError,\n} from '@errors/context-error';\nimport { addFlags } from '@cli/internal/flags/config/context.flags';\nimport { blueBright } from 'picocolors';\n\nexport default class ContextAdd extends Command {\n  static description = 'Add a context to the store';\n  static flags = addFlags();\n\n  static args = {\n    'context-name': Args.string({\n      description: 'context name',\n      required: true,\n    }),\n    'spec-file-path': Args.string({\n      description: 'file path of the spec file',\n      required: true,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(ContextAdd);\n    const contextName = args['context-name'];\n    const specFilePath = args['spec-file-path'];\n    const setAsCurrent = flags['set-current'];\n\n    try {\n      await addContext(contextName, specFilePath);\n      this.log(\n        `🎉 Context ${blueBright(contextName)} added successfully!\\nYou can set it as your current context:\\n  ${blueBright('asyncapi')} ${blueBright('config')} ${blueBright('context')} ${blueBright('use')} ${blueBright(contextName)}\\nYou can use this context when needed by passing ${blueBright(contextName)} as a parameter:\\n  ${blueBright('asyncapi')} ${blueBright('validate')} ${blueBright(contextName)}`,\n      );\n      if (setAsCurrent) {\n        await setCurrentContext(contextName);\n        this.log(\n          `\\nThe newly added context, ${blueBright(contextName)}, is set as your current context!`,\n        );\n      }\n    } catch (e) {\n      if (\n        e instanceof (MissingContextFileError || ContextFileWrongFormatError)\n      ) {\n        this.error(\n          `Unable to add context. You have no context file configured.\\nRun ${blueBright('asyncapi config context init')} to initialize it.`,\n        );\n      }\n      throw e;\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/current.ts",
    "content": "import Command from '@cli/internal/base';\nimport { getCurrentContext, CONTEXT_FILE_PATH } from '@models/Context';\nimport {\n  MissingContextFileError,\n  ContextFileWrongFormatError,\n  ContextFileEmptyError,\n  ContextNotFoundError,\n} from '@errors/context-error';\nimport { helpFlag } from '@cli/internal/flags/global.flags';\nimport { blueBright } from 'picocolors';\n\nexport default class ContextCurrent extends Command {\n  static description = 'Shows the current context that is being used';\n  static flags = helpFlag();\n\n  async run() {\n    await this.parse(ContextCurrent);\n    let fileContent;\n\n    try {\n      fileContent = await getCurrentContext();\n    } catch (e) {\n      if (\n        e instanceof (MissingContextFileError || ContextFileWrongFormatError)\n      ) {\n        this.error(\n          `Unable to show current context. You have no context file configured.\\nRun ${blueBright('asyncapi config context init')} to initialize it.`,\n        );\n      } else if (e instanceof ContextFileEmptyError) {\n        this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);\n      } else if (\n        e instanceof ContextNotFoundError ||\n        (fileContent && !fileContent.current)\n      ) {\n        this.error(\n          `No context is set as current.\\nRun ${blueBright('asyncapi config context')} to see all available options.`,\n        );\n      }\n      throw e;\n    }\n\n    if (fileContent) {\n      this.log(`${blueBright(fileContent.current)}: ${fileContent.context}`);\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/edit.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { editContext, CONTEXT_FILE_PATH } from '@models/Context';\nimport {\n  MissingContextFileError,\n  ContextFileWrongFormatError,\n  ContextFileEmptyError,\n} from '@errors/context-error';\nimport { helpFlag } from '@cli/internal/flags/global.flags';\nimport { blueBright } from 'picocolors';\n\nexport default class ContextEdit extends Command {\n  static description = 'Edit a context in the store';\n  static flags = helpFlag();\n\n  static args = {\n    'context-name': Args.string({\n      description: 'context name',\n      required: true,\n    }),\n    'new-spec-file-path': Args.string({\n      description: 'file path of the spec file',\n      required: true,\n    }),\n  };\n  async run() {\n    const { args } = await this.parse(ContextEdit);\n    const contextName = args['context-name'];\n    const newSpecFilePath = args['new-spec-file-path'];\n\n    try {\n      await editContext(contextName, newSpecFilePath);\n      this.log(\n        `🎉 Context ${blueBright(contextName)} edited successfully!\\nYou can set it as your current context:\\n  ${blueBright('asyncapi')} ${blueBright('config')} ${blueBright('context')} ${blueBright('use')} ${blueBright(contextName)}\\nYou can use this context when needed by passing ${blueBright(contextName)} as a parameter:\\n  ${blueBright('asyncapi')} ${blueBright('validate')} ${blueBright(contextName)}`,\n      );\n    } catch (e) {\n      if (\n        e instanceof (MissingContextFileError || ContextFileWrongFormatError)\n      ) {\n        this.error(\n          `Unable to edit context. You have no context file configured.\\nRun ${blueBright('asyncapi config context init')} to initialize it.`,\n        );\n      } else if (e instanceof ContextFileEmptyError) {\n        this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);\n      }\n      throw e;\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/index.ts",
    "content": "import { loadHelpClass } from '@oclif/core';\nimport Command from '@cli/internal/base';\n\nexport default class Context extends Command {\n  static description =\n    'Manage short aliases for full paths to AsyncAPI documents';\n\n  async run() {\n    const Help = await loadHelpClass(this.config);\n    const help = new Help(this.config);\n    help.showHelp(['config', 'context', '--help']);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/init.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { initContext } from '@models/Context';\nimport { helpFlag } from '@cli/internal/flags/global.flags';\nimport { blueBright } from 'picocolors';\nexport default class ContextInit extends Command {\n  static description = 'Initialize context';\n  static flags = helpFlag();\n\n  static contextFilePathMessage = `Specify directory in which context file should be created:\n    - current directory          : ${blueBright('asyncapi config context init .')}(default)\n    - root of current repository : ${blueBright('asyncapi config context init ./ ')}\n    - user's home directory      : ${blueBright('asyncapi config context init ~`')}`;\n\n  static args = {\n    'context-file-path': Args.string({\n      description: `${ContextInit.contextFilePathMessage}`,\n      required: false,\n    }),\n  };\n\n  async run() {\n    const { args } = await this.parse(ContextInit);\n    const contextFilePath = args['context-file-path'];\n\n    const contextWritePath = await initContext(contextFilePath as string);\n    this.log(`🎉 Context initialized at ${blueBright(contextWritePath)}`);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/list.ts",
    "content": "import Command from '@cli/internal/base';\nimport {\n  loadContextFile,\n  isContextFileEmpty,\n  CONTEXT_FILE_PATH,\n} from '@models/Context';\nimport {\n  MissingContextFileError,\n  ContextFileWrongFormatError,\n} from '@errors/context-error';\nimport { helpFlag } from '@cli/internal/flags/global.flags';\nimport { blueBright } from 'picocolors';\n\nexport default class ContextList extends Command {\n  static description = 'List all the stored contexts in the store';\n  static flags = helpFlag();\n\n  async run() {\n    await this.parse(ContextList);\n    try {\n      const fileContent = await loadContextFile();\n\n      if (await isContextFileEmpty(fileContent)) {\n        this.log(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);\n        return;\n      }\n\n      if (fileContent) {\n        for (const [contextName, filePath] of Object.entries(\n          fileContent.store,\n        )) {\n          this.log(`${blueBright(contextName)}: ${filePath}`);\n        }\n      }\n    } catch (e) {\n      if (\n        e instanceof (MissingContextFileError || ContextFileWrongFormatError)\n      ) {\n        this.log(\n          `Unable to list contexts. You have no context file configured.\\nRun ${blueBright('asyncapi config context init')} to initialize it.\\n`,\n        );\n        return;\n      }\n      throw e;\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/remove.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { removeContext, CONTEXT_FILE_PATH } from '@models/Context';\nimport {\n  MissingContextFileError,\n  ContextFileWrongFormatError,\n  ContextFileEmptyError,\n} from '@errors/context-error';\nimport { helpFlag } from '@cli/internal/flags/global.flags';\nimport { blueBright } from 'picocolors';\n\nexport default class ContextRemove extends Command {\n  static description = 'Delete a context from the store';\n  static flags = helpFlag();\n\n  static args = {\n    'context-name': Args.string({\n      description: 'Name of the context to delete',\n      required: true,\n    }),\n  };\n\n  async run() {\n    const { args } = await this.parse(ContextRemove);\n    const contextName = args['context-name'];\n\n    try {\n      await removeContext(contextName);\n      this.log(`Context ${blueBright(contextName)} removed successfully!\\n`);\n    } catch (e) {\n      if (\n        e instanceof (MissingContextFileError || ContextFileWrongFormatError)\n      ) {\n        this.error(\n          `Unable to remove context. You have no context file configured.\\nRun ${blueBright('asyncapi config context init')} to initialize it.`,\n        );\n      } else if (e instanceof ContextFileEmptyError) {\n        this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);\n      }\n      throw e;\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/context/use.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { setCurrentContext, CONTEXT_FILE_PATH } from '@models/Context';\nimport {\n  MissingContextFileError,\n  ContextFileWrongFormatError,\n  ContextFileEmptyError,\n} from '@errors/context-error';\nimport { helpFlag } from '@cli/internal/flags/global.flags';\nimport { blueBright } from 'picocolors';\n\nexport default class ContextUse extends Command {\n  static description = 'Set a context as current';\n  static flags = helpFlag();\n\n  static args = {\n    'context-name': Args.string({\n      description: 'name of the saved context',\n      required: true,\n    }),\n  };\n\n  async run() {\n    const { args } = await this.parse(ContextUse);\n    const contextName = args['context-name'];\n\n    try {\n      await setCurrentContext(contextName);\n      this.log(`Context ${blueBright(contextName)} is now set as current.`);\n    } catch (e) {\n      if (\n        e instanceof (MissingContextFileError || ContextFileWrongFormatError)\n      ) {\n        this.error(\n          `Unable to set the current context. You have no context file configured.\\nRun ${blueBright('asyncapi config context init')} to initialize it.`,\n        );\n      } else if (e instanceof ContextFileEmptyError) {\n        this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);\n        return;\n      }\n      throw e;\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/index.ts",
    "content": "import Command from '@cli/internal/base';\nimport { loadHelpClass } from '@oclif/core';\n\nexport default class Config extends Command {\n  static description = 'CLI config settings';\n  async run() {\n    const Help = await loadHelpClass(this.config);\n    const help = new Help(this.config);\n    help.showHelp(['config', '--help']);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/config/versions.ts",
    "content": "import Command from '@cli/internal/base';\nimport { helpFlag } from '@cli/internal/flags/global.flags';\nimport { blueBright, gray } from 'picocolors';\n\nexport default class Versions extends Command {\n  static description = 'Show versions of AsyncAPI tools used';\n\n  static flags = helpFlag();\n\n  async run() {\n    const dependencies: string[] = [];\n    let dependency = '';\n\n    // Preparation of the array with all dependencies '@asyncapi/*' along with\n    // their versions.\n    for (const key in this.config.pjson.dependencies) {\n      // Making sure with .indexOf() that only package names which START with\n      // string '@asyncapi' are considered.\n      if (key.indexOf('@asyncapi', 0) === 0) {\n        // Avoiding obvious crash on manual removal or alteration of an\n        // '@asyncapi' package.\n        try {\n          // Goofy name `importedPJSON` is chosen to distinguish from name `pjson`\n          // used in `@oclif` source code.\n          const importedPJSON = await import(`${key}/package.json`);\n          dependencies.push(`${key}/${importedPJSON.default.version}`);\n        } catch {\n          dependencies.push(`${key}/` + '`package.json` not found');\n        }\n      }\n    }\n\n    // Showing information available with `--version` flag.\n    this.log(gray(`\\n${this.config.userAgent}\\n`));\n\n    // Iteration through the array containing all dependencies '@asyncapi/*'\n    // along with their versions.\n    for (let i = 0; i < dependencies.length; i++) {\n      // Minimization of the theoretical possibility of a Generic Object\n      // Injection Sink, at the same time disabling eslint parsing for this\n      // line since it is actually a false positive.\n      // https://github.com/eslint-community/eslint-plugin-security/issues/21#issuecomment-530184612\n      // https://github.com/eslint-community/eslint-plugin-security/issues/21#issuecomment-1157887653\n      // https://web.archive.org/web/20150430062816/https://blog.liftsecurity.io/2015/01/15/the-dangers-of-square-bracket-notation\n      dependency = dependencies[i];  \n      if (i !== dependencies.length - 1) {\n        this.log(`  ├${dependency}`);\n      } else {\n        this.log(`  └${dependency}\\n`);\n      }\n    }\n\n    this.log(`Repository: ${blueBright(this.config.pjson.homepage)}`);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/convert.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { ValidationError } from '@errors/validation-error';\nimport { load } from '@models/SpecificationFile';\nimport { SpecificationFileNotFound } from '@errors/specification-file';\nimport type { AsyncAPIConvertVersion } from '@asyncapi/converter';\nimport { cyan } from 'picocolors';\nimport { proxyFlags } from '@cli/internal/flags/proxy.flags';\nimport specs from '@asyncapi/specs';\nimport { convertFlags } from '@cli/internal/flags/convert.flags';\nimport { ConversionService } from '@services/convert.service';\nimport { applyProxyToPath } from '@utils/proxy';\n\nconst latestVersion = Object.keys(specs.schemas).pop() as string;\nconst TARGET_VERSION_FLAG = 'target-version';\n\nexport default class Convert extends Command {\n  static description =\n    'Convert asyncapi documents older to newer versions or OpenAPI documents to AsyncAPI';\n  private conversionService = new ConversionService();\n  static flags = {\n    ...convertFlags(latestVersion),\n    ...proxyFlags(),\n  };\n\n  static args = {\n    'spec-file': Args.string({\n      description: 'spec path, url, or context-name',\n      required: false,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(Convert);\n    const filePath = applyProxyToPath(\n      args['spec-file'],\n      flags['proxyHost'],\n      flags['proxyPort']\n    );\n    const targetVersion = flags[TARGET_VERSION_FLAG];\n\n    try {\n      // LOAD FILE\n      this.specFile = await load(filePath);\n      this.metricsMetadata.to_version = targetVersion;\n      const conversionOptions = {\n        format: flags.format as 'asyncapi' | 'openapi',\n        [TARGET_VERSION_FLAG]: (targetVersion ||\n          latestVersion) as AsyncAPIConvertVersion,\n        perspective: flags['perspective'] as 'client' | 'server',\n      };\n\n      const result = await this.conversionService.convertDocument(\n        this.specFile,\n        conversionOptions,\n      );\n\n      if (!result.success || !result.data) {\n        this.error(result.error || 'Conversion failed', { exit: 1 });\n      }\n\n      this.metricsMetadata.conversion_result = result;\n\n      this.log(\n        this.conversionService.handleLogging(this.specFile, conversionOptions),\n      );\n\n      if (flags['output']) {\n        await this.conversionService.handleOutput(\n          flags['output'],\n          result.data.convertedDocument,\n        );\n      } else {\n        this.log(result.data.convertedDocument);\n      }\n    } catch (err) {\n      this.handleError(err, filePath ?? 'unknown', targetVersion);\n    }\n  }\n\n  // Helper function to handle errors\n  private handleError(err: unknown, filePath: string, targetVersion: string | undefined) {\n    if (err instanceof SpecificationFileNotFound) {\n      this.error(\n        new ValidationError({\n          type: 'invalid-file',\n          filepath: filePath,\n        }),\n      );\n    } else if (this.specFile?.toJson().asyncapi > (targetVersion ?? '')) {\n      this.error(\n        `The ${cyan(filePath)} file cannot be converted to an older version. Downgrading is not supported.`,\n      );\n    } else {\n      this.error(err as Error);\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/diff.ts",
    "content": " \nimport { Args } from '@oclif/core';\nimport * as diff from '@asyncapi/diff';\nimport AsyncAPIDiff from '@asyncapi/diff/lib/asyncapidiff';\nimport { promises as fs } from 'fs';\nimport chalk from 'chalk';\nimport { load, Specification } from '@models/SpecificationFile';\nimport Command from '@cli/internal/base';\nimport { ValidationError } from '@errors/validation-error';\nimport { SpecificationFileNotFound } from '@errors/specification-file';\nimport {\n  DiffBreakingChangeError,\n  DiffOverrideFileError,\n  DiffOverrideJSONError,\n} from '@errors/diff-error';\nimport { specWatcher } from '@cli/internal/globals';\n\nimport type { SpecWatcherParams } from '@cli/internal/globals';\nimport { diffFlags } from '@cli/internal/flags/diff.flags';\nimport {\n  ValidationService,\n  ValidationStatus,\n} from '@/domains/services/validation.service';\nimport { Diagnostic } from '@asyncapi/parser/cjs';\n\nconst { readFile } = fs;\n\nexport default class Diff extends Command {\n  static description = 'Find diff between two asyncapi files';\n  private validationService = new ValidationService();\n  static flags = diffFlags();\n\n  static args = {\n    old: Args.string({\n      description: 'old spec path, URL or context-name',\n      required: true,\n    }),\n    new: Args.string({\n      description: 'new spec path, URL or context-name',\n      required: true,\n    }),\n  };\n\n  /* eslint-disable sonarjs/cognitive-complexity */\n  async run() {\n    const { args, flags } = await this.parse(Diff); // NOSONAR\n    const firstDocumentPath = args['old'];\n    const secondDocumentPath = args['new'];\n\n    const outputFormat = flags['format'];\n    const outputType = flags['type'];\n    const overrideFilePath = flags['overrides'];\n    let markdownSubtype = flags['markdownSubtype'];\n    const watchMode = flags['watch'];\n    const noError = flags['no-error'];\n    const writeOutput = flags['save-output'];\n    let firstDocument: Specification, secondDocument: Specification;\n\n    checkAndWarnFalseFlag(outputFormat, markdownSubtype);\n    markdownSubtype = setDefaultMarkdownSubtype(\n      outputFormat,\n      markdownSubtype,\n    ) as string;\n\n    this.metricsMetadata.output_format = outputFormat;\n    this.metricsMetadata.output_type = outputType;\n    if (outputFormat === 'md') {\n      this.metricsMetadata.output_markdown_subtype = flags['markdownSubtype'];\n    }\n\n    try {\n      firstDocument = await load(firstDocumentPath);\n\n      enableWatch(watchMode, {\n        spec: firstDocument,\n        handler: this,\n        handlerName: 'diff',\n        docVersion: 'old',\n        label: 'DIFF_OLD',\n      });\n    } catch (err) {\n      if (err instanceof SpecificationFileNotFound) {\n        this.error(\n          new ValidationError({\n            type: 'invalid-file',\n            filepath: firstDocumentPath,\n          }),\n        );\n      }\n      this.error(err as Error);\n    }\n\n    try {\n      secondDocument = await load(secondDocumentPath);\n\n      enableWatch(watchMode, {\n        spec: secondDocument,\n        handler: this,\n        handlerName: 'diff',\n        docVersion: 'new',\n        label: 'DIFF_NEW',\n      });\n    } catch (err) {\n      if (err instanceof SpecificationFileNotFound) {\n        this.error(\n          new ValidationError({\n            type: 'invalid-file',\n            filepath: secondDocumentPath,\n          }),\n        );\n      }\n      this.error(err as Error);\n    }\n\n    let overrides: Awaited<ReturnType<typeof readOverrideFile>> = {};\n    if (overrideFilePath) {\n      try {\n        overrides = await readOverrideFile(overrideFilePath);\n      } catch (err) {\n        this.error(err as Error);\n      }\n    }\n\n    try {\n      const parsed = await this.parseDocuments(\n        this,\n        firstDocument,\n        secondDocument,\n        flags,\n      );\n      if (!parsed) {\n        return;\n      }\n\n      const diffOutput = diff.diff(\n        parsed.firstDocumentParsed.json(),\n        parsed.secondDocumentParsed.json(),\n        {\n          override: overrides,\n          outputType: outputFormat as diff.OutputType, // NOSONAR\n          markdownSubtype: markdownSubtype as diff.MarkdownSubtype,\n        },\n      );\n\n      if (writeOutput) {\n        await this.writeOutputToFile(diffOutput,outputType,writeOutput,outputFormat);\n      } else {\n        if (outputFormat === 'json') {\n          this.outputJSON(diffOutput, outputType);\n        } else if (outputFormat === 'yaml' || outputFormat === 'yml') {\n          this.outputYAML(diffOutput, outputType);\n        } else if (outputFormat === 'md') {\n          this.outputMarkdown(diffOutput, outputType);\n        } else {\n          this.log(\n            `The output format ${outputFormat} is not supported at the moment.`,\n          );\n        }\n        if (!noError) {\n          throwOnBreakingChange(diffOutput, outputFormat);\n        }\n      }\n    } catch (error) {\n      if (\n        error instanceof DiffBreakingChangeError ||\n        error instanceof TypeError\n      ) {\n        this.error(error);\n      }\n      throw new ValidationError({\n        type: 'parser-error',\n        err: error,\n      });\n    }\n  }\n\n  outputJSON(diffOutput: AsyncAPIDiff, outputType: string) {\n    if (outputType === 'breaking') {\n      this.log(JSON.stringify(diffOutput.breaking(), null, 2));\n    } else if (outputType === 'non-breaking') {\n      this.log(JSON.stringify(diffOutput.nonBreaking(), null, 2));\n    } else if (outputType === 'unclassified') {\n      this.log(JSON.stringify(diffOutput.unclassified(), null, 2));\n    } else if (outputType === 'all') {\n      this.log(JSON.stringify(diffOutput.getOutput(), null, 2));\n    } else {\n      this.log(`The output type ${outputType} is not supported at the moment.`);\n    }\n  }\n\n  async writeOutputToFile(diffOutput: AsyncAPIDiff, outputType: string, filePath: string, outputFormat: string) {\n    let content: string;\n    \n    if (outputFormat === 'json') {\n      if (outputType === 'breaking') {\n        content = JSON.stringify(diffOutput.breaking(), null, 2);\n      } else if (outputType === 'non-breaking') {\n        content = JSON.stringify(diffOutput.nonBreaking(), null, 2);\n      } else if (outputType === 'unclassified') {\n        content = JSON.stringify(diffOutput.unclassified(), null, 2);\n      } else if (outputType === 'all') {\n        content = JSON.stringify(diffOutput.getOutput(), null, 2);\n      } else {\n        content = `The output type ${outputType} is not supported at the moment.`;\n      }\n    } else if (outputFormat === 'yaml' || outputFormat === 'yml') {\n      content = genericOutput(diffOutput, outputType) as string;\n    } else if (outputFormat === 'md') {\n      content = genericOutput(diffOutput, outputType) as string;\n    } else {\n      content = `The output format ${outputFormat} is not supported at the moment.`;\n    }\n    \n    try {\n      await fs.writeFile(filePath, content);\n      this.log(`Output successfully written to: ${filePath}`);\n    } catch (error: any) {\n      this.error(`Failed to write output to file: ${error.message}`);\n    }\n  }\n\n  outputYAML(diffOutput: AsyncAPIDiff, outputType: string) {\n    this.log(genericOutput(diffOutput, outputType) as string);\n  }\n\n  outputMarkdown(diffOutput: AsyncAPIDiff, outputType: string) {\n    this.log(genericOutput(diffOutput, outputType) as string);\n  }\n\n  async parseDocuments(\n    command: Command,\n    firstDocument: Specification,\n    secondDocument: Specification,\n    flags: Record<string, any>,\n  ) {\n    const firstResult = await this.validationService.parseDocument(\n      firstDocument,\n      {},\n      flags,\n    );\n    const secondResult = await this.validationService.parseDocument(\n      secondDocument,\n      {},\n      flags,\n    );\n\n    if (!firstResult.success || !secondResult.success) {\n      this.error(\n        new ValidationError({\n          type: 'invalid-file',\n          filepath: firstDocument.getFilePath() || secondDocument.getFilePath(),\n          err: firstResult.error || secondResult.error,\n        }),\n      );\n    }\n\n    if (!firstResult.data || !secondResult.data) {\n      return;\n    }\n\n    const {\n      document: firstDocumentParsed,\n      status: firstDocumentStatus,\n      diagnostics: firstDiagnostics,\n    } = firstResult.data;\n    const {\n      document: secondDocumentParsed,\n      status: secondDocumentStatus,\n      diagnostics: secondDiagnostics,\n    } = secondResult.data;\n\n    if (flags['log-diagnostics']) {\n      this.log(\n        `Diagnostics for ${firstDocument.getFilePath() || firstDocument.getFileURL()}:`,\n      );\n      this.handleGovernanceMessage(\n        firstDocument,\n        firstDiagnostics,\n        firstDocumentStatus as ValidationStatus,\n      );\n      this.log(\n        this.validationService.formatDiagnosticsOutput(\n          firstDiagnostics,\n          flags['diagnostics-format'],\n          flags['fail-severity'],\n        ),\n      );\n      this.log(\n        `Diagnostics for ${secondDocument.getFilePath() || secondDocument.getFileURL()}:`,\n      );\n      this.handleGovernanceMessage(\n        secondDocument,\n        secondDiagnostics,\n        secondDocumentStatus as ValidationStatus,\n      );\n      this.log(\n        this.validationService.formatDiagnosticsOutput(\n          secondDiagnostics,\n          flags['diagnostics-format'],\n          flags['fail-severity'],\n        ),\n      );\n    }\n\n    if (\n      !firstDocumentParsed ||\n      !secondDocumentParsed ||\n      firstDocumentStatus === 'invalid' ||\n      secondDocumentStatus === 'invalid'\n    ) {\n      return;\n    }\n\n    return { firstDocumentParsed, secondDocumentParsed };\n  }\n\n  async handleGovernanceMessage(\n    document: Specification,\n    diagnostics: Diagnostic[],\n    status: ValidationStatus,\n  ) {\n    const sourceString = document.toSourceString();\n    const hasIssues = diagnostics && diagnostics.length > 0;\n    const isFailSeverity = status === ValidationStatus.INVALID;\n\n    const governanceMessage = this.validationService.generateGovernanceMessage(\n      sourceString,\n      hasIssues,\n      isFailSeverity,\n    );\n\n    if (isFailSeverity) {\n      this.logToStderr(governanceMessage);\n    } else {\n      this.log(governanceMessage);\n    }\n  }\n}\n\n/**\n * A generic output function for diff output\n * @param diffOutput The diff output data\n * @param outputType The output format requested by the user\n * @returns The output(if the format exists) or a message indicating the format doesn't exist\n */\nfunction genericOutput(diffOutput: AsyncAPIDiff, outputType: string) {\n  switch (outputType) {\n  case 'breaking':\n    return diffOutput.breaking();\n  case 'non-breaking':\n    return diffOutput.nonBreaking();\n  case 'unclassified':\n    return diffOutput.unclassified();\n  case 'all':\n    return diffOutput.getOutput();\n  default:\n    return `The output type ${outputType} is not supported at the moment.`;\n  }\n}\n\n/**\n * Reads the file from give path and parses it as JSON\n * @param path The path to override file\n * @returns The override object\n */\nasync function readOverrideFile(path: string): Promise<diff.OverrideObject> {\n  let overrideStringData;\n  try {\n    overrideStringData = await readFile(path, { encoding: 'utf8' });\n  } catch {\n    throw new DiffOverrideFileError();\n  }\n\n  try {\n    return JSON.parse(overrideStringData);\n  } catch {\n    throw new DiffOverrideJSONError();\n  }\n}\n\n/**\n * function to enable watchmode.\n * The function is abstracted here, to avoid eslint cognitive complexity error.\n */\nconst enableWatch = (status: boolean, watcher: SpecWatcherParams) => {\n  if (status) {\n    specWatcher(watcher);\n  }\n};\n\n/**\n * Throws `DiffBreakingChangeError` when breaking changes are detected\n */\nfunction throwOnBreakingChange(diffOutput: AsyncAPIDiff, outputFormat: string) {\n  const breakingChanges = diffOutput.breaking();\n  if (\n    (outputFormat === 'json' && breakingChanges.length !== 0) ||\n    ((outputFormat === 'yaml' || outputFormat === 'yml') &&\n      breakingChanges !== '[]\\n')\n  ) {\n    throw new DiffBreakingChangeError();\n  }\n}\n\n/**\n * Checks and warns user about providing unnecessary markdownSubtype option.\n */\nfunction checkAndWarnFalseFlag(\n  format: string,\n  markdownSubtype: string | undefined,\n) {\n  if (format !== 'md' && typeof markdownSubtype !== 'undefined') {\n    const warningMessage = chalk.yellowBright(\n      `Warning: The given markdownSubtype flag will not work with the given format.\\nProvided flag markdownSubtype: ${markdownSubtype}`,\n    );\n    console.log(warningMessage);\n  }\n}\n\n/**\n * Sets the default markdownSubtype option in case user doesn't provide one.\n */\nfunction setDefaultMarkdownSubtype(\n  format: string,\n  markdownSubtype: string | undefined,\n) {\n  if (format === 'md' && typeof markdownSubtype === 'undefined') {\n    return 'yaml';\n  }\n  return markdownSubtype;\n}\n"
  },
  {
    "path": "src/apps/cli/commands/format.ts",
    "content": "import { promises as fPromises } from 'fs';\nimport { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\n\nimport {\n  convertToJSON,\n  convertToYaml,\n  load,\n  retrieveFileFormat,\n} from '@models/SpecificationFile';\nimport { SpecificationWrongFileFormat } from '@errors/specification-file';\nimport { cyan, green } from 'picocolors';\nimport {\n  convertFormatFlags,\n  fileFormat,\n} from '@cli/internal/flags/format.flags';\n\nexport default class Format extends Command {\n  static description =\n    'Convert asyncapi documents from any format to yaml, yml or JSON';\n\n  static flags = convertFormatFlags();\n\n  static args = {\n    'spec-file': Args.string({\n      description: 'spec path, url, or context-name',\n      required: false,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(Format);\n    const filePath = args['spec-file'];\n    const outputFileFormat = flags['format'] as fileFormat;\n    let convertedFile;\n    try {\n      this.specFile = await load(filePath);\n      this.metricsMetadata.output_format = outputFileFormat;\n\n      const ff = retrieveFileFormat(this.specFile.text());\n      const isSpecFileJson = ff === 'json';\n      const isSpecFileYaml = ff === 'yaml';\n\n      if (!isSpecFileJson && !isSpecFileYaml) {\n        throw new SpecificationWrongFileFormat(filePath);\n      }\n\n      convertedFile = this.handleConversion(\n        isSpecFileJson,\n        isSpecFileYaml,\n        outputFileFormat,\n      );\n\n      if (!convertedFile) {\n        return;\n      }\n      await this.handleOutput(flags.output, convertedFile, outputFileFormat);\n    } catch (err) {\n      this.error(err as Error);\n    }\n  }\n\n  private handleConversion(\n    isSpecFileJson: boolean,\n    isSpecFileYaml: boolean,\n    outputFileFormat: fileFormat,\n  ): string | undefined {\n    const text = this.specFile?.text();\n    if (isSpecFileJson && text) {\n      if (outputFileFormat === 'json') {\n        throw new Error(`Your document is already a ${cyan('JSON')}`);\n      }\n      return convertToYaml(text);\n    }\n    if (isSpecFileYaml && text) {\n      if (outputFileFormat === 'yaml' || outputFileFormat === 'yml') {\n        throw new Error(`Your document is already a ${cyan('YAML')}`);\n      }\n      return convertToJSON(text);\n    }\n  }\n\n  private async handleOutput(\n    outputPath: string | undefined,\n    formattedFile: string,\n    outputFileFormat: fileFormat,\n  ) {\n    if (outputPath) {\n      outputPath = this.removeExtensionFromOutputPath(outputPath);\n      const finalFileName = `${outputPath}.${outputFileFormat}`;\n      await fPromises.writeFile(finalFileName, formattedFile, {\n        encoding: 'utf8',\n      });\n      this.log(\n        `succesfully formatted to ${outputFileFormat} at ${green(finalFileName)} ✅`,\n      );\n    } else {\n      this.log(formattedFile);\n      this.log(`succesfully logged after formatting to ${outputFileFormat} ✅`);\n    }\n  }\n\n  private removeExtensionFromOutputPath(filename: string): string {\n    // Removes the extension from a filename if it is .json, .yaml, or .yml\n    // this is so that we can remove the provided extension name in the -o flag and\n    // apply our own extension name according to the content of the file\n    const validExtensions = ['json', 'yaml', 'yml'];\n\n    const parts = filename.split('.');\n\n    if (parts.length > 1) {\n      const extension = parts.pop()?.toLowerCase();\n      if (extension && validExtensions.includes(extension)) {\n        return parts.join('.');\n      }\n    }\n\n    return filename;\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/generate/client.ts",
    "content": "import { Args } from '@oclif/core';\nimport { BaseGeneratorCommand } from '@cli/internal/base/BaseGeneratorCommand';\n// eslint-disable-next-line\n// @ts-ignore\nimport { listBakedInTemplates } from '@asyncapi/generator';\nimport { intro, note } from '@clack/prompts';\nimport { inverse, yellow } from 'picocolors';\nimport { clientsFlags } from '@cli/internal/flags/generate/clients.flags';\nimport { parseGeneratorFlags } from '@utils/generate/flags';\nimport { promptForLanguage } from '@utils/generate/prompts';\nimport { availableLanguages, AvailableLanguageType, getDefaultLanguage } from '@models/generate/ClientLanguages';\nimport { GeneratorError } from '@errors/generator-error';\n\nexport default class Client extends BaseGeneratorCommand {\n  static description = `Generates clients baked-in AsyncAPI Generator. Available for: ${availableLanguages.join(', ')}. If some language is not supported or you want to improve existing client, join us at https://github.com/asyncapi/generator`;\n\n  static examples = [\n    'asyncapi generate client javascript asyncapi.yaml --param version=1.0.0 singleFile=true --output ./docs --force-write'\n  ];\n\n  static readonly flags = {\n    ...clientsFlags(),\n    ...BaseGeneratorCommand.flags\n  };\n\n  static args = {\n    language: Args.string({ description: `The language you want the client generated for. Available target languages: ${availableLanguages.join(', ')}`, required: true }),\n    ...BaseGeneratorCommand.args\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(Client); // NOSONAR\n    const interactive = !flags['no-interactive'];\n    let asyncapi = args['asyncapi'] ?? '';\n    let language = args['language'] as AvailableLanguageType;\n    let output = flags.output as string;\n    const { proxyPort, proxyHost } = flags;\n    \n    if (interactive) {\n      intro(inverse('Client generation with AsyncAPI Generator'));\n      note(yellow('This feature is in the experimental phase. Please provide feedback at: https://github.com/asyncapi/generator/issues'));\n\n      const parsedArgs = await this.parseArgs(args, output);\n      asyncapi = parsedArgs.asyncapi;\n      language = parsedArgs.language as AvailableLanguageType;\n      output = parsedArgs.output;\n    }\n\n    const template = this.getTemplateName(language);\n\n    const parsedFlags = parseGeneratorFlags(\n      flags['disable-hook'],\n      flags['param'],\n      flags['map-base-url'],\n      flags['registry-url'],\n      flags['registry-auth'],\n      flags['registry-token']\n    );\n\n    const options = await this.buildGeneratorOptions(flags, parsedFlags);\n    \n    // Apply proxy configuration using base class method\n    asyncapi = this.applyProxyConfiguration(asyncapi, proxyHost, proxyPort);\n    \n    const asyncapiInput = await this.loadAsyncAPIInput(asyncapi);\n\n    this.specFile = asyncapiInput;\n    this.metricsMetadata.language = language;\n\n    const watchTemplate = flags['watch'];\n    const genOption = this.buildGenOption(flags, parsedFlags);\n\n    // Use GeneratorService for client generation\n    const specification = await this.loadSpecificationSafely(asyncapi);\n    const result = await this.generatorService.generate(\n      specification,\n      template,\n      output,\n      options as any, // GeneratorService expects different options interface\n      genOption,\n      interactive,\n    );\n    \n    if (!result.success) {\n      throw new GeneratorError(new Error(result.error));\n    }\n    \n    this.log(result.data?.logs?.join('\\n'));\n\n    if (watchTemplate) {\n      await this.handleWatchMode(asyncapi, template, output, options, genOption, interactive);\n    }\n  }\n\n  private async parseArgs(args: Record<string, any>, output?: string): Promise<{ asyncapi: string; language: string; output: string; }> {\n    // Use base class method for common args\n    const commonArgs = await this.parseCommonArgs(args, output);\n    \n    let language = args['language'] as AvailableLanguageType;\n\n    if (!language) {\n      const defaultLanguage = getDefaultLanguage();\n      language = await promptForLanguage(defaultLanguage) as AvailableLanguageType;\n    }\n\n    this.handleCancellation(language);\n\n    return { \n      asyncapi: commonArgs.asyncapi, \n      language, \n      output: commonArgs.output \n    };\n  }\n\n  private getTemplateName(language: AvailableLanguageType): string {\n    const template = listBakedInTemplates({ type: 'client' }).find((template: any) => {\n      return template.target === language;\n    })?.name;\n\n    if (!template) {\n      this.log(`❌ Client generation for \"${language}\" is not yet available.`);\n      this.log(`✅ Available languages: ${availableLanguages.join(', ')}`);\n      this.log('🙏 Help us create the missing one. Start discussion at: https://github.com/asyncapi/generator/issues.');\n      this.exit(1);\n    }\n\n    return template;\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/generate/fromTemplate.ts",
    "content": "import { Args } from '@oclif/core';\nimport { BaseGeneratorCommand } from '@cli/internal/base/BaseGeneratorCommand';\nimport { load, Specification } from '@models/SpecificationFile';\nimport { ValidationError } from '@errors/validation-error';\nimport { GeneratorError } from '@errors/generator-error';\nimport { intro } from '@clack/prompts';\nimport { inverse } from 'picocolors';\nimport { fromTemplateFlags } from '@cli/internal/flags/generate/fromTemplate.flags';\nimport { parseGeneratorFlags } from '@utils/generate/flags';\nimport { promptForTemplate } from '@utils/generate/prompts';\n\nexport default class Template extends BaseGeneratorCommand {\n  static description =\n    'Generates whatever you want using templates compatible with AsyncAPI Generator.';\n  static examples = [\n    'asyncapi generate fromTemplate asyncapi.yaml @asyncapi/html-template --param version=1.0.0 singleFile=true --output ./docs --force-write',\n  ];\n\n  static readonly flags = {\n    ...fromTemplateFlags(),\n    ...BaseGeneratorCommand.flags,\n  };\n\n  static args = {\n    ...BaseGeneratorCommand.args,\n    template: Args.string({ description: '- Name of the generator template like for example @asyncapi/html-template or https://github.com/asyncapi/html-template', required: false }),\n  };\n   \n  async run() {\n    const { args, flags } = await this.parse(Template); // NOSONAR\n    const interactive = !flags['no-interactive'];\n    let asyncapi = args['asyncapi'] ?? '';\n    let template = args['template'] ?? '';\n    let output = flags.output as string;\n    const { proxyPort, proxyHost } = flags;\n    \n    if (interactive) {\n      intro(inverse('AsyncAPI Generator'));\n\n      const parsedArgs = await this.parseArgs(args, output);\n      asyncapi = parsedArgs.asyncapi;\n      template = parsedArgs.template;\n      output = parsedArgs.output;\n    }\n\n    const parsedFlags = parseGeneratorFlags(\n      flags['disable-hook'],\n      flags['param'],\n      flags['map-base-url'],\n      flags['registry-url'],\n      flags['registry-auth'],\n      flags['registry-token']\n    );\n\n    const options = await this.buildGeneratorOptions(flags, parsedFlags);\n\n    // Apply proxy configuration using base class method\n    asyncapi = this.applyProxyConfiguration(asyncapi, proxyHost, proxyPort);\n    \n    const asyncapiInput = await this.loadAsyncAPIInput(asyncapi);\n\n    this.specFile = asyncapiInput;\n    this.metricsMetadata.template = template;\n\n    const watchTemplate = flags['watch'];\n    const genOption = this.buildGenOption(flags, parsedFlags);\n\n    let specification: Specification;\n    try {\n      specification = await load(asyncapi);\n    } catch {\n      return this.error(\n        new ValidationError({\n           \n          type: 'invalid-file',\n          filepath: asyncapi,\n        }),\n        { exit: 1 },\n      );\n    }\n\n    const result = await this.generatorService.generate(\n      specification,\n      template,\n      output,\n      options as any, // GeneratorService expects different options interface\n      genOption,\n      interactive,\n    );\n    if (!result.success) {\n      throw new GeneratorError(new Error(result.error));\n    }\n\n    // Output logs in non-interactive mode\n    if (!interactive && result.data?.logs) {\n      for (const log of result.data.logs) {\n        this.log(log);\n      }\n    }\n    \n    if (watchTemplate) {\n      await this.handleWatchMode(asyncapi, template, output, options, genOption, interactive);\n    }\n  }\n\n  private async parseArgs(\n    args: Record<string, any>,\n    output?: string,\n  ): Promise<{ asyncapi: string; template: string; output: string }> {\n    // Use base class method for common args\n    const commonArgs = await this.parseCommonArgs(args, output);\n    \n    let template = args['template'];\n\n    if (!template) {\n      template = await promptForTemplate();\n    }\n\n    this.handleCancellation(template);\n\n    return { \n      asyncapi: commonArgs.asyncapi, \n      template, \n      output: commonArgs.output \n    };\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/generate/index.ts",
    "content": "import Command from '@cli/internal/base';\nimport { Help } from '@oclif/core';\n\nexport default class Generate extends Command {\n  static description =\n    'Generate typed models or other things like clients, applications or docs using AsyncAPI Generator templates.';\n  async run() {\n    const help = new Help(this.config);\n    help.showHelp(['generate', '--help']);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/generate/models.ts",
    "content": "import Command from '@cli/internal/base';\nimport { load, Specification } from '@models/SpecificationFile';\nimport { cancel, intro, isCancel, select, spinner, text } from '@clack/prompts';\nimport { green, inverse } from 'picocolors';\nimport {\n  generateModels,\n  Languages,\n  ModelinaArgs,\n} from '@asyncapi/modelina-cli';\nimport { modelsFlags } from '@cli/internal/flags/generate/models.flags';\nimport { proxyFlags } from '@cli/internal/flags/proxy.flags';\nimport { ValidationOptions } from '@/interfaces';\nimport {\n  ValidationService,\n  ValidationStatus,\n} from '@/domains/services/validation.service';\nimport { Diagnostic } from '@asyncapi/parser/cjs';\nimport { applyProxyToPath } from '@utils/proxy';\n\nexport default class Models extends Command {\n  static description = 'Generates typed models';\n  private validationService = new ValidationService();\n  static readonly args = ModelinaArgs as any;\n\n  static readonly flags = {\n    ...modelsFlags(),\n    ...proxyFlags(),\n  };\n   \n  async run() {\n    const { args, flags } = await this.parse(Models);\n    let { language, file } = args;\n    let { output } = flags;\n    const { proxyPort, proxyHost } = flags;\n\n    const interactive = !flags['no-interactive'];\n\n    if (!interactive) {\n      intro(inverse('AsyncAPI Generate Models'));\n\n      const parsedArgs = await this.parseArgs(args, output);\n      language = parsedArgs.language;\n      file = parsedArgs.file;\n      output = parsedArgs.output;\n    }\n\n    const fileWithProxy = applyProxyToPath(file, proxyHost, proxyPort);\n    const inputFile = (await load(fileWithProxy)) || (await load());\n\n    const result = await this.validationService.parseDocument(\n      inputFile,\n      {},\n      flags as ValidationOptions,\n    );\n    if (!result.success) {\n      this.error(`Failed to parse the AsyncAPI document: ${result.error}`, {\n        exit: 1,\n      });\n    } else if (!result.data) {\n      this.error('No data returned from parsing the AsyncAPI document.', {\n        exit: 1,\n      });\n    }\n\n    const { document, diagnostics, status } = result.data;\n\n    if (!document || status === 'invalid') {\n      const severityErrors = diagnostics.filter((obj) => obj.severity === 0);\n      this.log(\n        `Input is not a correct AsyncAPI document so it cannot be processed.${this.validationService.formatDiagnosticsOutput(severityErrors, 'stylish', 'error')}`,\n      );\n      return;\n    }\n    if (flags['log-diagnostics'] && inputFile) {\n      this.handleGovernanceMessage(\n        inputFile,\n        diagnostics,\n        status as ValidationStatus,\n      );\n      this.log(\n        this.validationService.formatDiagnosticsOutput(\n          diagnostics,\n          flags['diagnostics-format'],\n          flags['fail-severity'],\n        ),\n      );\n    }\n\n    const logger = {\n      info: (message: string) => {\n        this.log(message);\n      },\n      debug: (message: string) => {\n        this.debug(message);\n      },\n      warn: (message: string) => {\n        this.warn(message);\n      },\n      error: (message: string) => {\n        this.error(message);\n      },\n    };\n\n    const s = spinner();\n    s.start('Generating models...');\n    try {\n      const generatedModels = await generateModels(\n        { ...flags, output },\n        document,\n        logger,\n        language as Languages,\n      );\n      if (output && output !== 'stdout') {\n        const generatedModelStrings = generatedModels.map((model) => {\n          return model.modelName;\n        });\n        s.stop(\n          green(\n            `Successfully generated the following models: ${generatedModelStrings.join(', ')}`,\n          ),\n        );\n        return;\n      }\n      const generatedModelStrings = generatedModels.map((model) => {\n        return `\n  ## Model name: ${model.modelName}\n  ${model.result}\n        `;\n      });\n      s.stop(\n        green(\n          `Successfully generated the following models: ${generatedModelStrings.join('\\n')}`,\n        ),\n      );\n    } catch (error) {\n      s.stop(green('Failed to generate models'));\n\n      if (error instanceof Error) {\n        this.error(error.message);\n      } else {\n        this.error('An unknown error occurred during model generation.');\n      }\n    }\n  }\n\n  private async parseArgs(args: Record<string, any>, output?: string) {\n    let { language, file } = args;\n    let askForOutput = false;\n    const operationCancelled = 'Operation cancelled by the user.';\n    if (!language) {\n      language = await select({\n        message: 'Select the language you want to generate models for',\n        options: Object.keys(Languages).map((key) => ({\n          value: key,\n          label: key,\n          hint: Languages[key as keyof typeof Languages],\n        })),\n      });\n\n      askForOutput = true;\n    }\n\n    if (isCancel(language)) {\n      cancel(operationCancelled);\n      this.exit();\n    }\n\n    if (!file) {\n      file = await text({\n        message: 'Enter the path or URL to the AsyncAPI document',\n        defaultValue: 'asyncapi.yaml',\n        placeholder: 'asyncapi.yaml',\n      });\n\n      askForOutput = true;\n    }\n\n    if (isCancel(file)) {\n      cancel(operationCancelled);\n      this.exit();\n    }\n\n    if (!output && askForOutput) {\n      output = (await text({\n        message: 'Enter the output directory or stdout to write the models to',\n        defaultValue: 'stdout',\n        placeholder: 'stdout',\n      })) as string;\n    }\n\n    if (isCancel(output)) {\n      cancel(operationCancelled);\n      this.exit();\n    }\n\n    return { language, file, output: output ?? 'stdout' };\n  }\n\n  async handleGovernanceMessage(\n    document: Specification,\n    diagnostics: Diagnostic[],\n    status: ValidationStatus,\n  ) {\n    const sourceString = document.toSourceString();\n    const hasIssues = diagnostics && diagnostics.length > 0;\n    const isFailSeverity = status === ValidationStatus.INVALID;\n\n    const governanceMessage = this.validationService.generateGovernanceMessage(\n      sourceString,\n      hasIssues,\n      isFailSeverity,\n    );\n\n    if (isFailSeverity) {\n      this.logToStderr(governanceMessage);\n    } else {\n      this.log(governanceMessage);\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/new/file.ts",
    "content": "import { promises as fPromises, readFileSync } from 'fs';\nimport Command from '@cli/internal/base';\nimport inquirer from 'inquirer';\nimport { start as startStudio, DEFAULT_PORT } from '@models/Studio';\nimport { resolve } from 'path';\nimport { load } from '@models/SpecificationFile';\nimport { cyan } from 'picocolors';\nimport { fileFlags } from '@cli/internal/flags/new/file.flags';\n\nconst { writeFile, readFile } = fPromises;\nconst DEFAULT_ASYNCAPI_FILE_NAME = 'asyncapi.yaml';\nconst DEFAULT_ASYNCAPI_YAML_TEMPLATE = 'default-example.yaml';\nconst DEFAULT_ASYNCAPI_JSON_TEMPLATE = 'default-example.json';\n\ninterface IExample {\n  name: string;\n  value: string;\n}\n\nfunction loadExampleFile(): IExample[] {\n  const exampleFiles = readFileSync(\n    resolve(__dirname, '../../../../../assets/examples/examples.json'),\n    { encoding: 'utf8' },\n  );\n  return JSON.parse(exampleFiles);\n}\n\nfunction getExamplesFlagDescription(): string {\n  const examples = loadExampleFile();\n  let description = 'name of the example to use. Available examples are:';\n  for (const example of examples) {\n    description += `\\n\\t - ${example.value}`;\n  }\n  return description;\n}\n\nexport default class NewFile extends Command {\n  static description = 'Creates a new asyncapi file';\n\n  static flags = fileFlags(getExamplesFlagDescription());\n\n  static examples = [\n    'asyncapi new\\t - start creation of a file in interactive mode',\n    'asyncapi new --file-name=my-asyncapi.yaml --example=default-example.yaml --no-tty\\t - create a new file with a specific name, using one of the examples and without interactive mode',\n  ];\n\n  async run() {\n    const { flags } = await this.parse(NewFile); // NOSONAR\n    const isTTY = process.stdout.isTTY;\n\n    if (!flags['no-tty'] && isTTY) {\n      return this.runInteractive();\n    }\n\n    const fileName = flags['file-name'] || DEFAULT_ASYNCAPI_FILE_NAME;\n    // Determine template based on file extension\n    let default_template;\n    if (fileName.endsWith('.json')) {\n      default_template = DEFAULT_ASYNCAPI_JSON_TEMPLATE;\n    } else {\n      default_template = DEFAULT_ASYNCAPI_YAML_TEMPLATE;\n    }\n    const template = flags['example'] || default_template;\n\n    await this.createAsyncapiFile(fileName, template);\n\n    if (flags.studio) {\n      if (isTTY) {\n        startStudio(fileName, flags.port || DEFAULT_PORT);\n      } else {\n        this.warn(\n          'Warning: --studio flag was passed but the terminal is not interactive. Ignoring...',\n        );\n      }\n    }\n  }\n\n  /* eslint-disable sonarjs/cognitive-complexity */\n  async runInteractive() {\n    // NOSONAR\n    const { flags } = await this.parse(NewFile); // NOSONAR\n    let fileName = flags['file-name'];\n    let selectedTemplate = flags['example'];\n    let openStudio = flags.studio;\n    let examples = [];\n\n    const questions = [];\n\n    if (!fileName) {\n      questions.push({\n        name: 'filename',\n        message: 'name of the file?',\n        type: 'input',\n        default: DEFAULT_ASYNCAPI_FILE_NAME,\n      });\n    }\n\n    try {\n      const exampleFiles = await readFile(\n        resolve(__dirname, '../../assets/examples/examples.json'),\n        { encoding: 'utf8' },\n      );\n      examples = JSON.parse(exampleFiles);\n    } catch {\n      // no examples found\n    }\n\n    if (!selectedTemplate && examples.length > 0) {\n      questions.push({\n        name: 'use-example',\n        message:\n          'would you like to start your new file from one of our examples?',\n        type: 'confirm',\n        default: true,\n      });\n      questions.push({\n        type: 'list',\n        name: 'selectedTemplate',\n        message: 'What example would you like to use?',\n        choices: examples,\n        when: (answers: any) => {\n          return answers['use-example'];\n        },\n      });\n    }\n\n    if (openStudio === undefined) {\n      questions.push({\n        name: 'studio',\n        message: 'open in Studio?',\n        type: 'confirm',\n        default: true,\n      });\n    }\n\n    if (questions.length) {\n      const answers: any = await inquirer.prompt(questions);\n\n      if (!fileName) {\n        fileName = answers.filename as string;\n      }\n      if (!selectedTemplate) {\n        selectedTemplate = answers.selectedTemplate as string;\n      }\n      if (openStudio === undefined) {\n        openStudio = answers.studio;\n      }\n    }\n\n    fileName = fileName || DEFAULT_ASYNCAPI_FILE_NAME;\n    // Determine template based on file extension\n    let default_template;\n    if (fileName.endsWith('.json')) {\n      default_template = DEFAULT_ASYNCAPI_JSON_TEMPLATE;\n    } else {\n      default_template = DEFAULT_ASYNCAPI_YAML_TEMPLATE;\n    }\n    selectedTemplate = selectedTemplate || default_template;\n\n    await this.createAsyncapiFile(fileName, selectedTemplate);\n    fileName = fileName.includes('.') ? fileName : `${fileName}.yaml`;\n    if (openStudio) {\n      startStudio(fileName, flags.port || DEFAULT_PORT);\n    }\n  }\n\n  async createAsyncapiFile(fileName: string, selectedTemplate: string) {\n    const asyncApiFile = await readFile(\n      resolve(__dirname, '../../../../../assets/examples/', selectedTemplate),\n      { encoding: 'utf8' },\n    );\n\n    let fileNameToWriteToDisk;\n\n    if (!fileName.includes('.')) {\n      fileNameToWriteToDisk = `${fileName}.yaml`;\n    } else {\n      const extension = fileName.split('.')[1];\n\n      if (extension === 'yml' || extension === 'yaml' || extension === 'json') {\n        fileNameToWriteToDisk = fileName;\n      } else {\n        console.log('CLI Support only yml, yaml and json extension for file');\n\n        return;\n      }\n    }\n\n    try {\n      const content = await readFile(fileNameToWriteToDisk, {\n        encoding: 'utf8',\n      });\n      if (content !== undefined) {\n        console.log(\n          `A file named ${fileNameToWriteToDisk} already exists. Please choose a different name.`,\n        );\n        return;\n      }\n    } catch (e: any) {\n      if (e.code === 'EACCES') {\n        this.error('Permission has been denied to access the file.');\n      }\n    }\n    await writeFile(fileNameToWriteToDisk, asyncApiFile, { encoding: 'utf8' });\n    console.log(\n      `The ${cyan(fileNameToWriteToDisk)} has been successfully created.`,\n    );\n    this.specFile = await load(fileNameToWriteToDisk);\n    this.metricsMetadata.selected_template = selectedTemplate;\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/new/index.ts",
    "content": "import Command from '@cli/internal/base';\nimport { Help } from '@oclif/core';\n\nexport default class New extends Command {\n  static readonly description =\n    'Create a new AsyncAPI project, specification files, or templates for clients and applications.';\n  async run() {\n    const help = new Help(this.config);\n    help.showHelp(['new', '--help']);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/new/template.ts",
    "content": "import { promises as fPromises } from 'fs';\nimport Command from '@cli/internal/base';\nimport { resolve, join } from 'path';\nimport { load } from '@models/SpecificationFile';\nimport fs from 'fs-extra';\nimport { templateFlags } from '@cli/internal/flags/new/template.flags';\nimport { cyan, gray } from 'picocolors';\nimport jsonfile from 'jsonfile';\nimport path from 'path';\n\nexport const successMessage = (projectName: string) =>\n  `🎉 Your template is succesfully created\n⏩ Next steps: follow the instructions ${cyan('below')} to manage your project:\n\n  cd ${projectName}\\t\\t ${gray('# Navigate to the project directory')}\n  npm install\\t\\t ${gray('# Install the project dependencies')}\n  asyncapi generate fromTemplate <templateName> ../${projectName} \\t\\t ${gray('# Execute the template from anasyncapi document')}\n\nYou can also open the project in your favourite editor and start tweaking it.\n`;\n\nconst errorMessages = {\n  alreadyExists: (projectName: string) =>\n    `Unable to create the project because the directory \"${cyan(projectName)}\" already exists at \"${process.cwd()}/${projectName}\".\nTo specify a different name for the new project, please run the command below with a unique project name:\n\n    ${gray('asyncapi new template --name ') + gray(projectName) + gray('-1')}`,\n};\n\nexport default class template extends Command {\n  static description = 'Creates a new template';\n  protected commandName = 'template';\n  static readonly successMessage = successMessage;\n  static readonly errorMessages = errorMessages;\n  static flags = templateFlags();\n\n  async run() {\n    const { flags } = await this.parse(template); // NOSONAR\n\n    const { name: projectName, template: templateName } = flags;\n\n    const PROJECT_DIRECTORY = join(process.cwd(), projectName);\n\n    const templateDirectory = resolve(\n      __dirname,\n      '../../../../../assets/create-template/templates/',\n      templateName,\n    );\n\n    {\n      try {\n        await fPromises.mkdir(PROJECT_DIRECTORY);\n      } catch (err: any) {\n        switch (err.code) {\n        case 'EEXIST':\n          this.error(errorMessages.alreadyExists(projectName));\n          break;\n        case 'EACCES':\n          this.error(\n            `Unable to create the project. We tried to access the \"${PROJECT_DIRECTORY}\" directory but it was not possible due to file access permissions. Please check the write permissions of your current working directory (\"${process.cwd()}\").`,\n          );\n          break;\n        case 'EPERM':\n          this.error(\n            `Unable to create the project. We tried to create the \"${PROJECT_DIRECTORY}\" directory but the operation requires elevated privileges. Please check the privileges for your current user.`,\n          );\n          break;\n        default:\n          this.error(\n            `Unable to create the project. Please check the following message for further info about the error:\\n\\n${err}`,\n          );\n        }\n      }\n\n      try {\n        await copyAndModify(templateDirectory, PROJECT_DIRECTORY, projectName);\n        this.log(successMessage(projectName));\n      } catch (err) {\n        this.error(\n          `Unable to create the project. Please check the following message for further info about the error:\\n\\n${err}`,\n        );\n      }\n      this.specFile = await load(`${templateDirectory}/asyncapi.yaml`);\n      this.metricsMetadata.template = flags.template;\n    }\n  }\n}\n\nasync function copyAndModify(\n  templateDirectory: string,\n  PROJECT_DIRECTORY: string,\n  projectName: string,\n) {\n  const packageJsonPath = path.join(templateDirectory, 'package.json');\n  try {\n    await fs.copy(templateDirectory, PROJECT_DIRECTORY, {\n      filter: (src) => {\n        return !src.endsWith('package.json');\n      },\n    });\n    const packageData = await jsonfile.readFile(packageJsonPath);\n    if (packageData.generator && 'renderer' in packageData.generator) {\n      packageData.generator.renderer = 'react';\n    }\n    if (packageData.name) {\n      packageData.name = projectName;\n    }\n\n    await fs.writeJSON(`${PROJECT_DIRECTORY}/package.json`, packageData, {\n      spaces: 2,\n    });\n  } catch (err) {\n    console.error('Error:', err);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/optimize.ts",
    "content": "import { Args } from '@oclif/core';\nimport { Optimizer, Output, Report, ReportElement } from '@asyncapi/optimizer';\nimport Command from '@cli/internal/base';\nimport { ValidationError } from '@errors/validation-error';\nimport { load, retrieveFileFormat } from '@models/SpecificationFile';\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\nimport { promises } from 'fs';\nimport { Parser } from '@asyncapi/parser';\nimport { optimizeFlags } from '@cli/internal/flags/optimize.flags';\nimport { proxyFlags } from '@cli/internal/flags/proxy.flags';\nimport { applyProxyToPath } from '@utils/proxy';\n\nconst { writeFile } = promises;\n\nexport enum Optimizations {\n  REMOVE_COMPONENTS = 'remove-components',\n  REUSE_COMPONENTS = 'reuse-components',\n  MOVE_DUPLICATES_TO_COMPONENTS = 'move-duplicates-to-components',\n  MOVE_ALL_TO_COMPONENTS = 'move-all-to-components',\n}\n\nexport enum DisableOptimizations {\n  SCHEMA = 'schema',\n}\n\nexport enum Outputs {\n  TERMINAL = 'terminal',\n  NEW_FILE = 'new-file',\n  OVERWRITE = 'overwrite',\n}\nexport default class Optimize extends Command {\n  static description = 'optimize asyncapi specification file';\n  isInteractive = false;\n  selectedOptimizations?: Optimizations[];\n  disableOptimizations?: DisableOptimizations[];\n  outputMethod?: Outputs;\n\n  static examples = [\n    'asyncapi optimize ./asyncapi.yaml',\n    'asyncapi optimize ./asyncapi.yaml --no-tty',\n    'asyncapi optimize ./asyncapi.yaml --optimization=remove-components --optimization=reuse-components --optimization=move-all-to-components --no-tty',\n    'asyncapi optimize ./asyncapi.yaml --optimization=remove-components --output=terminal --no-tty',\n    'asyncapi optimize ./asyncapi.yaml --ignore=schema',\n  ];\n\n  static flags = {\n    ...optimizeFlags(),\n    ...proxyFlags(),\n  };\n\n  static args = {\n    'spec-file': Args.string({\n      description: 'spec path, url, or context-name',\n      required: false,\n    }),\n  };\n\n  parser = new Parser();\n\n  async run() {\n    const { args, flags } = await this.parse(Optimize); //NOSONAR\n    const filePath = applyProxyToPath(\n      args['spec-file'],\n      flags['proxyHost'],\n      flags['proxyPort']\n    );\n    try {\n      this.specFile = await load(filePath);\n    } catch (err: any) {\n      if (err.message.includes('Failed to download')) {\n        throw new Error(\n          'Proxy Connection Error: Unable to establish a connection to the proxy check hostName or PortNumber.',\n        );\n      } else if (filePath) {\n        this.error(\n          new ValidationError({\n            type: 'invalid-file',\n            filepath: filePath,\n          }),\n        );\n      } else {\n        this.error(\n          new ValidationError({\n            type: 'no-spec-found',\n          }),\n        );\n      }\n    }\n\n    let optimizer: Optimizer;\n    let report: Report;\n    try {\n      optimizer = new Optimizer(this.specFile.text());\n      report = await optimizer.getReport();\n    } catch {\n      this.error(\n        new ValidationError({\n          type: 'invalid-syntax-file',\n          filepath: this.specFile.getFilePath(),\n        }),\n      );\n    }\n    this.isInteractive = !flags['no-tty'];\n    this.selectedOptimizations = flags.optimization as Optimizations[];\n    this.disableOptimizations = flags.ignore as DisableOptimizations[];\n    this.outputMethod = flags.output as Outputs;\n    this.metricsMetadata.optimized = false;\n\n    if (\n      !(\n        report.moveDuplicatesToComponents?.length ||\n        report.removeComponents?.length ||\n        report.reuseComponents?.length\n      )\n    ) {\n      this.log(\n        `🎉 Great news! Your file at ${this.specFile.getFilePath() ?? this.specFile.getFileURL()} is already optimized.`,\n      );\n      return;\n    }\n\n    const isTTY = process.stdout.isTTY;\n    if (this.isInteractive && isTTY) {\n      await this.interactiveRun(report);\n    }\n\n    try {\n      const fileFormat = retrieveFileFormat(this.specFile.text());\n      let optimizedDocument = optimizer.getOptimizedDocument({\n        rules: {\n          moveDuplicatesToComponents: this.selectedOptimizations.includes(\n            Optimizations.MOVE_DUPLICATES_TO_COMPONENTS,\n          ),\n          moveAllToComponents: this.selectedOptimizations.includes(\n            Optimizations.MOVE_ALL_TO_COMPONENTS,\n          ),\n          removeComponents: this.selectedOptimizations.includes(\n            Optimizations.REMOVE_COMPONENTS,\n          ),\n          reuseComponents: this.selectedOptimizations.includes(\n            Optimizations.REUSE_COMPONENTS,\n          ),\n        },\n        disableOptimizationFor: {\n          schema: this.disableOptimizations.includes(\n            DisableOptimizations.SCHEMA,\n          ),\n        },\n        output: fileFormat === 'json' ? Output.JSON : Output.YAML,\n      });\n      if (fileFormat === 'json') {\n        optimizedDocument = JSON.stringify((JSON.parse(optimizedDocument)), null, 2);\n      }\n\n      this.collectMetricsData(report);\n\n      const specPath = this.specFile.getFilePath();\n      let newPath = '';\n\n      if (specPath) {\n        const pos = specPath.lastIndexOf('.');\n        newPath = `${specPath.substring(0, pos)}_optimized.${specPath.substring(pos + 1)}`;\n      } else {\n        newPath = `optimized-asyncapi.${fileFormat}`;\n      }\n\n      switch (this.outputMethod) {\n      case Outputs.TERMINAL:\n        this.log('📄 Here is your optimized AsyncAPI document:\\n');\n        this.log(optimizedDocument);\n        break;\n      case Outputs.NEW_FILE:\n        await writeFile(newPath, optimizedDocument, { encoding: 'utf8' });\n        this.log(\n          `✅ Success! Your optimized file has been created at ${chalk.blue(newPath)}.`,\n        );\n        break;\n      case Outputs.OVERWRITE:\n        await writeFile(specPath ?? `asyncapi.${fileFormat}`, optimizedDocument, {\n          encoding: 'utf8',\n        });\n        this.log(\n          `✅ Success! Your original file at ${specPath} has been updated.`,\n        );\n        break;\n      }\n    } catch (error) {\n      throw new ValidationError({\n        type: 'parser-error',\n        err: error,\n      });\n    }\n  }\n\n  private showOptimizations(elements: ReportElement[] | undefined) {\n    if (!elements) {\n      return;\n    }\n\n    for (let i = 0; i < elements.length; i++) {\n      const element = elements[+i];\n      if (element.action === 'move') {\n        this.log(\n          `${chalk.green('move')} ${element.path} to ${element.target} and reference it.`,\n        );\n      } else if (element.action === 'reuse') {\n        this.log(\n          `${chalk.green('reuse')} ${element.target} in ${element.path}.`,\n        );\n      } else if (element.action === 'remove') {\n        this.log(`${chalk.red('remove')} ${element.path}.`);\n      }\n    }\n\n    this.log('\\n');\n  }\n\n  private async interactiveRun(report: Report) {\n    const canMoveDuplicates = report.moveDuplicatesToComponents?.length;\n    const canMoveAll = report.moveAllToComponents?.length;\n    const canRemove = report.removeComponents?.length;\n    const canReuse = report.reuseComponents?.length;\n    const choices = [];\n\n    if (canMoveAll) {\n      const totalMove = report.moveAllToComponents?.filter(\n        (e: ReportElement) => e.action === 'move',\n      ).length;\n      this.log(\n        `${chalk.green(totalMove)} components can be moved to the components sections.\\nthe following changes will be made:`,\n      );\n      this.showOptimizations(report.moveAllToComponents);\n      choices.push({\n        name: 'move all $refs to components section',\n        value: Optimizations.MOVE_ALL_TO_COMPONENTS,\n      });\n    }\n    if (canMoveDuplicates) {\n      const totalMove = report.moveDuplicatesToComponents?.filter(\n        (e: ReportElement) => e.action === 'move',\n      ).length;\n      this.log(\n        `\\n${chalk.green(totalMove)} components can be moved to the components sections.\\nthe following changes will be made:`,\n      );\n      this.showOptimizations(report.moveDuplicatesToComponents);\n      choices.push({\n        name: 'move to components section',\n        value: Optimizations.MOVE_DUPLICATES_TO_COMPONENTS,\n      });\n    }\n    if (canRemove) {\n      const totalMove = report.removeComponents?.length;\n      this.log(\n        `${chalk.green(totalMove)} unused components can be removed.\\nthe following changes will be made:`,\n      );\n      this.showOptimizations(report.removeComponents);\n      choices.push({\n        name: 'remove components',\n        value: Optimizations.REMOVE_COMPONENTS,\n      });\n    }\n    if (canReuse) {\n      const totalMove = report.reuseComponents?.length;\n      this.log(\n        `${chalk.green(totalMove)} components can be reused.\\nthe following changes will be made:`,\n      );\n      this.showOptimizations(report.reuseComponents);\n      choices.push({\n        name: 'reuse components',\n        value: Optimizations.REUSE_COMPONENTS,\n      });\n    }\n\n    if (this.disableOptimizations?.includes(DisableOptimizations.SCHEMA)) {\n      choices.push({\n        name: 'Do not ignore schema',\n        value: DisableOptimizations.SCHEMA,\n      });\n    } else {\n      choices.push({\n        name: 'Ignore schema',\n        value: DisableOptimizations.SCHEMA,\n      });\n    }\n\n    const optimizationRes = await inquirer.prompt([\n      {\n        name: 'optimization',\n        message: 'select the type of optimization that you want to apply:',\n        type: 'checkbox',\n        default: 'all',\n        choices,\n      },\n    ]);\n\n    if (optimizationRes.optimization.includes('schema')) {\n      if (this.disableOptimizations?.includes(DisableOptimizations.SCHEMA)) {\n        this.disableOptimizations = this.disableOptimizations?.filter(\n          (opt) => opt !== DisableOptimizations.SCHEMA,\n        );\n      } else {\n        this.disableOptimizations = [\n          ...(this.disableOptimizations || []),\n          DisableOptimizations.SCHEMA,\n        ];\n      }\n    }\n\n    this.selectedOptimizations = optimizationRes.optimization;\n\n    const outputRes = await inquirer.prompt([\n      {\n        name: 'output',\n        message: 'where do you want to save the result:',\n        type: 'list',\n        default: 'log to terminal',\n        choices: [\n          { name: 'log to terminal', value: Outputs.TERMINAL },\n          { name: 'create new file', value: Outputs.NEW_FILE },\n          { name: 'update original file', value: Outputs.OVERWRITE },\n        ],\n      },\n    ]);\n    this.outputMethod = outputRes.output;\n  }\n\n  private collectMetricsData(report: Report) {\n    for (const availableOptimization in report) {\n      const availableOptimizationKebabCase = availableOptimization\n        .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n        .toLowerCase(); // optimization flags are kebab case\n      if (\n        availableOptimization.length &&\n        this.selectedOptimizations?.includes(\n          availableOptimizationKebabCase as Optimizations,\n        )\n      ) {\n        this.metricsMetadata[`optimization_${availableOptimization}`] = true;\n        this.metricsMetadata.optimized = true;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/pretty.ts",
    "content": "import { Args } from '@oclif/core';\nimport { promises as fs } from 'fs';\nimport * as yaml from 'yaml';\nimport Command from '@cli/internal/base';\nimport { load, retrieveFileFormat } from '@models/SpecificationFile';\nimport { ValidationError } from '@errors/validation-error';\nimport { prettyFlags } from '@cli/internal/flags/pretty.flags';\n\nexport default class Pretty extends Command {\n  static readonly description =\n    'Beautify the AsyncAPI spec file (indentation, styling) in place or output the formatted spec to a new file.';\n\n  static readonly examples = [\n    'asyncapi pretty ./asyncapi.yaml',\n    'asyncapi pretty ./asyncapi.yaml --output formatted-asyncapi.yaml',\n  ];\n\n  static readonly flags = prettyFlags();\n\n  static readonly args = {\n    'spec-file': Args.string({\n      description: 'spec path, url, or context-name',\n      required: true,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(Pretty);\n    const filePath = args['spec-file'];\n    const outputPath = flags.output;\n\n    try {\n      this.specFile = await load(filePath);\n    } catch {\n      this.error(\n        new ValidationError({\n          type: 'invalid-file',\n          filepath: filePath,\n        }),\n      );\n    }\n\n    const content = this.specFile.text();\n    let formatted: string;\n\n    try {\n      const fileFormat = retrieveFileFormat(this.specFile.text());\n      if (fileFormat === 'yaml' || fileFormat === 'yml') {\n        const yamlDoc = yaml.parseDocument(content);\n        formatted = yamlDoc.toString({\n          lineWidth: 0,\n        });\n      } else if (fileFormat === 'json') {\n        const jsonObj = JSON.parse(content);\n        formatted = JSON.stringify(jsonObj, null, 2);\n      } else {\n        throw new Error('Unsupported file format');\n      }\n    } catch (err) {\n      this.error(`Error formatting file: ${err}`);\n    }\n\n    if (outputPath) {\n      await fs.writeFile(outputPath, formatted, 'utf8');\n      this.log(`Asyncapi document has been beautified ${outputPath}`);\n    } else {\n      await fs.writeFile(filePath, formatted, 'utf8');\n      this.log(`Asyncapi document ${filePath} has been beautified in-place.`);\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/start/api.ts",
    "content": "process.env['NODE_CONFIG_DIR'] = `${__dirname}/../../../api/configs`;\n\nimport Command from '@cli/internal/base';\nimport { apiFlags } from '../../internal/flags/start/api.flags';\nimport { App } from '@/apps/api/app';\nimport { CONTROLLERS } from '@/apps/api';\n\nexport default class Api extends Command {\n  static readonly description = 'starts the AsyncAPI server API.';\n\n  static readonly flags = apiFlags();\n\n  static readonly args = {};\n\n  async run() {\n    const { flags } = await this.parse(Api);\n\n    const app = new App(\n      CONTROLLERS,\n      flags.port || 3000, // Default port if not specified\n      flags.mode,\n    );\n\n    await app.init();\n    app.listen();\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/start/index.ts",
    "content": "import Command from '@cli/internal/base';\nimport { Help } from '@oclif/core';\n\nexport default class Start extends Command {\n  static description =\n    'Starts AsyncAPI-related services. Currently, it supports launching the AsyncAPI Studio';\n  async run() {\n    const help = new Help(this.config);\n    help.showHelp(['start', '--help']);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/start/preview.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { previewFlags } from '@cli/internal/flags/start/preview.flags';\nimport { load } from '@models/SpecificationFile';\nimport { startPreview } from '@models/Preview';\n\nexport default class PreviewStudio extends Command {\n  static readonly description =\n    'starts a new local instance of Studio in minimal state bundling all the refs of the schema file and with no editing allowed.';\n\n  static readonly flags = previewFlags();\n\n  static readonly args = {\n    'spec-file': Args.string({\n      description:\n        'the path to the file to be opened with studio or context name',\n      required: true,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(PreviewStudio);\n\n    let filePath: string | undefined = args['spec-file'] ?? flags.file;\n\n    const previewPort = parseInt(flags.port ?? '0',10);\n\n    if (!filePath) {\n      filePath = (await load()).getFilePath();\n      this.log(`Loaded the specification from: ${filePath}`);\n    }\n    try {\n      this.specFile = await load(filePath);\n    } catch (error) {\n      if (filePath) {\n        this.error(error as Error);\n      }\n    }\n    this.metricsMetadata.port = previewPort;\n    startPreview(\n      filePath as string,\n      flags.base,\n      flags.baseDir,\n      flags.xOrigin,\n      flags.suppressLogs,\n      previewPort,\n      flags.noBrowser\n    );\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/start/studio.ts",
    "content": "import Command from '@cli/internal/base';\nimport { start as startStudio } from '@models/Studio';\nimport { load } from '@models/SpecificationFile';\nimport { studioFlags } from '@cli/internal/flags/start/studio.flags';\nimport { Args } from '@oclif/core';\nimport { isCancel, text, cancel } from '@clack/prompts';\n\nexport default class StartStudio extends Command {\n  static description = 'starts a new local instance of Studio';\n\n  static flags = studioFlags();\n\n  static readonly args = {\n    'spec-file': Args.string({\n      description: 'spec path, url, or context-name',\n      required: false,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(StartStudio);\n\n    let filePath = args['spec-file'] ?? flags.file;\n\n    let port = parseInt(flags.port ?? '0',10);\n\n    if (flags.file) {\n      this.warn(\n        'The file flag has been removed and is being replaced by the argument spec-file. Please pass the filename directly like `asyncapi start studio asyncapi.yml`',\n      );\n    }\n\n    const isInteractive = !flags['no-interactive'];\n\n    if (isInteractive && !filePath) {\n      const parsedArgs = await this.parseArgs({ filePath }, port?.toString());\n      filePath = parsedArgs.filePath;\n      port = parseInt(parsedArgs.port, 10);\n    }\n\n    if (!filePath) {\n      try {\n        filePath = (await load()).getFilePath();\n        this.log(`Loaded specification from: ${filePath}`);\n      } catch {\n        filePath = '';\n        this.error('No file specified.');\n      }\n    }\n    try {\n      this.specFile = await load(filePath);\n    } catch (error) {\n      if (filePath) {\n        this.error(error as Error);\n      }\n    }\n    this.metricsMetadata.port = port;\n    startStudio(filePath as string, port,flags.noBrowser);\n  }\n\n  private async parseArgs(args: Record<string, any>, port?: string) {\n    const operationCancelled = 'Operation cancelled by the user.';\n    let askForPort = false;\n    let { filePath } = args;\n    if (!filePath) {\n      filePath = await text({\n        message: 'Enter the path to the AsyncAPI document',\n        defaultValue: 'asyncapi.yaml',\n        placeholder: 'asyncapi.yaml',\n        validate: (value) => {\n          if (!value) {\n            return 'The path to the AsyncAPI document is required';\n          }\n        },\n      });\n      askForPort = true;\n    }\n\n    if (isCancel(filePath)) {\n      cancel(operationCancelled);\n      this.exit();\n    }\n\n    if (!port && askForPort) {\n      port = (await text({\n        message: 'Enter the port in which to start Studio',\n        defaultValue: '3210',\n        placeholder: '3210',\n        validate: (value) =>\n          !value ? 'The port number is required' : undefined,\n      })) as string;\n    }\n\n    if (isCancel(port)) {\n      cancel(operationCancelled);\n      this.exit();\n    }\n\n    return { filePath, port: port ?? '3210' };\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/commands/validate.ts",
    "content": "import { Args } from '@oclif/core';\nimport Command from '@cli/internal/base';\nimport { load } from '@models/SpecificationFile';\nimport { specWatcher } from '@cli/internal/globals';\nimport { validateFlags } from '@cli/internal/flags/validate.flags';\nimport { proxyFlags } from '@cli/internal/flags/proxy.flags';\nimport {\n  ServiceResult,\n  ValidationOptions,\n  ValidationResult,\n} from '@/interfaces';\nimport {\n  ValidationService,\n  ValidationStatus,\n} from '@services/validation.service';\nimport { applyProxyToPath } from '@utils/proxy';\n\nexport default class Validate extends Command {\n  static description = 'validate asyncapi file';\n  private validationService = new ValidationService();\n\n  static flags = {\n    ...validateFlags(),\n    ...proxyFlags(), // Merge proxyFlags with validateFlags\n  };\n\n  static args = {\n    'spec-file': Args.string({\n      description: 'spec path, url, or context-name',\n      required: false,\n    }),\n  };\n\n  async run() {\n    const { args, flags } = await this.parse(Validate); //NOSONAR\n    const filePath = applyProxyToPath(\n      args['spec-file'],\n      flags['proxyHost'],\n      flags['proxyPort']\n    );\n\n    this.specFile = await load(filePath);\n    const watchMode = flags.watch;\n\n    if (watchMode) {\n      specWatcher({\n        spec: this.specFile,\n        handler: this,\n        handlerName: 'validate',\n      });\n    }\n\n    // Prepare validate options\n    const validateOptions: ValidationOptions = {\n      ...flags,\n      suppressWarnings: flags['suppressWarnings'],\n      suppressAllWarnings: flags['suppressAllWarnings'],\n    };\n\n    const result = await this.validationService.validateDocument(\n      this.specFile,\n      validateOptions,\n    );\n\n    if (!result.success) {\n      this.error(result.error || 'Validation failed', { exit: 1 });\n    }\n\n    this.metricsMetadata.validation_result = result;\n\n    if (flags['score']) {\n      this.log(`The score of the asyncapi document is ${result.data?.score}`);\n    }\n\n    if (flags['log-diagnostics']) {\n      await this.handleDiagnostics(result, flags);\n    }\n\n    if (result.data?.status === ValidationStatus.INVALID) {\n      process.exitCode = 1;\n    }\n  }\n\n  private async handleDiagnostics(\n    result: ServiceResult<ValidationResult>,\n    flags: any,\n  ): Promise<void> {\n    const diagnosticsFormat = flags['diagnostics-format'] ?? 'stylish';\n    const writeOutput = flags['save-output'];\n    const hasIssues =\n      (result.data?.diagnostics && result.data.diagnostics.length > 0) ?? false;\n    const isFailSeverity = result.data?.status === ValidationStatus.INVALID;\n    const sourceString = this.specFile?.toSourceString() || '';\n\n    const governanceMessage = this.validationService.generateGovernanceMessage(\n      sourceString,\n      hasIssues,\n      isFailSeverity,\n    );\n\n    if (isFailSeverity) {\n      this.logToStderr(governanceMessage);\n    } else {\n      this.log(governanceMessage);\n    }\n\n    const diagnosticsOutput = this.validationService.formatDiagnosticsOutput(\n      result.data?.diagnostics || [],\n      diagnosticsFormat,\n      flags['fail-severity'] ?? 'error',\n    );\n\n    if (writeOutput) {\n      const { success, error } =\n        await this.validationService.saveDiagnosticsToFile(\n          writeOutput,\n          diagnosticsFormat,\n          diagnosticsOutput,\n        );\n\n      if (!success) {\n        this.logToStderr(error || 'Failed to save diagnostics to file', {\n          exit: 1,\n        });\n      } else {\n        this.log(`Diagnostics saved to ${writeOutput}`);\n      }\n    } else {\n      this.log(diagnosticsOutput);\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/internal/args/generate.args.ts",
    "content": "import { Args } from '@oclif/core';\n\nexport const generateArgs = {\n  asyncapi: Args.string({\n    description: '- Local path, url or context-name pointing to AsyncAPI file',\n    required: false\n  })\n};\n"
  },
  {
    "path": "src/apps/cli/internal/base/BaseGeneratorCommand.ts",
    "content": "import Command from '@cli/internal/base';\n// eslint-disable-next-line\n// @ts-ignore\nimport AsyncAPIGenerator from '@asyncapi/generator';\n\nimport { load, Specification } from '@models/SpecificationFile';\nimport { ValidationError } from '@errors/validation-error';\nimport { GeneratorError } from '@errors/generator-error';\nimport { Parser } from '@asyncapi/parser';\nimport { isCancel } from '@clack/prompts';\nimport { proxyFlags } from '@cli/internal/flags/proxy.flags';\nimport { generateArgs } from '@cli/internal/args/generate.args';\nimport { watcherHandler, runWatchMode } from '@utils/generate/watcher';\nimport { getMapBaseUrlToFolderResolver } from '@utils/generate/mapBaseUrl';\nimport { promptForAsyncAPIPath, promptForOutputDir } from '@utils/generate/prompts';\nimport { ParsedFlags } from '@models/generate/Flags';\nimport { GeneratorService } from '@services/generator.service';\nimport { applyProxyToPath } from '@utils/proxy';\n\nexport interface GeneratorOptions {\n  forceWrite: boolean;\n  install: boolean;\n  debug: boolean;\n  templateParams: any;\n  noOverwriteGlobs: string[];\n  mapBaseUrlToFolder: any;\n  disabledHooks: Record<string, string>;\n  registry: {\n    url?: string;\n    auth?: string;\n    token?: string;\n  };\n}\n\nexport abstract class BaseGeneratorCommand extends Command {\n  static readonly flags = {\n    ...proxyFlags(),\n  };\n\n  static args = {\n    ...generateArgs,\n  };\n\n  parser = new Parser();\n  protected generatorService = new GeneratorService();\n\n  protected async buildGeneratorOptions(flags: any, parsedFlags: ParsedFlags): Promise<GeneratorOptions> {\n    return {\n      forceWrite: flags['force-write'],\n      install: flags.install,\n      debug: flags.debug,\n      templateParams: parsedFlags.params,\n      noOverwriteGlobs: flags['no-overwrite'],\n      mapBaseUrlToFolder: parsedFlags.mapBaseUrlToFolder,\n      disabledHooks: parsedFlags.disableHooks,\n      registry: {\n        url: flags['registry-url'],\n        auth: flags['registry-auth'],\n        token: flags['registry-token'],\n      },\n    };\n  }\n\n  protected applyProxyConfiguration(asyncapi: string, proxyHost?: string, proxyPort?: string): string {\n    return applyProxyToPath(asyncapi, proxyHost, proxyPort) ?? asyncapi;\n  }\n\n  protected async handleWatchMode(\n    asyncapi: string,\n    template: string,\n    output: string,\n    options: GeneratorOptions,\n    genOption: any,\n    interactive: boolean\n  ): Promise<void> {\n    const watcher = watcherHandler(this, asyncapi, template, output, options, genOption, interactive);\n    await runWatchMode(this, asyncapi, template, output, AsyncAPIGenerator, watcher);\n  }\n\n  protected buildGenOption(flags: any, parsedFlags: ParsedFlags): any {\n    const genOption: any = {};\n    if (flags['map-base-url']) {\n      genOption.resolve = { resolve: getMapBaseUrlToFolderResolver(parsedFlags.mapBaseUrlToFolder) };\n    }\n    return genOption;\n  }\n\n  protected async generate(\n    asyncapi: string | undefined,\n    template: string,\n    output: string,\n    options: GeneratorOptions,\n    genOption: any,\n    interactive = true\n  ): Promise<void> {\n    const specification = await this.loadSpecificationSafely(asyncapi);\n    \n    const result = await this.generatorService.generate(\n      specification,\n      template,\n      output,\n      options as any, // GeneratorService expects different options interface\n      genOption,\n      interactive,\n    );\n    \n    if (!result.success) {\n      throw new GeneratorError(new Error(result.error));\n    }\n  }\n\n  protected async parseCommonArgs(\n    args: Record<string, any>,\n    output?: string\n  ): Promise<{ asyncapi: string; output: string }> {\n    let asyncapi = args['asyncapi'];\n    const cancellationMessage = 'Operation cancelled';\n\n    if (!asyncapi) {\n      asyncapi = await promptForAsyncAPIPath();\n    }\n\n    if (isCancel(asyncapi)) {\n      this.error(cancellationMessage, { exit: 1 });\n    }\n\n    if (!output) {\n      output = await promptForOutputDir();\n    }\n\n    if (isCancel(output)) {\n      this.error(cancellationMessage, { exit: 1 });\n    }\n\n    return { asyncapi, output };\n  }\n\n  protected async loadAsyncAPIInput(asyncapi: string) {\n    return (await load(asyncapi)) || (await load());\n  }\n\n  protected handleCancellation(value: any): void {\n    if (isCancel(value)) {\n      this.error('Operation cancelled', { exit: 1 });\n    }\n  }\n\n  protected async loadSpecificationSafely(asyncapi: string | undefined): Promise<Specification> {\n    try {\n      return await load(asyncapi);\n    } catch {\n      return this.error(\n        new ValidationError({\n          type: 'invalid-file',\n          filepath: asyncapi,\n        }),\n        { exit: 1 },\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/internal/base.ts",
    "content": "import { Command } from '@oclif/core';\nimport {\n  MetadataFromDocument,\n  MetricMetadata,\n  NewRelicSink,\n  Recorder,\n  Sink,\n  StdOutSink,\n} from '@smoya/asyncapi-adoption-metrics';\nimport { Parser } from '@asyncapi/parser';\nimport { Specification } from '@models/SpecificationFile';\nimport { join, resolve } from 'path';\nimport { existsSync } from 'fs-extra';\nimport { promises as fPromises } from 'fs';\nimport { v4 as uuidv4 } from 'uuid';\nimport { homedir } from 'os';\n\nconst { readFile, writeFile, stat } = fPromises;\n\nclass DiscardSink implements Sink {\n  async send() {\n    // noop\n  }\n}\n\nexport default abstract class extends Command {\n  recorder = this.recorderFromEnv('asyncapi_adoption');\n  parser = new Parser();\n  metricsMetadata: MetricMetadata = {};\n  specFile: Specification | undefined;\n\n  async init(): Promise<void> {\n    await super.init();\n    const commandName: string = this.id || '';\n    await this.recordActionInvoked(commandName, this.metricsMetadata);\n  }\n\n  async catch(err: Error & { exitCode?: number }): Promise<void> {\n    try {\n      await super.catch(err);\n    } catch (e: unknown) {\n      if (e instanceof Error) {\n        if (e.message.includes('EEXIT: 0')) {\n          process.exitCode = 0;\n          return;\n        }\n        this.logToStderr(`${e.name}: ${e.message}`);\n        process.exitCode = 1;\n      }\n    }\n  }\n\n  async recordActionFinished(\n    action: string,\n    metadata: MetricMetadata = {},\n    rawDocument?: string,\n  ) {\n    if (rawDocument !== undefined) {\n      try {\n        const { document } = await this.parser.parse(rawDocument);\n        if (document !== undefined) {\n          // @ts-ignore\n          metadata = MetadataFromDocument(document, metadata);\n        }\n      } catch (e: unknown) {\n        if (e instanceof Error) {\n          this.log(\n            `Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`,\n          );\n        }\n      }\n    }\n\n    const callable = async function (recorder: Recorder) {\n      await recorder.recordActionFinished(action, metadata);\n    };\n\n    await this.recordActionMetric(callable);\n  }\n\n  async recordActionInvoked(action: string, metadata?: MetricMetadata) {\n    const callable = async function (recorder: Recorder) {\n      await recorder.recordActionInvoked(action, metadata);\n    };\n\n    await this.recordActionMetric(callable);\n  }\n\n  async recordActionMetric(recordFunc: (recorder: Recorder) => Promise<void>) {\n    try {\n      await this.setSource();\n      await recordFunc(await this.recorder);\n      await (await this.recorder).flush();\n    } catch (e: unknown) {\n      if (e instanceof Error) {\n        this.log(\n          `Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`,\n        );\n      }\n    }\n  }\n\n  async setSource() {\n    const specFilePath = this.specFile?.getFilePath();\n    if (!specFilePath) {\n      return;\n    }\n    try {\n      const stats = await stat(specFilePath);\n      this.metricsMetadata['file_creation_timestamp'] = stats.birthtimeMs;\n    } catch {\n      // If there's an error with the file, we don't handle it here because it's expected to be handled and reported in the 'finally' method of the command.\n    }\n  }\n  async finally(error: Error | undefined): Promise<any> {\n    await super.finally(error);\n    this.metricsMetadata['success'] = error === undefined;\n    await this.recordActionFinished(\n      this.id as string,\n      this.metricsMetadata,\n      this.specFile?.text(),\n    );\n  }\n\n  async recorderFromEnv(prefix: string): Promise<Recorder> {\n    let sink: Sink = new DiscardSink();\n    const analyticsConfigFile =\n      process.env.ASYNCAPI_METRICS_CONFIG_PATH ||\n      join(homedir(), '.asyncapi-analytics');\n\n    if (!existsSync(analyticsConfigFile)) {\n      await writeFile(\n        analyticsConfigFile,\n        JSON.stringify({\n          analyticsEnabled: 'true',\n          infoMessageShown: 'false',\n          userID: uuidv4(),\n        }),\n        { encoding: 'utf8' },\n      );\n    }\n\n    const analyticsConfigFileContent = JSON.parse(\n      await readFile(resolve(analyticsConfigFile), { encoding: 'utf8' }),\n    );\n    this.metricsMetadata['user'] = analyticsConfigFileContent.userID;\n\n    if (\n      analyticsConfigFileContent.analyticsEnabled !== 'false' &&\n      process.env.CI !== 'true'\n    ) {\n      switch (process.env.NODE_ENV) {\n      case 'development':\n        // NODE_ENV set to `development` in bin/run\n        if (!process.env.TEST) {\n          // Do not pollute stdout when running tests\n          sink = new StdOutSink();\n        }\n        break;\n      case 'production':\n        // NODE_ENV set to `production` in bin/run_bin, which is specified in 'bin' package.json section\n        sink = new NewRelicSink(\n          process.env.ASYNCAPI_METRICS_NEWRELIC_KEY ||\n              'eu01xx73a8521047150dd9414f6aedd2FFFFNRAL',\n        );\n\n        if (analyticsConfigFileContent.infoMessageShown === 'false') {\n          this.log(\n            '\\nAsyncAPI anonymously tracks command executions to improve the specification and tools, ensuring no sensitive data reaches our servers. It aids in comprehending how AsyncAPI tools are used and adopted, facilitating ongoing improvements to our specifications and tools.\\n\\nTo disable tracking, please run the following command:\\n  asyncapi config analytics --disable\\n\\nOnce disabled, if you want to enable tracking back again then run:\\n  asyncapi config analytics --enable\\n',\n          );\n          analyticsConfigFileContent.infoMessageShown = 'true';\n          await writeFile(\n            analyticsConfigFile,\n            JSON.stringify(analyticsConfigFileContent),\n            { encoding: 'utf8' },\n          );\n        }\n        break;\n      }\n    }\n\n    return new Recorder(prefix, sink);\n  }\n}\n"
  },
  {
    "path": "src/apps/cli/internal/flags/bundle.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const bundleFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    output: Flags.string({\n      char: 'o',\n      description:\n        'The output file name. Omitting this flag the result will be printed in the console.',\n    }),\n    base: Flags.string({\n      char: 'b',\n      description:\n        'Path to the file which will act as a base. This is required when some properties need to be overwritten.',\n    }),\n    baseDir: Flags.string({\n      char: 'd',\n      description:\n        'One relative/absolute path to directory relative to which paths to AsyncAPI Documents that should be bundled will be resolved.',\n    }),\n    xOrigin: Flags.boolean({\n      char: 'x',\n      description:\n        'Pass this switch to generate properties \"x-origin\" that will contain historical values of dereferenced \"$ref\"s.',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/config/analytics.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const analyticsFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    disable: Flags.boolean({\n      char: 'd',\n      description: 'disable analytics',\n      default: false,\n    }),\n    enable: Flags.boolean({\n      char: 'e',\n      description: 'enable analytics',\n      default: false,\n    }),\n    status: Flags.boolean({\n      char: 's',\n      description: 'show current status of analytics',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/config/context.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const addFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    'set-current': Flags.boolean({\n      char: 's',\n      description: 'Set context being added as the current context',\n      default: false,\n      required: false,\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/convert.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const convertFlags = (latestVersion: string) => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    output: Flags.string({\n      char: 'o',\n      description: 'path to the file where the result is saved',\n    }),\n    format: Flags.string({\n      char: 'f',\n      description: 'Specify the format to convert from (openapi or asyncapi)',\n      options: ['openapi', 'asyncapi'],\n      required: true,\n      default: 'asyncapi',\n    }),\n    'target-version': Flags.string({\n      char: 't',\n      description: 'asyncapi version to convert to',\n      default: latestVersion,\n    }),\n    perspective: Flags.string({\n      char: 'p',\n      description:\n        'Perspective to use when converting OpenAPI to AsyncAPI (client or server). Note: This option is only applicable for OpenAPI to AsyncAPI conversions.',\n      options: ['client', 'server'],\n      default: 'server',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/diff.flags.ts",
    "content": "import { Flags } from '@oclif/core';\nimport { watchFlag } from './global.flags';\nimport { parserFlags } from './parser.flags';\n\nexport const diffFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    format: Flags.string({\n      char: 'f',\n      description: 'format of the output',\n      default: 'yaml',\n      options: ['json', 'yaml', 'yml', 'md'],\n    }),\n    type: Flags.string({\n      char: 't',\n      description: 'type of the output',\n      default: 'all',\n      options: ['breaking', 'non-breaking', 'unclassified', 'all'],\n    }),\n    markdownSubtype: Flags.string({\n      description:\n        'the format of changes made to AsyncAPI document. It works only when diff is generated using md type. For example, when you specify subtype as json, then diff information in markdown is dumped as json structure.',\n      default: undefined,\n      options: ['json', 'yaml', 'yml'],\n    }),\n    overrides: Flags.string({\n      char: 'o',\n      description: 'path to JSON file containing the override properties',\n    }),\n    'no-error': Flags.boolean({\n      description: 'don\\'t show error on breaking changes',\n    }),\n    watch: watchFlag(),\n    ...parserFlags({ logDiagnostics: false }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/format.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport type fileFormat = 'yaml' | 'yml' | 'json';\n\nconst availFileFormats: fileFormat[] = ['yaml', 'yml', 'json'];\n\nexport const convertFormatFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    format: Flags.string({\n      char: 'f',\n      description: 'Specify the format to convert to',\n      options: availFileFormats,\n      required: true,\n      default: 'json',\n    }),\n    output: Flags.string({\n      char: 'o',\n      description: 'path to the file where the result is saved',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/generate/clients.flags.ts",
    "content": "import { sharedFlags } from './sharedFlags';\n\nexport const clientsFlags = () => {\n  return {\n    ...sharedFlags\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/generate/fromTemplate.flags.ts",
    "content": "import { sharedFlags } from './sharedFlags';\n\nexport const fromTemplateFlags = () => {\n  return {\n    ...sharedFlags,\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/generate/models.flags.ts",
    "content": "import { Flags } from '@oclif/core';\nimport { ModelinaFlags } from '@asyncapi/modelina-cli';\nimport { parserFlags } from '../parser.flags';\n\nexport const modelsFlags = (): Record<string, any> => {\n  return {\n    ...ModelinaFlags,\n    'no-interactive': Flags.boolean({\n      description: 'Disable interactive mode and run with the provided flags.',\n      required: false,\n      default: false,\n    }),\n    ...parserFlags({ logDiagnostics: false }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/generate/sharedFlags.ts",
    "content": "import { Flags } from '@oclif/core';\nimport { watchFlag } from '../global.flags';\n\nexport const sharedFlags = {\n  help: Flags.help({ char: 'h' }),\n  'disable-hook': Flags.string({\n    char: 'd',\n    description: 'Disable a specific hook type or hooks from a given hook type',\n    multiple: true\n  }),\n  'no-interactive': Flags.boolean({\n    description: 'Disable interactive mode and run with the provided flags.',\n    default: false,\n  }),\n  install: Flags.boolean({\n    char: 'i',\n    default: false,\n    description: 'Installs the template and its dependencies (defaults to false)'\n  }),\n  debug: Flags.boolean({\n    description: 'Enable more specific errors in the console'\n  }),\n  'no-overwrite': Flags.string({\n    char: 'n',\n    multiple: true,\n    description: 'Glob or path of the file(s) to skip when regenerating'\n  }),\n  output: Flags.string({\n    char: 'o',\n    description: 'Directory where to put the generated files (defaults to current directory)',\n  }),\n  'force-write': Flags.boolean({\n    default: false,\n    description: 'Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)'\n  }),\n  watch: watchFlag(\n    'Watches the template directory and the AsyncAPI document, and re-generate the files when changes occur. Ignores the output directory.'\n  ),\n  param: Flags.string({\n    char: 'p',\n    description: 'Additional param to pass to templates',\n    multiple: true\n  }),\n  'map-base-url': Flags.string({\n    description: 'Maps all schema references from base url to local folder'\n  }),\n  'registry-url': Flags.string({\n    default: 'https://registry.npmjs.org',\n    description: 'Specifies the URL of the private registry for fetching templates and dependencies'\n  }),\n  'registry-auth': Flags.string({\n    description: 'The registry username and password encoded with base64, formatted as username:password'\n  }),\n  'registry-token': Flags.string({\n    description: 'The npm registry authentication token, that can be passed instead of base64 encoded username and password'\n  }),\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/global.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const watchFlag = (description?: string) => {\n  return Flags.boolean({\n    default: false,\n    char: 'w',\n    description: description ?? 'Enable watch mode',\n  });\n};\n\nexport const helpFlag = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/new/file.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const fileFlags = (exampleFlagDescription: string) => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    'file-name': Flags.string({ char: 'n', description: 'name of the file' }),\n    example: Flags.string({ char: 'e', description: exampleFlagDescription }),\n    studio: Flags.boolean({ char: 's', description: 'open in Studio' }),\n    port: Flags.integer({\n      char: 'p',\n      description: 'port in which to start Studio',\n    }),\n    'no-tty': Flags.boolean({\n      description: 'do not use an interactive terminal',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/new/template.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const templateFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    name: Flags.string({\n      char: 'n',\n      description: 'Name of the Project',\n      default: 'project',\n    }),\n    template: Flags.string({\n      char: 't',\n      description: 'Name of the Template',\n      default: 'default',\n    }),\n    file: Flags.string({\n      char: 'f',\n      description: 'The path to the AsyncAPI file for generating a template.',\n    }),\n    'force-write': Flags.boolean({\n      default: false,\n      description:\n        'Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/optimize.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport enum Optimizations {\n  REMOVE_COMPONENTS = 'remove-components',\n  REUSE_COMPONENTS = 'reuse-components',\n  MOVE_DUPLICATES_TO_COMPONENTS = 'move-duplicates-to-components',\n  MOVE_ALL_TO_COMPONENTS = 'move-all-to-components',\n}\n\nexport enum DisableOptimizations {\n  SCHEMA = 'schema',\n}\n\nexport enum Outputs {\n  TERMINAL = 'terminal',\n  NEW_FILE = 'new-file',\n  OVERWRITE = 'overwrite',\n}\n\nexport const optimizeFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    optimization: Flags.string({\n      char: 'p',\n      default: Object.values(Optimizations),\n      options: Object.values(Optimizations),\n      multiple: true,\n      description: 'select the type of optimizations that you want to apply.',\n    }),\n    ignore: Flags.string({\n      char: 'i',\n      default: [],\n      options: Object.values(DisableOptimizations),\n      multiple: true,\n      description:\n        'list of components to be ignored from the optimization process',\n    }),\n    output: Flags.string({\n      char: 'o',\n      default: Outputs.TERMINAL,\n      options: Object.values(Outputs),\n      description: 'select where you want the output.',\n    }),\n    'no-tty': Flags.boolean({\n      description: 'do not use an interactive terminal',\n      default: false,\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/parser.flags.ts",
    "content": "import { Flags } from '@oclif/core';\nimport { OutputFormat } from '@stoplight/spectral-cli/dist/services/config';\n\nexport interface ValidationFlagsOptions {\n  logDiagnostics?: boolean;\n}\n\nexport function parserFlags({\n  logDiagnostics = true,\n}: ValidationFlagsOptions = {}) {\n  return {\n    'log-diagnostics': Flags.boolean({\n      description: 'log validation diagnostics or not',\n      default: logDiagnostics,\n      allowNo: true,\n    }),\n    'diagnostics-format': Flags.option({\n      description: 'format to use for validation diagnostics',\n      options: Object.values(OutputFormat),\n      default: OutputFormat.STYLISH,\n    })(),\n    'fail-severity': Flags.option({\n      description:\n        'diagnostics of this level or above will trigger a failure exit code',\n      options: ['error', 'warn', 'info', 'hint'] as const,\n      default: 'error',\n    })(),\n    'save-output': Flags.string({\n      description:\n        'The output file name. Omitting this flag the result will be printed in the console.',\n      char: 's',\n    }),\n  };\n}\n"
  },
  {
    "path": "src/apps/cli/internal/flags/pretty.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const prettyFlags = () => {\n  return {\n    output: Flags.string({\n      char: 'o',\n      description: 'Output file path',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/proxy.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const proxyFlags = () => {\n  return {\n    proxyHost: Flags.string({\n      description: 'Name of the ProxyHost',\n      required: false,\n    }),\n    proxyPort: Flags.string({\n      description: 'Port number number for the proxyHost.',\n      required: false,\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/start/api.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const apiFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    mode: Flags.string({\n      char: 'm',\n      description: 'mode in which to start the API',\n      default: 'production',\n      options: ['development', 'production', 'test'],\n    }),\n    port: Flags.integer({\n      char: 'p',\n      description: 'port in which to start the API',\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/start/preview.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const previewFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    port: Flags.string({\n      char: 'p',\n      description: 'port in which to start Studio in the preview mode',\n    }),\n    base: Flags.string({\n      char: 'b',\n      description:\n        'Path to the file which will act as a base. This is required when some properties need to be overwritten while bundling with the file.',\n    }),\n    baseDir: Flags.string({\n      char: 'd',\n      description:\n        'One relative/absolute path to directory relative to which paths to AsyncAPI Documents that should be bundled will be resolved.',\n    }),\n    xOrigin: Flags.boolean({\n      char: 'x',\n      description:\n        'Pass this switch to generate properties \"x-origin\" that will contain historical values of dereferenced \"$ref\"s.',\n    }),\n    suppressLogs: Flags.boolean({\n      char: 'l',\n      description: 'Pass this to suppress the detiled error logs.',\n      default: false,\n    }),\n    noBrowser: Flags.boolean({char: 'B', description: 'Pass this to not open browser automatically upon running the command', default: false})\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/start/studio.flags.ts",
    "content": "import { Flags } from '@oclif/core';\n\nexport const studioFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    file: Flags.string({\n      char: 'f',\n      description: 'path to the AsyncAPI file to link with Studio',\n      deprecated: true,\n    }),\n    port: Flags.string({\n      char: 'p',\n      description: 'port in which to start Studio',\n    }),\n    'no-interactive': Flags.boolean({\n      description: 'disable prompts for this command which asks for file path if not passed via the arguments.',\n      required: false,\n      default: false,\n    }),\n    noBrowser: Flags.boolean({char: 'B', description: 'Pass this to not open browser automatically upon running the command', default: false})\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/flags/validate.flags.ts",
    "content": "import { Flags } from '@oclif/core';\nimport { watchFlag } from './global.flags';\nimport { parserFlags } from './parser.flags';\n\nexport const validateFlags = () => {\n  return {\n    help: Flags.help({ char: 'h' }),\n    watch: watchFlag(),\n    ...parserFlags(),\n    score: Flags.boolean({\n      description:\n        'Compute the score of the AsyncAPI document. Scoring is based on whether the document has description, license, server and/or channels.',\n      required: false,\n      default: false,\n    }),\n    suppressWarnings: Flags.string({\n      description:\n        'List of warning codes to suppress from the validation output.',\n      required: false,\n      multiple: true,\n    }),\n    suppressAllWarnings: Flags.boolean({\n      description: 'Suppress all warnings from the validation output.',\n      required: false,\n      default: false,\n    }),\n  };\n};\n"
  },
  {
    "path": "src/apps/cli/internal/global.d.ts",
    "content": "declare module '@asyncapi/specs';\n\ndeclare module '@asyncapi/openapi-schema-parser';\ndeclare module '@asyncapi/avro-schema-parser';\ndeclare module '@asyncapi/raml-dt-schema-parser';\ndeclare module '@asyncapi/protobuf-schema-parser';\n"
  },
  {
    "path": "src/apps/cli/internal/globals.ts",
    "content": "import chokidar from 'chokidar';\nimport chalk from 'chalk';\nimport Command from './base';\nimport { Specification } from '@models/SpecificationFile';\nimport { getErrorMessage } from '@utils/error-handler';\n\nconst GreenLog = chalk.hex('#00FF00');\nconst OrangeLog = chalk.hex('#FFA500');\nconst CHOKIDAR_CONFIG = {\n  // awaitWriteFinish: true // Used for large size specification files.\n};\nconst WATCH_MESSAGES = {\n  logOnStart: (filePath: string) =>\n    console.log(GreenLog(`Watching AsyncAPI file at ${filePath}\\n`)),\n  logOnChange: (handlerName: string) =>\n    console.log(OrangeLog(`Change detected, running ${handlerName}\\n`)),\n  logOnAutoDisable: (docVersion: 'old' | 'new' | '' = '') =>\n    console.log(\n      OrangeLog(\n        `Watch mode for ${docVersion || 'AsyncAPI'} file was not enabled.`,\n      ),\n      OrangeLog('\\nINFO: Watch works only with files from local file system\\n'),\n    ),\n};\n\nconst CHOKIDAR_INSTANCE_STORE = new Map<string, boolean>();\n\nexport type SpecWatcherParams = {\n  spec: Specification;\n  handler: Command;\n  handlerName: string;\n  label?: string;\n  docVersion?: 'old' | 'new';\n};\n\nexport const specWatcher = (params: SpecWatcherParams) => {\n  if (!params.spec.getFilePath()) {\n    return WATCH_MESSAGES.logOnAutoDisable(params.docVersion);\n  }\n  if (CHOKIDAR_INSTANCE_STORE.get(params.label ?? '_default')) {\n    return;\n  }\n\n  const filePath = params.spec.getFilePath() as string;\n  try {\n    WATCH_MESSAGES.logOnStart(filePath);\n    chokidar.watch(filePath, CHOKIDAR_CONFIG).on('change', async () => {\n      if (params.handlerName) {\n        WATCH_MESSAGES.logOnChange(params.handlerName);\n      }\n\n      try {\n        await params.handler.run();\n      } catch (err: unknown) {\n        await params.handler.catch(err as Error);\n      }\n    });\n    CHOKIDAR_INSTANCE_STORE.set(params.label || '_default', true);\n  } catch (error: unknown) {\n    console.error(chalk.red(`Watch error: ${getErrorMessage(error)}`));\n  }\n};\n"
  },
  {
    "path": "src/apps/cli/internal/hooks/command_not_found/myhook.ts",
    "content": "import { Help, Hook, toConfiguredId } from '@oclif/core';\nimport { confirm } from '@clack/prompts';\nimport chalk from 'chalk';\nimport { default as levenshtein } from 'fast-levenshtein';\n\nexport const closest = (target: string, possibilities: string[]): string =>\n  possibilities\n    .map((id) => ({\n      distance: levenshtein.get(target, id, { useCollator: true }),\n      id,\n    }))\n    .sort((a, b) => a.distance - b.distance)[0]?.id ?? '';\n\nconst hook: Hook.CommandNotFound = async function (opts) {\n  if (opts.id === '--help') {\n    const help = new Help(this.config);\n    help.showHelp(['--help']);\n    return;\n  }\n  const hiddenCommandIds = new Set(\n    opts.config.commands.filter((c) => c.hidden).map((c) => c.id),\n  );\n  const commandIDs = [\n    ...opts.config.commandIDs,\n    ...opts.config.commands.flatMap((c) => c.aliases),\n  ].filter((c) => !hiddenCommandIds.has(c));\n\n  if (commandIDs.length === 0) {\n    return;\n  }\n\n  // now we we return if the command id are not there.\n\n  let binHelp = `${opts.config.bin} --help`;\n\n  const idSplit = opts.id.split(':');\n  if (opts.config.findTopic(idSplit[0])) {\n    // if valid topic, update binHelp with topic\n    binHelp = `${binHelp} ${idSplit[0]}`;\n  }\n\n  //if there is a topic in the opts we just upgrade the our commnad like\n\n  // alter the suggestion in the help scenario so that help is the first command\n  // otherwise the user will be presented 'did you mean 'help'?' instead of 'did you mean \"help <command>\"?'\n  let suggestion = (/:?help:?/).test(opts.id)\n    ? ['help', ...opts.id.split(':').filter((cmd) => cmd !== 'help')].join(':')\n    : closest(opts.id, commandIDs);\n\n  let readableSuggestion = toConfiguredId(suggestion, this.config);\n  const originalCmd = toConfiguredId(opts.id, this.config);\n  this.warn(\n    `${chalk.yellow(originalCmd)} is not a ${opts.config.bin} command.`,\n  );\n\n  let response;\n  try {\n    if (opts.id === 'help') {\n      readableSuggestion = '--help';\n    }\n    response = await confirm({\n      message: `Did you mean ${chalk.blueBright(readableSuggestion)}? [y/n]`,\n    });\n  } catch (error) {\n    this.log('');\n    this.debug(error);\n  }\n\n  if (response === true) {\n    // this will split the original command from the suggested replacement, and gather the remaining args as varargs to help with situations like:\n    // confit set foo-bar -> confit:set:foo-bar -> config:set:foo-bar -> config:set foo-bar\n    let argv = opts.argv?.length\n      ? opts.argv\n      : opts.id.split(':').slice(suggestion.split(':').length);\n    if (suggestion.startsWith('help:')) {\n      // the args are the command/partial command you need help for (package:version)\n      // we created the suggestion variable to start with \"help\" so slice the first entry\n      argv = suggestion.split(':').slice(1);\n      // the command is just the word \"help\"\n      suggestion = 'help';\n    }\n    if (opts.id === 'help') {\n      return this.config.runCommand('--help');\n    }\n    return this.config.runCommand(suggestion, argv);\n  }\n\n  this.error(\n    `Run ${chalk.bold.cyan(binHelp)} for a list of available commands.`,\n    { exit: 127 },\n  );\n};\n\nexport default hook;\n"
  },
  {
    "path": "src/domains/models/Context.ts",
    "content": "import { readFileSync, promises as fs, existsSync, lstatSync } from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\n\nimport {\n  ContextNotFoundError,\n  MissingContextFileError,\n  MissingCurrentContextError,\n  ContextFileWrongFormatError,\n  ContextAlreadyExistsError,\n  ContextFileEmptyError,\n  ContextFileWriteError,\n} from '@errors/context-error';\n\nconst { readFile, writeFile } = fs;\n\n// `repoRootPath` is optimistically assigned current working directory's\n// filesystem path because chances are it will become 'official' repository root\n// down the execution.\n//\n// `REPO_ROOT_PATH` will be converted to a real constant after migration of the\n// codebase to ES2022 or higher and introduction of construction\n//\n// const REPO_ROOT_PATH = await getRepoRootPath(process.cwd());\n//\n// See explanation of the situation with `CONTEXT_FILE_PATH` below.\nlet REPO_ROOT_PATH = process.cwd();\ngetRepoRootPath(process.cwd());\n\nconst DEFAULT_CONTEXT_FILENAME = '.asyncapi-cli';\nconst DEFAULT_CONTEXT_FILE_LOCATION = os.homedir();\nexport const DEFAULT_CONTEXT_FILE_PATH = path.resolve(\n  DEFAULT_CONTEXT_FILE_LOCATION,\n  DEFAULT_CONTEXT_FILENAME,\n);\n\nconst CONTEXT_FILENAME =\n  process.env.CUSTOM_CONTEXT_FILENAME || DEFAULT_CONTEXT_FILENAME;\nconst CONTEXT_FILE_LOCATION =\n  process.env.CUSTOM_CONTEXT_FILE_LOCATION || DEFAULT_CONTEXT_FILE_LOCATION;\n\n// Usage of promises for assignment of their resolved values to constants is\n// known to be troublesome:\n// https://www.reddit.com/r/learnjavascript/comments/p7p7zw/assigning_data_from_a_promise_to_a_constant\n//\n// In this particular case and usage of ES6, there is a race condition during\n// code execution, due to faster assignment of default values to\n// `CONTEXT_FILE_PATH` than resolution of the promise. This is the cause\n// `CONTEXT_FILE_PATH` will always pick default values for context file's path\n// instead of waiting for resolution of the promise from `getContextFilePath()`.\n// The situation might become better with use of top-level await which should\n// pause code execution, until promise in construction\n//\n// const CONTEXT_FILE_PATH = await getContextFilePath() || path.resolve(CONTEXT_FILE_LOCATION, CONTEXT_FILENAME) || DEFAULT_CONTEXT_FILE_PATH;\n//\n// is resolved, but for this to be checked, all codebase (including\n// `@oclif/core`) needs to be migrated to ES2022 or higher.\n//\n// Until then `CONTEXT_FILE_PATH` name is mimicking a `const` while right now it\n// is a `let` reassigned inside of `getContextFilePath()`.\nexport let CONTEXT_FILE_PATH =\n  path.resolve(CONTEXT_FILE_LOCATION, CONTEXT_FILENAME) ||\n  DEFAULT_CONTEXT_FILE_PATH;\n\n// Sonar recognizes next line as a bug `Promises must be awaited, end with a\n// call to .catch, or end with a call to .then with a rejection handler.` but\n// due to absence of top-level await in ES6, this bug cannot be fixed without\n// migrating the codebase to ES2022 or higher, thus suppressing Sonar analysis\n// for this line.\ngetContextFilePath(); // NOSONAR\n\nexport interface IContextFile {\n  current?: string;\n  readonly store: {\n    [name: string]: string;\n  };\n}\n\nexport interface ICurrentContext {\n  readonly current: string;\n  readonly context: string;\n}\n\nexport async function initContext(contextFilePath: string) {\n  const fileContent: IContextFile = {\n    store: {},\n  };\n  let contextWritePath = '';\n\n  // prettier-ignore\n  switch (contextFilePath) {\n  /* eslint-disable indent */\n    case '.':\n      contextWritePath = process.cwd() + path.sep + CONTEXT_FILENAME;\n      break;\n    case './':\n      contextWritePath = REPO_ROOT_PATH + path.sep + CONTEXT_FILENAME;\n      break;\n    // There are two variants of `~` case because tilde expansion in UNIX\n    // systems is not a guaranteed feature - sometimes `~` can return just `~`\n    // instead of home directory path.\n    // https://stackoverflow.com/questions/491877/how-to-find-a-users-home-directory-on-linux-or-unix#comment17161699_492669\n    case os.homedir():\n      contextWritePath = os.homedir() + path.sep + CONTEXT_FILENAME;\n      break;\n    case '~':\n      contextWritePath = os.homedir() + path.sep + CONTEXT_FILENAME;\n      break;\n    default:\n      contextWritePath = process.cwd() + path.sep + CONTEXT_FILENAME;\n  }\n\n  try {\n    await writeFile(contextWritePath, JSON.stringify(fileContent), {\n      encoding: 'utf8',\n    });\n  } catch {\n    throw new ContextFileWriteError(contextWritePath);\n  }\n\n  return contextWritePath;\n}\n\nexport async function loadContext(contextName?: string): Promise<string> {\n  const fileContent: IContextFile = await loadContextFile();\n  if (contextName) {\n    const context = fileContent.store[String(contextName)];\n    if (!context) {\n      throw new ContextNotFoundError(contextName);\n    }\n    return context;\n  } else if (fileContent.current) {\n    const context = fileContent.store[fileContent.current];\n    if (!context) {\n      throw new ContextNotFoundError(fileContent.current);\n    }\n    return context;\n  }\n  throw new MissingCurrentContextError();\n}\n\nexport async function addContext(contextName: string, pathToFile: string) {\n  const fileContent: IContextFile = await loadContextFile();\n\n  // If context file already has context name similar to the one specified as\n  // an argument, notify user about it (throw `ContextAlreadyExistsError`\n  // error) and exit.\n  if (fileContent.store.hasOwnProperty.call(fileContent.store, contextName)) {\n    throw new ContextAlreadyExistsError(contextName, CONTEXT_FILE_PATH);\n  }\n\n  fileContent.store[String(contextName)] = String(pathToFile);\n  await saveContextFile(fileContent);\n}\n\nexport async function removeContext(contextName: string) {\n  const fileContent: IContextFile = await loadContextFile();\n\n  if (await isContextFileEmpty(fileContent)) {\n    throw new ContextFileEmptyError(CONTEXT_FILE_PATH);\n  }\n  if (!fileContent.store[String(contextName)]) {\n    throw new ContextNotFoundError(contextName);\n  }\n  if (fileContent.current === contextName) {\n    delete fileContent.current;\n  }\n\n  delete fileContent.store[String(contextName)];\n  await saveContextFile(fileContent);\n}\n\nexport async function getCurrentContext(): Promise<ICurrentContext> {\n  const fileContent: IContextFile = await loadContextFile();\n\n  if (await isContextFileEmpty(fileContent)) {\n    throw new ContextFileEmptyError(CONTEXT_FILE_PATH);\n  }\n\n  const context = await loadContext();\n\n  return {\n    current: fileContent.current as string,\n    context,\n  };\n}\n\nexport async function setCurrentContext(contextName: string) {\n  const fileContent: IContextFile = await loadContextFile();\n\n  if (await isContextFileEmpty(fileContent)) {\n    throw new ContextFileEmptyError(CONTEXT_FILE_PATH);\n  }\n\n  if (!fileContent.store[String(contextName)]) {\n    throw new ContextNotFoundError(contextName);\n  }\n\n  fileContent.current = String(contextName);\n  await saveContextFile(fileContent);\n}\n\nexport async function editContext(contextName: string, pathToFile: string) {\n  // The expression is not wrapped in a `try...catch` block and is allowed to\n  // throw automatically because it is assumed that `loadContextFile()` works\n  // with a 100%-existing valid file in this case, thus if it threw anyway -\n  // some REAL error happened and user should know about it.\n  const fileContent: IContextFile = await loadContextFile();\n\n  if (await isContextFileEmpty(fileContent)) {\n    throw new ContextFileEmptyError(CONTEXT_FILE_PATH);\n  }\n\n  fileContent.store[String(contextName)] = String(pathToFile);\n  await saveContextFile(fileContent);\n}\n\nexport async function loadContextFile(): Promise<IContextFile> {\n  let fileContent: IContextFile;\n\n  // If the context file cannot be read then it's a 'MissingContextFileError'\n  // error.\n  try {\n    await readFile(CONTEXT_FILE_PATH, { encoding: 'utf8' });\n  } catch {\n    throw new MissingContextFileError();\n  }\n\n  // If the context file cannot be parsed then it's a\n  // 'ContextFileWrongFormatError' error.\n  try {\n    fileContent = JSON.parse(\n      await readFile(CONTEXT_FILE_PATH, { encoding: 'utf8' }),\n    );\n  } catch {\n    // https://stackoverflow.com/questions/29797946/handling-bad-json-parse-in-node-safely\n    throw new ContextFileWrongFormatError(CONTEXT_FILE_PATH);\n  }\n\n  // If the context file cannot be validated then it's a\n  // 'ContextFileWrongFormatError' error.\n  if (!(await isContextFileValid(fileContent))) {\n    throw new ContextFileWrongFormatError(CONTEXT_FILE_PATH);\n  }\n\n  return fileContent;\n}\n\nasync function saveContextFile(fileContent: IContextFile) {\n  try {\n    await writeFile(\n      CONTEXT_FILE_PATH,\n      JSON.stringify({\n        current: fileContent.current,\n        store: fileContent.store,\n      }),\n      { encoding: 'utf8' },\n    );\n  } catch {\n    throw new ContextFileWriteError(CONTEXT_FILE_PATH);\n  }\n}\n\nasync function getRepoRootPath(repoRootPath: string): Promise<string | null> {\n  // Asynchronous `fs.exists()` is deprecated, asynchronous `fs.stat()`\n  // introduces race condition, thus synchronous functions are used.\n\n  let pathToCheck = `${repoRootPath}${path.sep}.git`;\n\n  // If directory where `init` was requested in, happens to contain `.git`\n  // directory, then it surely is a root of repository, no need to search\n  // further and `REPO_ROOT_PATH` will remain as it was.\n  if (existsSync(pathToCheck) && lstatSync(pathToCheck).isDirectory()) {\n    return null;\n  }\n\n  // Directory where `init` was requested in, did not happen to contain `.git`\n  // directory, so preparation for iterating through array of filesystem paths\n  // is started.\n  const repoRootPathArray = repoRootPath.split(path.sep);\n\n  // Last element in array is thrown away because it is already known that it\n  // does not contain directory `.git`.\n  repoRootPathArray.pop();\n\n  // Backwards search of the array of filesystem paths will now be performed.\n  let i = repoRootPathArray.length - 1;\n\n  while (i > 0) {\n    pathToCheck = `${repoRootPathArray.join(path.sep)}${path.sep}.git`;\n\n    if (existsSync(pathToCheck) && lstatSync(pathToCheck).isDirectory()) {\n      REPO_ROOT_PATH = repoRootPathArray.join(path.sep);\n      return REPO_ROOT_PATH;\n    }\n\n    // Last (`0th`) element is an empty string, so if directory `.git` was not\n    // found on 1st element (last actual directory in filesystem), the search\n    // does not need to continue and `REPO_ROOT_PATH` will remain having the\n    // value of current (where `init` was requested in) directory.\n    if (i === 1) {\n      return null;\n    }\n\n    repoRootPathArray.pop();\n\n    i--;\n  }\n  return null;\n}\n\nasync function getContextFilePath(): Promise<string | null> {\n  const currentPath = process\n    .cwd()\n    .slice(REPO_ROOT_PATH.length + 1)\n    .split(path.sep);\n  currentPath.unshift(REPO_ROOT_PATH);\n\n  for (let i = currentPath.length; i >= 0; i--) {\n    const currentPathString = currentPath[0]\n      ? currentPath.join(path.sep) + path.sep + CONTEXT_FILENAME\n      : os.homedir() + path.sep + CONTEXT_FILENAME;\n\n    // This `try...catch` is a part of `for` loop and is used only to swallow\n    // errors if the file does not exist or cannot be read, to continue\n    // uninterrupted execution of the loop.\n    try {\n      // If a file is found which can be read and passed validation as a\n      // legitimate context file, then it is considered a legitimate context\n      // file indeed.\n      const fileContent = JSON.parse(\n        //we do not use await readFile because getContextFilePath cannot be called inside async function\n        readFileSync(currentPathString, { encoding: 'utf8' }),\n      );\n      if (\n        fileContent &&\n        (await isContextFileValid(fileContent as unknown as IContextFile))\n      ) {\n        CONTEXT_FILE_PATH = currentPathString;\n        return CONTEXT_FILE_PATH;\n      }\n    } catch {\n      // Silently continue to parent directory if file doesn't exist or is invalid JSON\n    }\n\n    currentPath.pop();\n  }\n  return null;\n}\n\nasync function isContextFileValid(fileContent: IContextFile): Promise<boolean> {\n  // Validation of context file's format against interface `IContextFile`.\n  return (\n    [1, 2].includes(Object.keys(fileContent).length) &&\n    fileContent.hasOwnProperty.call(fileContent, 'store') &&\n    !Array.from(Object.keys(fileContent.store)).find(\n      (elem) => typeof elem !== 'string',\n    ) &&\n    !Array.from(Object.values(fileContent.store)).find(\n      (elem) => typeof elem !== 'string',\n    )\n  );\n}\n\nexport async function isContextFileEmpty(\n  fileContent: IContextFile,\n): Promise<boolean> {\n  // If context file contains only one empty property `store` then the whole\n  // context file is considered empty.\n  return (\n    fileContent &&\n    Object.keys(fileContent).length === 1 &&\n    Object.keys(fileContent.store).length === 0\n  );\n}\n"
  },
  {
    "path": "src/domains/models/Preview.ts",
    "content": "import { SpecificationFileNotFound } from '@errors/specification-file';\nimport { existsSync,readFileSync } from 'fs';\nimport bundle from '@asyncapi/bundler';\nimport { createServer } from 'http';\nimport { WebSocketServer } from 'ws';\nimport chokidar from 'chokidar';\nimport open from 'open';\nimport path from 'path';\nimport yaml from 'js-yaml';\nimport { blueBright, redBright } from 'picocolors';\nimport { version as studioVersion } from '@asyncapi/studio/package.json';\n\nconst sockets: any[] = [];\nconst messageQueue: string[] = [];\nconst filePathsToWatch: Set<string> = new Set<string>();\nconst defaultErrorMessage = 'error occured while bundling files. use --detailedLog or -l flag to get more details.';\n\nlet bundleError = true;\n\nexport const DEFAULT_PORT = 0;\n\nfunction isValidFilePath(filePath: string): boolean {\n  return existsSync(filePath);\n}\n\ntype NextFactory = (config?: any) => any;\n\n// Using require here is necessary for dynamic module resolution\nfunction resolveStudioNextInstance(studioPath: string): NextFactory {\n  const resolvedNextPath = require.resolve('next', { paths: [studioPath] });\n  const nextModule = require(resolvedNextPath);\n  return nextModule.default ?? nextModule;\n}\n \nexport function startPreview(filePath:string,base:string | undefined,baseDirectory:string | undefined ,xOrigin:boolean | undefined,suppressLogs:boolean|undefined,port: number = DEFAULT_PORT, noBrowser?: boolean):void {\n  if (filePath && !isValidFilePath(filePath)) {\n    throw new SpecificationFileNotFound(filePath);\n  }\n  \n  const baseDir = path.dirname(path.resolve(filePath));\n  bundle(filePath).then((doc) => {\n    if (doc) {\n      bundleError = false;\n    }\n  }).catch((err) => {\n    if (suppressLogs) {\n      console.log(defaultErrorMessage);\n    } else {\n      console.log(err);\n    }\n  });\n\n  const studioPath = path.dirname(require.resolve('@asyncapi/studio/package.json'));\n  const nextInstance = resolveStudioNextInstance(studioPath);\n  const app = nextInstance({\n    dev: false,\n    dir: studioPath,\n    conf: {\n      distDir: 'build',\n    } as any,\n  });\n\n  const handle = app.getRequestHandler();\n  \n  const wsServer = new WebSocketServer({ noServer: true });\n\n  wsServer.on('connection',(socket:any) => {\n    sockets.push(socket);\n    sendQueuedMessages();\n  });\n\n  wsServer.on('close', (socket: any) => {\n    sockets.splice(sockets.findIndex(s => s === socket));\n  });\n\n  app.prepare().then(() => {\n    if (filePath && !bundleError) {\n      messageQueue.push(JSON.stringify({\n        type: 'preview:connected',\n        code: 'Preview server connected'\n      }));\n      sendQueuedMessages();\n      findPathsToWatchFromSchemaRef(filePath,baseDir);\n      filePathsToWatch.add(path.resolve(baseDir, filePath));\n      chokidar.watch([...filePathsToWatch]).on('all',(event) => {\n        switch (event) {\n        case 'add':\n          bundle([filePath],{\n            base,\n            baseDir: baseDirectory,\n            xOrigin,\n          }).then((intitalDocument) => {\n            messageQueue.push(JSON.stringify({\n              type: 'preview:file:added',\n              code: (path.extname(filePath) === '.yaml' || path.extname(filePath) === '.yml') ? \n                intitalDocument.yml() : intitalDocument.string()\n            }));\n            sendQueuedMessages();\n          }).catch((e) => {\n            if (suppressLogs) {\n              console.log(defaultErrorMessage);\n            } else {\n              console.log(e);\n            }\n          });\n          break;\n        case 'change':\n          bundle([filePath],{\n            base,\n            baseDir: baseDirectory,\n            xOrigin,\n          }).then((modifiedDocument) => {\n            messageQueue.push(JSON.stringify({\n              type: 'preview:file:changed',\n              code: (path.extname(filePath) === '.yaml' || path.extname(filePath) === '.yml') ? \n                modifiedDocument.yml() : modifiedDocument.string()\n            }));\n            sendQueuedMessages();\n          }).catch((error) => {\n            if (suppressLogs) {\n              console.log(defaultErrorMessage);\n            } else {\n              console.log(error);\n            }\n          });\n          break;      \n        case 'unlink':\n          messageQueue.push(JSON.stringify({\n            type: 'preview:file:deleted',\n            filePath,\n          }));\n          sendQueuedMessages();\n          break;\n        }\n      });\n    }\n\n    const server = createServer((req, res) => {\n      if (req.url === '/close') {\n        res.writeHead(200, { 'Content-Type': 'text/plain' });\n        res.end('Shutting down server');\n        for (const socket of wsServer.clients) {\n          socket.close();\n        }\n        // Close the server\n        server.close(() => {\n          // eslint-disable-next-line no-process-exit\n          process.exit(0);\n        });\n        return;\n      }\n      handle(req, res);\n    });\n\n    server.on('upgrade', (request, socket, head) => {\n      if (request.url === '/preview-server' && request.headers['origin'] === `http://localhost:${port}`) {\n        console.log('🔗 WebSocket connection established for the preview.');\n        wsServer.handleUpgrade(request, socket, head, (sock: any) => {\n          wsServer.emit('connection', sock, request);\n        });\n      } else {\n        console.log('🔗 WebSocket connection not established.');\n        socket.destroy();\n      }\n    });\n    \n    if (!bundleError) {\n      server.listen(port, () => {\n        const previewServerAddr = server.address();\n        const currentPort = (previewServerAddr && typeof previewServerAddr === 'object' && 'port' in previewServerAddr) ? (previewServerAddr as any).port : port;\n        const url = `http://localhost:${currentPort}?previewServer=${currentPort}&studio-version=${studioVersion}`;\n        console.log(`🎉 Connected to Preview Server running at ${blueBright(url)}.`);\n        console.log(`🌐 Open this URL in your web browser: ${blueBright(url)}`);\n        console.log(`🛑 If needed, press ${redBright('Ctrl + C')} to stop the server.`);\n        \n        if (filePath) {\n          for (const entry of filePathsToWatch) {\n            console.log(`👁️ Watching changes on file ${blueBright(entry)}`);\n          }\n        } else {\n          console.warn(\n            'Warning: No file was provided, and we couldn\\'t find a default file (like \"asyncapi.yaml\" or \"asyncapi.json\") in the current folder. Starting Studio with a blank workspace.'\n          );\n        }\n        if (!bundleError && !noBrowser) {\n          open(url);\n        }\n      }).on('error', (error) => {\n        if (error.message.includes('EADDRINUSE')) {\n          console.log(error);\n          console.error(redBright(`Error: Port ${port} is already in use.`));\n          // eslint-disable-next-line no-process-exit\n          process.exit(1);\n        } else {\n          console.error(`Failed to start server on port ${port}:`, 'cause',error.cause, '\\n', 'name', error.name , '\\n' , 'stack' , error.stack , '\\n', 'message',error.message);\n        }\n      }); \n    }\n  });\n}\n\nfunction sendQueuedMessages() {\n  while (messageQueue.length && sockets.length) {\n    const nextMessage = messageQueue.shift();\n    for (const socket of sockets) {\n      socket.send(nextMessage);\n    }\n  }\n}\n\nfunction isLocalRefAPath(key: string, value: any): boolean {\n  return (typeof value === 'string' && key === '$ref' && \n    (value.startsWith('.') || value.startsWith('./') || \n    value.startsWith('../') || !value.startsWith('#')));\n}\n\nfunction findPathsToWatchFromSchemaRef(filePath: string,baseDir:string) {\n  if (filePath && !isValidFilePath(filePath)) {\n    throw new SpecificationFileNotFound(filePath);\n  }\n  const document = yaml.load(readFileSync(filePath,'utf-8'));\n  const stack:object[] = [document as object];\n\n  while (stack.length > 0) {\n    const current = stack.pop();\n\n    if (current === null || typeof current !== 'object') {\n      continue;\n    }\n\n    for (const [key,value] of Object.entries(current)) {\n      if (isLocalRefAPath(key, value)) {\n        const absolutePath = path.resolve(baseDir, value);\n        filePathsToWatch.add(absolutePath);\n      }\n\n      if (value !== null && typeof value === 'object') {\n        stack.push(value);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/domains/models/SpecificationFile.ts",
    "content": "import { promises as fs } from 'fs';\nimport path from 'path';\nimport { URL } from 'url';\nimport yaml from 'js-yaml';\nimport { loadContext } from './Context';\nimport { ErrorLoadingSpec } from '@errors/specification-file';\nimport { MissingContextFileError } from '@errors/context-error';\nimport { fileFormat } from '@cli/internal/flags/format.flags';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { logger } from '@utils/logger';\nimport { getErrorMessage } from '@utils/error-handler';\nconst { readFile, lstat } = fs;\nconst allowedFileNames: string[] = [\n  'asyncapi.json',\n  'asyncapi.yml',\n  'asyncapi.yaml',\n];\nconst TYPE_CONTEXT_NAME = 'context-name';\nconst TYPE_FILE_PATH = 'file-path';\nconst TYPE_URL = 'url-path';\n\nexport class Specification {\n  private readonly spec: string;\n  private readonly filePath?: string;\n  private readonly fileURL?: string;\n  private readonly kind?: 'file' | 'url';\n\n  constructor(\n    spec: string,\n    options: { filepath?: string; fileURL?: string } = {},\n  ) {\n    this.spec = spec;\n    if (options.filepath) {\n      this.filePath = options.filepath;\n      this.kind = 'file';\n    } else if (options.fileURL) {\n      this.fileURL = options.fileURL;\n      this.kind = 'url';\n    }\n  }\n\n  isAsyncAPI3() {\n    const jsObj = this.toJson();\n    return jsObj.asyncapi.startsWith('3.');\n  }\n\n  toJson(): Record<string, any> {\n    try {\n      return yaml.load(this.spec, { json: true }) as Record<string, any>;\n    } catch {\n      return JSON.parse(this.spec);\n    }\n  }\n\n  text() {\n    return this.spec;\n  }\n\n  getFilePath() {\n    return this.filePath;\n  }\n\n  getFileURL() {\n    return this.fileURL;\n  }\n\n  getKind() {\n    return this.kind;\n  }\n\n  getSource() {\n    return this.getFilePath() ?? this.getFileURL();\n  }\n\n  toSourceString() {\n    if (this.kind === 'file') {\n      return `File ${this.filePath}`;\n    }\n    return `URL ${this.fileURL}`;\n  }\n\n  static async fromFile(filepath: string) {\n    let spec;\n    try {\n      spec = await readFile(filepath, { encoding: 'utf8' });\n    } catch {\n      throw new ErrorLoadingSpec('file', filepath);\n    }\n    return new Specification(spec, { filepath });\n  }\n\n  static async fromURL(URLpath: string) {\n    let response;\n    const delimiter = '+';\n    let targetUrl = URLpath;\n    let proxyUrl = '';\n\n    // Check if URLpath contains a proxy URL\n    if (URLpath.includes(delimiter)) {\n      [targetUrl, proxyUrl] = URLpath.split(delimiter);\n    }\n\n    try {\n      // Validate the target URL\n      new URL(targetUrl);\n\n      const fetchOptions: RequestInit & { agent?: HttpsProxyAgent<string> } = { method: 'GET' };\n\n      // If proxy URL is provided, create a proxy agent\n      if (proxyUrl) {\n        try {\n          new URL(proxyUrl);\n          const proxyAgent = new HttpsProxyAgent(proxyUrl);\n          fetchOptions.agent = proxyAgent;\n          response = await fetch(targetUrl, fetchOptions);\n        } catch (err: unknown) {\n          logger.error(`Proxy connection error: ${getErrorMessage(err)}`);\n          throw new Error(\n            'Proxy Connection Error: Unable to establish a connection to the proxy check hostName or PortNumber',\n          );\n        }\n      } else {\n        response = await fetch(targetUrl);\n        if (!response.ok) {\n          throw new ErrorLoadingSpec('url', targetUrl);\n        }\n      }\n    } catch (error: unknown) {\n      logger.error(`Error loading spec from URL: ${getErrorMessage(error)}`);\n      throw new ErrorLoadingSpec('url', targetUrl);\n    }\n\n    return new Specification((await response?.text()) as string, {\n      fileURL: targetUrl,\n    });\n  }\n}\n\nexport default class SpecificationFile {\n  private readonly pathToFile: string;\n\n  constructor(filePath: string) {\n    this.pathToFile = filePath;\n  }\n\n  getPath(): string {\n    return this.pathToFile;\n  }\n\n  async read(): Promise<string> {\n    return readFile(this.pathToFile, { encoding: 'utf8' });\n  }\n}\n\ninterface LoadType {\n  file?: boolean;\n  url?: boolean;\n  context?: boolean;\n}\n\n/* eslint-disable sonarjs/cognitive-complexity */\nexport async function load(\n  filePathOrContextName?: string,\n  loadType?: LoadType,\n): Promise<Specification> {\n  // NOSONAR\n  try {\n    if (filePathOrContextName) {\n      if (loadType?.file) {\n        return Specification.fromFile(filePathOrContextName);\n      }\n      if (loadType?.context) {\n        return loadFromContext(filePathOrContextName);\n      }\n      if (loadType?.url) {\n        return Specification.fromURL(filePathOrContextName);\n      }\n\n      const type = await nameType(filePathOrContextName);\n      if (type === TYPE_CONTEXT_NAME) {\n        return loadFromContext(filePathOrContextName);\n      }\n\n      if (type === TYPE_URL) {\n        return Specification.fromURL(filePathOrContextName);\n      }\n      await fileExists(filePathOrContextName);\n\n      return Specification.fromFile(filePathOrContextName);\n    }\n\n    return await loadFromContext();\n  } catch (e) {\n    const autoDetectedSpecFile = await detectSpecFile();\n    if (autoDetectedSpecFile) {\n      return Specification.fromFile(autoDetectedSpecFile);\n    }\n\n    if (e instanceof MissingContextFileError) {\n      throw new ErrorLoadingSpec();\n    }\n\n    throw e;\n  }\n}\n\nexport async function nameType(name: string): Promise<string> {\n  if (name.startsWith('.')) {\n    return TYPE_FILE_PATH;\n  }\n\n  try {\n    if (await fileExists(name)) {\n      return TYPE_FILE_PATH;\n    }\n    return TYPE_CONTEXT_NAME;\n  } catch {\n    if (await isURL(name)) {\n      return TYPE_URL;\n    }\n    return TYPE_CONTEXT_NAME;\n  }\n}\n\nexport async function isURL(urlpath: string): Promise<boolean> {\n  try {\n    const url = new URL(urlpath);\n    return url.protocol === 'http:' || url.protocol === 'https:';\n  } catch {\n    return false;\n  }\n}\n\nexport async function fileExists(name: string): Promise<boolean> {\n  try {\n    if ((await lstat(name)).isFile()) {\n      return true;\n    }\n\n    const extension = name.split('.')[1];\n\n    const allowedExtenstion = ['yml', 'yaml', 'json'];\n\n    if (!allowedExtenstion.includes(extension)) {\n      throw new ErrorLoadingSpec('invalid file', name);\n    }\n\n    throw new ErrorLoadingSpec('file', name);\n  } catch {\n    throw new ErrorLoadingSpec('file', name);\n  }\n}\n\nasync function loadFromContext(contextName?: string): Promise<Specification> {\n  try {\n    const context = await loadContext(contextName);\n    return Specification.fromFile(context);\n  } catch (error) {\n    if (error instanceof MissingContextFileError) {\n      throw new ErrorLoadingSpec();\n    }\n    throw error;\n  }\n}\n\nasync function detectSpecFile(): Promise<string | undefined> {\n  const existingFileNames = await Promise.all(\n    allowedFileNames.map(async (filename) => {\n      try {\n        const exists = await fileExists(path.resolve(process.cwd(), filename));\n        return exists ? filename : undefined;\n      } catch {\n        // We did our best...\n      }\n    }),\n  );\n  return existingFileNames.find((filename) => filename !== undefined);\n}\n\nexport function retrieveFileFormat(content: string): fileFormat | undefined {\n  try {\n    if (content.trimStart()[0] === '{') {\n      JSON.parse(content);\n      return 'json';\n    }\n    // below yaml.load is not a definitive way to determine if a file is yaml or not.\n    // it is able to load .txt text files also.\n    yaml.load(content);\n    return 'yaml';\n  } catch {\n    return undefined;\n  }\n}\n\n/**\n * Converts a JSON or YAML specification to YAML format.\n * \n * @param spec - The specification content as a string\n * @returns The YAML formatted string, or undefined if conversion fails\n */\nexport function convertToYaml(spec: string): string | undefined {\n  try {\n    // JS object -> YAML string\n    const jsonContent = yaml.load(spec);\n    return yaml.dump(jsonContent);\n  } catch (err: unknown) {\n    logger.error(`Failed to convert spec to YAML: ${getErrorMessage(err)}`);\n    return undefined;\n  }\n}\n\n/**\n * Converts a JSON or YAML specification to JSON format.\n * \n * @param spec - The specification content as a string\n * @returns The JSON formatted string, or undefined if conversion fails\n */\nexport function convertToJSON(spec: string): string | undefined {\n  try {\n    // JSON or YAML String -> JS object\n    const jsonContent = yaml.load(spec);\n    // JS Object -> pretty JSON string\n    return JSON.stringify(jsonContent, null, 2);\n  } catch (err: unknown) {\n    logger.error(`Failed to convert spec to JSON: ${getErrorMessage(err)}`);\n    return undefined;\n  }\n}\n"
  },
  {
    "path": "src/domains/models/Studio.ts",
    "content": "import { existsSync, promises as fPromises } from 'fs';\nimport { SpecificationFileNotFound } from '@errors/specification-file';\nimport { createServer } from 'http';\nimport { WebSocketServer } from 'ws';\nimport chokidar from 'chokidar';\nimport open from 'open';\nimport path from 'path';\nimport { version as studioVersion } from '@asyncapi/studio/package.json';\nimport { blueBright, redBright } from 'picocolors';\n\nconst { readFile, writeFile } = fPromises;\n\nconst sockets: any[] = [];\nconst messageQueue: string[] = [];\n\nexport const DEFAULT_PORT = 0;\n\nfunction isValidFilePath(filePath: string): boolean {\n  return existsSync(filePath);\n}\n\ntype NextFactory = (config?: any) => any;\n\n// Using require here is necessary for dynamic module resolution\nfunction resolveStudioNextInstance(studioPath: string): NextFactory {\n  const resolvedNextPath = require.resolve('next', { paths: [studioPath] });\n  const nextModule = require(resolvedNextPath);\n  return nextModule.default ?? nextModule;\n}\n \nexport function start(filePath: string, port: number = DEFAULT_PORT, noBrowser?:boolean): void {\n  if (filePath && !isValidFilePath(filePath)) {\n    throw new SpecificationFileNotFound(filePath);\n  }\n\n  // Locate @asyncapi/studio package\n  const studioPath = path.dirname(\n    require.resolve('@asyncapi/studio/package.json'),\n  );\n  const nextInstance = resolveStudioNextInstance(studioPath);\n  const app = nextInstance({\n    dev: false,\n    dir: studioPath,\n    conf: {\n      distDir: 'build',\n    } as any,\n  });\n\n  const handle = app.getRequestHandler();\n\n  const wsServer = new WebSocketServer({ noServer: true });\n\n  wsServer.on('connection', (socket: any) => {\n    sockets.push(socket);\n    if (filePath) {\n      getFileContent(filePath).then((code: string) => {\n        messageQueue.push(\n          JSON.stringify({\n            type: 'file:loaded',\n            code,\n          }),\n        );\n        sendQueuedMessages();\n      });\n    } else {\n      messageQueue.push(\n        JSON.stringify({\n          type: 'file:loaded',\n          code: '',\n        }),\n      );\n      sendQueuedMessages();\n    }\n\n    socket.on('message', (event: string) => {\n      try {\n        const json: any = JSON.parse(event);\n        if (filePath && json.type === 'file:update') {\n          saveFileContent(filePath, json.code);\n        } else {\n          console.warn(\n            'Live Server: An unknown event has been received. See details:',\n          );\n          console.log(json);\n        }\n      } catch {\n        console.error(\n          `Live Server: An invalid event has been received. See details:\\n${event}`,\n        );\n      }\n    });\n  });\n\n  wsServer.on('close', (socket: any) => {\n    sockets.splice(sockets.findIndex((s) => s === socket));\n  });\n\n  app.prepare().then(() => {\n    if (filePath) {\n      chokidar.watch(filePath).on('all', (event, path) => {\n        switch (event) {\n        case 'add':\n        case 'change':\n          getFileContent(path).then((code: string) => {\n            messageQueue.push(\n              JSON.stringify({\n                type: 'file:changed',\n                code,\n              }),\n            );\n            sendQueuedMessages();\n          });\n          break;\n        case 'unlink':\n          messageQueue.push(\n            JSON.stringify({\n              type: 'file:deleted',\n              filePath,\n            }),\n          );\n          sendQueuedMessages();\n          break;\n        }\n      });\n    }\n\n    const server = createServer((req, res) => {\n      if (req.url === '/close') {\n        for (const socket of wsServer.clients) {\n          socket.close();\n        }\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: 'Server is shutting down' }));\n        // Close the server\n        server.close(() => {\n          // eslint-disable-next-line no-process-exit\n          process.exit(0);\n        });\n        return;\n      }\n      handle(req, res);\n    });\n\n    server.on('upgrade', (request, socket, head) => {\n      if (request.url === '/live-server') {\n        console.log('🔗 WebSocket connection established.');\n        wsServer.handleUpgrade(request, socket, head, (sock: any) => {\n          wsServer.emit('connection', sock, request);\n        });\n      } else {\n        socket.destroy();\n      }\n    });\n\n    server.listen(port, () => {\n      const addr = server.address();\n      const listenPort = (addr && typeof addr === 'object' && 'port' in addr) ? (addr as any).port : port;\n      const url = `http://localhost:${listenPort}?liveServer=${listenPort}&studio-version=${studioVersion}`;\n      console.log(`🎉 Connected to Live Server running at ${blueBright(url)}.`);\n      console.log(`🌐 Open this URL in your web browser: ${blueBright(url)}`);\n      console.log(\n        `🛑 If needed, press ${redBright('Ctrl + C')} to stop the process.`,\n      );\n      if (filePath) {\n        console.log(`👁️ Watching changes on file ${blueBright(filePath)}`);\n      } else {\n        console.warn(\n          'Warning: No file was provided, and we couldn\\'t find a default file (like \"asyncapi.yaml\" or \"asyncapi.json\") in the current folder. Starting Studio with a blank workspace.',\n        );\n      }\n      if (!noBrowser) {\n        open(url);\n      }\n    }).on('error', (error) => {\n      if (error.message.includes('EADDRINUSE')) {\n        console.log(error);\n        console.error(redBright(`Error: Port ${port} is already in use.`));\n        // eslint-disable-next-line no-process-exit\n        process.exit(2);\n      } else {\n        console.error(`Failed to start server on port ${port}`);\n      }\n    });\n  });\n}\n\nfunction sendQueuedMessages() {\n  while (messageQueue.length && sockets.length) {\n    const nextMessage = messageQueue.shift();\n    for (const socket of sockets) {\n      socket.send(nextMessage);\n    }\n  }\n}\n\nfunction getFileContent(filePath: string): Promise<string> {\n  return new Promise((resolve) => {\n    readFile(filePath, { encoding: 'utf8' })\n      .then((code: string) => {\n        resolve(code);\n      })\n      .catch(console.error);\n  });\n}\n\nfunction saveFileContent(filePath: string, fileContent: string): void {\n  writeFile(filePath, fileContent, { encoding: 'utf8' }).catch(console.error);\n}\n"
  },
  {
    "path": "src/domains/models/generate/Flags.ts",
    "content": "export interface IMapBaseUrlToFlag {\n  url: string,\n  folder: string\n}\n\nexport interface ParsedFlags {\n  params: Record<string, string>,\n  disableHooks: Record<string, string>,\n  mapBaseUrlToFolder: IMapBaseUrlToFlag\n}\n"
  },
  {
    "path": "src/domains/services/archiver.service.ts",
    "content": "import archiver, { Archiver } from 'archiver';\nimport { Response } from 'express';\n\nimport { retrieveLangauge } from '@utils/retrieve-language';\nimport { createTempDirectory, removeTempDirectory } from '@utils/temp-dir';\n\n/**\n * Service wrapping the `archiver` module:\n    - easier zip creation\n    - adding proper `Content-Type` header\n    - easier adding an AsyncAPI document to the archive\n    - easier stream finalization\n */\nexport class ArchiverService {\n  public createZip(res?: Response) {\n    const zip = archiver('zip', { zlib: { level: 9 } });\n    if (res) {\n      zip.pipe(res);\n      res.attachment('asyncapi.zip');\n    }\n    return zip;\n  }\n\n  public appendDirectory(archive: Archiver, from: string, to: string) {\n    archive.directory(from, to);\n  }\n\n  public appendAsyncAPIDocument(\n    archive: Archiver,\n    asyncapi: string,\n    fileName = 'asyncapi',\n  ) {\n    asyncapi = JSON.stringify(asyncapi);\n    const language = retrieveLangauge(asyncapi);\n    if (language === 'yaml') {\n      archive.append(asyncapi, { name: `${fileName}.yml` });\n    } else {\n      archive.append(asyncapi, { name: `${fileName}.json` });\n    }\n  }\n\n  public async finalize(archive: Archiver) {\n    await new Promise<void>((resolve) => {\n      // wait for end stream\n      archive.on('end', resolve);\n      archive.finalize();\n    });\n  }\n\n  public createTempDirectory() {\n    return createTempDirectory();\n  }\n\n  public removeTempDirectory(tmpDir: string) {\n    return removeTempDirectory(tmpDir);\n  }\n}\n"
  },
  {
    "path": "src/domains/services/base.service.ts",
    "content": "import { ServiceResult } from '@/interfaces';\nimport type { Diagnostic } from '@asyncapi/parser/cjs';\n\n/**\n * Base service class providing common functionality for all domain services.\n * Provides standardized result handling and error management.\n */\nexport abstract class BaseService {\n  /**\n   * Creates a successful service result with the provided data.\n   *\n   * @param data - The data to include in the result\n   * @returns A successful ServiceResult containing the data\n   */\n  protected createSuccessResult<T>(data: T): ServiceResult<T> {\n    return {\n      success: true,\n      data,\n    };\n  }\n\n  /**\n   * Creates an error service result with the provided error message.\n   *\n   * @param error - The error message\n   * @param diagnostics - Optional diagnostics array for validation errors\n   * @returns A failed ServiceResult containing the error\n   */\n  protected createErrorResult<T>(\n    error: string,\n    diagnostics?: Diagnostic[],\n  ): ServiceResult<T> {\n    return {\n      success: false,\n      error,\n      diagnostics,\n    };\n  }\n\n  /**\n   * Handles service errors by converting them to a standardized error result.\n   *\n   * @param error - The caught error (can be any type)\n   * @returns A failed ServiceResult with the error message\n   */\n  protected async handleServiceError<T>(error: unknown): Promise<ServiceResult<T>> {\n    const errorMessage = error instanceof Error ? error.message : String(error);\n    return this.createErrorResult<T>(errorMessage);\n  }\n}\n"
  },
  {
    "path": "src/domains/services/config.service.ts",
    "content": "import path from 'path';\nimport os from 'os';\nimport { promises as fs } from 'fs';\n\nconst CONFIG_DIR = path.join(os.homedir(), '.asyncapi');\nconst CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\n\nexport interface AuthEntry {\n  pattern: string;\n  token: string;\n  authType?: string;\n  headers?: Record<string, string>;\n}\n\nexport interface AuthResult {\n  token: string;\n  authType: string;\n  headers: Record<string, string>;\n}\n\ninterface Config {\n  auth?: AuthEntry[];\n}\n\nexport class ConfigService {\n  /**\n   * Load config file (~/.asyncapi/config.json)\n   */\n  static async loadConfig(): Promise<Config> {\n    try {\n      const content = await fs.readFile(CONFIG_FILE, 'utf8');\n      return JSON.parse(content) as Config;\n    } catch (err: any) {\n      if (err.code === 'ENOENT') {\n        return {}; // no config yet\n      }\n      throw new Error(`Error reading config file: ${err.message}`);\n    }\n  }\n\n  /**\n   * Save config back to file\n   */\n  static async saveConfig(config: Config): Promise<void> {\n    await fs.mkdir(CONFIG_DIR, { recursive: true });\n    await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');\n  }\n\n  /**\n   * Add a new auth entry\n   */\n  static async addAuthEntry(entry: AuthEntry): Promise<void> {\n    const config = await this.loadConfig();\n    if (!config.auth) {\n      config.auth = [];\n    }\n    config.auth.push(entry);\n    await this.saveConfig(config);\n  }\n\n  /**\n   * Reads auth config from ~/.asyncapi/config.json and\n   * returns auth info matching the given URL, or null if no match.\n   * \n   * @param url - URL to match against auth patterns\n   * @returns Auth info or null if no match found\n   */\n  static async getAuthForUrl(url: string): Promise<AuthResult | null> {\n    const config = await this.loadConfig();\n\n    if (!config.auth || !Array.isArray(config.auth)) {\n      console.warn('⚠️ No valid \"auth\" array found in config');\n      return null;\n    }\n\n    for (const entry of config.auth) {\n      try {\n        const regex = this.wildcardToRegex(entry.pattern);\n        if (regex.test(url)) {\n          return {\n            token: entry.token,\n            authType: entry.authType || 'Bearer',\n            headers: entry.headers || {}\n          };\n        }\n      } catch (err: any) {\n        console.warn(`⚠️ Invalid pattern \"${entry.pattern}\": ${err.message}`);\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * Convert wildcard pattern (*, **) to RegExp matching start of string\n   * @param pattern - wildcard pattern\n   */\n  private static wildcardToRegex(pattern: string): RegExp {\n    const escaped = pattern.replace(/[-/\\\\^$+?.()|[\\]{}]/g, '\\\\$&');\n\n    const regexStr = escaped\n      .replace(/\\*\\*/g, '.*')\n      .replace(/\\*/g, '[^/]*');\n     \n    return new RegExp(`^${regexStr}`);\n  }\n}\n"
  },
  {
    "path": "src/domains/services/convert.service.ts",
    "content": "import {\n  ConversionOptions,\n  ConversionResult,\n  ServiceResult,\n} from '@/interfaces';\nimport { Specification } from '@models/SpecificationFile';\nimport { BaseService } from './base.service';\nimport {\n  convert,\n  convertOpenAPI,\n  OpenAPIConvertVersion,\n} from '@asyncapi/converter';\nimport { cyan, green } from 'picocolors';\nimport { promises as fPromises } from 'fs';\n\nexport class ConversionService extends BaseService {\n  /**\n   * Handles the conversion of AsyncAPI or OpenAPI documents based on the provided flags.\n   */\n  async convertDocument(\n    specFile: Specification,\n    options: ConversionOptions,\n  ): Promise<ServiceResult<ConversionResult>> {\n    let convertedDocument: string;\n    switch (options.format) {\n    case 'asyncapi':\n      convertedDocument = convert(\n        specFile.text() ?? '',\n        options['target-version'] || '3.1.0',\n      );\n      break;\n    case 'openapi':\n      convertedDocument = convertOpenAPI(\n        specFile.text() ?? '',\n          specFile.toJson().openapi as OpenAPIConvertVersion,\n          {\n            perspective: options.perspective,\n          },\n      );\n      break;\n    default:\n      return this.createErrorResult(\n        `Unsupported conversion format: ${options.format}`,\n      );\n    }\n\n    return this.createSuccessResult<ConversionResult>({\n      convertedDocument: this.formatConvertedFile(convertedDocument),\n      originalFormat: options.format,\n    });\n  }\n\n  handleLogging(specFile: Specification, flags: ConversionOptions): string {\n    const sourcePath = specFile.getFilePath() || specFile.getFileURL();\n    const targetVersion = flags['target-version'] || 'latest';\n    const outputMap = {\n      asyncapi: 'AsyncAPI document',\n      openapi: 'OpenAPI document',\n    };\n\n    return `🎉 The ${outputMap[flags.format]} from ${cyan(sourcePath)} has been successfully converted to AsyncAPI version ${green(targetVersion)}!!`;\n  }\n\n  /**\n   * Formats the converted document for output.\n   * Objects are stringified with indentation, strings are returned as-is.\n   *\n   * @param convertedFile - The converted file content\n   * @returns Formatted string representation\n   */\n  private formatConvertedFile(convertedFile: string | object): string {\n    return typeof convertedFile === 'object'\n      ? JSON.stringify(convertedFile, null, 4)\n      : convertedFile;\n  }\n\n  /**\n   * Writes the converted document to the specified output path.\n   *\n   * @param outputPath - The path to write the file to\n   * @param convertedFileFormatted - The formatted content to write\n   */\n  async handleOutput(outputPath: string, convertedFileFormatted: string): Promise<void> {\n    await fPromises.writeFile(outputPath, convertedFileFormatted, {\n      encoding: 'utf8',\n    });\n  }\n}\n"
  },
  {
    "path": "src/domains/services/generator.service.ts",
    "content": "import {\n  GenerationOptions,\n  GenerationResult,\n  ServiceResult,\n} from '@/interfaces';\nimport { Specification } from '../models/SpecificationFile';\nimport { BaseService } from './base.service';\n\nimport AsyncAPIGenerator from '@asyncapi/generator';\nimport { spinner } from '@clack/prompts';\nimport path from 'path';\nimport os from 'os';\nimport { pathToFileURL } from 'url';\nimport { yellow, magenta } from 'picocolors';\nimport { getErrorMessage } from '@utils/error-handler';\n\n/**\n * Options passed to the generator for code generation.\n * `path` must be a string (document base URI) for @asyncapi/generator to resolve local $ref files.\n */\ninterface GeneratorRunOptions {\n  path?: string;\n  [key: string]: unknown;\n}\n\nexport class GeneratorService extends BaseService {\n  private defaultInteractive: boolean;\n\n  constructor(interactive = false) {\n    super();\n    this.defaultInteractive = interactive;\n  }\n\n  private templatesNotSupportingV3: Record<string, string> = {\n    '@asyncapi/minimaltemplate': 'some link', // For testing purpose\n    '@asyncapi/dotnet-nats-template':\n      'https://github.com/asyncapi/dotnet-nats-template/issues/384',\n    '@asyncapi/ts-nats-template':\n      'https://github.com/asyncapi/ts-nats-template/issues/545',\n    '@asyncapi/python-paho-template':\n      'https://github.com/asyncapi/python-paho-template/issues/189',\n    '@asyncapi/nodejs-ws-template':\n      'https://github.com/asyncapi/nodejs-ws-template/issues/294',\n    '@asyncapi/java-spring-cloud-stream-template':\n      'https://github.com/asyncapi/java-spring-cloud-stream-template/issues/336',\n    '@asyncapi/go-watermill-template':\n      'https://github.com/asyncapi/go-watermill-template/issues/243',\n    '@asyncapi/java-spring-template':\n      'https://github.com/asyncapi/java-spring-template/issues/308',\n    '@asyncapi/php-template':\n      'https://github.com/asyncapi/php-template/issues/191',\n  };\n\n  /**\n   * Verify that a given template support v3, if not, return the link to the issue that needs to be solved.\n   */\n  private verifyTemplateSupportForV3(template: string) {\n    if (this.templatesNotSupportingV3[`${template}`] !== undefined) {\n      return this.templatesNotSupportingV3[`${template}`];\n    }\n    return undefined;\n  }\n\n  private getGenerationSuccessMessage(output: string): string {\n    return `${yellow('Check out your shiny new generated files at ') + magenta(output) + yellow('.')}\\n\\n`;\n  }\n\n  private checkV3NotSupported(asyncapi: Specification, template: string) {\n    if (asyncapi.isAsyncAPI3()) {\n      const v3IssueLink = this.verifyTemplateSupportForV3(template);\n      if (v3IssueLink !== undefined) {\n        return `${template} template does not support AsyncAPI v3 documents, please checkout ${v3IssueLink}`;\n      }\n    }\n  }\n\n  /**\n   * Base URI for parsing, required so relative file $refs resolve next to the spec file (not cwd).\n   */\n  private getGeneratorParseSource(spec: Specification): string | undefined {\n    const filePath = spec.getFilePath();\n    if (filePath !== undefined) {\n      const absolute = path.isAbsolute(filePath)\n        ? filePath\n        : path.resolve(filePath);\n      return pathToFileURL(absolute).href;\n    }\n    return spec.getFileURL();\n  }\n\n  /**\n   * Generates code from an AsyncAPI specification using the specified template.\n   *\n   * @param asyncapi - The AsyncAPI specification to generate from\n   * @param template - The template to use for generation\n   * @param output - The output directory for generated files\n   * @param options - Generator options\n   * @param genOption - Additional generator run options\n   * @param interactive - Whether to show interactive spinner (default: false)\n   * @returns ServiceResult containing generation result or error\n   */\n  async generate(\n    asyncapi: Specification,\n    template: string,\n    output: string,\n    options: GenerationOptions,\n    genOption: GeneratorRunOptions = {},\n    interactive = this.defaultInteractive,\n  ): Promise<ServiceResult<GenerationResult>> {\n    const v3NotSupported = this.checkV3NotSupported(asyncapi, template);\n    if (v3NotSupported) {\n      return this.createErrorResult(v3NotSupported);\n    }\n    const logs: string[] = [];\n\n    const generator = new AsyncAPIGenerator(\n      template,\n      output || path.resolve(os.tmpdir(), 'asyncapi-generator'),\n      options,\n    );\n    const s = interactive\n      ? spinner()\n      : { start: () => null, stop: (message: string) => logs.push(message) };\n    s.start('Generation in progress. Keep calm and wait a bit');\n    try {\n      const parseSource =\n        this.getGeneratorParseSource(asyncapi) ??\n        (typeof genOption.path === 'string' ? genOption.path : undefined);\n      await generator.generateFromString(asyncapi.text(), {\n        ...genOption,\n        ...(parseSource !== undefined ? { path: parseSource } : {}),\n      });\n    } catch (err: unknown) {\n      s.stop('Generation failed');\n      const errorMessage = getErrorMessage(err, 'Generation failed');\n      const diagnostics = err && typeof err === 'object' && 'diagnostics' in err \n        ? (err as { diagnostics?: unknown[] }).diagnostics as Parameters<typeof this.createErrorResult>[1]\n        : undefined;\n      return this.createErrorResult(errorMessage, diagnostics);\n    }\n    s.stop(\n      this.getGenerationSuccessMessage(output),\n    );\n\n    return this.createSuccessResult({\n      success: true,\n      outputPath: output,\n      logs,\n    } as GenerationResult);\n  }\n}\n"
  },
  {
    "path": "src/domains/services/module.d.ts",
    "content": "declare module '@asyncapi/generator';\n"
  },
  {
    "path": "src/domains/services/validation.service.ts",
    "content": "import { BaseService } from './base.service';\nimport {\n  ValidationOptions,\n  ValidationResult,\n  ServiceResult,\n  ParsedDocument,\n  DiagnosticsFormat,\n  SeverityKind,\n} from '@/interfaces';\nimport { AvroSchemaParser } from '@asyncapi/avro-schema-parser';\nimport { OpenAPISchemaParser } from '@asyncapi/openapi-schema-parser';\nimport { DiagnosticSeverity, Parser } from '@asyncapi/parser/cjs';\nimport { RamlDTSchemaParser } from '@asyncapi/raml-dt-schema-parser';\nimport { ProtoBuffSchemaParser } from '@asyncapi/protobuf-schema-parser';\nimport { getDiagnosticSeverity } from '@stoplight/spectral-core';\nimport {\n  html,\n  json,\n  junit,\n  pretty,\n  stylish,\n  teamcity,\n  text,\n} from '@stoplight/spectral-formatters';\nimport chalk from 'chalk';\nimport { promises } from 'fs';\nimport path from 'path';\n\nimport type { Diagnostic } from '@asyncapi/parser/cjs';\nimport { Specification } from '@models/SpecificationFile';\nimport { ParseOptions } from '@asyncapi/parser';\nimport { ParserOptions } from '@asyncapi/parser/cjs/parser';\nimport { calculateScore } from '@/utils/scoreCalculator';\n\nimport { ConfigService } from './config.service';\n\n// GitHub API response type\ninterface GitHubFileInfo {\n  download_url: string;\n  content?: string;\n  encoding?: string;\n  name: string;\n  path: string;\n  sha: string;\n  size: number;\n  type: string;\n}\n\n/**\n * Helper function to validate if a URL is a GitHub blob URL\n */\nconst isValidGitHubBlobUrl = (url: string): boolean => {\n  try {\n    const parsedUrl = new URL(url);\n    return (\n      parsedUrl.hostname === 'github.com' &&\n      parsedUrl.pathname.split('/')[3] === 'blob'\n    );\n  } catch {\n    return false;\n  }\n};\n\n/**\n * Convert GitHub web URL to API URL\n */\nconst convertGitHubWebUrl = (url: string): string => {\n  // Remove fragment from URL before processing\n  const urlWithoutFragment = url.split('#')[0];\n\n  // Handle GitHub web URLs like: https://github.com/owner/repo/blob/branch/path\n  // eslint-disable-next-line no-useless-escape\n  const githubWebPattern = /^https:\\/\\/github\\.com\\/([^\\/]+)\\/([^\\/]+)\\/blob\\/([^\\/]+)\\/(.+)$/;\n  const match = urlWithoutFragment.match(githubWebPattern);\n\n  if (match) {\n    const [, owner, repo, branch, filePath] = match;\n    return `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}?ref=${branch}`;\n  }\n\n  return url;\n};\n\n/**\n * Helper function to fetch with error handling\n */\nconst fetchWithErrorHandling = async (\n  url: string,\n  headers: Record<string, string>,\n  errorMessage: string,\n): Promise<Response> => {\n  const res = await fetch(url, { headers });\n  if (!res.ok) {\n    throw new Error(`${errorMessage}: ${url} - ${res.statusText}`);\n  }\n  return res;\n};\n\n/**\n * Helper function to fetch content from GitHub API\n */\nconst fetchGitHubApiContent = async (\n  url: string,\n  headers: Record<string, string>,\n): Promise<string> => {\n  headers['Accept'] = 'application/vnd.github.v3+json';\n  const res = await fetchWithErrorHandling(\n    url,\n    headers,\n    'Failed to fetch GitHub API URL',\n  );\n  const fileInfo = (await res.json()) as GitHubFileInfo;\n\n  if (!fileInfo.download_url) {\n    throw new Error(\n      `No download URL found in GitHub API response for: ${url}`,\n    );\n  }\n\n  const contentRes = await fetchWithErrorHandling(\n    fileInfo.download_url,\n    headers,\n    'Failed to fetch content from download URL',\n  );\n  return await contentRes.text();\n};\n\n/**\n * Custom resolver for private repositories\n */\nconst createHttpWithAuthResolver = () => ({\n  schema: 'https',\n  order: 1,\n\n  read: async (uri: any) => {\n    let url = uri.toString();\n\n    // Default headers\n    const headers: Record<string, string> = {\n      'User-Agent': 'AsyncAPI-CLI',\n    };\n\n    const authInfo = await ConfigService.getAuthForUrl(url);\n\n    if (isValidGitHubBlobUrl(url)) {\n      url = convertGitHubWebUrl(url);\n    }\n\n    if (authInfo) {\n      headers['Authorization'] = `${authInfo.authType} ${authInfo.token}`;\n      Object.assign(headers, authInfo.headers); // merge custom headers\n    }\n\n    if (url.includes('api.github.com')) {\n      return await fetchGitHubApiContent(url, headers);\n    }\n    if (url.includes('raw.githubusercontent.com')) {\n      headers['Accept'] = 'application/vnd.github.v3.raw';\n      const res = await fetchWithErrorHandling(\n        url,\n        headers,\n        'Failed to fetch GitHub URL',\n      );\n      return await res.text();\n    }\n    const res = await fetchWithErrorHandling(\n      url,\n      headers,\n      'Failed to fetch URL',\n    );\n    return await res.text();\n  },\n});\n\nconst { writeFile } = promises;\n\nexport enum ValidationStatus {\n  INVALID = 'invalid',\n  VALID = 'valid',\n}\n\nconst formatExtensions: Record<DiagnosticsFormat, string> = {\n  stylish: '.txt',\n  json: '.json',\n  junit: '.xml',\n  html: '.html',\n  text: '.txt',\n  teamcity: '.txt',\n  pretty: '.txt',\n};\n\nconst validFormats = [\n  'stylish',\n  'json',\n  'junit',\n  'html',\n  'text',\n  'teamcity',\n  'pretty',\n];\n\nexport class ValidationService extends BaseService {\n  private parser: Parser;\n\n  constructor(parserOptions: ParserOptions = {}) {\n    super(); \n    // Create parser with custom GitHub resolver\n    const customParserOptions = {\n      ...parserOptions,\n      __unstable: {\n        ...parserOptions.__unstable,\n        resolver: {\n          ...parserOptions.__unstable?.resolver,\n          cache: false,\n          resolvers: [\n            createHttpWithAuthResolver(),\n            ...(parserOptions.__unstable?.resolver?.resolvers || [])\n          ],\n        },\n      },\n    };\n\n    this.parser = new Parser(customParserOptions);\n\n    this.parser.registerSchemaParser(OpenAPISchemaParser());\n    this.parser.registerSchemaParser(RamlDTSchemaParser());\n    this.parser.registerSchemaParser(AvroSchemaParser());\n    this.parser.registerSchemaParser(ProtoBuffSchemaParser());\n  }\n\n  /**\n   * Determine validation status from diagnostics\n   */\n  private determineDiagnosticsStatus(\n    diagnostics: Diagnostic[],\n    options: ValidationOptions,\n  ): ValidationStatus {\n    const failSeverity = options['fail-severity'] ?? 'error';\n    const hasIssues = diagnostics.length > 0;\n    const isFailSeverity =\n      hasIssues && this.hasFailSeverity(diagnostics, failSeverity);\n\n    return isFailSeverity ? ValidationStatus.INVALID : ValidationStatus.VALID;\n  }\n\n  /**\n   * Parses an AsyncAPI document and returns the parsed result\n   */\n  async parseDocument(\n    specFile: Specification,\n    parseOptions?: ParseOptions,\n    options: ValidationOptions = {},\n  ): Promise<ServiceResult<ParsedDocument>> {\n    try {\n      const { document, diagnostics } = await this.parser.parse(\n        specFile.text(),\n        {\n          source: specFile.getSource(),\n          ...parseOptions,\n        },\n      );\n\n      if (!document) {\n        return this.createErrorResult('Failed to parse document');\n      }\n\n      const status = this.determineDiagnosticsStatus(diagnostics, options);\n\n      const result: ParsedDocument = {\n        document,\n        diagnostics,\n        status: status as 'valid' | 'invalid',\n      };\n\n      return this.createSuccessResult(result);\n    } catch (error) {\n      return this.handleServiceError(error);\n    }\n  }\n\n  /**\n   * Validates an AsyncAPI document\n   */\n  async validateDocument(\n    specFile: Specification,\n    options: ValidationOptions = {},\n  ): Promise<ServiceResult<ValidationResult>> {\n    try {\n      const suppressAllWarnings = options.suppressAllWarnings ?? false;\n      const suppressedWarnings = options.suppressWarnings ?? [];\n      let activeParser: Parser;\n\n      if (suppressAllWarnings || suppressedWarnings.length) {\n        activeParser = await this.buildAndRegisterCustomParser(\n          specFile,\n          suppressedWarnings,\n          suppressAllWarnings,\n        );\n      } else {\n        activeParser = this.parser;\n      }\n\n      const { document, diagnostics } = await activeParser.parse(\n        specFile.text(),\n        {\n          source: specFile.getSource(),\n        },\n      );\n\n      const status = this.determineDiagnosticsStatus(diagnostics, options);\n\n      const result: ValidationResult = {\n        status: status as 'valid' | 'invalid',\n        diagnostics,\n        score: await calculateScore(document),\n        document: document?.json ? document.json() : undefined,\n      };\n\n      return this.createSuccessResult<ValidationResult>(result);\n    } catch (error) {\n      return this.handleServiceError(error);\n    }\n  }\n\n  /**\n   * Creates a custom parser with specific rules turned off.\n   */\n  private buildCustomParser(rulesToSuppress: string[]): Parser {\n    return new Parser({\n      ruleset: {\n        extends: [],\n        rules: Object.fromEntries(\n          rulesToSuppress.map((rule) => [rule, 'off']),\n        ),\n      },\n      __unstable: {\n        resolver: {\n          cache: false,\n          resolvers: [createHttpWithAuthResolver()],\n        },\n      },\n    });\n  }\n\n  /**\n   * Registers all schema parsers for the given parser instance.\n   */\n  private registerSchemaParsers(parser: Parser): void {\n    parser.registerSchemaParser(AvroSchemaParser());\n    parser.registerSchemaParser(OpenAPISchemaParser());\n    parser.registerSchemaParser(RamlDTSchemaParser());\n    parser.registerSchemaParser(ProtoBuffSchemaParser());\n  }\n\n  /**\n   * Builds a parser that suppresses all discovered warnings.\n   */\n  private async buildParserWithAllWarningsSuppressed(\n    specFile: Specification\n  ): Promise<Parser> {\n    const diagnostics = await this.parser.validate(specFile.text(), {\n      source: specFile.getSource(),\n    });\n    const allRuleNames = Array.from(\n      new Set(\n        diagnostics\n          .map((d) => d.code)\n          .filter((c): c is string => typeof c === 'string'),\n      ),\n    );\n    return this.buildCustomParser(allRuleNames);\n  }\n\n  /**\n   * Builds a parser that suppresses specific warnings, handling invalid rules gracefully.\n   */\n  private buildParserWithSpecificWarningsSuppressed(\n    suppressedWarnings: string[]\n  ): Parser {\n    try {\n      return this.buildCustomParser(suppressedWarnings);\n    } catch (e: unknown) {\n      const msg = e instanceof Error ? e.message : '';\n      const matches = [\n        ...msg.matchAll(/Cannot extend non-existing rule: \"([^\"]+)\"/g),\n      ];\n      const invalidRules = matches.map((m) => m[1]);\n      if (invalidRules.length > 0) {\n        const validRules = suppressedWarnings.filter(\n          (rule) => !invalidRules.includes(rule),\n        );\n        return this.buildCustomParser(validRules);\n      }\n      throw e;\n    }\n  }\n\n  /**\n   * Helper to build and register a custom parser with suppressed rules\n   */\n  private async buildAndRegisterCustomParser(\n    specFile: Specification,\n    suppressedWarnings: string[],\n    suppressAllWarnings: boolean,\n  ): Promise<Parser> {\n    if (!suppressAllWarnings && !suppressedWarnings.length) {\n      throw new Error('No rules to suppress provided');\n    }\n\n    const activeParser = suppressAllWarnings\n      ? await this.buildParserWithAllWarningsSuppressed(specFile)\n      : this.buildParserWithSpecificWarningsSuppressed(suppressedWarnings);\n\n    this.registerSchemaParsers(activeParser);\n    return activeParser;\n  }\n\n  /**\n   * Save validation diagnostics to file\n   */\n  async saveDiagnosticsToFile(\n    outputPath: string,\n    format: DiagnosticsFormat,\n    formatOutput: string,\n  ): Promise<ServiceResult<string>> {\n    try {\n      if (!validFormats.includes(format)) {\n        return this.createErrorResult(\n          `Invalid diagnostics format: \"${format}\"`,\n        );\n      }\n\n      const expectedExtension =\n        formatExtensions[format as keyof typeof formatExtensions];\n      const actualExtension = path.extname(outputPath);\n\n      // Validate file extension against diagnostics format\n      if (expectedExtension && actualExtension !== expectedExtension) {\n        return this.createErrorResult(\n          `Invalid file extension for format \"${format}\". Expected extension: \"${expectedExtension}\"`,\n        );\n      }\n\n      await writeFile(path.resolve(process.cwd(), outputPath), formatOutput, {\n        encoding: 'utf-8',\n      });\n\n      return this.createSuccessResult(outputPath);\n    } catch (error) {\n      return this.handleServiceError(error);\n    }\n  }\n\n  /**\n   * Generate governance message based on validation results\n   */\n  generateGovernanceMessage(\n    sourceString: string,\n    hasIssues: boolean,\n    isFailSeverity: boolean,\n  ): string {\n    if (!hasIssues) {\n      return `\\n${sourceString} is valid! ${sourceString} and referenced documents don't have governance issues.`;\n    }\n    if (isFailSeverity) {\n      return `\\n${sourceString} and/or referenced documents have governance issues.`;\n    }\n    return `\\n${sourceString} is valid but has (itself and/or referenced documents) governance issues.`;\n  }\n\n  /**\n   * Check if diagnostics contain failure severity issues\n   */\n  private hasFailSeverity(\n    diagnostics: Diagnostic[],\n    failSeverity: SeverityKind,\n  ): boolean {\n    const diagnosticSeverity = getDiagnosticSeverity(failSeverity);\n    return diagnostics.some(\n      (diagnostic) => diagnostic.severity <= diagnosticSeverity,\n    );\n  }\n\n  /**\n   * Format validation diagnostics output\n   */\n  formatDiagnosticsOutput(\n    diagnostics: Diagnostic[],\n    format: DiagnosticsFormat = 'stylish',\n    failSeverity: SeverityKind = 'error',\n  ): string {\n    const diagnosticSeverity = getDiagnosticSeverity(failSeverity);\n    const options = {\n      failSeverity:\n        diagnosticSeverity !== -1\n          ? diagnosticSeverity\n          : DiagnosticSeverity.Error,\n    };\n\n    switch (format) {\n    case 'stylish':\n      return this.formatStylish(diagnostics, options);\n    case 'json':\n      return json(diagnostics, options);\n    case 'junit':\n      return junit(diagnostics, options);\n    case 'html':\n      return html(diagnostics, options);\n    case 'text':\n      return text(diagnostics, options);\n    case 'teamcity':\n      return teamcity(diagnostics, options);\n    case 'pretty':\n      return pretty(diagnostics, options);\n    default:\n      return stylish(diagnostics, options);\n    }\n  }\n\n  /**\n   * Format diagnostics in stylish format with severity grouping\n   */\n  private formatStylish(\n    diagnostics: Diagnostic[],\n    options: { failSeverity: DiagnosticSeverity },\n  ): string {\n    const groupedDiagnostics = diagnostics.reduce(\n      (acc, diagnostic) => {\n        const severity = diagnostic.severity;\n        if (!acc[severity as DiagnosticSeverity]) {\n          acc[severity as DiagnosticSeverity] = [];\n        }\n        acc[severity as DiagnosticSeverity].push(diagnostic);\n        return acc;\n      },\n      {} as Record<DiagnosticSeverity, Diagnostic[]>,\n    );\n\n    return Object.entries(groupedDiagnostics)\n      .map(([severity, diagnostics]) => {\n        return `${this.getSeverityTitle(Number(severity))} ${stylish(diagnostics, options)}`;\n      })\n      .join('\\n');\n  }\n\n  /**\n   * Get colored severity title\n   */\n  private getSeverityTitle(severity: DiagnosticSeverity): string {\n    switch (severity) {\n    case DiagnosticSeverity.Error:\n      return chalk.red('Errors');\n    case DiagnosticSeverity.Warning:\n      return chalk.yellow('Warnings');\n    case DiagnosticSeverity.Information:\n      return chalk.cyan('Information');\n    case DiagnosticSeverity.Hint:\n      return chalk.green('Hints');\n    default:\n      return 'Unknown';\n    }\n  }\n}\n"
  },
  {
    "path": "src/errors/context-error.ts",
    "content": "export const NO_CONTEXTS_SAVED = `These are your options to specify in the CLI what AsyncAPI file should be used:\n\t- You can provide a path to the AsyncAPI file: asyncapi <command> path/to/file/asyncapi.yml\n\t- You can provide URL to the AsyncAPI file: asyncapi <command> https://example.com/path/to/file/asyncapi.yml\n\t- You can also pass a saved context that points to your AsyncAPI file: asyncapi <command> context-name\n\t- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi config context use mycontext\n\t- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.\n`;\nconst MISSING_CURRENT_CONTEXT =\n  'No context is set as current, please set a current context.';\nconst CONTEXT_NOT_FOUND = (contextName: string) =>\n  `Context \"${contextName}\" does not exist.`;\nconst CONTEXT_ALREADY_EXISTS = (contextName: string, contextFileName: string) =>\n  `Context with name \"${contextName}\" already exists in context file \"${contextFileName}\".`;\nconst CONTEXT_FILE_WRONG_FORMAT = (contextFileName: string) =>\n  `Context file \"${contextFileName}\" has wrong format. Make sure your context file follows the structure described in section \"Context File structure\" at https://www.asyncapi.com/docs/tools/cli/context#context-file-structure`;\nconst CONTEXT_FILE_EMPTY = (contextFileName: string) =>\n  `Context file \"${contextFileName}\" is empty.`;\nconst CONTEXT_FILE_WRITE_ERROR = (contextFileName: string) =>\n  `Error writing context file \"${contextFileName}\".`;\n\nclass ContextError extends Error {\n  constructor() {\n    super();\n    this.name = 'ContextError';\n  }\n}\n\nexport class MissingContextFileError extends ContextError {\n  constructor() {\n    super();\n    this.message = NO_CONTEXTS_SAVED;\n  }\n}\n\nexport class MissingCurrentContextError extends ContextError {\n  constructor() {\n    super();\n    this.message = MISSING_CURRENT_CONTEXT;\n  }\n}\n\nexport class ContextNotFoundError extends ContextError {\n  constructor(contextName: string) {\n    super();\n    this.message = CONTEXT_NOT_FOUND(contextName);\n  }\n}\n\nexport class ContextAlreadyExistsError extends ContextError {\n  constructor(contextName: string, contextFileName: string) {\n    super();\n    this.message = CONTEXT_ALREADY_EXISTS(contextName, contextFileName);\n  }\n}\n\nexport class ContextFileWrongFormatError extends ContextError {\n  constructor(contextFileName: string) {\n    super();\n    this.message = CONTEXT_FILE_WRONG_FORMAT(contextFileName);\n  }\n}\n\nexport class ContextFileEmptyError extends ContextError {\n  constructor(contextFileName: string) {\n    super();\n    this.message = CONTEXT_FILE_EMPTY(contextFileName);\n  }\n}\n\nexport class ContextFileWriteError extends ContextError {\n  constructor(contextFileName: string) {\n    super();\n    this.message = CONTEXT_FILE_WRITE_ERROR(contextFileName);\n  }\n}\n"
  },
  {
    "path": "src/errors/diff-error.ts",
    "content": "export class DiffOverrideFileError extends Error {\n  constructor() {\n    super();\n    this.name = 'DiffOverrideFileError';\n    this.message = 'Override file not found';\n  }\n}\n\nexport class DiffOverrideJSONError extends Error {\n  constructor() {\n    super();\n    this.name = 'DiffOverrideJSONError';\n    this.message = 'Provided override file is not a valid JSON file';\n  }\n}\n\nexport class DiffBreakingChangeError extends Error {\n  constructor() {\n    super();\n    this.name = 'DiffBreakingChangeError';\n    this.message = 'Breaking changes detected';\n  }\n}\n"
  },
  {
    "path": "src/errors/generator-error.ts",
    "content": "export class GeneratorError extends Error {\n  constructor(err: Error) {\n    super();\n    this.name = 'Generator Error';\n    this.message = err.message;\n  }\n}\n"
  },
  {
    "path": "src/errors/specification-file.ts",
    "content": "import { NO_CONTEXTS_SAVED } from './context-error';\nclass SpecificationFileError extends Error {\n  constructor() {\n    super();\n    this.name = 'SpecificationFileError';\n  }\n}\n\nexport class SpecificationFileNotFound extends SpecificationFileError {\n  constructor(filePath?: string) {\n    super();\n    if (filePath) {\n      this.message = `File ${filePath} does not exist.`;\n    } else {\n      this.message = 'We could not find any AsyncAPI file.';\n    }\n  }\n}\n\nexport class SpecificationWrongFileFormat extends SpecificationFileError {\n  constructor(filePath?: string) {\n    super();\n    this.message = `File ${filePath} is not of correct format.`;\n  }\n}\n\nexport class SpecificationURLNotFound extends SpecificationFileError {\n  constructor(URL: string) {\n    super();\n    this.message = `Unable to fetch specification file from url: ${URL}`;\n  }\n}\n\ntype From = 'file' | 'url' | 'context' | 'invalid file';\n\nexport class ErrorLoadingSpec extends Error {\n  private readonly errorMessages = {\n    default: NO_CONTEXTS_SAVED,\n  };\n  constructor(from?: From, param?: string) {\n    super();\n    if (from === 'file') {\n      this.name = 'error loading AsyncAPI document from file';\n      this.message = `${param} file does not exist.`;\n    }\n    if (from === 'url') {\n      this.name = 'error loading AsyncAPI document from url';\n      this.message = `Failed to download ${param}.`;\n    }\n    if (from === 'context') {\n      this.name = 'error loading AsyncAPI document from context';\n      this.message = `${param} context name does not exist.`;\n    }\n    if (from === 'invalid file') {\n      this.name = 'Invalid AsyncAPI file type';\n      this.message = 'cli only supports yml ,yaml ,json extension';\n    }\n\n    if (!from) {\n      this.name = 'error locating AsyncAPI document';\n      this.message = this.errorMessages.default;\n    }\n  }\n}\n"
  },
  {
    "path": "src/errors/validation-error.ts",
    "content": "type ErrorType =\n  | 'parser-error'\n  | 'invalid-file'\n  | 'no-spec-found'\n  | 'invalid-syntax-file';\n\ninterface IValidationErrorInput {\n  type: ErrorType;\n  err?: any;\n  filepath?: string;\n}\n\nexport class ValidationError extends Error {\n  constructor(error: IValidationErrorInput) {\n    super();\n    if (error.type === 'parser-error') {\n      this.buildError(error.err);\n    }\n    if (error.type === 'invalid-file') {\n      this.message = `There is no file or context with name \"${error.filepath}\".`;\n    }\n    if (error.type === 'invalid-syntax-file') {\n      this.message = `Syntax Error in \"${error.filepath}\".`;\n    }\n    if (error.type === 'no-spec-found') {\n      this.message =\n        'Unable to perform validation. Specify what AsyncAPI file should be validated.\\n\\nThese are your options to specify in the CLI what AsyncAPI file should be used:\\n- You can provide a path to the AsyncAPI file: asyncapi validate path/to/file/asyncapi.yml\\n- You can also pass a saved context that points to your AsyncAPI file: asyncapi validate mycontext\\n- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi context use mycontext\\n- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.';\n    }\n    this.name = 'ValidationError';\n  }\n\n  private buildError(err: any) {\n    const errorsInfo: Array<string> = [];\n\n    if (err.title) {\n      errorsInfo.push(err.title);\n    }\n\n    if (err.detail) {\n      errorsInfo.push(err.details);\n    }\n\n    if (err.validationErrors) {\n      for (const e of err.validationErrors) {\n        const errorHasTitle = !!e.title;\n        const errorHasLocation = !!e.location;\n        /*\n         * All the conditions below are needed since validationErrors (from ParserError) come from Parser JS library,\n         * so we cannot assure that all the fields or properties are always provided in the error. There might be cases\n         * that even title is not provided.\n         */\n        if (errorHasTitle && errorHasLocation) {\n          errorsInfo.push(\n            `${e.title} ${e.location.startLine}:${e.location.startColumn}`,\n          );\n          continue;\n        }\n        if (errorHasTitle) {\n          errorsInfo.push(`${e.title}`);\n          continue;\n        }\n        if (errorHasLocation) {\n          errorsInfo.push(`${e.location.startLine}:${e.location.startColumn}`);\n        }\n      }\n    }\n    this.message = errorsInfo.join('\\n');\n  }\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export { run } from '@oclif/core';\n\n// Export utilities for external consumption\nexport * from './utils/error-handler';\nexport * from './utils/validation';\nexport * from './utils/proxy';\n\n// Export interfaces\nexport * from './interfaces';\n\n/**\n * For NodeJS < 15, unhandled rejections are treated as warnings.\n * This is required for consistency in error handling.\n */\nprocess.on('unhandledRejection', (reason) => {\n  throw new Error(reason as string);\n});\n"
  },
  {
    "path": "src/interfaces/index.ts",
    "content": "import { AsyncAPIDocumentInterface, Diagnostic } from '@asyncapi/parser/cjs';\nimport { OutputFormat } from '@stoplight/spectral-cli/dist/services/config';\n\n/**\n * Supported diagnostic output formats for validation results.\n */\nexport type DiagnosticsFormat =\n  | 'stylish'\n  | 'json'\n  | 'junit'\n  | 'html'\n  | 'text'\n  | 'teamcity'\n  | 'pretty';\n\n/**\n * Severity levels for validation diagnostics.\n */\nexport type SeverityKind = 'error' | 'warn' | 'info' | 'hint';\n\n/**\n * Adapter type for CLI or API usage.\n */\nexport type Adapter = 'cli' | 'api';\n\nimport specs from '@asyncapi/specs';\nimport { Router } from 'express';\nimport { AsyncAPIConvertVersion } from '@asyncapi/converter';\n\n/**\n * Controller interface for Express routers.\n */\nexport interface Controller {\n  basepath: string;\n  boot(): Router | Promise<Router>;\n}\n\n/**\n * RFC 7807 Problem Details object.\n */\nexport interface Problem {\n  type: string;\n  title: string;\n  status: number;\n  detail?: string;\n  instance?: string;\n  [key: string]: string | number | undefined;\n}\n\nexport type AsyncAPIDocument = { asyncapi: string } & Record<string, unknown>;\n\nexport const ALL_SPECS = [...Object.keys(specs)];\nexport const LAST_SPEC_VERSION = ALL_SPECS[ALL_SPECS.length - 1];\n\nexport type SpecsEnum = keyof typeof specs | 'latest';\n\nexport interface AsyncAPIServiceOptions {\n  source?: string;\n  path?: string;\n}\n\n/**\n * Standard service result wrapper for consistent response handling.\n *\n * @template T - The type of data returned on success\n */\nexport interface ServiceResult<T = unknown> {\n  success: boolean;\n  data?: T;\n  error?: string;\n  diagnostics?: Diagnostic[];\n}\n\n/**\n * Result of parsing an AsyncAPI document.\n */\nexport interface ParsedDocument {\n  document: AsyncAPIDocumentInterface;\n  diagnostics: Diagnostic[];\n  status: 'valid' | 'invalid';\n}\n\n/**\n * Options for document validation.\n */\nexport interface ValidationOptions {\n  'log-diagnostics'?: boolean;\n  'diagnostics-format'?: `${OutputFormat}`;\n  'fail-severity'?: SeverityKind;\n  output?: string;\n  suppressWarnings?: string[];\n  suppressAllWarnings?: boolean;\n}\n\n/**\n * Result of document validation.\n */\nexport interface ValidationResult {\n  status: 'valid' | 'invalid';\n  document?: AsyncAPIDocument;\n  diagnostics?: Diagnostic[];\n  score?: number;\n}\n\nexport interface ConversionOptions {\n  format: 'asyncapi' | 'openapi';\n  'target-version'?: AsyncAPIConvertVersion;\n  perspective?: 'client' | 'server';\n}\n\nexport interface ConversionResult {\n  convertedDocument: string;\n  originalFormat: string;\n}\n\n/**\n * Base URL mapping configuration for template generation.\n */\nexport interface IMapBaseUrlToFlag {\n  url: string;\n  folder: string;\n}\n\n/**\n * Configuration for npm registry authentication.\n */\nexport interface RegistryConfig {\n  url?: string;\n  auth?: string;\n  token?: string;\n}\n\n/**\n * Options for code generation from AsyncAPI documents.\n */\nexport interface GenerationOptions {\n  templateParams?: Record<string, unknown>;\n  forceWrite?: boolean;\n  install?: boolean;\n  debug?: boolean;\n  noOverwriteGlobs?: string[];\n  disabledHooks?: Record<string, string>;\n  mapBaseUrl?: IMapBaseUrlToFlag;\n  registry?: RegistryConfig;\n}\n\n/**\n * Result of a code generation operation.\n */\nexport interface GenerationResult {\n  success: boolean;\n  outputPath: string;\n  logs?: string[];\n}\n"
  },
  {
    "path": "src/utils/ajv.ts",
    "content": "import Ajv from 'ajv';\nimport addFormats from 'ajv-formats';\n\nimport type AjvCore from 'ajv/dist/core';\n\nexport function createAjvInstance(): AjvCore {\n  const ajv = new Ajv({\n    allErrors: true,\n    meta: true,\n    strict: false,\n    allowUnionTypes: true,\n    logger: false,\n    unicodeRegExp: false,\n  });\n  addFormats(ajv);\n  return ajv;\n}\n"
  },
  {
    "path": "src/utils/app-openapi.ts",
    "content": "import { promises as fs } from 'fs';\nimport path from 'path';\n\nimport YAML from 'js-yaml';\nimport $RefParser from '@apidevtools/json-schema-ref-parser';\n\nlet parsedOpenAPI: object | undefined = undefined;\n\n/**\n * Retrieve application's OpenAPI document.\n */\nexport async function getAppOpenAPI(): Promise<any> {\n  if (parsedOpenAPI) {\n    return parsedOpenAPI;\n  }\n\n  const openaAPI = await fs.readFile(\n    path.join(__dirname, '../../openapi.yaml'),\n    'utf-8',\n  );\n  parsedOpenAPI = YAML.load(openaAPI) as object;\n  // due to the fact that `@asyncapi/specs: 3.0.0` have moved to a new way of bundling schemas, it makes no sense to resolve the references for AsyncAPI specs\n  (parsedOpenAPI as any).components.schemas.AsyncAPIDocument.oneOf = {\n    type: ['string', 'object'],\n  };\n  const refParser = new $RefParser();\n  await refParser.dereference(parsedOpenAPI);\n\n  return parsedOpenAPI;\n}\n"
  },
  {
    "path": "src/utils/error-handler.ts",
    "content": "/**\n * Error handling utilities for consistent error management across the CLI and API.\n * Provides type-safe error handling and standardized error messages.\n */\n\n/**\n * Extracts a safe error message from any caught error.\n * Handles various error types including Error objects, strings, and unknown types.\n *\n * @param error - The caught error (can be any type)\n * @param fallbackMessage - Default message if error cannot be parsed\n * @returns A string error message\n *\n * @example\n * try {\n *   await riskyOperation();\n * } catch (error) {\n *   const message = getErrorMessage(error, 'Operation failed');\n *   console.error(message);\n * }\n */\nexport function getErrorMessage(error: unknown, fallbackMessage = 'An unknown error occurred'): string {\n  if (error instanceof Error) {\n    return error.message;\n  }\n  if (typeof error === 'string') {\n    return error;\n  }\n  if (error && typeof error === 'object' && 'message' in error) {\n    return String((error as { message: unknown }).message);\n  }\n  return fallbackMessage;\n}\n\n/**\n * Extracts the error stack trace if available.\n *\n * @param error - The caught error\n * @returns Stack trace string or undefined\n */\nexport function getErrorStack(error: unknown): string | undefined {\n  if (error instanceof Error) {\n    return error.stack;\n  }\n  return undefined;\n}\n\n/**\n * Type guard to check if a value is an Error instance.\n *\n * @param value - Value to check\n * @returns True if value is an Error\n */\nexport function isError(value: unknown): value is Error {\n  return value instanceof Error;\n}\n\n/**\n * Type guard to check if an error has a specific code property.\n * Useful for Node.js system errors (ENOENT, EACCES, etc.)\n *\n * @param error - The error to check\n * @param code - The error code to match\n * @returns True if error has the specified code\n *\n * @example\n * try {\n *   await fs.readFile(path);\n * } catch (error) {\n *   if (hasErrorCode(error, 'ENOENT')) {\n *     console.log('File not found');\n *   }\n * }\n */\nexport function hasErrorCode(error: unknown, code: string): boolean {\n  return (\n    error !== null &&\n    typeof error === 'object' &&\n    'code' in error &&\n    (error as { code: unknown }).code === code\n  );\n}\n\n/**\n * Wraps an async function to catch and transform errors.\n * Useful for standardizing error handling in command handlers.\n *\n * @param fn - Async function to wrap\n * @param errorHandler - Function to handle caught errors\n * @returns Wrapped function\n *\n * @example\n * const safeRun = withErrorHandling(\n *   async () => await riskyOperation(),\n *   (error) => console.error('Failed:', getErrorMessage(error))\n * );\n */\nexport function withErrorHandling<T, Args extends unknown[]>(\n  fn: (...args: Args) => Promise<T>,\n  errorHandler: (error: unknown) => void\n): (...args: Args) => Promise<T | undefined> {\n  return async (...args: Args): Promise<T | undefined> => {\n    try {\n      return await fn(...args);\n    } catch (error) {\n      errorHandler(error);\n      return undefined;\n    }\n  };\n}\n\n/**\n * Creates a typed error result for service functions.\n * Alternative to throwing errors - returns a result object instead.\n */\nexport interface ErrorResult {\n  success: false;\n  error: string;\n  code?: string;\n  details?: Record<string, unknown>;\n}\n\nexport interface SuccessResult<T> {\n  success: true;\n  data: T;\n}\n\nexport type Result<T> = SuccessResult<T> | ErrorResult;\n\n/**\n * Creates a success result.\n */\nexport function success<T>(data: T): SuccessResult<T> {\n  return { success: true, data };\n}\n\n/**\n * Creates an error result.\n */\nexport function failure(error: string, code?: string, details?: Record<string, unknown>): ErrorResult {\n  return { success: false, error, code, details };\n}\n\n/**\n * Creates an error result from a caught error.\n */\nexport function failureFromError(\n  error: unknown,\n  fallbackMessage = 'An unknown error occurred'\n): ErrorResult {\n  return {\n    success: false,\n    error: getErrorMessage(error, fallbackMessage),\n    details: isError(error) ? { stack: error.stack } : undefined,\n  };\n}\n\n"
  },
  {
    "path": "src/utils/generate/flags.ts",
    "content": "import { ParsedFlags } from '../../domains/models/generate/Flags';\nimport {\n  paramParser,\n  disableHooksParser,\n  mapBaseURLParser\n} from './parseParams';\nimport {\n  registryURLParser,\n  registryValidation\n} from './registry';\n\nexport function parseGeneratorFlags(\n  disableHooks?: string[],\n  params?: string[],\n  mapBaseUrl?: string,\n  registryUrl?: string,\n  registryAuth?: string,\n  registryToken?: string\n): ParsedFlags {\n  return {\n    params: paramParser(params),\n    disableHooks: disableHooksParser(disableHooks),\n    mapBaseUrlToFolder: mapBaseURLParser(mapBaseUrl),\n    registryURLValidation: registryURLParser(registryUrl),\n    registryAuthentication: registryValidation(registryUrl, registryAuth, registryToken)\n  } as ParsedFlags;\n}\n"
  },
  {
    "path": "src/utils/generate/mapBaseUrl.ts",
    "content": "import * as fs from 'fs';\nimport { IMapBaseUrlToFlag } from '../../domains/models/generate/Flags';\n\nexport function getMapBaseUrlToFolderResolver(urlToFolder: IMapBaseUrlToFlag) {\n  return {\n    order: 1,\n    canRead() {\n      return true;\n    },\n    read(file: any) {\n      const baseUrl = urlToFolder.url;\n      const baseDir = urlToFolder.folder;\n\n      return new Promise((resolve, reject) => {\n        let localpath = file.url;\n        localpath = localpath.replace(baseUrl, baseDir);\n        try {\n          fs.readFile(localpath, (err, data) => {\n            if (err) {\n              reject(`Error opening file \"${localpath}\"`);\n            } else {\n              resolve(data);\n            }\n          });\n        } catch {\n          reject(`Error opening file \"${localpath}\"`);\n        }\n      });\n    }\n  };\n}\n"
  },
  {
    "path": "src/utils/generate/parseParams.ts",
    "content": "import path from 'path';\n\nexport function paramParser(inputs?: string[]) {\n  if (!inputs) { return {}; }\n  const params: Record<string, any> = {};\n  for (const input of inputs) {\n    if (!input.includes('=')) {\n      throw new Error(`Invalid param ${input}. It must be in the format of --param name1=value1 name2=value2 `);\n    }\n    const [paramName, paramValue] = input.split(/=(.+)/, 2);\n    params[String(paramName)] = paramValue;\n  }\n  return params;\n}\n\nexport function disableHooksParser(inputs?: string[]) {\n  if (!inputs) { return {}; }\n  const disableHooks: Record<string, any> = {};\n\n  for (const input of inputs) {\n    const [hookType, hookNames] = input.split(/=/);\n    if (!hookType) {\n      throw new Error('Invalid --disable-hook flag. It must be in the format of: --disable-hook <hookType> or --disable-hook <hookType>=<hookName1>,<hookName2>,...');\n    }\n    if (hookNames) {\n      disableHooks[String(hookType)] = hookNames.split(',');\n    } else {\n      disableHooks[String(hookType)] = true;\n    }\n  }\n  return disableHooks;\n}\n\nexport function mapBaseURLParser(input?: string) {\n  if (!input) { return; }\n  const mapBaseURLToFolder: any = {};\n  const re = /(.*):(.*)/g; // NOSONAR\n  let mapping: any[] | null = [];\n  if ((mapping = re.exec(input)) === null || mapping.length !== 3) {\n    throw new Error('Invalid --map-base-url flag. A mapping <url>:<folder> with delimiter : expected.');\n  }\n\n  mapBaseURLToFolder.url = mapping[1].replace(/\\/$/, '');\n  mapBaseURLToFolder.folder = path.resolve(mapping[2]);\n\n  const isURL = /^https?:/;\n  if (!isURL.test(mapBaseURLToFolder.url.toLowerCase())) {\n    throw new Error('Invalid --map-base-url flag. The mapping <url>:<folder> requires a valid http/https url and valid folder with delimiter `:`.');\n  }\n\n  return mapBaseURLToFolder;\n}\n"
  },
  {
    "path": "src/utils/generate/prompts.ts",
    "content": "import fs from 'fs';\nimport { text, isCancel } from '@clack/prompts';\n\nconst OPERATION_CANCELLED_ERROR = 'Operation cancelled';\n\nexport async function promptForAsyncAPIPath(): Promise<string> {\n  const asyncapi = await text({\n    message: 'Please provide the path to the AsyncAPI document',\n    placeholder: 'asyncapi.yaml',\n    defaultValue: 'asyncapi.yaml',\n    validate(value: string) {\n      if (!value) {\n        return 'The path to the AsyncAPI document is required';\n      } else if (!fs.existsSync(value)) {\n        return 'The file does not exist';\n      }\n    }\n  });\n\n  if (isCancel(asyncapi)) {\n    throw new Error(OPERATION_CANCELLED_ERROR);\n  }\n\n  return asyncapi;\n}\n\nexport async function promptForLanguage(defaultLanguage: string): Promise<string> {\n  const language = await text({\n    message: 'Please provide the language of the generated client',\n    placeholder: defaultLanguage,\n    defaultValue: defaultLanguage,\n  });\n\n  if (isCancel(language)) {\n    throw new Error(OPERATION_CANCELLED_ERROR);\n  }\n\n  return language;\n}\n\nexport async function promptForTemplate(): Promise<string> {\n  const template = await text({\n    message: 'Please provide the name of the generator template',\n    placeholder: '@asyncapi/html-template',\n    defaultValue: '@asyncapi/html-template',\n  });\n\n  if (isCancel(template)) {\n    throw new Error(OPERATION_CANCELLED_ERROR);\n  }\n\n  return template;\n}\n\nexport async function promptForOutputDir(): Promise<string> {\n  const output = await text({\n    message: 'Please provide the output directory',\n    placeholder: './docs',\n    validate(value: string) {\n      if (!value) {\n        return 'The output directory is required';\n      } else if (typeof value !== 'string') {\n        return 'The output directory must be a string';\n      }\n    }\n  });\n\n  if (isCancel(output)) {\n    throw new Error(OPERATION_CANCELLED_ERROR);\n  }\n\n  return output;\n}\n"
  },
  {
    "path": "src/utils/generate/registry.ts",
    "content": "/**\n * Default wait before registry reachability check fails (https://github.com/asyncapi/cli/issues/2027).\n * 15s balances fail-fast vs slow VPNs/proxies; override with ASYNCAPI_REGISTRY_CHECK_TIMEOUT_MS.\n */\nconst REGISTRY_REACHABILITY_TIMEOUT_MS = 15_000;\n\nfunction registryReachabilityTimeoutMs(): number {\n  const env = process.env.ASYNCAPI_REGISTRY_CHECK_TIMEOUT_MS;\n  if (env !== undefined && env !== '') {\n    const parsed = Number(env);\n    if (Number.isFinite(parsed) && parsed >= 0) {\n      return parsed;\n    }\n  }\n  return REGISTRY_REACHABILITY_TIMEOUT_MS;\n}\n\nexport function registryURLParser(input?: string) {\n  if (!input) { return; }\n  const isURL = /^https?:/;\n  if (!isURL.test(input.toLowerCase())) {\n    throw new Error('Invalid --registry-url flag. The param requires a valid http/https url.');\n  }\n}\n\nfunction isAbortError(err: unknown): boolean {\n  if (err instanceof Error && err.name === 'AbortError') {\n    return true;\n  }\n  return (\n    typeof DOMException !== 'undefined' &&\n    err instanceof DOMException &&\n    err.name === 'AbortError'\n  );\n}\n\nasync function fetchWithTimeout(\n  url: string,\n  init: Omit<RequestInit, 'signal'>,\n): Promise<Response> {\n  const controller = new AbortController();\n  const ms = registryReachabilityTimeoutMs();\n  const timeoutId = setTimeout(() => controller.abort(), ms);\n  try {\n    return await fetch(url, { ...init, signal: controller.signal });\n  } finally {\n    clearTimeout(timeoutId);\n  }\n}\n\nexport async function registryValidation(registryUrl?: string, registryAuth?: string, registryToken?: string) {\n  if (!registryUrl) { return; }\n  try {\n    let response = await fetchWithTimeout(registryUrl as string, {\n      method: 'HEAD',\n      redirect: 'follow',\n    });\n    if (response.status === 405) {\n      response = await fetchWithTimeout(registryUrl as string, {\n        method: 'GET',\n        redirect: 'follow',\n      });\n    }\n    if (response.status === 401 && !registryAuth && !registryToken) {\n      throw new Error('You Need to pass either registryAuth in username:password encoded in Base64 or need to pass registryToken');\n    }\n  } catch (err: unknown) {\n    if (err instanceof Error && err.message.startsWith('You Need to pass')) {\n      throw err;\n    }\n    if (isAbortError(err)) {\n      const seconds = registryReachabilityTimeoutMs() / 1000;\n      throw new Error(\n        `Registry URL timed out or is unreachable after ${seconds}s: ${registryUrl}`,\n      );\n    }\n    const detail = err instanceof Error ? err.message : String(err);\n    throw new Error(`Can't fetch registryURL: ${registryUrl} (${detail})`);\n  }\n}\n"
  },
  {
    "path": "src/utils/generate/watcher.ts",
    "content": "// eslint-disable security/detect-object-injection\nimport * as fs from 'fs';\nimport { promisify } from 'util';\nimport chokidar from 'chokidar';\nconst lstat = promisify(fs.lstat);\nimport path from 'path';\nimport { magenta, yellow, red, green } from 'picocolors';\nimport { load } from '@models/SpecificationFile';\nimport { GeneratorError } from '@errors/generator-error';\n\nexport async function isLocalTemplate(templatePath: string) {\n  const stats = await lstat(templatePath);\n  return stats.isSymbolicLink();\n}\n\nexport class Watcher {\n  private watchers: any;\n  private fsWait: any;\n  private filesChanged: any;\n  private ignorePaths: string[];\n  private paths: any;\n\n  constructor(paths: string | string[], ignorePaths: string[]) {\n    if (Array.isArray(paths)) {\n      this.paths = paths;\n    } else {\n      this.paths = [paths];\n    }\n    //Ensure all backwards slashes are replaced with forward slash based on the requirement from chokidar\n    for (const pathIndex in this.paths) {\n      const path = this.paths[String(pathIndex)];\n      this.paths[String(pathIndex)] = path.replace(/[\\\\]/g, '/');\n    }\n    this.fsWait = false;\n    this.watchers = {};\n    this.filesChanged = {};\n    this.ignorePaths = ignorePaths;\n  }\n\n  /**\n   * Initiates watch on a path.\n   * @param {*} path The path the watcher is listening on.\n   * @param {*} changeCallback Callback to call when changed occur.\n   * @param {*} errorCallback Calback to call when it is no longer possible to watch a file.\n   */\n  initiateWatchOnPath(path: string, changeCallback: any, errorCallback: any) {\n    const watcher = chokidar.watch(path, {ignoreInitial: true, ignored: this.ignorePaths});\n    watcher.on('all', (eventType, changedPath) => this.fileChanged(path, changedPath, eventType, changeCallback, errorCallback));\n    this.watchers[String(path)] = watcher;\n  }\n\n  /**\n   * This method initiate the watch for change in all files\n   * @param {*} callback called when the file(s) change\n   */\n  async watch(changeCallback: any, errorCallback: any) {\n    for (const index in this.paths) {\n      const path = this.paths[String(index)];\n      this.initiateWatchOnPath(path, changeCallback, errorCallback);\n    }\n  }\n\n  /**\n   * Should be called when a file has changed one way or another.\n   * @param {*} listenerPath The path the watcher is listening on.\n   * @param {*} changedPath The file/dir that was changed\n   * @param {*} eventType What kind of change\n   * @param {*} changeCallback Callback to call when changed occur.\n   * @param {*} errorCallback Calback to call when it is no longer possible to watch a file.\n   */\n  fileChanged(listenerPath: string, changedPath: string, eventType: string, changeCallback: any, errorCallback: any) {\n    try {\n      if (fs.existsSync(listenerPath)) {\n        const newEventType = this.convertEventType(eventType);\n        this.filesChanged[String(changedPath)] = { eventType: newEventType, path: changedPath};\n        // Since multiple changes can occur at the same time, lets wait a bit before processing.\n        if (this.fsWait) {return;}\n        this.fsWait = setTimeout(async () => {\n          await changeCallback(this.filesChanged);\n          this.filesChanged = {};\n          this.fsWait = false;\n        }, 500);\n      }\n    } catch {\n      // File was not, find all files that are missing..\n      const unknownPaths = this.getAllNonExistingPaths();\n      this.closeWatchers();\n      errorCallback(unknownPaths);\n    }\n  }\n\n  /**\n   * Convert the event type to a more usefull one.\n   * @param {*} currentEventType The current event type (from chokidar)\n   */\n  convertEventType(currentEventType: string) {\n    let newEventType = currentEventType;\n    //Change the naming of the event type\n    switch (newEventType) {\n    case 'unlink':\n    case 'unlinkDir':\n      newEventType = 'removed';\n      break;\n    case 'addDir':\n    case 'add':\n      newEventType = 'added';\n      break;\n    case 'change':\n      newEventType = 'changed';\n      break;\n    case 'rename':\n      newEventType = 'renamed';\n      break;\n    default:\n      newEventType = `unknown (${currentEventType})`;\n    }\n    return newEventType;\n  }\n\n  /**\n   * Get all paths which no longer exists\n   */\n  getAllNonExistingPaths() {\n    const unknownPaths = [];\n    for (const index in this.paths) {\n      const path = this.paths[String(index)];\n      if (!fs.existsSync(path)) {\n        unknownPaths.push(path);\n      }\n    }\n    return unknownPaths;\n  }\n\n  /**\n   * Closes all active watchers down.\n   */\n  closeWatchers() {\n    this.filesChanged = {};\n    for (const index in this.paths) {\n      const path = this.paths[String(index)];\n      this.closeWatcher(path);\n    }\n  }\n\n  /**\n   * Closes an active watcher down.\n   * @param {*} path The path to close the watcher for.\n   */\n  closeWatcher(path: string) {\n    // Ensure if called before `watch` to do nothing\n    if (path !== null) {\n      const watcher = this.watchers[String(path)];\n      if (watcher !== null) {\n        watcher.close();\n        this.watchers[String(path)] = null;\n      } else {\n        //Watcher not found for path\n      }\n    }\n  }\n}\n\nexport async function runWatchMode(\n  thisArg: any,\n  asyncapi: string,\n  template: string,\n  output: string,\n  generatorClass: any, // ✅ passed in\n  watchHandler: (changedFiles: Record<string, any>) => Promise<void>\n) {\n  const specification = await load(asyncapi);\n\n  const watchDir = path.resolve(template);\n  const outputPath = path.resolve(watchDir, output);\n  const transpiledTemplatePath = path.resolve(watchDir, generatorClass.TRANSPILED_TEMPLATE_LOCATION); // ✅ use dynamic class\n  const ignorePaths = [outputPath, transpiledTemplatePath];\n  const specificationFile = specification.getFilePath();\n\n  // Template name is needed as it is not always a part of the cli command\n  // There is a use case that you run generator from a root of the template with `./` path\n  let templateName = '';\n  try {\n    // eslint-disable-next-line\n    templateName = require(path.resolve(watchDir, 'package.json')).name;\n  } catch {\n    // intentional\n  }\n\n  let watcher;\n  if (specificationFile) {\n    thisArg.log(`[WATCHER] Watching for changes in the template directory ${magenta(watchDir)} and in the AsyncAPI file ${magenta(specificationFile)}`);\n    watcher = new Watcher([specificationFile, watchDir], ignorePaths);\n  } else {\n    thisArg.log(`[WATCHER] Watching for changes in the template directory ${magenta(watchDir)}`);\n    watcher = new Watcher(watchDir, ignorePaths);\n  }\n\n  if (!await thisArg.isLocalTemplate(path.resolve(generatorClass.DEFAULT_TEMPLATES_DIR, templateName))) {\n    thisArg.warn(`WARNING: ${template} is a remote template. Changes may be lost on subsequent installations.`);\n  }\n\n  await watcher.watch(watchHandler, (paths: any) => {\n    thisArg.error(`[WATCHER] Could not find the file path ${paths}, are you sure it still exists? If it has been deleted or moved please rerun the generator.`, {\n      exit: 1,\n    });\n  });\n}\n\nexport function watcherHandler(\n  thisArg: any,\n  asyncapi: string,\n  template: string,\n  output: string,\n  options: Record<string, any>,\n  genOption: any,\n  interactive: boolean\n): (changedFiles: Record<string, any>) => Promise<void> {\n  return async (changedFiles: Record<string, any>): Promise<void> => {\n    console.clear();\n    console.log('[WATCHER] Change detected');\n    for (const [, value] of Object.entries(changedFiles)) {\n      let eventText;\n      switch (value.eventType) {\n      case 'changed':\n        eventText = green(value.eventType);\n        break;\n      case 'removed':\n        eventText = red(value.eventType);\n        break;\n      case 'renamed':\n        eventText = yellow(value.eventType);\n        break;\n      default:\n        eventText = yellow(value.eventType);\n      }\n      thisArg.log(`\\t${magenta(value.path)} was ${eventText}`);\n    }\n    try {\n      await thisArg.generate(asyncapi, template, output, options, genOption, interactive);\n    } catch (err: any) {\n      throw new GeneratorError(err);\n    }\n  };\n}\n"
  },
  {
    "path": "src/utils/logger.ts",
    "content": "import config from 'config';\nimport fs from 'fs';\nimport path from 'path';\nimport winston from 'winston';\n\nconst logDir: string = path.join(__dirname, config.has('log.dir') ? config.get('log.dir') : 'logs');\nif (!fs.existsSync(logDir)) {\n  fs.mkdirSync(logDir);\n}\n\n/*\n * Log Level\n * error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6\n */\nconst logger = winston.createLogger({\n  format: winston.format.combine(\n    winston.format.timestamp({\n      format: 'YYYY-MM-DD HH:mm:ss',\n    }),\n    // Define log format\n    winston.format.printf(\n      ({ timestamp, level, message }) => `${timestamp} ${level}: ${message}`,\n    ),\n  ),\n});\n\nlogger.add(\n  new winston.transports.Console({\n    format: winston.format.combine(\n      winston.format.splat(),\n      winston.format.colorize(),\n    ),\n  }),\n);\n\nconst stream = {\n  write: (message: string) => {\n    logger.info(message.substring(0, message.lastIndexOf('\\n')));\n  },\n};\n\nexport { logger, stream };\n"
  },
  {
    "path": "src/utils/proxy.ts",
    "content": "/**\n * Utility functions for proxy configuration.\n * Centralizes proxy URL handling to avoid code duplication across commands.\n */\n\n/**\n * Applies proxy configuration to a file path or URL.\n * If both proxyHost and proxyPort are provided, appends the proxy URL to the path.\n * \n * @param filePath - The original file path or URL\n * @param proxyHost - The proxy host name (optional)\n * @param proxyPort - The proxy port number (optional)\n * @returns The file path with proxy configuration appended, or the original path\n * \n * @example\n * applyProxyToPath('./asyncapi.yaml', 'localhost', '8080')\n * // Returns: './asyncapi.yaml+http://localhost:8080'\n * \n * applyProxyToPath('./asyncapi.yaml')\n * // Returns: './asyncapi.yaml'\n */\nexport function applyProxyToPath(\n  filePath: string | undefined,\n  proxyHost?: string,\n  proxyPort?: string | number\n): string | undefined {\n  if (!filePath) {\n    return filePath;\n  }\n  \n  if (proxyHost && proxyPort) {\n    const proxyUrl = `http://${proxyHost}:${proxyPort}`;\n    return `${filePath}+${proxyUrl}`;\n  }\n  \n  return filePath;\n}\n\n/**\n * Builds a proxy URL from host and port.\n * \n * @param proxyHost - The proxy host name\n * @param proxyPort - The proxy port number\n * @returns The proxy URL or undefined if either parameter is missing\n */\nexport function buildProxyUrl(\n  proxyHost?: string,\n  proxyPort?: string | number\n): string | undefined {\n  if (proxyHost && proxyPort) {\n    return `http://${proxyHost}:${proxyPort}`;\n  }\n  return undefined;\n}\n\n"
  },
  {
    "path": "src/utils/retrieve-language.ts",
    "content": "export function retrieveLangauge(content: string): 'json' | 'yaml' {\n  if (content.trim()[0] === '{') {\n    return 'json';\n  }\n  return 'yaml';\n}\n"
  },
  {
    "path": "src/utils/scoreCalculator.ts",
    "content": "import { AsyncAPIDocumentInterface } from '@asyncapi/parser/cjs/models';\n\nexport async function calculateScore(\n  document: AsyncAPIDocumentInterface | undefined,\n) {\n  let scoreEvaluate = 0;\n  if (document?.info().hasDescription()) {\n    scoreEvaluate += 0.15;\n  }\n  if (document?.info().hasLicense()) {\n    scoreEvaluate += 0.25;\n  }\n  if (!document?.servers().isEmpty()) {\n    scoreEvaluate += 0.25;\n  }\n  if (!document?.channels().isEmpty()) {\n    scoreEvaluate += 0.35;\n  }\n  return (scoreEvaluate / 1) * 100;\n}\n"
  },
  {
    "path": "src/utils/temp-dir.ts",
    "content": "import os from 'os';\nimport fs, { promises as fsp } from 'fs';\nimport path from 'path';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { logger } from './logger';\n\nexport function createTempDirectory() {\n  return fsp.mkdtemp(path.join(os.tmpdir(), uuidv4()));\n}\n\nexport async function removeTempDirectory(tmpDir: string) {\n  try {\n    if (tmpDir && fs.existsSync(tmpDir)) {\n      await fsp.rm(tmpDir, { recursive: true });\n    }\n  } catch (e) {\n    logger.error(\n      `An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`,\n    );\n  }\n}\n"
  },
  {
    "path": "src/utils/validation.ts",
    "content": "/**\n * Input validation utilities for CLI commands and API endpoints.\n * Provides reusable validation functions with consistent error messages.\n */\n\n/**\n * Validates that a file path is provided and not empty.\n *\n * @param filePath - The file path to validate\n * @param fieldName - Name of the field for error messages\n * @returns Validation result with error message if invalid\n */\nexport function validateFilePath(\n  filePath: string | undefined,\n  fieldName = 'file path'\n): { valid: true; value: string } | { valid: false; error: string } {\n  if (!filePath || filePath.trim() === '') {\n    return {\n      valid: false,\n      error: `${fieldName} is required and cannot be empty`,\n    };\n  }\n  return { valid: true, value: filePath.trim() };\n}\n\n/**\n * Validates that a value is one of the allowed options.\n *\n * @param value - The value to validate\n * @param allowedValues - Array of allowed values\n * @param fieldName - Name of the field for error messages\n * @returns Validation result\n */\nexport function validateEnum<T extends string>(\n  value: string | undefined,\n  allowedValues: readonly T[],\n  fieldName = 'value'\n): { valid: true; value: T } | { valid: false; error: string } {\n  if (!value) {\n    return {\n      valid: false,\n      error: `${fieldName} is required`,\n    };\n  }\n  if (!allowedValues.includes(value as T)) {\n    return {\n      valid: false,\n      error: `${fieldName} must be one of: ${allowedValues.join(', ')}`,\n    };\n  }\n  return { valid: true, value: value as T };\n}\n\n/**\n * Validates that a port number is valid.\n *\n * @param port - The port to validate\n * @param fieldName - Name of the field for error messages\n * @returns Validation result\n */\nexport function validatePort(\n  port: string | number | undefined,\n  fieldName = 'port'\n): { valid: true; value: number } | { valid: false; error: string } {\n  if (port === undefined || port === '') {\n    return {\n      valid: false,\n      error: `${fieldName} is required`,\n    };\n  }\n\n  const portNum = typeof port === 'string' ? parseInt(port, 10) : port;\n\n  if (isNaN(portNum) || portNum < 1 || portNum > 65535) {\n    return {\n      valid: false,\n      error: `${fieldName} must be a valid port number (1-65535)`,\n    };\n  }\n\n  return { valid: true, value: portNum };\n}\n\n/**\n * Validates a URL string.\n *\n * @param url - The URL to validate\n * @param fieldName - Name of the field for error messages\n * @returns Validation result\n */\nexport function validateUrl(\n  url: string | undefined,\n  fieldName = 'URL'\n): { valid: true; value: string } | { valid: false; error: string } {\n  if (!url || url.trim() === '') {\n    return {\n      valid: false,\n      error: `${fieldName} is required`,\n    };\n  }\n\n  try {\n    new URL(url);\n    return { valid: true, value: url };\n  } catch {\n    return {\n      valid: false,\n      error: `${fieldName} is not a valid URL`,\n    };\n  }\n}\n\n/**\n * Validates that a value is a non-empty string.\n *\n * @param value - The value to validate\n * @param fieldName - Name of the field for error messages\n * @returns Validation result\n */\nexport function validateNonEmptyString(\n  value: string | undefined,\n  fieldName = 'value'\n): { valid: true; value: string } | { valid: false; error: string } {\n  if (!value || value.trim() === '') {\n    return {\n      valid: false,\n      error: `${fieldName} is required and cannot be empty`,\n    };\n  }\n  return { valid: true, value: value.trim() };\n}\n\n/**\n * Validates that a version string matches semantic versioning pattern.\n *\n * @param version - The version string to validate\n * @param fieldName - Name of the field for error messages\n * @returns Validation result\n */\nexport function validateVersion(\n  version: string | undefined,\n  fieldName = 'version'\n): { valid: true; value: string } | { valid: false; error: string } {\n  if (!version) {\n    return {\n      valid: false,\n      error: `${fieldName} is required`,\n    };\n  }\n\n  // Matches patterns like 1.0.0, 2.0.0, 3.0.0, etc.\n  const semverPattern = /^\\d+\\.\\d+\\.\\d+$/;\n  if (!semverPattern.test(version)) {\n    return {\n      valid: false,\n      error: `${fieldName} must be a valid semantic version (e.g., 3.0.0)`,\n    };\n  }\n\n  return { valid: true, value: version };\n}\n\n/**\n * Type guard to check if a value is defined (not null or undefined).\n *\n * @param value - Value to check\n * @returns True if value is defined\n */\nexport function isDefined<T>(value: T | null | undefined): value is T {\n  return value !== null && value !== undefined;\n}\n\n/**\n * Type guard to check if a value is a non-empty array.\n *\n * @param value - Value to check\n * @returns True if value is a non-empty array\n */\nexport function isNonEmptyArray<T>(value: T[] | null | undefined): value is [T, ...T[]] {\n  return Array.isArray(value) && value.length > 0;\n}\n\n"
  },
  {
    "path": "test/fixtures/asyncapiTestingScore.yml",
    "content": "asyncapi: 3.1.0\ninfo:\n  title: Streetlights Kafka API\n  version: 1.0.0\n  description: |-\n    The Smartylighting Streetlights API allows you to remotely manage the city\n    lights.\n    ### Check out its awesome features:\n\n    * Turn a specific streetlight on/off 🌃  \n    * Dim a specific streetlight 😎\n    * Receive real-time information about environmental lighting conditions 📈\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0\ndefaultContentType: application/json\nservers:\n  scram-connections:\n    host: test.mykafkacluster.org:18092\n    protocol: kafka-secure\n    description: Test broker secured with scramSha256\n    security:\n      - $ref: '#/components/securitySchemes/saslScram'\n    tags:\n      - name: env:test-scram\n        description: >-\n          This environment is meant for running internal tests through\n          scramSha256\n      - name: kind:remote\n        description: This server is a remote server. Not exposed by the application\n      - name: visibility:private\n        description: This resource is private and only available to certain users\n  mtls-connections:\n    host: test.mykafkacluster.org:28092\n    protocol: kafka-secure\n    description: Test broker secured with X509\n    security:\n      - $ref: '#/components/securitySchemes/certs'\n    tags:\n      - name: env:test-mtls\n        description: This environment is meant for running internal tests through mtls\n      - name: kind:remote\n        description: This server is a remote server. Not exposed by the application\n      - name: visibility:private\n        description: This resource is private and only available to certain users\nchannels:\n  lightingMeasured:\n    address: smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured\n    messages:\n      lightMeasured:\n        $ref: '#/components/messages/lightMeasured'\n    description: The topic on which measured values may be produced and consumed.\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n  lightTurnOn:\n    address: smartylighting.streetlights.1.0.action.{streetlightId}.turn.on\n    messages:\n      turnOn:\n        $ref: '#/components/messages/turnOnOff'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n  lightTurnOff:\n    address: smartylighting.streetlights.1.0.action.{streetlightId}.turn.off\n    messages:\n      turnOff:\n        $ref: '#/components/messages/turnOnOff'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n  lightsDim:\n    address: smartylighting.streetlights.1.0.action.{streetlightId}.dim\n    messages:\n      dimLight:\n        $ref: '#/components/messages/dimLight'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\noperations:\n  receiveLightMeasurement:\n    action: receive\n    channel:\n      $ref: '#/channels/lightingMeasured'\n    summary: >-\n      Inform about environmental lighting conditions of a particular\n      streetlight.\n    traits:\n      - $ref: '#/components/operationTraits/kafka'\n    messages:\n      - $ref: '#/channels/lightingMeasured/messages/lightMeasured'\n  turnOn:\n    action: send\n    channel:\n      $ref: '#/channels/lightTurnOn'\n    traits:\n      - $ref: '#/components/operationTraits/kafka'\n    messages:\n      - $ref: '#/channels/lightTurnOn/messages/turnOn'\n  turnOff:\n    action: send\n    channel:\n      $ref: '#/channels/lightTurnOff'\n    traits:\n      - $ref: '#/components/operationTraits/kafka'\n    messages:\n      - $ref: '#/channels/lightTurnOff/messages/turnOff'\n  dimLight:\n    action: send\n    channel:\n      $ref: '#/channels/lightsDim'\n    traits:\n      - $ref: '#/components/operationTraits/kafka'\n    messages:\n      - $ref: '#/channels/lightsDim/messages/dimLight'\ncomponents:\n  messages:\n    lightMeasured:\n      name: lightMeasured\n      title: Light measured\n      summary: >-\n        Inform about environmental lighting conditions of a particular\n        streetlight.\n      contentType: application/json\n      traits:\n        - $ref: '#/components/messageTraits/commonHeaders'\n      payload:\n        $ref: '#/components/schemas/lightMeasuredPayload'\n    turnOnOff:\n      name: turnOnOff\n      title: Turn on/off\n      summary: Command a particular streetlight to turn the lights on or off.\n      traits:\n        - $ref: '#/components/messageTraits/commonHeaders'\n      payload:\n        $ref: '#/components/schemas/turnOnOffPayload'\n    dimLight:\n      name: dimLight\n      title: Dim light\n      summary: Command a particular streetlight to dim the lights.\n      traits:\n        - $ref: '#/components/messageTraits/commonHeaders'\n      payload:\n        $ref: '#/components/schemas/dimLightPayload'\n  schemas:\n    lightMeasuredPayload:\n      type: object\n      properties:\n        lumens:\n          type: integer\n          minimum: 0\n          description: Light intensity measured in lumens.\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    turnOnOffPayload:\n      type: object\n      properties:\n        command:\n          type: string\n          enum:\n            - 'on'\n            - 'off'\n          description: Whether to turn on or off the light.\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    dimLightPayload:\n      type: object\n      properties:\n        percentage:\n          type: integer\n          description: Percentage to which the light should be dimmed to.\n          minimum: 0\n          maximum: 100\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    sentAt:\n      type: string\n      format: date-time\n      description: Date and time when the message was sent.\n  securitySchemes:\n    saslScram:\n      type: scramSha256\n      description: Provide your username and password for SASL/SCRAM authentication\n    certs:\n      type: X509\n      description: Download the certificate files from service provider\n  parameters:\n    streetlightId:\n      description: The ID of the streetlight.\n  messageTraits:\n    commonHeaders:\n      headers:\n        type: object\n        properties:\n          my-app-header:\n            type: integer\n            minimum: 0\n            maximum: 100\n  operationTraits:\n    kafka:\n      bindings:\n        kafka:\n          clientId:\n            type: string\n            enum:\n              - my-app-id\n"
  },
  {
    "path": "test/fixtures/asyncapiValid_v1.yml",
    "content": "asyncapi: \"2.1.0\"\ninfo:\n  title: Streetlights API\n  version: \"1.0.0\"\n  description: |\n    The Smartylighting Streetlights API allows you\n    to remotely manage the city lights.\n  license:\n    name: Apache 2.0\n    url: \"https://www.apache.org/licenses/LICENSE-2.0\"\nservers:\n  mosquitto:\n    url: mqtt://test.mosquitto.org\n    protocol: mqtt\nchannels:\n  light/measured:\n    publish:\n      summary: Inform about environmental lighting conditions for a particular streetlight.\n      operationId: onLightMeasured\n      message:\n        name: LightMeasured\n        payload:\n          type: object\n          properties:\n            id:\n              type: integer\n              minimum: 0\n              description: Id of the streetlight.\n            lumens:\n              type: integer\n              minimum: 0\n              description: Light intensity measured in lumens.\n            sentAt:\n              type: string\n              format: date-time\n              description: Date and time when the message was sent.\n"
  },
  {
    "path": "test/fixtures/asyncapi_v1.yml",
    "content": "asyncapi: \"2.1.0\"\ninfo:\n  title: Streetlights API\n  version: \"1.0.0\"\n  description: |\n    The Smartylighting Streetlights API allows you\n    to remotely manage the city lights.\n  license:\n    name: Apache 2.0\n    url: \"https://www.apache.org/licenses/LICENSE-2.0\"\nservers:\n  mosquitto:\n    url: mqtt://test.mosquitto.org\n    protocol: mqtt\nchannels:\n  light/measured:\n    publish:\n      summary: Inform about environmental lighting conditions for a particular streetlight.\n      operationId: onLightMeasured\n      message:\n        name: LightMeasured\n        payload:\n          type: object\n          properties:\n            id:\n              type: integer\n              minimum: 0\n              description: Id of the streetlight.\n            lumens:\n              type: integer\n              minimum: 0\n              description: Light intensity measured in lumens.\n            sentAt:\n              type: string\n              format: date-time\n              description: Date and time when the message was sent.\n"
  },
  {
    "path": "test/fixtures/asyncapi_v2.yml",
    "content": "asyncapi: \"2.1.0\"\ninfo:\n  title: Streetlights API V2\n  version: \"1.0.0\"\n  description: |\n    The Smartylighting Streetlights API allows you\n    to remotely manage the city lights.\n  license:\n    name: Apache 2.0\n    url: \"https://www.apache.org/licenses/LICENSE-2.0\"\nservers:\n  mosquitto:\n    url: http://test.mosquitto.org\n    protocol: http\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: \"#/components/messages/UserSignedUp\"\n  light/measured:\n    publish:\n      summary: Inform about environmental lighting conditions for a particular streetlight.\n      operationId: onLightMeasured\n      message:\n        name: LightMeasured\n        payload:\n          type: object\n          properties:\n            id:\n              type: integer\n              minimum: 1\n              description: Id of the streetlight.\n            lumens:\n              type: integer\n              minimum: 0\n              description: Light intensity measured in lumens.\n            sentAt:\n              type: string\n              format: date-time\n              description: Date and time when the message was sent.\n\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n"
  },
  {
    "path": "test/fixtures/badFormatAsyncapi.json",
    "content": "{\n  \"asyncapi\": \"2.2.0\",\n  \"info\": {\n    \"title\": \"Account Service\",\n    \"version\": \"1.0.0\",\n    \"description\": \"This service is in charge of processing user signups\"\n  },\n  \"channels\": {\n    \"user/signedup\": {\n      \"subscribe\": {\n        \"message\": {\n          \"$ref\": \"#/components/messages/UserSignedUp\"\n        }\n      }\n    }\n  },\n  \"components\": {\n    \"messages\": {\n      \"UserSignedUp\": {\n        \"payload\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"displayName\": {\n              \"type\": \"string\",\n              \"description\": \"Name of the user\"\n            },\n            \"email\": {\n              \"type\": \"string\",\n              \"format\": \"email\",\n              \"description\": \"Email of the user\"\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/dummyspec/apiwithref.json",
    "content": "{\n    \"asyncapi\": \"2.0.0\",\n    \"info\": {\n      \"title\": \"This is Async API with a schema reference\",\n      \"version\": \"0.0.1\"\n    },\n    \"channels\": {\n      \"VirtualTopic.crmservice\": {\n        \"publish\": {\n          \"message\": {\n            \"oneOf\": [\n              {\n                \"$ref\": \"#/components/messages/crm:Customer.Created\"\n              },\n              {\n                \"$ref\": \"#/components/messages/crm:Customer.Updated\"\n              }\n            ]\n          }\n        }\n      }\n    },\n    \"components\": {\n      \"messages\": {\n        \"crm:Customer.Created\": {\n          \"summary\": \"A customer was created\",\n          \"schemaFormat\": \"application/schema+json;version=draft-07\",\n          \"payload\": {\n            \"$ref\": \"https://schema.example.com/crm/shared.json\"\n          }\n        },\n        \"crm:Customer.Updated\": {\n          \"summary\": \"A customer was created\",\n          \"schemaFormat\": \"application/schema+json;version=draft-07\",\n          \"payload\": {\n            \"$ref\": \"https://schema.example.com/crm/shared.json\"\n          }\n        }\n      }\n    }\n  }"
  },
  {
    "path": "test/fixtures/dummyspec/dummySpec.yml",
    "content": "asyncapi: 2.4.0\ninfo:\n  title: Kafka test\n  version: '1'\nservers:\n  mykafka:\n    url: kafka://pkc-6ojv2.us-west4.gcp.confluent.cloud:9092\n    protocol: kafka-secure\n    security:\n      - saslScramExample: []\nchannels:\n  test:\n    publish:\n      operationId: onTest\n      message:\n        $ref: '#/components/messages/testMessage'\n  produce:\n    subscribe:\n      message:\n        $ref: '#/components/messages/testMessage'\ncomponents:\n  messages:\n    testMessage:\n      payload:\n        type: object\n        properties:\n          test:\n            type: string \n  securitySchemes:\n      saslScramExample:\n        type: scramSha256"
  },
  {
    "path": "test/fixtures/dummyspec/dummySpecWithoutSecurity.yml",
    "content": "asyncapi: 2.4.0\ninfo:\n  title: Kafka test\n  version: '1'\nservers:\n  mykafka:\n    url: kafka://pkc-6ojv2.us-west4.gcp.confluent.cloud:9092\n    protocol: kafka-secure\nchannels:\n  test:\n    publish:\n      operationId: onTest\n      message:\n        $ref: '#/components/messages/testMessage'\n  produce:\n    subscribe:\n      message:\n        $ref: '#/components/messages/testMessage'\ncomponents:\n  messages:\n    testMessage:\n      payload:\n        type: object\n        properties:\n          test:\n            type: string \n"
  },
  {
    "path": "test/fixtures/dummyspec/shared.json",
    "content": "{\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n    \"$id\": \"https://schema.example.com/crm/shared.json\",\n    \"description\": \"Shared Customer Relationship Management models\",\n    \"type\": \"object\",\n    \"properties\": {\n        \"type\": {\n            \"enum\": [\n                \"CustomerProfile\"\n            ]\n        },\n        \"id\": {\n            \"type\": \"string\",\n            \"format\": \"uuid\",\n            \"example\": \"c0e5e95a-e26a-4d16-a185-32cb8f9725fc\"\n        },\n        \"attributes\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"name\": {\n                    \"type\": \"string\",\n                    \"example\": \"Mustermann\"\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "test/fixtures/dummyspec/unoptimizedSpec.json",
    "content": "{\n  \"asyncapi\": \"2.0.0\",\n  \"info\": {\n    \"title\": \"Streetlights API\",\n    \"version\": \"1.0.0\"\n  },\n  \"channels\": {\n    \"smartylighting/event/{streetlightId}/lighting/measured\": {\n      \"parameters\": {\n        \"streetlightId\": {\n          \"schema\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"subscribe\": {\n        \"operationId\": \"receiveLightMeasurement\",\n        \"traits\": [\n          {\n            \"bindings\": {\n              \"kafka\": {\n                \"clientId\": \"my-app-id\"\n              }\n            }\n          }\n        ],\n        \"message\": {\n          \"name\": \"lightMeasured\",\n          \"title\": \"Light measured\",\n          \"contentType\": \"application/json\",\n          \"traits\": [\n            {\n              \"headers\": {\n                \"type\": \"object\",\n                \"properties\": {\n                  \"my-app-header\": {\n                    \"type\": \"integer\",\n                    \"minimum\": 0,\n                    \"maximum\": 100\n                  }\n                }\n              }\n            }\n          ],\n          \"payload\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"lumens\": {\n                \"type\": \"integer\",\n                \"minimum\": 0\n              },\n              \"sentAt\": {\n                \"type\": \"string\",\n                \"format\": \"date-time\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"smartylighting/action/{streetlightId}/turn/on\": {\n      \"parameters\": {\n        \"streetlightId\": {\n          \"schema\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"publish\": {\n        \"operationId\": \"turnOn\",\n        \"traits\": [\n          {\n            \"bindings\": {\n              \"kafka\": {\n                \"clientId\": \"my-app-id\"\n              }\n            }\n          }\n        ],\n        \"message\": {\n          \"name\": \"turnOnOff\",\n          \"title\": \"Turn on/off\",\n          \"traits\": [\n            {\n              \"headers\": {\n                \"type\": \"object\",\n                \"properties\": {\n                  \"my-app-header\": {\n                    \"type\": \"integer\",\n                    \"minimum\": 0,\n                    \"maximum\": 100\n                  }\n                }\n              }\n            }\n          ],\n          \"payload\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"sentAt\": {\n                \"$ref\": \"#/components/schemas/sentAt\"\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  \"components\": {\n    \"messages\": {\n      \"unusedMessage\": {\n        \"name\": \"unusedMessage\",\n        \"title\": \"This message is not used in any channel.\"\n      }\n    },\n    \"schemas\": {\n      \"sentAt\": {\n        \"type\": \"string\",\n        \"format\": \"date-time\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/dummyspec/unoptimizedSpec.yml",
    "content": "asyncapi: 2.0.0\ninfo:\n  title: Streetlights API\n  version: '1.0.0'\nchannels:\n  smartylighting/event/{streetlightId}/lighting/measured:\n    parameters:\n      #this parameter is duplicated. it can be moved to components and ref-ed from here.\n      streetlightId:\n        schema:\n          type: string\n    subscribe:\n      operationId: receiveLightMeasurement\n      traits:\n        - bindings:\n            kafka:\n              clientId: my-app-id\n      message:\n        name: lightMeasured\n        title: Light measured\n        contentType: application/json\n        traits:\n          - headers:\n              type: object\n              properties:\n                my-app-header:\n                  type: integer\n                  minimum: 0\n                  maximum: 100\n        payload:\n          type: object\n          properties:\n            lumens:\n              type: integer\n              minimum: 0\n            #full form is used, we can ref it to: #/components/schemas/sentAt\n            sentAt:\n              type: string\n              format: date-time\n  smartylighting/action/{streetlightId}/turn/on:\n    parameters:\n      streetlightId:\n        schema:\n          type: string\n    publish:\n      operationId: turnOn\n      traits:\n        - bindings:\n            kafka:\n              clientId: my-app-id\n      message:\n        name: turnOnOff\n        title: Turn on/off\n        traits:\n          - headers:\n              type: object\n              properties:\n                my-app-header:\n                  type: integer\n                  minimum: 0\n                  maximum: 100\n        payload:\n          type: object\n          properties:\n            sentAt:\n              $ref: \"#/components/schemas/sentAt\"\ncomponents:\n  messages:\n    #libarary should be able to find and delete this message, because it is not used anywhere.\n    unusedMessage:\n      name: unusedMessage\n      title: This message is not used in any channel.\n      \n  schemas:\n    #this schema is ref-ed in one channel and used full form in another. library should be able to identify and ref the second channel as well.\n    sentAt:\n      type: string\n      format: date-time"
  },
  {
    "path": "test/fixtures/external-refs/main.yaml",
    "content": "asyncapi: \"2.6.0\"\ninfo:\n  title: Service with External Refs\n  version: 1.0.0\n  description: AsyncAPI document with external file references in same directory\nchannels:\n  user/created:\n    subscribe:\n      message:\n        payload:\n          $ref: \"./schemas.yaml#/components/schemas/UserSchema\"\n"
  },
  {
    "path": "test/fixtures/external-refs/schemas.yaml",
    "content": "components:\n  schemas:\n    UserSchema:\n      type: object\n      properties:\n        id:\n          type: string\n          format: uuid\n          description: User ID\n        name:\n          type: string\n          description: User name\n        email:\n          type: string\n          format: email\n          description: User email\n      required:\n        - id\n        - name\n        - email\n"
  },
  {
    "path": "test/fixtures/generate-same-dir-ref/asyncapi.yaml",
    "content": "asyncapi: \"2.6.0\"\ninfo:\n  title: Same-dir ref fixture\n  version: 1.0.0\nchannels:\n  test:\n    publish:\n      message:\n        $ref: \"./messages.yaml#/messages/TestMessage\"\n"
  },
  {
    "path": "test/fixtures/generate-same-dir-ref/messages.yaml",
    "content": "messages:\n  TestMessage:\n    payload:\n      type: string\n"
  },
  {
    "path": "test/fixtures/invalid-overrides.json",
    "content": "{\n\t\"\"\n}"
  },
  {
    "path": "test/fixtures/minimaltemplate/hooks/generateAsyncapiFile.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n\nmodule.exports = {\n    'generate:before': generator => {\n        const asyncapi = generator.originalAsyncAPI;\n        let extension;\n        try {\n            JSON.parse(asyncapi);\n            extension = 'json'\n        } catch (error) {\n            extension = 'yaml'\n        }\n\n        fs.writeFileSync(\n            path.resolve(\n                generator.targetDir, `asyncapi.${extension}`\n            )\n            , asyncapi, { encoding: 'utf-8' }\n        );\n    }\n}"
  },
  {
    "path": "test/fixtures/minimaltemplate/package.json",
    "content": "{\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"generator\": {\n    \"renderer\": \"react\",\n    \"parameters\": {\n      \"version\": {\n        \"description\": \"Custom version to be used\"\n      },\n      \"mode\": {\n        \"description\": \"development or production\"\n      }\n    }\n  },\n  \"dependencies\": {\n    \"@asyncapi/generator-react-sdk\": \"^1.1.2\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/minimaltemplate/template/index.js",
    "content": "import { File, Text } from '@asyncapi/generator-react-sdk';\n\nexport default function({ asyncapi, params }) {\n  return (\n    <File name=\"asyncapi.md\">\n      <Text>This is a markdown file for my application.</Text>\n      <Text>App name is: **{ asyncapi.info().title() }**</Text>\n      <Text>Version {params.version} running on {params.mode} mode </Text>\n    </File>\n  );\n}\n"
  },
  {
    "path": "test/fixtures/newtemplate/hooks/generateAsyncapiFile.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n\nmodule.exports = {\n  'generate:before': generator => {\n    const asyncapi = generator.originalAsyncAPI;\n    let extension;\n    try {\n      JSON.parse(asyncapi);\n      extension = 'json';\n    } catch (error) {\n      extension = 'yaml';\n    }\n\n    fs.writeFileSync(\n      path.resolve(\n        generator.targetDir, `asyncapi.${extension}`\n      )\n      , asyncapi, { encoding: 'utf-8' }\n    );\n  }\n};\n"
  },
  {
    "path": "test/fixtures/newtemplate/package.json",
    "content": "{\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"generator\": {\n    \"renderer\": \"react\",\n    \"apiVersion\": \"v3\",\n    \"generator\": \">=3.0.0 <4.0.0\",\n    \"parameters\": {\n      \"version\": {\n        \"description\": \"Custom version to be used\"\n      },\n      \"mode\": {\n        \"description\": \"development or production\"\n      }\n    }\n  },\n  \"dependencies\": {\n    \"@asyncapi/generator-react-sdk\": \"^1.1.1\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/newtemplate/template/index.js",
    "content": "import { File, Text } from '@asyncapi/generator-react-sdk';\n\nexport default function({ asyncapi, params }) {\n  return (\n    <File name=\"asyncapi.md\">\n      <Text>This is a markdown file for my application.</Text>\n      <Text>App name is: **{ asyncapi.info().title() }**</Text>\n      <Text>Version {params.version} running on {params.mode} mode </Text>\n    </File>\n  );\n}\n"
  },
  {
    "path": "test/fixtures/openapi.yml",
    "content": "openapi: 3.0.0\ninfo:\n  title: Callbacks, Links, and Content Types API\n  version: 1.0.0\n  description: An API showcasing callbacks, links, and various content types\nservers:\n  - url: https://api.example.com/v1\npaths:\n  /webhooks:\n    post:\n      summary: Subscribe to webhook\n      operationId: subscribeWebhook\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                callbackUrl:\n                  type: string\n                  format: uri\n      responses:\n        '201':\n          description: Subscription created\n      callbacks:\n        onEvent:\n          '{$request.body#/callbackUrl}':\n            post:\n              requestBody:\n                required: true\n                content:\n                  application/json:\n                    schema:\n                      type: object\n                      properties:\n                        eventType:\n                          type: string\n                        eventData:\n                          type: object\n              responses:\n                '200':\n                  description: Webhook processed\n  /users/{userId}:\n    get:\n      summary: Get a user\n      operationId: getUser\n      parameters:\n        - in: path\n          name: userId\n          required: true\n          schema:\n            type: string\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n          links:\n            userPosts:\n              operationId: getUserPosts\n              parameters:\n                userId: '$response.body#/id'\n  /users/{userId}/posts:\n    get:\n      summary: Get user posts\n      operationId: getUserPosts\n      parameters:\n        - in: path\n          name: userId\n          required: true\n          schema:\n            type: string\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Post'\n  /upload:\n    post:\n      summary: Upload a file\n      operationId: uploadFile\n      requestBody:\n        content:\n          multipart/form-data:\n            schema:\n              type: object\n              properties:\n                file:\n                  type: string\n                  format: binary\n      responses:\n        '200':\n          description: Successful upload\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  fileId:\n                    type: string\n  /stream:\n    get:\n      summary: Get a data stream\n      operationId: getStream\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/octet-stream:\n              schema:\n                type: string\n                format: binary\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: string\n        name:\n          type: string\n    Post:\n      type: object\n      properties:\n        id:\n          type: string\n        title:\n          type: string\n        content:\n          type: string"
  },
  {
    "path": "test/fixtures/overrides.json",
    "content": "{\n  \"/servers/*/protocol\": {\n    \"add\": \"breaking\",\n    \"remove\": \"breaking\",\n    \"edit\": \"unclassified\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/specification-avro.yml",
    "content": "asyncapi: 2.2.0\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: '#/components/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      schemaFormat: 'application/vnd.apache.avro;version=1.9.0'\n      payload:\n        type: record\n        namespace: com.example\n        name: User\n        fields:\n          - name: displayName\n            type: string\n            doc: Name of the user\n          - name: email\n            type: string\n            doc: Email of the user\n"
  },
  {
    "path": "test/fixtures/specification-invalid.yml",
    "content": "asyncapi: 2.2.0\ninfo:\n  title: Account Service\nversion: 1.0.0\ndescription: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: '#/components/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n"
  },
  {
    "path": "test/fixtures/specification-v3-diff.yml",
    "content": "asyncapi: 3.1.0\nid: 'urn:example:com:smartylighting:streetlights:server'\ninfo:\n  title: AsyncAPI App\n  version: 1.0.1\n  description: This is a sample app.\n  termsOfService: 'https://asyncapi.com/terms/'\n  contact:\n    name: API Support\n    url: 'https://www.asyncapi.com/support'\n    email: support@asyncapi.org\n  license:\n    name: Apache 2.0\n    url: 'https://www.apache.org/licenses/LICENSE-2.0.html'\n  tags:\n    - name: e-commerce\n    - name: another-tag\n      description: Description...\n  externalDocs:\n    description: Find more info here\n    url: 'https://www.asyncapi.com'\ndefaultContentType: application/json\nservers:\n  default:\n    host: 'api.streetlights.smartylighting.com:{port}'\n    protocol: mqtt\n    description: Test broker\n    variables:\n      port:\n        description: Secure connection (TLS) is available through port 8883.\n        default: '8883'\n        enum:\n          - '1883'\n          - '8883'\n    security:\n      - $ref: '#/components/securitySchemes/apiKey'\n      - type: oauth2\n        flows:\n          implicit:\n            authorizationUrl: 'https://example.com/api/oauth/dialog'\n            availableScopes:\n              'write:pets': modify pets in your account\n              'read:pets': read your pets\n        scopes:\n          - 'write:pets'\n      - type: openIdConnect\n        openIdConnectUrl: https://example.com/api\n        scopes:\n          - 'some:scope:1'\n          - 'some:scope:2'\n  production:\n    host: 'api.streetlights.smartylighting.com:{port}'\n    pathname: /some/path-name\n    protocol: mqtt\n    description: Test broker\n    variables:\n      port:\n        description: Secure connection (TLS) is available through port 8883.\n        default: '1883'\n        enum:\n          - '1883'\n          - '8883'\n    security:\n      - $ref: '#/components/securitySchemes/apiKey'\n  withProtocol:\n    host: 'api.streetlights.smartylighting.com:{port}'\n    pathname: /some/path\n    protocol: mqtt\n    description: Test broker\n    variables:\n      port:\n        description: Secure connection (TLS) is available through port 8883.\n        default: '1883'\n        enum:\n          - '1883'\n          - '8883'\n    security:\n      - $ref: '#/components/securitySchemes/apiKey'\nchannels:\n  'lightingMeasured':\n    address: 'smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured'\n    messages:\n      lightMeasured:\n        payload:\n          type: object\n    servers:\n      - $ref: '#/servers/production'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n  'turnOn':\n    address: 'smartylighting/streetlights/1/0/action/{streetlightId}/turn/on'\n    messages:\n      lightMeasured:\n        $ref: '#/components/messages/lightMeasured'\n      subscribe.message.0:\n        $ref: '#/components/messages/turnOnOff'\n      customMessageId:\n        payload:\n          type: object\n      subscribe.message.2:\n        payload:\n          type: object\n    servers:\n      - $ref: '#/servers/default'\n      - $ref: '#/servers/production'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n  customChannelId:\n    address: 'smartylighting/streetlights/1/0/action/{streetlightId}/turn/off'\n    messages:\n      turnOnOff:\n        $ref: '#/components/messages/turnOnOff'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n    x-channelId: customChannelId\n  'dim':\n    address: 'smartylighting/streetlights/1/0/action/{streetlightId}/dim'\n    messages:\n      dimLight:\n        $ref: '#/components/messages/dimLight'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\noperations:\n  receiveLightMeasured:\n    action: receive\n    channel:\n      $ref: '#/channels/lightingMeasured'\n    messages:\n      - $ref: '#/channels/lightingMeasured/messages/lightMeasured'\n  'receiveTurnOn':\n    action: receive\n    channel:\n      $ref: '#/channels/turnOn'\n    messages:\n      - $ref: '#/channels/turnOn/messages/lightMeasured'\n  'smartylighting/streetlights/1/0/action/{streetlightId}/turn/on.subscribe':\n    action: send\n    channel:\n      $ref: '#/channels/turnOn'\n    messages:\n      - $ref: '#/channels/turnOn/messages/customMessageId'\n      - $ref: '#/channels/turnOn/messages/subscribe.message.2'\n  turnOnOff:\n    action: send\n    channel:\n      $ref: '#/channels/customChannelId'\n    messages:\n      - $ref: '#/channels/customChannelId/messages/turnOnOff'\n  dimLight:\n    action: send\n    channel:\n      $ref: '#/channels/dim'\n    security:\n      - type: oauth2\n        flows:\n          implicit:\n            authorizationUrl: 'https://example.com/api/oauth/dialog'\n            availableScopes:\n              'write:pets': modify pets in your account\n              'read:pets': read your pets\n        scopes:\n          - 'write:pets'\n    messages:\n      - $ref: '#/channels/dim/messages/dimLight'\ncomponents:\n  messages:\n    lightMeasured:\n      summary: >-\n        Inform about environmental lighting conditions for a particular\n        streetlight.\n      payload:\n        $ref: '#/components/schemas/lightMeasuredPayload'\n    turnOnOff:\n      summary: Command a particular streetlight to turn the lights on or off.\n      payload:\n        $ref: '#/components/schemas/turnOnOffPayload'\n    dimLight:\n      summary: Command a particular streetlight to dim the lights.\n      payload:\n        $ref: '#/components/schemas/dimLightPayload'\n  schemas:\n    lightMeasuredPayload:\n      type: object\n      properties:\n        lumens:\n          type: integer\n          minimum: 0\n          description: Light intensity measured in lumens.\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    turnOnOffPayload:\n      type: object\n      properties:\n        command:\n          type: string\n          enum:\n            - 'on'\n            - 'off'\n          description: Whether to turn on or off the light.\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    dimLightPayload:\n      type: object\n      properties:\n        percentage:\n          type: integer\n          description: Percentage to which the light should be dimmed to.\n          minimum: 0\n          maximum: 100\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    sentAt:\n      type: string\n      format: date-time\n      description: Date and time when the message was sent.\n  securitySchemes:\n    apiKey:\n      type: apiKey\n      in: user\n      description: Provide your API key as the user and leave the password empty.\n    flows:\n      type: oauth2\n      flows:\n        implicit:\n          authorizationUrl: 'https://example.com/api/oauth/dialog'\n          availableScopes:\n            'write:pets': modify pets in your account\n            'read:pets': read your pets\n    openIdConnect:\n      type: openIdConnect\n      openIdConnectUrl: https://example.com/api\n      scopes:\n        - 'some:scope:1'\n        - 'some:scope:2'\n    unusedFlows:\n      type: oauth2\n      flows:\n        implicit:\n          authorizationUrl: 'https://example.com/api/oauth/dialog'\n          availableScopes:\n            'write:pets': modify pets in your account\n            'read:pets': read your pets\n  parameters:\n    streetlightId:\n      description: The ID of the streetlight.\n"
  },
  {
    "path": "test/fixtures/specification-v3.yml",
    "content": "asyncapi: 3.1.0\nid: 'urn:example:com:smartylighting:streetlights:server'\ninfo:\n  title: AsyncAPI Sample App\n  version: 1.0.1\n  description: This is a sample app.\n  termsOfService: 'https://asyncapi.com/terms/'\n  contact:\n    name: API Support\n    url: 'https://www.asyncapi.com/support'\n    email: support@asyncapi.org\n  license:\n    name: Apache 2.0\n    url: 'https://www.apache.org/licenses/LICENSE-2.0.html'\n  tags:\n    - name: e-commerce\n    - name: another-tag\n      description: Description...\n  externalDocs:\n    description: Find more info here\n    url: 'https://www.asyncapi.com'\ndefaultContentType: application/json\nservers:\n  default:\n    host: 'api.streetlights.smartylighting.com:{port}'\n    protocol: mqtt\n    description: Test broker\n    variables:\n      port:\n        description: Secure connection (TLS) is available through port 8883.\n        default: '1883'\n        enum:\n          - '1883'\n          - '8883'\n    security:\n      - $ref: '#/components/securitySchemes/apiKey'\n      - type: oauth2\n        flows:\n          implicit:\n            authorizationUrl: 'https://example.com/api/oauth/dialog'\n            availableScopes:\n              'write:pets': modify pets in your account\n              'read:pets': read your pets\n        scopes:\n          - 'write:pets'\n      - type: openIdConnect\n        openIdConnectUrl: https://example.com/api\n        scopes:\n          - 'some:scope:1'\n          - 'some:scope:2'\n  production:\n    host: 'api.streetlights.smartylighting.com:{port}'\n    pathname: /some/path\n    protocol: mqtt\n    description: Test broker\n    variables:\n      port:\n        description: Secure connection (TLS) is available through port 8883.\n        default: '1883'\n        enum:\n          - '1883'\n          - '8883'\n    security:\n      - $ref: '#/components/securitySchemes/apiKey'\n  withProtocol:\n    host: 'api.streetlights.smartylighting.com:{port}'\n    pathname: /some/path\n    protocol: mqtt\n    description: Test broker\n    variables:\n      port:\n        description: Secure connection (TLS) is available through port 8883.\n        default: '1883'\n        enum:\n          - '1883'\n          - '8883'\n    security:\n      - $ref: '#/components/securitySchemes/apiKey'\nchannels:\n  'lightingMeasured':\n    address: 'smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured'\n    messages:\n      lightMeasured:\n        payload:\n          type: object\n    servers:\n      - $ref: '#/servers/production'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n  'turnOn':\n    address: 'smartylighting/streetlights/1/0/action/{streetlightId}/turn/on'\n    messages:\n      lightMeasured:\n        $ref: '#/components/messages/lightMeasured'\n      subscribe.message.0:\n        $ref: '#/components/messages/turnOnOff'\n      customMessageId:\n        payload:\n          type: object\n      subscribe.message.2:\n        payload:\n          type: object\n    servers:\n      - $ref: '#/servers/default'\n      - $ref: '#/servers/production'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n  customChannelId:\n    address: 'smartylighting/streetlights/1/0/action/{streetlightId}/turn/off'\n    messages:\n      turnOnOff:\n        $ref: '#/components/messages/turnOnOff'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\n    x-channelId: customChannelId\n  'dim':\n    address: 'smartylighting/streetlights/1/0/action/{streetlightId}/dim'\n    messages:\n      dimLight:\n        $ref: '#/components/messages/dimLight'\n    parameters:\n      streetlightId:\n        $ref: '#/components/parameters/streetlightId'\noperations:\n  receiveLightMeasured:\n    action: receive\n    channel:\n      $ref: '#/channels/lightingMeasured'\n    messages:\n      - $ref: '#/channels/lightingMeasured/messages/lightMeasured'\n  'receiveTurnOn':\n    action: receive\n    channel:\n      $ref: '#/channels/turnOn'\n    messages:\n      - $ref: '#/channels/turnOn/messages/lightMeasured'\n  'smartylighting/streetlights/1/0/action/{streetlightId}/turn/on.subscribe':\n    action: send\n    channel:\n      $ref: '#/channels/turnOn'\n    messages:\n      - $ref: '#/channels/turnOn/messages/customMessageId'\n      - $ref: '#/channels/turnOn/messages/subscribe.message.2'\n  turnOnOff:\n    action: send\n    channel:\n      $ref: '#/channels/customChannelId'\n    messages:\n      - $ref: '#/channels/customChannelId/messages/turnOnOff'\n  dimLight:\n    action: send\n    channel:\n      $ref: '#/channels/dim'\n    security:\n      - type: oauth2\n        flows:\n          implicit:\n            authorizationUrl: 'https://example.com/api/oauth/dialog'\n            availableScopes:\n              'write:pets': modify pets in your account\n              'read:pets': read your pets\n        scopes:\n          - 'write:pets'\n    messages:\n      - $ref: '#/channels/dim/messages/dimLight'\ncomponents:\n  messages:\n    lightMeasured:\n      summary: >-\n        Inform about environmental lighting conditions for a particular\n        streetlight.\n      payload:\n        $ref: '#/components/schemas/lightMeasuredPayload'\n    turnOnOff:\n      summary: Command a particular streetlight to turn the lights on or off.\n      payload:\n        $ref: '#/components/schemas/turnOnOffPayload'\n    dimLight:\n      summary: Command a particular streetlight to dim the lights.\n      payload:\n        $ref: '#/components/schemas/dimLightPayload'\n  schemas:\n    lightMeasuredPayload:\n      type: object\n      properties:\n        lumens:\n          type: integer\n          minimum: 0\n          description: Light intensity measured in lumens.\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    turnOnOffPayload:\n      type: object\n      properties:\n        command:\n          type: string\n          enum:\n            - 'on'\n            - 'off'\n          description: Whether to turn on or off the light.\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    dimLightPayload:\n      type: object\n      properties:\n        percentage:\n          type: integer\n          description: Percentage to which the light should be dimmed to.\n          minimum: 0\n          maximum: 100\n        sentAt:\n          $ref: '#/components/schemas/sentAt'\n    sentAt:\n      type: string\n      format: date-time\n      description: Date and time when the message was sent.\n  securitySchemes:\n    apiKey:\n      type: apiKey\n      in: user\n      description: Provide your API key as the user and leave the password empty.\n    flows:\n      type: oauth2\n      flows:\n        implicit:\n          authorizationUrl: 'https://example.com/api/oauth/dialog'\n          availableScopes:\n            'write:pets': modify pets in your account\n            'read:pets': read your pets\n    openIdConnect:\n      type: openIdConnect\n      openIdConnectUrl: https://example.com/api\n      scopes:\n        - 'some:scope:1'\n        - 'some:scope:2'\n    unusedFlows:\n      type: oauth2\n      flows:\n        implicit:\n          authorizationUrl: 'https://example.com/api/oauth/dialog'\n          availableScopes:\n            'write:pets': modify pets in your account\n            'read:pets': read your pets\n  parameters:\n    streetlightId:\n      description: The ID of the streetlight.\n"
  },
  {
    "path": "test/fixtures/specification.json",
    "content": "{\n    \"asyncapi\": \"2.2.0\",\n    \"info\": {\n        \"title\": \"Rust server\",\n        \"description\": \"test\",\n        \"version\": \"0.8.0\",\n        \"license\": {\n            \"name\": \"Apache 2.0\",\n            \"url\": \"https://www.apache.org/licenses/LICENSE-2.0.html\"\n        }\n    },\n    \"servers\": {\n        \"production\": {\n            \"url\": \"148.251.43.103:4222\",\n            \"protocol\": \"nats\",\n            \"description\": \"GamingAPI NATS production broker\"\n        }\n    },\n    \"defaultContentType\": \"json\",\n    \"channels\": {\n        \"v0/rust/servers/events/started\": {\n            \"description\": \"Channel for the API to process for when a server has started\",\n            \"subscribe\": {\n                \"operationId\": \"ServerStarted\",\n                \"description\": \"The Rust server can publish to this channel when the server has started\",\n                \"message\": {\n                    \"payload\": {\n                        \"type\": \"string\"\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "test/fixtures/specification.yml",
    "content": "asyncapi: 2.2.0\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: '#/components/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n"
  },
  {
    "path": "test/fixtures/valid-specification-latest.yml",
    "content": "asyncapi: 3.1.0\nid: urn:some:company\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0\n  contact:\n    name: API Support\n    url: https://www.example.com/support\n    email: support@example.com\n  tags:\n  - name: some-tag\ndefaultContentType: 'application/json'\nservers:\n  production:\n    host: development.gigantic-server.com\n    description: Development server\n    protocol: kafka\n    protocolVersion: '1.0.0'\nchannels:\n  UserSignedUp:\n    address: 'user/signedup'\n    messages: \n      UserSignedUp:\n        $ref: '#/components/messages/UserSignedUp'\noperations:\n  UserSignedUp:\n    description: User signedup\n    action: 'send'\n    channel:\n      $ref: '#/channels/UserSignedUp'        \ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n"
  },
  {
    "path": "test/fixtures/valid-specification.yml",
    "content": "asyncapi: 2.6.0\nid: urn:some:company\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0\n  contact:\n    name: API Support\n    url: https://www.example.com/support\n    email: support@example.com\ndefaultContentType: 'application/json'\ntags:\n  - name: some-tag\nservers:\n  production:\n    url: development.gigantic-server.com\n    description: Development server\n    protocol: kafka\n    protocolVersion: '1.0.0'\nchannels:\n  user/signedup:\n    subscribe:\n      operationId: user-signedup\n      description: User signedup\n      message:\n        $ref: '#/components/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n"
  },
  {
    "path": "test/helpers/index.ts",
    "content": "import { existsSync, writeFileSync, unlinkSync,rmdirSync, mkdirSync, promises as fs } from 'fs';\nimport * as path from 'path';\nimport { IContextFile, CONTEXT_FILE_PATH } from '../../src/domains/models/Context';\nimport SpecificationFile from '../../src/domains/models/SpecificationFile';\nimport http from 'http';\nimport { rimrafSync } from 'rimraf';\nimport puppeteer from 'puppeteer';\nimport { version as studioVersion } from '@asyncapi/studio/package.json';\n\nconst ASYNCAPI_FILE_PATH = path.resolve(process.cwd(), 'specification.yaml');\nconst SERVER_DIRECTORY= path.join(__dirname, '../fixtures/dummyspec');\nexport const PROJECT_DIRECTORY_PATH = path.join(process.cwd(), 'test-project');\n\nlet server: http.Server;\n\nexport default class ContextTestingHelper {\n  private _context: IContextFile;\n  constructor() {\n    const homeSpecFile = new SpecificationFile(path.resolve(__dirname, '../fixtures/specification.yml'));\n\n    const codeSpecFile = new SpecificationFile(path.resolve(__dirname, '../fixtures/specification.yml'));\n    this._context = {\n      current: 'home',\n      store: {\n        home: homeSpecFile.getPath(),\n        code: codeSpecFile.getPath()\n      }\n    };\n  }\n\n  get context(): IContextFile {\n    return this._context;\n  }\n\n  createDummyContextFile(): void {\n    writeFileSync(CONTEXT_FILE_PATH, JSON.stringify(this._context), { encoding: 'utf8' });\n  }\n\n  createDummyContextFileWrong(data: string): void {\n    writeFileSync(CONTEXT_FILE_PATH, JSON.stringify(data));\n  }\n\n  deleteDummyContextFile(): void {\n    if (existsSync(CONTEXT_FILE_PATH)) {\n      unlinkSync(CONTEXT_FILE_PATH);\n    }\n  }\n\n  unsetCurrentContext(): void {\n    delete this._context.current;\n  }\n\n  setCurrentContext(context: string): void {\n    this._context.current = context;\n  }\n\n  getPath(key: string): string | undefined {\n    return this._context.store[String(key)];\n  }\n\n  createSpecFileAtWorkingDir(): void {\n    writeFileSync(ASYNCAPI_FILE_PATH, '');\n  }\n\n  deleteSpecFileAtWorkingDir(): void {\n    unlinkSync(ASYNCAPI_FILE_PATH);\n  }\n\n  createDummyProjectDirectory(): void {\n    mkdirSync(PROJECT_DIRECTORY_PATH);\n  }\n\n  deleteDummyProjectDirectory(): void {\n    rimrafSync(PROJECT_DIRECTORY_PATH);\n  }\n}\n\nexport function fileCleanup(filepath: string) {\n  unlinkSync(filepath);\n}\n\nexport async function testStudio(){\n  const browser = await puppeteer.launch({\n    args: ['--no-sandbox']\n  });\n  const page = await browser.newPage();\n\n  await page.goto(`http://127.0.0.1:3210?liveServer=3210&studio-version=${studioVersion}`);\n  await page.setViewport({width: 1080, height: 1024});\n\n  const logo = await page.locator('body > div:nth-child(1) > div > div > div > div > img').waitHandle()\n\n  const logoTitle = await logo?.evaluate((e:any) => e.title)\n  await browser.close();\n  return {logoTitle}\n}\n\nexport async function testPreview(){\n  const browser = await puppeteer.launch({\n    args: ['--no-sandbox']\n  });\n  const page = await browser.newPage();\n\n  await page.goto(`http://127.0.0.1:4321?previewServer=4321&studio-version=${studioVersion}`);\n  await page.setViewport({width: 1080, height: 1024});\n\n  const logo = await page.locator('body > div:nth-child(1) > div > div > div > div > img').waitHandle()\n  const introductionSection = await page.locator('#introduction').waitHandle()\n\n  const logoTitle = await logo?.evaluate((e:any) => e.title)\n  const introductionSectionId = await introductionSection?.evaluate((e:any)=> e.id)\n\n  await browser.close();\n  return {logoTitle,introductionSectionId}\n}\nexport function createMockServer (port = 8080) {\n  server = http.createServer(async (req,res) => {\n    if (req.method ==='GET') {\n      const filePath= path.join(SERVER_DIRECTORY, req.url || '/');\n      try {\n        const content = await fs.readFile(filePath, {encoding: 'utf8'});\n        res.writeHead(200, {'Content-Type': getContentType(filePath)});\n        res.end(content);\n      } catch (error: any) {\n        if (error.code === 'ENOENT') {\n          res.writeHead(404);\n          res.end('404 NOT FOUND');\n        } else {\n          res.writeHead(500);\n          res.end('Internal Server Error');\n        }\n      }\n    }\n  });\n  server.listen(port);\n}\n\nexport function stopMockServer() {\n  server.close();\n}\n\nexport async function closeStudioServer(port = 3210): Promise<void> {\n  try {\n    const response = await fetch(`http://localhost:${port}/close`);\n    if (response.ok) {\n      const text = await response.text();\n    } else {\n      console.log(`Failed to close server. Status: ${response.status}`);\n    }\n  } catch (error) {\n    console.error('Error closing studio server:', error);\n  }\n}\n\nfunction getContentType(filePath:string):string {\n  const extname = path.extname(filePath);\n  switch (extname) {\n  case '.json':\n    return 'application/json';\n  case '.yml':\n  case '.yaml':\n    return 'application/yaml';\n  default:\n    // Any other suggestion?\n    return 'application/octet-stream';\n  }\n}\n"
  },
  {
    "path": "test/helpers/init.js",
    "content": "const path = require('path');\nprocess.env.TS_NODE_PROJECT = path.resolve('test/tsconfig.json');\nprocess.env.NODE_ENV = 'development';\n\nglobal.oclif = global.oclif || {};\nglobal.oclif.columns = 80;\n\nrequire('events').EventEmitter.defaultMaxListeners = 30;"
  },
  {
    "path": "test/hooks/command_not_found/myhook.spec.ts",
    "content": "import {expect, test} from '@oclif/test';\n\ndescribe('hooks', () => {\n  test\n    .stdout()\n    .hook('command_not_found', {id: 'help'})\n    .do(output => expect(output.stdout).to.contain('help command not found.'))\n    .it('shows a message');\n});\n\n"
  },
  {
    "path": "test/integration/bundle/bundle.test.ts",
    "content": "import { expect, test } from '@oclif/test';\nimport fs from 'fs';\nimport path from 'path';\nimport { fileCleanup } from '../../helpers/index';\n\nconst spec = fs.readFileSync('./test/integration/bundle/final-asyncapi.yaml', {encoding: 'utf-8'});\n\nfunction validateGeneratedSpec(filePath: string, spec: string) {\n  const generatedSPec = fs.readFileSync(path.resolve(filePath), { encoding: 'utf-8' });\n  return generatedSPec === spec;\n}\n\ndescribe('bundle', () => {\n  describe('bundle successful', () => {\n    test\n      .stdout()\n      .command([\n        'bundle', './test/integration/bundle/first-asyncapi.yaml',\n        '--output=./test/integration/bundle/final.yaml',\n      ])\n      .it('should successfully bundle specification', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'Check out your shiny new bundled files at ./test/integration/bundle/final.yaml'\n        );\n        fileCleanup('./test/integration/bundle/final.yaml');\n        done();\n      });\n  });\n\n  describe('bundle into json file', () => {\n    test\n      .stdout()\n      .command([\n        'bundle', './test/integration/bundle/first-asyncapi.yaml',\n        '--output=./test/integration/bundle/final.json'\n      ])\n      .it('should successfully bundle specification into json file', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'Check out your shiny new bundled files at ./test/integration/bundle/final.json'\n        );\n        fileCleanup('./test/integration/bundle/final.json');\n        done();\n      });\n  });\n\n  describe('when file path is wrong', () => {\n    test\n      .stderr()\n      .command([\n        'bundle', './test/integration/bundle/asyncapi.yml'\n      ])\n      .it('should throw error message if the file path is wrong', (ctx, done) => {\n        expect(ctx.stderr).to.contain('Error: ENOENT: no such file or directory');\n        done();\n      });\n  });\n\n  describe('with custom reference', () => {\n    test\n      .stdout()\n      .command([\n        'bundle', './test/integration/bundle/first-asyncapi.yaml', './test/integration/bundle/feature.yaml', '--output=test/integration/bundle/final.yaml'\n      ])\n      .it('should be able to bundle multiple specs along with custom reference', (ctx, done) => {\n        expect(ctx.stdout).to.contain('Check out your shiny new bundled files at test/integration/bundle/final.yaml\\n');\n        expect(validateGeneratedSpec('test/integration/bundle/final.yaml', spec));\n        fileCleanup('./test/integration/bundle/final.yaml');\n        done();\n      });\n  });\n\n  describe('with base file', () => {\n    test\n      .stdout()\n      .command([\n        'bundle', './test/integration/bundle/first-asyncapi.yaml', './test/integration/bundle/feature.yaml', '--output=test/integration/bundle/final.yaml', '--base=./test/integration/bundle/first-asyncapi.yaml'\n      ])\n      .it('should be able to bundle correctly with overwriting base file', (ctx, done) => {\n        expect(ctx.stdout).to.contain('Check out your shiny new bundled files at test/integration/bundle/final.yaml\\n');\n        expect(validateGeneratedSpec('test/integration/bundle/final-asyncapi.yaml', spec));\n        fileCleanup('./test/integration/bundle/final.yaml');\n        done();\n      });\n  });\n});\n\ndescribe('bundle, with spec v3', () => {\n  test\n    .stdout()\n    .command([\n      'bundle', './test/integration/bundle/first-asyncapiv3.yaml',\n      '--output=test/integration/bundle/final.yaml',\n    ]).it('should be able to bundle v3 spec correctly', (ctx, done) => {\n      expect(ctx.stdout).to.contain('Check out your shiny new bundled files at test/integration/bundle/final.yaml\\n');\n      fileCleanup('./test/integration/bundle/final.yaml');\n      done();\n    });\n});\n\n"
  },
  {
    "path": "test/integration/bundle/channels.yaml",
    "content": "channels:\n  commentLikedChannel:\n    address: comment/liked"
  },
  {
    "path": "test/integration/bundle/feature.yaml",
    "content": "asyncapi: \"2.6.0\"\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user logouts\n"
  },
  {
    "path": "test/integration/bundle/final-asyncapi.yaml",
    "content": "asyncapi: 2.6.0\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: '#/components/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n    UserLoggedOut:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          userId:\n            type: string\n            description: Id the user\n          timestamp:\n            type: number\n            descriptio: Time stamp when the user logged out\n"
  },
  {
    "path": "test/integration/bundle/first-asyncapi.yaml",
    "content": "asyncapi: \"2.6.0\"\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: \"./messages.yaml#/messages/UserSignedUp\""
  },
  {
    "path": "test/integration/bundle/first-asyncapiv3.yaml",
    "content": "asyncapi: 3.1.0\ninfo:\n  title: Example Service\n  version: 1.0.0\n  description: Example Service.\nchannels:\n  commentLikedChannel:\n    $ref: './channels.yaml#/channels/commentLikedChannel'\n"
  },
  {
    "path": "test/integration/bundle/messages.yaml",
    "content": "messages:\n  UserSignedUp:\n    payload:\n      type: object\n      properties:\n        displayName:\n          type: string\n          description: Name of the user\n        email:\n          type: string\n          format: email\n          description: Email of the user\n  UserLoggedOut:\n    payload:\n      type: object\n      properties:\n        displayName:\n          type: string\n          description: Name of the user\n        userId:\n          type: string\n          description: Id the user\n        timestamp:\n          type: number\n          description: Time stamp when the user logged out\n\n"
  },
  {
    "path": "test/integration/config/analytics.test.ts",
    "content": "import { expect, test } from '@oclif/test';\nimport { fileCleanup } from '../../helpers/index';\n\nconst analyticsConfigFilePath = './test/fixtures/.asyncapi-analytics';\n\ndescribe('config:analytics', () => {\n  beforeEach(() => {\n    process.env = Object.assign(process.env, { ASYNCAPI_METRICS_CONFIG_PATH: analyticsConfigFilePath });\n  });\n\n  afterEach(() => {\n    fileCleanup(analyticsConfigFilePath);\n  });\n\n  describe('with disable flag', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:analytics', '--disable'])\n      .it('should show a successful message once the analytics are disabled', async (ctx, done) => {\n        expect(ctx.stdout).to.equal('\\nAnalytics disabled.\\n\\n');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with enable flag', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:analytics', '--enable'])\n      .it('should show a successful message once the analytics are enabled', (ctx, done) => {\n        expect(ctx.stdout).to.equal('\\nAnalytics enabled.\\n\\n');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with no flags', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:analytics'])\n      .it('should show informational message when no flags are used', (ctx, done) => {\n        expect(ctx.stdout).to.equal('\\nPlease append the --disable flag to the command if you prefer to disable analytics, or use the --enable flag if you want to enable analytics again. To check the current analytics status, use the --status flag.\\n\\n');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with status flag', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:analytics', '--status'])\n      .it('should show a different informational message depending on the analytics status', (ctx, done) => {\n        expect(ctx.stdout).to.contain('\\nAnalytics are ');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/integration/config/versions.test.ts",
    "content": "import { expect, test } from '@oclif/test';\n\ndescribe('config', () => {\n  describe('config:versions', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:versions'])\n      .it('should show versions of AsyncAPI tools used', (ctx, done) => {\n        expect(ctx.stdout).to.contain('@asyncapi/cli/');\n        expect(ctx.stdout).to.contain('├@asyncapi/');\n        expect(ctx.stdout).to.contain('└@asyncapi/');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['config:versions'])\n      .it('should show address of repository of AsyncAPI CLI', (ctx, done) => {\n        expect(ctx.stdout).to.contain('https://github.com/asyncapi/cli');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/integration/context.test.ts",
    "content": "import path from 'path';\nimport { expect, test } from '@oclif/test';\n\nimport TestHelper from '../helpers/index';\nimport { CONTEXT_FILE_PATH } from '../../src/domains/models/Context';\n\nconst testHelper = new TestHelper();\n\ndescribe('config:context, positive scenario', () => {\n  after(() => {\n    testHelper.deleteDummyContextFile();\n  });\n\n  before(() => {\n    testHelper.createDummyContextFile();\n  });\n\n  describe('config:context:current', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:current'])\n      .it('should show current context', (ctx, done) => {\n        expect(ctx.stdout).to.equals(\n          `${testHelper.context.current}: ${testHelper.context.store['home']}\\n`\n        );\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  describe('config:context:list', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:list'])\n      .it(\n        'should list contexts prints list if context file is present',\n        (ctx, done) => {\n          expect(ctx.stdout).to.equals(\n            `home: ${path.resolve(\n              __dirname,\n              '../fixtures/specification.yml'\n            )}\\ncode: ${path.resolve(__dirname, '../fixtures/specification.yml')}\\n`\n          );\n          expect(ctx.stderr).to.equals('');\n          done();\n        }\n      );\n  });\n\n  describe('config:context:add', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:add', 'test', './test/integration/specification.yml'])\n      .it('should add new context called \"test\"', (ctx, done) => {\n        expect(ctx.stdout).to.equals(\n          '🎉 Context test added successfully!\\nYou can set it as your current context:\\n  asyncapi config context use test\\nYou can use this context when needed by passing test as a parameter:\\n  asyncapi validate test\\n'\n        );\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  describe('config:context:add', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:add', 'test', './test/specification.yml'])\n      .it(\n        'should NOT add new context with already existing in context file name \"test\"',\n        (ctx, done) => {\n          expect(ctx.stdout).to.equals('');\n          expect(ctx.stderr).to.equals(\n            `ContextError: Context with name \"test\" already exists in context file \"${CONTEXT_FILE_PATH}\".\\n`\n          );\n          done();\n        }\n      );\n  });\n\n  describe('config:context:edit', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:edit', 'test', './test/specification2.yml'])\n      .it('should edit existing context \"test\"', (ctx, done) => {\n        expect(ctx.stdout).to.contain('🎉 Context test edited successfully!');\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  describe('config:context:use', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:use', 'code'])\n      .it('should update the current context', (ctx, done) => {\n        expect(ctx.stdout).to.equals('Context code is now set as current.\\n');\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  // On direct execution of `chmodSync(CONTEXT_FILE_PATH, '444')` context file's\n  // permissions get changed to 'read only' in file system, but `@oclif/test`'s\n  // test still passes. Thus there is no sense in implementation of\n  // `writeFile()` faulty scenario with `@oclif/test` framework.\n  describe('config:context:remove', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:remove', 'code'])\n      .it('should remove existing context', (ctx, done) => {\n        expect(ctx.stdout).to.equals('Context code removed successfully!\\n\\n');\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  describe('config:context:init', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:init'])\n      .it('should initialize new empty context file without a switch', (ctx, done) => {\n        expect(ctx.stdout).to.contain('🎉 Context initialized at');\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  describe('config:context:init', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:init', '.'])\n      .it('should initialize new empty context file with switch \".\"', (ctx, done) => {\n        expect(ctx.stdout).to.contain('🎉 Context initialized at');\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  describe('config:context:init', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:init', './'])\n      .it('should initialize new empty context file with switch \"./\"', (ctx, done) => {\n        expect(ctx.stdout).to.contain('🎉 Context initialized at');\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n\n  describe('config:context:init', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:init', '~'])\n      .it('should initialize new empty context file with switch \"~\"', (ctx, done) => {\n        expect(ctx.stdout).to.contain('🎉 Context initialized at');\n        expect(ctx.stderr).to.equals('');\n        done();\n      });\n  });\n});\n\ndescribe('config:context, negative scenario', () => {\n  before(() => {\n    // Any context file needs to be created before starting test suite,\n    // otherwise a totally legitimate context file will be created automatically\n    // by `addContext()`.\n    testHelper.createDummyContextFileWrong('');\n  });\n\n  after(() => {\n    testHelper.deleteDummyContextFile();\n  });\n\n  describe('config:context:add', () => {\n    testHelper.deleteDummyContextFile();\n    testHelper.createDummyContextFileWrong('');\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:add', 'home', './test/specification.yml'])\n      .it(\n        'should throw error on zero-sized file saying that context file has wrong format.',\n        (ctx, done) => {\n          expect(ctx.stdout).to.equals('');\n          expect(ctx.stderr).to.contain(\n            `ContextError: Context file \"${CONTEXT_FILE_PATH}\" has wrong format.`\n          );\n          done();\n        }\n      );\n  });\n\n  describe('config:context:add', () => {\n    testHelper.deleteDummyContextFile();\n    testHelper.createDummyContextFileWrong('{}');\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:add', 'home', './test/specification.yml'])\n      .it(\n        'should throw error on file with empty object saying that context file has wrong format.',\n        (ctx, done) => {\n          expect(ctx.stdout).to.equals('');\n          expect(ctx.stderr).to.contain(\n            `ContextError: Context file \"${CONTEXT_FILE_PATH}\" has wrong format.`\n          );\n          done();\n        }\n      );\n  });\n\n  describe('config:context:add', () => {\n    testHelper.deleteDummyContextFile();\n    testHelper.createDummyContextFileWrong('[]');\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:add', 'home', './test/specification.yml'])\n      .it(\n        'should throw error on file with empty array saying that context file has wrong format.',\n        (ctx, done) => {\n          expect(ctx.stdout).to.equals('');\n          expect(ctx.stderr).to.contain(\n            `ContextError: Context file \"${CONTEXT_FILE_PATH}\" has wrong format.`\n          );\n          done();\n        }\n      );\n  });\n\n  // Totally correct (and considered correct by `@oclif/core`) format of the\n  // context file\n  // `{\"current\":\"home\",\"store\":{\"home\":\"homeSpecFile\",\"code\":\"codeSpecFile\"}}`\n  // is considered wrong in `@oclif/test`, limiting possibilities of negative\n  // scenarios coding.\n  describe('config:context:add', () => {\n    testHelper.deleteDummyContextFile();\n    testHelper.createDummyContextFileWrong(\n      '{\"current\":\"home\",\"current2\":\"test\",\"store\":{\"home\":\"homeSpecFile\",\"code\":\"codeSpecFile\"}}'\n    );\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:add', 'home', './test/specification.yml'])\n      .it(\n        'should throw error on file with object having three root properties, saying that context file has wrong format.',\n        (ctx, done) => {\n          expect(ctx.stdout).to.equals('');\n          expect(ctx.stderr).to.contain(\n            `ContextError: Context file \"${CONTEXT_FILE_PATH}\" has wrong format.`\n          );\n          done();\n        }\n      );\n  });\n});\n\ndescribe('config:context, negative scenario', () => {\n  after(() => {\n    testHelper.deleteDummyContextFile();\n  });\n\n  describe('config:context:list', () => {\n    testHelper.deleteDummyContextFile();\n    test\n      .stderr()\n      .stdout()\n      .command(['config:context:list'])\n      .it(\n        'should output info message (to stdout, NOT stderr) about absence of context file.',\n        (ctx, done) => {\n          expect(ctx.stdout).to.contain('Unable to list contexts. You have no context file configured.');\n          expect(ctx.stderr).to.equals('');\n          done();\n        }\n      );\n  });\n});\n"
  },
  {
    "path": "test/integration/convert.test.ts",
    "content": "import path from 'path';\nimport { test } from '@oclif/test';\nimport { NO_CONTEXTS_SAVED } from '../../src/errors/context-error';\nimport TestHelper, { createMockServer, stopMockServer } from '../helpers/index';\nimport fs from 'fs-extra';\nimport { expect } from '@oclif/test';\n\nconst testHelper = new TestHelper();\nconst filePath = './test/fixtures/specification.yml';\nconst JSONFilePath = './test/fixtures/specification.json';\nconst openAPIFilePath = './test/fixtures/openapi.yml';\n\ndescribe('convert', () => {\n  describe('with file paths', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    before(() => {\n      createMockServer();\n    });\n\n    after(() => {\n      stopMockServer();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', filePath])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain('The AsyncAPI document from ./test/fixtures/specification.yml has been successfully converted to AsyncAPI version 3.1.0!!');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', './test/fixtures/not-found.yml'])\n      .it('should throw error if file path is wrong', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal('error loading AsyncAPI document from file: ./test/fixtures/not-found.yml file does not exist.\\n');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', 'http://localhost:8080/dummySpec.yml'])\n      .it('works when url is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain('The AsyncAPI document from http://localhost:8080/dummySpec.yml has been successfully converted to AsyncAPI version 3.1.0!!');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', 'http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080'])\n      .it('should throw error when url is passed with proxyHost and proxyPort with invalid host ', (ctx, done) => {\n        expect(ctx.stdout).to.contain('');\n        expect(ctx.stderr).to.equal('error loading AsyncAPI document from url: Failed to download http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080.\\n');\n        done();\n      });\n  });\n\n  describe('with no arguments', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.setCurrentContext('home');\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert'])\n      .it('converts from current context', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`The AsyncAPI document from ${path.resolve(__dirname, '../fixtures/specification.yml')} has been successfully converted to AsyncAPI version 3.1.0!!\\n`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .do(() => {\n        testHelper.unsetCurrentContext();\n        testHelper.createDummyContextFile();\n      })\n      .command(['convert', '-f', 'asyncapi'])\n      .it('throws error message if no current context', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal('ContextError: No context is set as current, please set a current context.\\n');\n        done();\n      });\n  });\n\n  describe('with no context file', () => {\n    beforeEach(() => {\n      try {\n        testHelper.deleteDummyContextFile();\n      } catch (e: any) {\n        if (e.code !== 'ENOENT') {\n          throw e;\n        }\n      }\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', '-f', 'asyncapi'])\n      .it('throws error message if no context file exists', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal(`error locating AsyncAPI document: ${NO_CONTEXTS_SAVED}\\n`);\n        done();\n      });\n  });\n\n  describe('with target-version flag', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', filePath, '-f', 'asyncapi', '-t=2.3.0'])\n      .it('works when supported target-version is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain('asyncapi: 2.3.0');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', filePath, '-f', 'asyncapi', '-t=2.95.0'])\n      .it('should throw error if non-supported target-version is passed', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.contain('Error: Cannot convert');\n        done();\n      });\n  });\n\n  describe('with output flag', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', filePath, '-f', 'asyncapi', '-o=./test/fixtures/specification_output.yml'])\n      .it('works when .yml file is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`The AsyncAPI document from ${filePath} has been successfully converted to AsyncAPI version 3.1.0!!`);\n        expect(fs.existsSync('./test/fixtures/specification_output.yml')).to.equal(true);\n        expect(ctx.stderr).to.equal('');\n        fs.unlinkSync('./test/fixtures/specification_output.yml');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', JSONFilePath, '-f', 'asyncapi', '-o=./test/fixtures/specification_output.json'])\n      .it('works when .json file is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`The AsyncAPI document from ${JSONFilePath} has been successfully converted to AsyncAPI version 3.1.0!!`);\n        expect(fs.existsSync('./test/fixtures/specification_output.json')).to.equal(true);\n        expect(ctx.stderr).to.equal('');\n        fs.unlinkSync('./test/fixtures/specification_output.json');\n        done();\n      });\n  });\n\n  describe('with OpenAPI input', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', openAPIFilePath, '-f', 'openapi'])\n      .it('works when OpenAPI file path is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`The OpenAPI document from ${openAPIFilePath} has been successfully converted to AsyncAPI version 3.1.0!`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', openAPIFilePath, '-f', 'openapi', '-p=client'])\n      .it('works when OpenAPI file path is passed with client perspective', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`The OpenAPI document from ${openAPIFilePath} has been successfully converted to AsyncAPI version 3.1.0!`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', openAPIFilePath, '-f', 'openapi','-p=server'])\n      .it('works when OpenAPI file path is passed with server perspective', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`The OpenAPI document from ${openAPIFilePath} has been successfully converted to AsyncAPI version 3.1.0!`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', openAPIFilePath, '-f', 'openapi', '-p=invalid'])\n      .it('should throw error if invalid perspective is passed', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.contain('Error: Expected --perspective=invalid to be one of: client, server');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['convert', openAPIFilePath, '-f', 'openapi', '-o=./test/fixtures/openapi_converted_output.yml'])\n      .it('works when OpenAPI file is converted and output is saved', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`The OpenAPI document from ${openAPIFilePath} has been successfully converted to AsyncAPI version 3.1.0!`);\n        expect(fs.existsSync('./test/fixtures/openapi_converted_output.yml')).to.equal(true);\n        expect(ctx.stderr).to.equal('');\n        fs.unlinkSync('./test/fixtures/openapi_converted_output.yml');\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/integration/diff.test.ts",
    "content": " \nimport { expect, test } from '@oclif/test';\n\nconst asyncapiv3 = './test/fixtures/specification-v3.yml';\nconst asyncapiv3Diff = './test/fixtures/specification-v3-diff.yml';\nconst asyncapiv2 = './test/fixtures/specification.yml';\nconst noChangesJson = '\"{\\\\n  \\\\\"changes\\\\\": []\\\\n}\\\\n\"';\nconst breakingChangesJson = '\"[\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/servers/mosquitto/protocol\\\\\",\\\\n    \\\\\"before\\\\\": \\\\\"mqtt\\\\\",\\\\n    \\\\\"after\\\\\": \\\\\"http\\\\\",\\\\n    \\\\\"type\\\\\": \\\\\"breaking\\\\\"\\\\n  },\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/servers/mosquitto/url\\\\\",\\\\n    \\\\\"before\\\\\": \\\\\"mqtt://test.mosquitto.org\\\\\",\\\\n    \\\\\"after\\\\\": \\\\\"http://test.mosquitto.org\\\\\",\\\\n    \\\\\"type\\\\\": \\\\\"breaking\\\\\"\\\\n  }\\\\n]\\\\n\"';\nconst nonBreakingChangesJson = '\"[\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"add\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/channels/user~1signedup\\\\\",\\\\n    \\\\\"after\\\\\": {\\\\n      \\\\\"subscribe\\\\\": {\\\\n        \\\\\"message\\\\\": {\\\\n          \\\\\"payload\\\\\": {\\\\n            \\\\\"type\\\\\": \\\\\"object\\\\\",\\\\n            \\\\\"properties\\\\\": {\\\\n              \\\\\"displayName\\\\\": {\\\\n                \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                \\\\\"description\\\\\": \\\\\"Name of the user\\\\\",\\\\n                \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-2>\\\\\"\\\\n              },\\\\n              \\\\\"email\\\\\": {\\\\n                \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                \\\\\"format\\\\\": \\\\\"email\\\\\",\\\\n                \\\\\"description\\\\\": \\\\\"Email of the user\\\\\",\\\\n                \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-3>\\\\\"\\\\n              }\\\\n            },\\\\n            \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-1>\\\\\"\\\\n          },\\\\n          \\\\\"x-parser-message-name\\\\\": \\\\\"UserSignedUp\\\\\"\\\\n        }\\\\n      }\\\\n    },\\\\n    \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n  },\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/info/title\\\\\",\\\\n    \\\\\"before\\\\\": \\\\\"Streetlights API\\\\\",\\\\n    \\\\\"after\\\\\": \\\\\"Streetlights API V2\\\\\",\\\\n    \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n  },\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"add\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/components\\\\\",\\\\n    \\\\\"after\\\\\": {\\\\n      \\\\\"messages\\\\\": {\\\\n        \\\\\"UserSignedUp\\\\\": {\\\\n          \\\\\"payload\\\\\": {\\\\n            \\\\\"type\\\\\": \\\\\"object\\\\\",\\\\n            \\\\\"properties\\\\\": {\\\\n              \\\\\"displayName\\\\\": {\\\\n                \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                \\\\\"description\\\\\": \\\\\"Name of the user\\\\\",\\\\n                \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-2>\\\\\"\\\\n              },\\\\n              \\\\\"email\\\\\": {\\\\n                \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                \\\\\"format\\\\\": \\\\\"email\\\\\",\\\\n                \\\\\"description\\\\\": \\\\\"Email of the user\\\\\",\\\\n                \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-3>\\\\\"\\\\n              }\\\\n            },\\\\n            \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-1>\\\\\"\\\\n          },\\\\n          \\\\\"x-parser-message-name\\\\\": \\\\\"UserSignedUp\\\\\"\\\\n        }\\\\n      }\\\\n    },\\\\n    \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n  }\\\\n]\\\\n\"';\nconst unclassifiedChangesJson = '\"[\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/x-parser-schema-id\\\\\",\\\\n    \\\\\"before\\\\\": \\\\\"<anonymous-schema-1>\\\\\",\\\\n    \\\\\"after\\\\\": \\\\\"<anonymous-schema-4>\\\\\",\\\\n    \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n  },\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/sentAt/x-parser-schema-id\\\\\",\\\\n    \\\\\"before\\\\\": \\\\\"<anonymous-schema-4>\\\\\",\\\\n    \\\\\"after\\\\\": \\\\\"<anonymous-schema-7>\\\\\",\\\\n    \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n  },\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/lumens/x-parser-schema-id\\\\\",\\\\n    \\\\\"before\\\\\": \\\\\"<anonymous-schema-3>\\\\\",\\\\n    \\\\\"after\\\\\": \\\\\"<anonymous-schema-6>\\\\\",\\\\n    \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n  },\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/id/x-parser-schema-id\\\\\",\\\\n    \\\\\"before\\\\\": \\\\\"<anonymous-schema-2>\\\\\",\\\\n    \\\\\"after\\\\\": \\\\\"<anonymous-schema-5>\\\\\",\\\\n    \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n  },\\\\n  {\\\\n    \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n    \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/id/minimum\\\\\",\\\\n    \\\\\"before\\\\\": 0,\\\\n    \\\\\"after\\\\\": 1,\\\\n    \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n  }\\\\n]\\\\n\"';\nconst commonJsonOutput = '{\\\\n  \\\\\"changes\\\\\": [\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-1>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-4>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/sentAt/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-4>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-7>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/lumens/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-3>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-6>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/id/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-2>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-5>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/id/minimum\\\\\",\\\\n      \\\\\"before\\\\\": 0,\\\\n      \\\\\"after\\\\\": 1,\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"add\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/user~1signedup\\\\\",\\\\n      \\\\\"after\\\\\": {\\\\n        \\\\\"subscribe\\\\\": {\\\\n          \\\\\"message\\\\\": {\\\\n            \\\\\"payload\\\\\": {\\\\n              \\\\\"type\\\\\": \\\\\"object\\\\\",\\\\n              \\\\\"properties\\\\\": {\\\\n                \\\\\"displayName\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Name of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-2>\\\\\"\\\\n                },\\\\n                \\\\\"email\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"format\\\\\": \\\\\"email\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Email of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-3>\\\\\"\\\\n                }\\\\n              },\\\\n              \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-1>\\\\\"\\\\n            },\\\\n            \\\\\"x-parser-message-name\\\\\": \\\\\"UserSignedUp\\\\\"\\\\n          }\\\\n        }\\\\n      },\\\\n      \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/servers/mosquitto/protocol\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"mqtt\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"http\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/servers/mosquitto/url\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"mqtt://test.mosquitto.org\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"http://test.mosquitto.org\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/info/title\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"Streetlights API\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"Streetlights API V2\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"add\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/components\\\\\",\\\\n      \\\\\"after\\\\\": {\\\\n        \\\\\"messages\\\\\": {\\\\n          \\\\\"UserSignedUp\\\\\": {\\\\n            \\\\\"payload\\\\\": {\\\\n              \\\\\"type\\\\\": \\\\\"object\\\\\",\\\\n              \\\\\"properties\\\\\": {\\\\n                \\\\\"displayName\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Name of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-2>\\\\\"\\\\n                },\\\\n                \\\\\"email\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"format\\\\\": \\\\\"email\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Email of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-3>\\\\\"\\\\n                }\\\\n              },\\\\n              \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-1>\\\\\"\\\\n            },\\\\n            \\\\\"x-parser-message-name\\\\\": \\\\\"UserSignedUp\\\\\"\\\\n          }\\\\n        }\\\\n      },\\\\n      \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n    }\\\\n  ]\\\\n}\\\\n';\nconst customJsonOutput = '\"{\\\\n  \\\\\"changes\\\\\": [\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-1>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-4>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/sentAt/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-4>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-7>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/lumens/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-3>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-6>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/id/x-parser-schema-id\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"<anonymous-schema-2>\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"<anonymous-schema-5>\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/light~1measured/publish/message/payload/properties/id/minimum\\\\\",\\\\n      \\\\\"before\\\\\": 0,\\\\n      \\\\\"after\\\\\": 1,\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"add\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/user~1signedup\\\\\",\\\\n      \\\\\"after\\\\\": {\\\\n        \\\\\"subscribe\\\\\": {\\\\n          \\\\\"message\\\\\": {\\\\n            \\\\\"payload\\\\\": {\\\\n              \\\\\"type\\\\\": \\\\\"object\\\\\",\\\\n              \\\\\"properties\\\\\": {\\\\n                \\\\\"displayName\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Name of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-2>\\\\\"\\\\n                },\\\\n                \\\\\"email\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"format\\\\\": \\\\\"email\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Email of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-3>\\\\\"\\\\n                }\\\\n              },\\\\n              \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-1>\\\\\"\\\\n            },\\\\n            \\\\\"x-parser-message-name\\\\\": \\\\\"UserSignedUp\\\\\"\\\\n          }\\\\n        }\\\\n      },\\\\n      \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/servers/mosquitto/protocol\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"mqtt\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"http\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/servers/mosquitto/url\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"mqtt://test.mosquitto.org\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"http://test.mosquitto.org\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/info/title\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"Streetlights API\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"Streetlights API V2\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"add\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/components\\\\\",\\\\n      \\\\\"after\\\\\": {\\\\n        \\\\\"messages\\\\\": {\\\\n          \\\\\"UserSignedUp\\\\\": {\\\\n            \\\\\"payload\\\\\": {\\\\n              \\\\\"type\\\\\": \\\\\"object\\\\\",\\\\n              \\\\\"properties\\\\\": {\\\\n                \\\\\"displayName\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Name of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-2>\\\\\"\\\\n                },\\\\n                \\\\\"email\\\\\": {\\\\n                  \\\\\"type\\\\\": \\\\\"string\\\\\",\\\\n                  \\\\\"format\\\\\": \\\\\"email\\\\\",\\\\n                  \\\\\"description\\\\\": \\\\\"Email of the user\\\\\",\\\\n                  \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-3>\\\\\"\\\\n                }\\\\n              },\\\\n              \\\\\"x-parser-schema-id\\\\\": \\\\\"<anonymous-schema-1>\\\\\"\\\\n            },\\\\n            \\\\\"x-parser-message-name\\\\\": \\\\\"UserSignedUp\\\\\"\\\\n          }\\\\n        }\\\\n      },\\\\n      \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n    }\\\\n  ]\\\\n}\\\\n\"';\nconst commonJsonOutputV3 = '\"{\\\\n  \\\\\"changes\\\\\": [\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/operations/smartylighting~1streetlights~11~10~1action~1{streetlightId}~1turn~1on.subscribe/channel/servers/1/pathname\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"/some/path\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"/some/path-name\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/operations/smartylighting~1streetlights~11~10~1action~1{streetlightId}~1turn~1on.subscribe/channel/servers/0/variables/port/default\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"1883\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"8883\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/operations/receiveTurnOn/channel/servers/1/pathname\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"/some/path\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"/some/path-name\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/operations/receiveTurnOn/channel/servers/0/variables/port/default\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"1883\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"8883\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/operations/receiveLightMeasured/channel/servers/0/pathname\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"/some/path\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"/some/path-name\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/turnOn/servers/1/pathname\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"/some/path\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"/some/path-name\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/turnOn/servers/0/variables/port/default\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"1883\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"8883\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/channels/lightingMeasured/servers/0/pathname\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"/some/path\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"/some/path-name\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"unclassified\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/servers/production/pathname\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"/some/path\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"/some/path-name\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/servers/default/variables/port/default\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"1883\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"8883\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"breaking\\\\\"\\\\n    },\\\\n    {\\\\n      \\\\\"action\\\\\": \\\\\"edit\\\\\",\\\\n      \\\\\"path\\\\\": \\\\\"/info/title\\\\\",\\\\n      \\\\\"before\\\\\": \\\\\"AsyncAPI Sample App\\\\\",\\\\n      \\\\\"after\\\\\": \\\\\"AsyncAPI App\\\\\",\\\\n      \\\\\"type\\\\\": \\\\\"non-breaking\\\\\"\\\\n    }\\\\n  ]\\\\n}\\\\n\"';\n\nconst noChangesYaml = '\"changes: []\\\\n\\\\n\"';\nconst commonYamlOutput = '\"changes:\\\\n  - action: edit\\\\n    path: /channels/light~1measured/publish/message/payload/x-parser-schema-id\\\\n    before: <anonymous-schema-1>\\\\n    after: <anonymous-schema-4>\\\\n    type: unclassified\\\\n  - action: edit\\\\n    path: >-\\\\n      /channels/light~1measured/publish/message/payload/properties/sentAt/x-parser-schema-id\\\\n    before: <anonymous-schema-4>\\\\n    after: <anonymous-schema-7>\\\\n    type: unclassified\\\\n  - action: edit\\\\n    path: >-\\\\n      /channels/light~1measured/publish/message/payload/properties/lumens/x-parser-schema-id\\\\n    before: <anonymous-schema-3>\\\\n    after: <anonymous-schema-6>\\\\n    type: unclassified\\\\n  - action: edit\\\\n    path: >-\\\\n      /channels/light~1measured/publish/message/payload/properties/id/x-parser-schema-id\\\\n    before: <anonymous-schema-2>\\\\n    after: <anonymous-schema-5>\\\\n    type: unclassified\\\\n  - action: edit\\\\n    path: /channels/light~1measured/publish/message/payload/properties/id/minimum\\\\n    before: 0\\\\n    after: 1\\\\n    type: unclassified\\\\n  - action: add\\\\n    path: /channels/user~1signedup\\\\n    after:\\\\n      subscribe:\\\\n        message:\\\\n          payload:\\\\n            type: object\\\\n            properties:\\\\n              displayName:\\\\n                type: string\\\\n                description: Name of the user\\\\n                x-parser-schema-id: <anonymous-schema-2>\\\\n              email:\\\\n                type: string\\\\n                format: email\\\\n                description: Email of the user\\\\n                x-parser-schema-id: <anonymous-schema-3>\\\\n            x-parser-schema-id: <anonymous-schema-1>\\\\n          x-parser-message-name: UserSignedUp\\\\n    type: non-breaking\\\\n  - action: edit\\\\n    path: /servers/mosquitto/protocol\\\\n    before: mqtt\\\\n    after: http\\\\n    type: breaking\\\\n  - action: edit\\\\n    path: /servers/mosquitto/url\\\\n    before: mqtt://test.mosquitto.org\\\\n    after: http://test.mosquitto.org\\\\n    type: breaking\\\\n  - action: edit\\\\n    path: /info/title\\\\n    before: Streetlights API\\\\n    after: Streetlights API V2\\\\n    type: non-breaking\\\\n  - action: add\\\\n    path: /components\\\\n    after:\\\\n      messages:\\\\n        UserSignedUp:\\\\n          payload:\\\\n            type: object\\\\n            properties:\\\\n              displayName:\\\\n                type: string\\\\n                description: Name of the user\\\\n                x-parser-schema-id: <anonymous-schema-2>\\\\n              email:\\\\n                type: string\\\\n                format: email\\\\n                description: Email of the user\\\\n                x-parser-schema-id: <anonymous-schema-3>\\\\n            x-parser-schema-id: <anonymous-schema-1>\\\\n          x-parser-message-name: UserSignedUp\\\\n    type: non-breaking\\\\n\\\\n\"';\n\n// eslint-disable-next-line quotes\nconst markdownJsonOutput = \"\\\"## Unclassified\\\\n\\\\n\\\\n - **Path**: `/channels/light~1measured/publish/message/payload/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-1>\\\\n     - **After**: <anonymous-schema-4>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/sentAt/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-4>\\\\n     - **After**: <anonymous-schema-7>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/lumens/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-3>\\\\n     - **After**: <anonymous-schema-6>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/id/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-2>\\\\n     - **After**: <anonymous-schema-5>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/id/minimum`\\\\n     - **Action**: edit\\\\n     - **Before**: 0\\\\n     - **After**: 1\\\\n    \\\\n\\\\n## Non-breaking\\\\n\\\\n\\\\n - **Path**: `/channels/user~1signedup`\\\\n     - **Action**: add\\\\n     - <details>\\\\n            <summary> After </summary>\\\\n            \\\\n        ```json\\\\n        {\\\\n          \\\\\\\"subscribe\\\\\\\": {\\\\n            \\\\\\\"message\\\\\\\": {\\\\n              \\\\\\\"payload\\\\\\\": {\\\\n                \\\\\\\"type\\\\\\\": \\\\\\\"object\\\\\\\",\\\\n                \\\\\\\"properties\\\\\\\": {\\\\n                  \\\\\\\"displayName\\\\\\\": {\\\\n                    \\\\\\\"type\\\\\\\": \\\\\\\"string\\\\\\\",\\\\n                    \\\\\\\"description\\\\\\\": \\\\\\\"Name of the user\\\\\\\",\\\\n                    \\\\\\\"x-parser-schema-id\\\\\\\": \\\\\\\"<anonymous-schema-2>\\\\\\\"\\\\n                  },\\\\n                  \\\\\\\"email\\\\\\\": {\\\\n                    \\\\\\\"type\\\\\\\": \\\\\\\"string\\\\\\\",\\\\n                    \\\\\\\"format\\\\\\\": \\\\\\\"email\\\\\\\",\\\\n                    \\\\\\\"description\\\\\\\": \\\\\\\"Email of the user\\\\\\\",\\\\n                    \\\\\\\"x-parser-schema-id\\\\\\\": \\\\\\\"<anonymous-schema-3>\\\\\\\"\\\\n                  }\\\\n                },\\\\n                \\\\\\\"x-parser-schema-id\\\\\\\": \\\\\\\"<anonymous-schema-1>\\\\\\\"\\\\n              },\\\\n              \\\\\\\"x-parser-message-name\\\\\\\": \\\\\\\"UserSignedUp\\\\\\\"\\\\n            }\\\\n          }\\\\n        }\\\\n        ```            \\\\n        </details>  \\\\n        \\\\n    \\\\n - **Path**: `/info/title`\\\\n     - **Action**: edit\\\\n     - **Before**: Streetlights API\\\\n     - **After**: Streetlights API V2\\\\n    \\\\n - **Path**: `/components`\\\\n     - **Action**: add\\\\n     - <details>\\\\n            <summary> After </summary>\\\\n            \\\\n        ```json\\\\n        {\\\\n          \\\\\\\"messages\\\\\\\": {\\\\n            \\\\\\\"UserSignedUp\\\\\\\": {\\\\n              \\\\\\\"payload\\\\\\\": {\\\\n                \\\\\\\"type\\\\\\\": \\\\\\\"object\\\\\\\",\\\\n                \\\\\\\"properties\\\\\\\": {\\\\n                  \\\\\\\"displayName\\\\\\\": {\\\\n                    \\\\\\\"type\\\\\\\": \\\\\\\"string\\\\\\\",\\\\n                    \\\\\\\"description\\\\\\\": \\\\\\\"Name of the user\\\\\\\",\\\\n                    \\\\\\\"x-parser-schema-id\\\\\\\": \\\\\\\"<anonymous-schema-2>\\\\\\\"\\\\n                  },\\\\n                  \\\\\\\"email\\\\\\\": {\\\\n                    \\\\\\\"type\\\\\\\": \\\\\\\"string\\\\\\\",\\\\n                    \\\\\\\"format\\\\\\\": \\\\\\\"email\\\\\\\",\\\\n                    \\\\\\\"description\\\\\\\": \\\\\\\"Email of the user\\\\\\\",\\\\n                    \\\\\\\"x-parser-schema-id\\\\\\\": \\\\\\\"<anonymous-schema-3>\\\\\\\"\\\\n                  }\\\\n                },\\\\n                \\\\\\\"x-parser-schema-id\\\\\\\": \\\\\\\"<anonymous-schema-1>\\\\\\\"\\\\n              },\\\\n              \\\\\\\"x-parser-message-name\\\\\\\": \\\\\\\"UserSignedUp\\\\\\\"\\\\n            }\\\\n          }\\\\n        }\\\\n        ```            \\\\n        </details>  \\\\n        \\\\n    \\\\n\\\\n## Breaking\\\\n\\\\n\\\\n - **Path**: `/servers/mosquitto/protocol`\\\\n     - **Action**: edit\\\\n     - **Before**: mqtt\\\\n     - **After**: http\\\\n    \\\\n - **Path**: `/servers/mosquitto/url`\\\\n     - **Action**: edit\\\\n     - **Before**: mqtt://test.mosquitto.org\\\\n     - **After**: http://test.mosquitto.org\\\\n    \\\\n\\\\n\\\"\";\nconst markdownYamlOutput = '\"## Unclassified\\\\n\\\\n\\\\n - **Path**: `/channels/light~1measured/publish/message/payload/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-1>\\\\n     - **After**: <anonymous-schema-4>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/sentAt/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-4>\\\\n     - **After**: <anonymous-schema-7>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/lumens/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-3>\\\\n     - **After**: <anonymous-schema-6>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/id/x-parser-schema-id`\\\\n     - **Action**: edit\\\\n     - **Before**: <anonymous-schema-2>\\\\n     - **After**: <anonymous-schema-5>\\\\n    \\\\n - **Path**: `/channels/light~1measured/publish/message/payload/properties/id/minimum`\\\\n     - **Action**: edit\\\\n     - **Before**: 0\\\\n     - **After**: 1\\\\n    \\\\n\\\\n## Non-breaking\\\\n\\\\n\\\\n - **Path**: `/channels/user~1signedup`\\\\n     - **Action**: add\\\\n     - <details>\\\\n            <summary> After </summary>\\\\n            \\\\n        ```yaml\\\\n        subscribe:\\\\n          message:\\\\n            payload:\\\\n              type: object\\\\n              properties:\\\\n                displayName:\\\\n                  type: string\\\\n                  description: Name of the user\\\\n                  x-parser-schema-id: <anonymous-schema-2>\\\\n                email:\\\\n                  type: string\\\\n                  format: email\\\\n                  description: Email of the user\\\\n                  x-parser-schema-id: <anonymous-schema-3>\\\\n              x-parser-schema-id: <anonymous-schema-1>\\\\n            x-parser-message-name: UserSignedUp\\\\n        \\\\n        ```            \\\\n        </details>  \\\\n        \\\\n    \\\\n - **Path**: `/info/title`\\\\n     - **Action**: edit\\\\n     - **Before**: Streetlights API\\\\n     - **After**: Streetlights API V2\\\\n    \\\\n - **Path**: `/components`\\\\n     - **Action**: add\\\\n     - <details>\\\\n            <summary> After </summary>\\\\n            \\\\n        ```yaml\\\\n        messages:\\\\n          UserSignedUp:\\\\n            payload:\\\\n              type: object\\\\n              properties:\\\\n                displayName:\\\\n                  type: string\\\\n                  description: Name of the user\\\\n                  x-parser-schema-id: <anonymous-schema-2>\\\\n                email:\\\\n                  type: string\\\\n                  format: email\\\\n                  description: Email of the user\\\\n                  x-parser-schema-id: <anonymous-schema-3>\\\\n              x-parser-schema-id: <anonymous-schema-1>\\\\n            x-parser-message-name: UserSignedUp\\\\n        \\\\n        ```            \\\\n        </details>  \\\\n        \\\\n    \\\\n\\\\n## Breaking\\\\n\\\\n\\\\n - **Path**: `/servers/mosquitto/protocol`\\\\n     - **Action**: edit\\\\n     - **Before**: mqtt\\\\n     - **After**: http\\\\n    \\\\n - **Path**: `/servers/mosquitto/url`\\\\n     - **Action**: edit\\\\n     - **Before**: mqtt://test.mosquitto.org\\\\n     - **After**: http://test.mosquitto.org\\\\n    \\\\n\\\\n\"';\n\ndescribe('diff', () => {\n  describe('comparing AsyncAPI v2 and v3 documents', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['diff', asyncapiv3, asyncapiv2])\n      .it('give error when different AsyncAPI version', (ctx, done) => {\n        expect(ctx.stderr).to.contain('TypeError: diff between different AsyncAPI version is not allowed\\n');\n        expect(ctx.stdout).to.equal('');\n        done();\n      });\n  });\n  describe('should handle AsyncAPI v3 document correctly', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['diff', asyncapiv3, asyncapiv3Diff, '--no-error', '--format=json'])\n      .it('give error when different AsyncAPI version', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(commonJsonOutputV3);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n  describe('with file paths, and there are no difference between the files', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['diff', './test/fixtures/specification.yml', './test/fixtures/specification.yml', '--format=json'])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(noChangesJson);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('yaml output: with file paths, and there are no difference between the files', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['diff', './test/fixtures/specification.yml', './test/fixtures/specification.yml'])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(noChangesYaml);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with file paths, and getting all changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--type=all',\n        '--format=json',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(`\"${commonJsonOutput}\"`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with file paths, and getting breaking changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--type=breaking',\n        '--format=json',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(breakingChangesJson);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with file paths, and getting non-breaking changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--type=non-breaking',\n        '--format=json',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(nonBreakingChangesJson);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with file paths, and getting unclassified changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--type=unclassified',\n        '--format=json',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(unclassifiedChangesJson);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with file paths, and getting all changes, passing flag', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--format=json',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(`\"${commonJsonOutput}\"`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('YAML output, getting all changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--type=all',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(commonYamlOutput);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('should show error on breaking changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(commonYamlOutput);\n        expect(ctx.stderr).to.equal('DiffBreakingChangeError: Breaking changes detected\\n');\n        done();\n      });\n  });\n\n  describe('Markdown output with subtype as json, getting all changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--format=md',\n        '--markdownSubtype=json',\n        '--type=all',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(markdownJsonOutput);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('Markdown output with subtype as yaml, getting all changes', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--format=md',\n        '--markdownSubtype=yaml',\n        '--type=all',\n        '--no-error',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(markdownYamlOutput);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('Other output with markdownSubtype flag provided, check for warning', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--format=json',\n        '--markdownSubtype=yaml',\n      ])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(\n          `\"Warning: The given markdownSubtype flag will not work with the given format.\\\\nProvided flag markdownSubtype: yaml\\\\n${commonJsonOutput}\"`\n        );\n        expect(ctx.stderr).to.equal('DiffBreakingChangeError: Breaking changes detected\\n');\n        done();\n      });\n  });\n\n  describe('with logging diagnostics', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['diff', './test/fixtures/specification.yml', './test/fixtures/specification.yml', '--format=json', '--log-diagnostics'])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(ctx.stdout).to.match(/File .\\/test\\/fixtures\\/specification.yml is valid but has \\(itself and\\/or referenced documents\\) governance issues./);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  // passing override files actually overrides standard for other tests that come below this test case\n  // thus, this test case should always be at last\n  describe('with custom standard file', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--overrides=./test/fixtures/overrides.json',\n        '--format=json',\n        '--no-error',\n      ])\n      .it((ctx, done) => {\n        expect(JSON.stringify(ctx.stdout)).to.equal(customJsonOutput);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('should throw error for invalid override path', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--overrides=./overrides-wrong.json',\n        '--format=json',\n      ])\n      .it((ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal(\n          'DiffOverrideFileError: Override file not found\\n'\n        );\n        done();\n      });\n  });\n\n  describe('should throw error for invalid override json', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'diff',\n        './test/fixtures/asyncapi_v1.yml',\n        './test/fixtures/asyncapi_v2.yml',\n        '--overrides=./test/fixtures/invalid-overrides.json',\n      ])\n      .it((ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal(\n          'DiffOverrideJSONError: Provided override file is not a valid JSON file\\n'\n        );\n        done();\n      });\n  });\n\n  describe('save-output flag tests', () => {\n    describe('JSON format with save-output flag', () => {\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/specification.yml',\n          './test/fixtures/specification.yml',\n          '--format=json',\n          '--type=all',\n          '--save-output=./test/fixtures/output-test.json',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test.json');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write JSON output to file when no changes', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test.json');\n          \n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test.json', 'utf8');\n          const actualContent = JSON.parse(fileContent);\n          expect(actualContent).to.deep.equal({ changes: [] });\n        });\n\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/asyncapi_v1.yml',\n          './test/fixtures/asyncapi_v2.yml',\n          '--format=json',\n          '--type=all',\n          '--save-output=./test/fixtures/output-test-all.json',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test-all.json');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write all changes to JSON file', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test-all.json');\n          \n          // Read and verify file content\n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test-all.json', 'utf8');\n          const actualContent = JSON.parse(fileContent);\n          expect(actualContent).to.have.property('changes');\n          expect(actualContent.changes).to.be.an('array');\n          expect(actualContent.changes.length).to.be.greaterThan(0);\n          const types = actualContent.changes.map((c: any) => c.type);\n          expect(types).to.include('breaking');\n          expect(types).to.include('non-breaking');\n          expect(types).to.include('unclassified');\n        });\n\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/asyncapi_v1.yml',\n          './test/fixtures/asyncapi_v2.yml',\n          '--format=json',\n          '--type=breaking',\n          '--save-output=./test/fixtures/output-test-breaking.json',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test-breaking.json');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write breaking changes to JSON file', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test-breaking.json');\n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test-breaking.json', 'utf8');\n          const actualContent = JSON.parse(fileContent);\n          expect(actualContent).to.be.an('array');\n          expect(actualContent.length).to.be.greaterThan(0);\n          for (const change of actualContent) {\n            expect(change.type).to.equal('breaking');\n          }\n        });\n\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/asyncapi_v1.yml',\n          './test/fixtures/asyncapi_v2.yml',\n          '--format=json',\n          '--type=non-breaking',\n          '--save-output=./test/fixtures/output-test-non-breaking.json',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test-non-breaking.json');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write non-breaking changes to JSON file', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test-non-breaking.json');\n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test-non-breaking.json', 'utf8');\n          const actualContent = JSON.parse(fileContent);\n          expect(actualContent).to.be.an('array');\n          expect(actualContent.length).to.be.greaterThan(0);\n          for (const change of actualContent) {\n            expect(change.type).to.equal('non-breaking');\n          }\n        });\n\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/asyncapi_v1.yml',\n          './test/fixtures/asyncapi_v2.yml',\n          '--format=json',\n          '--type=unclassified',\n          '--save-output=./test/fixtures/output-test-unclassified.json',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test-unclassified.json');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write unclassified changes to JSON file', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test-unclassified.json');\n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test-unclassified.json', 'utf8');\n          const actualContent = JSON.parse(fileContent);\n          expect(actualContent).to.be.an('array');\n          expect(actualContent.length).to.be.greaterThan(0);\n          for (const change of actualContent) {\n            expect(change.type).to.equal('unclassified');\n          }\n        });\n    });\n\n    describe('YAML format with save-output flag', () => {\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/specification.yml',\n          './test/fixtures/specification.yml',\n          '--format=yaml',\n          '--type=all',\n          '--save-output=./test/fixtures/output-test.yaml',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test.yaml');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write YAML output to file when no changes', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test.yaml');\n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test.yaml', 'utf8');\n          expect(fileContent).to.equal('changes: []\\n');\n        });\n\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/asyncapi_v1.yml',\n          './test/fixtures/asyncapi_v2.yml',\n          '--format=yaml',\n          '--type=all',\n          '--save-output=./test/fixtures/output-test-all.yaml',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test-all.yaml');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write all changes to YAML file', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test-all.yaml');\n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test-all.yaml', 'utf8');\n          expect(fileContent).to.include('changes:');\n          expect(fileContent).to.include('type: breaking');\n          expect(fileContent).to.include('type: non-breaking');\n          expect(fileContent).to.include('type: unclassified');\n        });\n\n      test\n        .stderr()\n        .stdout()\n        .command([\n          'diff',\n          './test/fixtures/asyncapi_v1.yml',\n          './test/fixtures/asyncapi_v2.yml',\n          '--format=yml',\n          '--type=breaking',\n          '--save-output=./test/fixtures/output-test-breaking.yml',\n          '--no-error',\n        ])\n        .finally(async () => {\n          const fs = await import('fs');\n          try {\n            await fs.promises.unlink('./test/fixtures/output-test-breaking.yml');\n          } catch (err) {\n            // Ignore error if file doesn't exist\n          }\n        })\n        .it('should write breaking changes to YML file', async (ctx) => {\n          const fs = await import('fs');\n          expect(ctx.stdout).to.contain('Output successfully written to: ./test/fixtures/output-test-breaking.yml');\n          const fileContent = await fs.promises.readFile('./test/fixtures/output-test-breaking.yml', 'utf8');\n          expect(fileContent).to.include('action: edit');\n          expect(fileContent).to.include('type: breaking');\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/format.test.ts",
    "content": "import { test } from '@oclif/test';\nimport { NO_CONTEXTS_SAVED } from '../../src/errors/context-error';\nimport TestHelper, { createMockServer, stopMockServer } from '../helpers/index';\nimport { expect } from '@oclif/test';\n\nconst testHelper = new TestHelper();\nconst yamlFilePath = './test/fixtures/specification.yml';\nconst JSONFilePath = './test/fixtures/specification.json';\nconst convYmlFilePath = './test/fixtures/specification-conv.yml';\nconst convYamlFilePath = './test/fixtures/specification-conv.yaml';\nconst convJSONFilePath = './test/fixtures/specification-conv.json';\n\ndescribe('format', () => {\n  describe('with file paths', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    before(() => {\n      createMockServer();\n    });\n\n    after(() => {\n      stopMockServer();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', yamlFilePath, '-f', 'json'])\n      .it('should log formatted content if no -o is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'succesfully logged after formatting to json ✅',\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', './test/fixtures/not-found.yml', '-f', 'json'])\n      .it('should throw error if file path is wrong', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal(\n          'error loading AsyncAPI document from file: ./test/fixtures/not-found.yml file does not exist.\\n',\n        );\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', 'http://localhost:8080/dummySpec.yml', '-f', 'json'])\n      .it('works when url is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'succesfully logged after formatting to json ✅',\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with no arguments or required flags', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.setCurrentContext('home');\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', yamlFilePath])\n      .it('should default to json without -f flag', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'succesfully logged after formatting to json ✅',\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', '-f', 'json'])\n      .it('converts from current context', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'succesfully logged after formatting to json ✅',\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .do(() => {\n        testHelper.unsetCurrentContext();\n        testHelper.createDummyContextFile();\n      })\n      .command(['format', '-f', 'json'])\n      .it('throws error message if no current context', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal(\n          'ContextError: No context is set as current, please set a current context.\\n',\n        );\n        done();\n      });\n  });\n\n  describe('with no spec file', () => {\n    beforeEach(() => {\n      try {\n        testHelper.deleteDummyContextFile();\n      } catch (e: any) {\n        if (e.code !== 'ENOENT') {\n          throw e;\n        }\n      }\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', '-f', 'json'])\n      .it('throws error message if no spec file exists', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal(\n          `error locating AsyncAPI document: ${NO_CONTEXTS_SAVED}\\n`,\n        );\n        done();\n      });\n  });\n\n  describe('format with output flag', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', yamlFilePath, '-f', 'json', '-o', convJSONFilePath])\n      .it('create file yaml -> json', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          `succesfully formatted to json at ${convJSONFilePath} ✅`,\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', JSONFilePath, '-f', 'yaml', '-o', convYamlFilePath])\n      .it('create file json -> yaml', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          `succesfully formatted to yaml at ${convYamlFilePath} ✅`,\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', JSONFilePath, '-f', 'yml', '-o', convYmlFilePath])\n      .it('create file json -> yml', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          `succesfully formatted to yml at ${convYmlFilePath} ✅`,\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('invalid or redundant format conversions', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['format', yamlFilePath, '-f', 'yaml'])\n      .it('yaml -> yaml', (ctx, done) => {\n        expect(ctx.stderr).to.contain('Your document is already a YAML');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['format', JSONFilePath, '-f', 'json'])\n      .it('json -> json', (ctx, done) => {\n        expect(ctx.stderr).to.contain('Your document is already a JSON');\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/integration/generate/__snapshots__/models.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`models for TypeScript works when tsIncludeComments is set 1`] = `\n\"Successfully generated the following models: \n## Model name: AnonymousSchema_1\n\nclass AnonymousSchema_1 {\n  /**\n   * Name of the user\n   */\n  private _displayName?: string;\n  /**\n   * Email of the user\n   */\n  private _email?: string;\n  private _additionalProperties?: Map<string, any>;\n\n  constructor(input: {\n    displayName?: string,\n    email?: string,\n    additionalProperties?: Map<string, any>,\n  }) {\n    this._displayName = input.displayName;\n    this._email = input.email;\n    this._additionalProperties = input.additionalProperties;\n  }\n\n  get displayName(): string | undefined { return this._displayName; }\n  set displayName(displayName: string | undefined) { this._displayName = displayName; }\n\n  get email(): string | undefined { return this._email; }\n  set email(email: string | undefined) { this._email = email; }\n\n  get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }\n  set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }\n}\nexport default AnonymousSchema_1;\n\n\n\"\n`;\n\nexports[`models works when generating in memory 1`] = `\n\"Successfully generated the following models: \n## Model name: AnonymousSchema_1\n\nclass AnonymousSchema_1 {\n  private _displayName?: string;\n  private _email?: string;\n  private _additionalProperties?: Map<string, any>;\n\n  constructor(input: {\n    displayName?: string,\n    email?: string,\n    additionalProperties?: Map<string, any>,\n  }) {\n    this._displayName = input.displayName;\n    this._email = input.email;\n    this._additionalProperties = input.additionalProperties;\n  }\n\n  get displayName(): string | undefined { return this._displayName; }\n  set displayName(displayName: string | undefined) { this._displayName = displayName; }\n\n  get email(): string | undefined { return this._email; }\n  set email(email: string | undefined) { this._email = email; }\n\n  get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }\n  set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }\n}\nexport default AnonymousSchema_1;\n\n\n\"\n`;\n\nexports[`models works with remote AsyncAPI files 1`] = `\n\"Successfully generated the following models: \n## Model name: LightMeasuredPayload\n\nclass LightMeasuredPayload {\n  private _lumens?: number;\n  private _sentAt?: string;\n  private _additionalProperties?: Map<string, any>;\n\n  constructor(input: {\n    lumens?: number,\n    sentAt?: string,\n    additionalProperties?: Map<string, any>,\n  }) {\n    this._lumens = input.lumens;\n    this._sentAt = input.sentAt;\n    this._additionalProperties = input.additionalProperties;\n  }\n\n  get lumens(): number | undefined { return this._lumens; }\n  set lumens(lumens: number | undefined) { this._lumens = lumens; }\n\n  get sentAt(): string | undefined { return this._sentAt; }\n  set sentAt(sentAt: string | undefined) { this._sentAt = sentAt; }\n\n  get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }\n  set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }\n}\nexport default LightMeasuredPayload;\n\n\n\n## Model name: TurnOnOffPayload\nimport AnonymousSchema_6 from './AnonymousSchema_6';\nclass TurnOnOffPayload {\n  private _command?: AnonymousSchema_6;\n  private _sentAt?: string;\n  private _additionalProperties?: Map<string, any>;\n\n  constructor(input: {\n    command?: AnonymousSchema_6,\n    sentAt?: string,\n    additionalProperties?: Map<string, any>,\n  }) {\n    this._command = input.command;\n    this._sentAt = input.sentAt;\n    this._additionalProperties = input.additionalProperties;\n  }\n\n  get command(): AnonymousSchema_6 | undefined { return this._command; }\n  set command(command: AnonymousSchema_6 | undefined) { this._command = command; }\n\n  get sentAt(): string | undefined { return this._sentAt; }\n  set sentAt(sentAt: string | undefined) { this._sentAt = sentAt; }\n\n  get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }\n  set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }\n}\nexport default TurnOnOffPayload;\n\n\n\n## Model name: AnonymousSchema_6\n\nenum AnonymousSchema_6 {\n  ON = \"on\",\n  OFF = \"off\",\n}\nexport default AnonymousSchema_6;\n\n\n\n## Model name: DimLightPayload\n\nclass DimLightPayload {\n  private _percentage?: number;\n  private _sentAt?: string;\n  private _additionalProperties?: Map<string, any>;\n\n  constructor(input: {\n    percentage?: number,\n    sentAt?: string,\n    additionalProperties?: Map<string, any>,\n  }) {\n    this._percentage = input.percentage;\n    this._sentAt = input.sentAt;\n    this._additionalProperties = input.additionalProperties;\n  }\n\n  get percentage(): number | undefined { return this._percentage; }\n  set percentage(percentage: number | undefined) { this._percentage = percentage; }\n\n  get sentAt(): string | undefined { return this._sentAt; }\n  set sentAt(sentAt: string | undefined) { this._sentAt = sentAt; }\n\n  get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }\n  set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }\n}\nexport default DimLightPayload;\n\n\n\"\n`;\n"
  },
  {
    "path": "test/integration/generate/client.test.ts",
    "content": "import { test } from '@oclif/test';\nimport { rimrafSync } from 'rimraf';\nimport { expect } from '@oclif/test';\n\nfunction cleanup(filepath: string) {\n  rimrafSync(filepath);\n}\n\ndescribe('client', () => {\n  after(() => {\n    cleanup('./test/docs');\n  });\n\n  describe('should be able to generate client', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'generate:client',\n        'javascript',\n        './test/fixtures/specification-v3.yml',\n        '-pserver=default',\n        '--output=./test/docs/test-output',\n        '--force-write',\n        '--no-interactive',\n      ])\n      .it('should generate client successfully with v3 document', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'Check out your shiny new generated files at ./test/docs/test-output.\\n\\n'\n        );\n        cleanup('./test/docs/test-output');\n        done();\n      });\n  }).timeout(200000);\n});\n"
  },
  {
    "path": "test/integration/generate/fromTemplate.test.ts",
    "content": "import * as fs from 'fs';\nimport * as path from 'path';\nimport { test } from '@oclif/test';\nimport { rimrafSync } from 'rimraf';\nimport { expect } from '@oclif/test';\n\nconst nonInteractive = '--no-interactive';\n\nconst generalOptions = [\n  'generate:fromTemplate',\n  './test/fixtures/specification.yml',\n  '@asyncapi/minimaltemplate',\n];\nconst asyncapiv3 = './test/fixtures/specification-v3.yml';\n\nfunction cleanup(filepath: string) {\n  rimrafSync(filepath);\n}\n\ndescribe('template', () => {\n  after(() => {\n    cleanup('./test/docs');\n  });\n  test\n    .stdout()\n    .command([...generalOptions, '--output=./test/docs/1', '--force-write', '--no-interactive'])\n    .it('should generate minimal template', (ctx, done) => {\n      console.log(ctx.stdout);\n      expect(ctx.stdout).to.contain(\n        'Check out your shiny new generated files at ./test/docs/1.\\n\\n'\n      );\n      cleanup('./test/docs/1');\n      done();\n    });\n\n  describe('should handle AsyncAPI v3 document correctly', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'generate:fromTemplate',\n        asyncapiv3,\n        '@asyncapi/minimaltemplate',\n        nonInteractive,\n      ])\n      .it('give error on disabled template', (ctx, done) => {\n        expect(ctx.stderr).to.contain('Error: @asyncapi/minimaltemplate template does not support AsyncAPI v3 documents, please checkout some link\\n');\n        expect(ctx.stdout).to.equal('');\n        done();\n      });\n  }).timeout(200000);\n\n  describe('should be able to generate from template', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'generate:fromTemplate',\n        './test/fixtures/asyncapi_v2.yml',\n        '@asyncapi/newtemplate',\n        '--output=./test/docs/2',\n        '--force-write',\n      ])\n      .it('should be able to generate using the generator', (ctx, done) => {\n        expect(ctx.stdout).to.contain('Check out your shiny new generated files at ./test/docs/2.\\n\\n');\n        cleanup('./test/docs/2');\n        done();\n      });\n  });\n\n  describe('should error out on proxy port', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'generate:fromTemplate',\n        'http://localhost:8080/dummySpec.yml',\n        '@asyncapi/newtemplate',\n        '--output=./test/docs/2',\n        '--force-write',\n        '--proxyHost=host',\n        '--proxyPort=8080',\n      ])\n      .it('should throw error when url is passed with proxyHost and proxyPort with invalid host', (ctx, done) => {\n        expect(ctx.stdout).to.contain('');\n        expect(ctx.stderr).to.equal('error loading AsyncAPI document from url: Failed to download http://localhost:8080/dummySpec.yml.\\n');\n        cleanup('./test/docs/2');\n        done();\n      });\n  });\n\n  describe('git clash', () => {\n    const pathToOutput = './test/docs/2';\n    before(() => {\n      fs.mkdirSync(pathToOutput, { recursive: true });\n      // Write a random file to trigger that dir has unstaged changes.\n      fs.writeFileSync(path.join(pathToOutput, 'random.md'), '');\n    });\n    test\n      .stderr()\n      .command([...generalOptions, `--output=${pathToOutput}`, nonInteractive])\n      .it(\n        'should throw error if output folder is in a git repository',\n        (ctx, done) => {\n          expect(ctx.stderr).to.contain(\n            `Error: \"${pathToOutput}\" is in a git repository with unstaged changes.`\n          );\n          cleanup(pathToOutput);\n          done();\n        }\n      );\n  });\n\n  describe('custom params', () => {\n    test\n      .stdout()\n      .command([\n        ...generalOptions,\n        '-p=version=1.0.0 mode=development',\n        '--output=./test/docs/3',\n        '--force-write',\n        nonInteractive\n      ])\n      .it('should pass custom param in the template', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'Check out your shiny new generated files at ./test/docs/3.\\n\\n'\n        );\n        cleanup('./test/docs/3');\n        done();\n      });\n  });\n\n  describe('disable-hooks', () => {\n    test\n      .stdout()\n      .command([\n        ...generalOptions,\n        '--output=./test/docs/4',\n        '--force-write',\n        '-d=generate:after',\n        nonInteractive\n      ])\n      .it('should not create asyncapi.yaml file', async (_, done) => {\n        const exits = fs.existsSync(path.resolve('./docs/asyncapi.yaml'));\n        expect(!!exits).to.equal(false);\n        cleanup('./test/docs/4');\n        done();\n      });\n  });\n\n  describe('debug', () => {\n    test\n      .stdout()\n      .command([\n        ...generalOptions,\n        '--output=./test/docs/5',\n        '--force-write',\n        '--debug',\n        nonInteractive\n      ])\n      .it('should print debug logs', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          `Template sources taken from ${path.resolve(\n            './test/fixtures/minimaltemplate'\n          )}.`\n        );\n        cleanup('./test/docs/5');\n        done();\n      });\n  });\n\n  describe('no-overwrite', () => {\n    test\n      .stdout()\n      .command([\n        ...generalOptions,\n        '--output=./test/docs/6',\n        '--force-write',\n        '--no-overwrite=./test/docs/asyncapi.md',\n        nonInteractive\n      ])\n      .it('should skip the filepath and generate normally', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          'Check out your shiny new generated files at ./test/docs/6.\\n\\n'\n        );\n        cleanup('./test/docs/6');\n        done();\n      });\n  });\n\n  describe('should install template', () => {\n    test\n      .stdout()\n      .command([\n        'generate:fromTemplate',\n        './test/fixtures/specification.yml',\n        './test/fixtures/minimaltemplate',\n        '--install',\n        '--force-write',\n        '--output=./test/docs/7'\n      ])\n      .it('should install template', (ctx, done) => {\n        expect(ctx.stdout).to.contain('Template installation started because you passed --install flag.');\n        cleanup('./test/docs/7');\n        done();\n      });\n  }).timeout(1000000);\n\n  describe('map-base-url', () => {\n    test\n      .stdout()\n      .command([\n        'generate:fromTemplate',\n        './test/fixtures/dummyspec/apiwithref.json',\n        '@asyncapi/minimaltemplate',\n        '--output=./test/docs/8',\n        '--force-write',\n        '--map-base-url=https://schema.example.com/crm/:./test/fixtures/dummyspec',\n      ])\n      .it(\n        'should resolve reference and generate from template',\n        (ctx, done) => {\n          expect(ctx.stdout).to.contain(\n            'Check out your shiny new generated files at ./test/docs/8.\\n\\n'\n          );\n          cleanup('./test/docs/8');\n          done();\n        }\n      );\n  });\n\n  describe('same-directory file $ref', () => {\n    test\n      .stdout()\n      .command([\n        'generate:fromTemplate',\n        './test/fixtures/generate-same-dir-ref/asyncapi.yaml',\n        '@asyncapi/minimaltemplate',\n        '--output=./test/docs/same-dir-ref-out',\n        '--force-write',\n        nonInteractive,\n      ])\n      .it(\n        'resolves relative file refs next to the spec file, not cwd (issue #1839)',\n        (ctx, done) => {\n          expect(ctx.stdout).to.contain(\n            'Check out your shiny new generated files at ./test/docs/same-dir-ref-out.\\n\\n'\n          );\n          cleanup('./test/docs/same-dir-ref-out');\n          done();\n        }\n      );\n  }).timeout(200000);\n});\n"
  },
  {
    "path": "test/integration/generate/models.test.ts",
    "content": "import { expect, test } from '@oclif/test';\nimport path from 'path';\nimport { rimrafSync } from 'rimraf';\nimport { createMockServer, stopMockServer } from '../../helpers/index';\nconst generalOptions = ['generate:models'];\nconst outputDir = './test/fixtures/generate/models';\n\ndescribe('models', () => {\n  before(() => {\n    createMockServer();\n  });\n  after(() => {\n    stopMockServer();\n    rimrafSync(outputDir);\n  });\n\n  test\n    .stderr()\n    .stdout()\n    .command([...generalOptions, 'typescript', 'http://localhost:8080/dummySpec.yml'])\n    .it('works with remote AsyncAPI files', (ctx, done) => {\n      expect(ctx.stdout).to.contain(\n        'Successfully generated the following models: '\n      );\n      done();\n    });\n  \n  test\n    .stderr()\n    .stdout()\n    .command([...generalOptions, 'typescript', './test/fixtures/specification.yml'])\n    .it('works when file path is passed without specified output directory', (ctx, done) => {\n      expect(ctx.stdout).to.match(/Successfully generated the following models:\\s+## Model name:/);\n      done();\n    });\n  \n  test\n    .stderr()\n    .stdout()\n    .command([...generalOptions, 'typescript', './test/fixtures/specification.yml', `-o=${ path.resolve(outputDir, './ts')}`])\n    .it('works when file path is passed with specified output directory', (ctx, done) => {\n      expect(ctx.stdout).to.contain(\n        'Successfully generated the following models: '\n      );\n      done();\n    });\n    \n  test\n    .stderr()\n    .stdout()\n    .command([...generalOptions,'typescript','http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080'])\n    .it('should throw error when url is passed with proxyHost and proxyPort with invalid host ', (ctx, done) => {\n      expect(ctx.stdout).to.contain('');\n      expect(ctx.stderr).to.equal('error loading AsyncAPI document from url: Failed to download http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080.\\n');\n      done();\n    });\n\n  describe('with logging diagnostics', () => {\n    test\n      .stderr()\n      .stdout()\n      .command([...generalOptions, 'typescript', 'http://localhost:8080/dummySpec.yml', '--log-diagnostics'])\n      .it('works with remote AsyncAPI files', (ctx, done) => {\n        expect(ctx.stdout).to.match(/URL http:\\/\\/localhost:8080\\/dummySpec.yml is valid but has \\(itself and\\/or referenced documents\\) governance issues./);\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/integration/new/file.test.ts",
    "content": "import { test } from '@oclif/test';\nimport TestHelper from '../../helpers/index';\nimport { expect } from '@oclif/test';\n\nconst testHelper = new TestHelper();\n\ndescribe('new', () => {\n  before(() => {\n    try {\n      testHelper.deleteSpecFileAtWorkingDir();\n    } catch (e: any) {\n      if (e.code !== 'ENOENT') {\n        throw e;\n      }\n    }\n  });\n  \n  describe('create new file', () => {\n    afterEach(() => {\n      testHelper.deleteSpecFileAtWorkingDir();\n    });\n    \n    test\n      .stderr()\n      .stdout()\n      .command(['new:file', '--no-tty', '-n=specification.yaml'])\n      .it('runs new file command', async (ctx,done) => {\n        expect(ctx.stderr).to.equal('');\n        expect(ctx.stdout).to.equal('The specification.yaml has been successfully created.\\n');\n        done();\n      });\n  });\n\n  describe('when asyncapi file already exists', () => {\n    beforeEach(() => {\n      try {\n        testHelper.createSpecFileAtWorkingDir();\n      } catch (e: any) {\n        if (e.code !== 'EEXIST') {\n          throw e;\n        }\n      }\n    });\n\n    afterEach(() => {\n      testHelper.deleteSpecFileAtWorkingDir();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['new:file', '--no-tty', '-n=specification.yaml'])\n      .it('should inform about the existing file and finish the process', async (ctx,done) => {\n        expect(ctx.stderr).to.equal('');\n        expect(ctx.stdout).to.equal('A file named specification.yaml already exists. Please choose a different name.\\n');\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/integration/new/template.test.ts",
    "content": "import { test } from '@oclif/test';\nimport TestHelper from '../../helpers/index';\nimport { expect } from '@oclif/test';\nimport { cyan, gray } from 'picocolors';\nconst testHelper = new TestHelper();\nconst successMessage = (projectName: string) =>\n  '🎉 Your template is succesfully created';\n\nconst errorMessages = {\n  alreadyExists: (projectName: string) =>\n    'Unable to create the project',\n};\ndescribe('new template', () => {\n  before(() => {\n    try {\n      testHelper.deleteDummyProjectDirectory();\n    } catch (e: any) {\n      if (e.code !== 'ENOENT') {\n        throw e;\n      }\n    }\n  });\n\n  describe('creation of new project is successful', () => {\n    afterEach(() => {\n      testHelper.deleteDummyProjectDirectory();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['new:template', '-n=test-project'])\n      .it('runs new template command with name flag', async (ctx,done) => {\n        expect(ctx.stderr).to.equal('');\n        expect(ctx.stdout).to.contains(successMessage('test-project'));\n        done();\n      });\n  });\n\n  describe('when new project name already exists', () => {\n    beforeEach(() => {\n      try {\n        testHelper.createDummyProjectDirectory();\n      } catch (e: any) {\n        if (e.code !== 'EEXIST') {\n          throw e;\n        }\n      }\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyProjectDirectory();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['new:template', '-n=test-project'])\n      .it('should throw error if name of the new project already exists', async (ctx,done) => {\n        expect(ctx.stderr).to.contains(`Error: ${errorMessages.alreadyExists('test-project')}`);\n        expect(ctx.stdout).to.equal('');\n        done();\n      });\n  });\n});\n\n"
  },
  {
    "path": "test/integration/optimize.test.ts",
    "content": "import path from 'path';\nimport { test } from '@oclif/test';\nimport TestHelper, { createMockServer, stopMockServer } from '../helpers/index';\nimport fs from 'fs-extra';\nimport inquirer from 'inquirer';\nimport {Optimizations, Outputs} from '../../src/apps/cli/commands/optimize';\nimport { expect } from '@oclif/test';\n\nconst testHelper = new TestHelper();\nconst optimizedFilePath = './test/fixtures/specification.yml';\nconst unoptimizedYamlFile = './test/fixtures/dummyspec/unoptimizedSpec.yml';\nconst unoptimizedJsonFile = './test/fixtures/dummyspec/unoptimizedSpec.json';\nconst invalidFile = './test/fixtures/specification-invalid.yml';\nconst asyncapiv3 = './test/fixtures/specification-v3.yml';\n\ndescribe('optimize', () => {\n  describe('no optimization needed', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    before(() => {\n      createMockServer();\n    });\n\n    after(() => {\n      stopMockServer();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize', optimizedFilePath])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`🎉 Great news! Your file at ${optimizedFilePath} is already optimized.`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize', './test/fixtures/not-found.yml'])\n      .it('should throw error if file path is wrong', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.contain('ValidationError: There is no file or context with name \"./test/fixtures/not-found.yml\".');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize', 'http://localhost:8080/dummySpecWithoutSecurity.yml'])\n      .it('works when url is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain('🎉 Great news! Your file at http://localhost:8080/dummySpecWithoutSecurity.yml is already optimized.');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize', 'http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080'])\n      .it('should throw error when url is passed with proxyHost and proxyPort with invalid host ', (ctx, done) => {\n        expect(ctx.stdout).to.contain('');\n        expect(ctx.stderr).to.equal('Error: Proxy Connection Error: Unable to establish a connection to the proxy check hostName or PortNumber.\\n');\n        done();\n      });\n  });\n\n  describe('with no arguments', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.setCurrentContext('home');\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize'])\n      .it('converts from current context', (ctx, done) => {\n        expect(ctx.stdout).to.contain(`🎉 Great news! Your file at ${path.resolve(__dirname, '../fixtures/specification.yml')} is already optimized.`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .do(() => {\n        testHelper.unsetCurrentContext();\n        testHelper.createDummyContextFile();\n      })\n      .command(['optimize'])\n      .it('throws error message if no current context', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.contain('ValidationError');\n        done();\n      });\n  });\n\n  describe('with no context file', () => {\n    beforeEach(() => {\n      try {\n        testHelper.deleteDummyContextFile();\n      } catch (e: any) {\n        if (e.code !== 'ENOENT') {\n          throw e;\n        }\n      }\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize'])\n      .it('throws error message if no context file exists', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal('ValidationError: Unable to perform validation. Specify what AsyncAPI file should be validated.\\n\\nThese are your options to specify in the CLI what AsyncAPI file should be used:\\n- You can provide a path to the AsyncAPI file: asyncapi validate path/to/file/asyncapi.yml\\n- You can also pass a saved context that points to your AsyncAPI file: asyncapi validate mycontext\\n- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi context use mycontext\\n- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.\\n');\n        done();\n      });\n  });\n\n  describe('no-tty flag', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize', unoptimizedYamlFile, '--no-tty'])\n      .it('process without going to interactive mode.', (ctx, done) => {\n        expect(ctx.stdout).to.contain('asyncapi: 2.0.0');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize', unoptimizedYamlFile, '--no-tty', '-o', 'new-file'])\n      .it('generate YAML output against YAML input and show its path.', (ctx, done) => {\n        const pos = unoptimizedYamlFile.lastIndexOf('.');\n        const optimizedFile = `${unoptimizedYamlFile.substring(0, pos)}_optimized.${unoptimizedYamlFile.substring(pos + 1)}`;\n        expect(ctx.stdout).to.contain(`✅ Success! Your optimized file has been created at ${optimizedFile}.`);\n        expect(ctx.stderr).to.equal('');\n        expect(fs.readFileSync(optimizedFile, 'utf8')).to.contain('asyncapi: 2.0.0');\n        fs.unlinkSync(optimizedFile);\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize', unoptimizedJsonFile, '--no-tty', '-o', 'new-file'])\n      .it('generate JSON output against JSON input and show its path.', (ctx, done) => {\n        const pos = unoptimizedJsonFile.lastIndexOf('.');\n        const optimizedFile = `${unoptimizedJsonFile.substring(0, pos)}_optimized.${unoptimizedJsonFile.substring(pos + 1)}`;\n        expect(ctx.stdout).to.contain(`✅ Success! Your optimized file has been created at ${optimizedFile}.`);\n        expect(ctx.stderr).to.equal('');\n        expect(fs.readFileSync(optimizedFile, 'utf8')).to.contain('\"asyncapi\": \"2.0.0\"');\n        fs.unlinkSync(optimizedFile);\n        done();\n      });\n  });\n\n  describe('interactive terminal', () => {\n    test\n      .stub(inquirer, 'prompt', (stub) => stub.resolves({optimization: [Optimizations.REMOVE_COMPONENTS] , output: Outputs.TERMINAL}))\n      .stderr()\n      .stdout()\n      .command(['optimize', unoptimizedYamlFile])\n      .it('interactive terminal, only remove components and outputs to terminal', (ctx, done) => {\n        expect(ctx.stdout).to.contain('asyncapi: 2.0.0');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n  describe('error if the asyncapi file is invalid', () => {\n    test\n      .stderr()\n      .stdout()\n      .command(['optimize',invalidFile])\n      .it('give ValidationError', (ctx, done) => {\n        expect(ctx.stderr).to.contain(`ValidationError: Syntax Error in \"${invalidFile}\".`);\n        expect(ctx.stdout).to.equal('');\n        done();\n      });\n  });\n});\n\n"
  },
  {
    "path": "test/integration/pretty.test.ts",
    "content": "import { test } from '@oclif/test';\nimport TestHelper, { createMockServer, stopMockServer } from '../helpers/index';\nimport { expect } from '@oclif/test';\n\nconst testHelper = new TestHelper();\nconst badFormatPath = './test/fixtures/asyncapi_v1.yml';\nconst validFormatPath = './test/fixtures/asyncapiValid_v1.yml';\nconst badFormatPathJson = './test/fixtures/badFormatAsyncapi.json';\n\ndescribe('pretty', () => {\n  describe('with file paths', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    before(() => {\n      createMockServer();\n    });\n\n    after(() => {\n      stopMockServer();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['pretty', badFormatPath])\n      .it('should log the information file has been beautified', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          `Asyncapi document ${badFormatPath} has been beautified in-place`,\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['pretty', badFormatPath ,'-o', validFormatPath])\n      .it('should log the information file has been beautified', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          `Asyncapi document has been beautified ${validFormatPath}`,\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['pretty', badFormatPathJson])\n      .it('should log the information file has been beautified json file', (ctx, done) => {\n        expect(ctx.stdout).to.contain(\n          `Asyncapi document ${badFormatPathJson} has been beautified in-place`,\n        );\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/integration/studio.test.ts",
    "content": "import { test } from '@oclif/test';\nimport { expect } from '@oclif/test';\nimport { testPreview, testStudio, closeStudioServer } from '../helpers/index';\n\ndescribe('Test live studio', () => {\n  test\n    .stdout()\n    .command([\n      'start studio','-B','-p','3210','./test/fixtures/specification-v3.yml',\n    ]).finally(async () => {\n      await closeStudioServer(3210);\n    })\n    .it('should successfully open and navigate the site', async () => {\n      const {logoTitle} = await testStudio();\n      expect(logoTitle).to.equal('AsyncAPI Logo');\n    });\n});\n\ndescribe('Test preview mode', () => {\n  test\n    .stdout()\n    .command([\n      'start preview','-B','-p','4321','./test/fixtures/asyncapi_v2.yml',\n    ]).finally(async () => {\n      await closeStudioServer(4321);\n    })\n    .it('should successfully open and navigate the site', async () => {\n      const {logoTitle,introductionSectionId} = await testPreview();\n      expect(logoTitle).to.equal('AsyncAPI Logo');\n      expect(introductionSectionId).to.equal('introduction');\n    });\n});\n"
  },
  {
    "path": "test/integration/validate.test.ts",
    "content": "\nimport path from 'path';\nimport { test } from '@oclif/test';\nimport { NO_CONTEXTS_SAVED } from '../../src/errors/context-error';\nimport TestHelper, {createMockServer, stopMockServer } from '../helpers/index';\nimport { expect } from '@oclif/test';\n\nconst testHelper = new TestHelper();\n\ndescribe('validate', () => {\n  describe('with file paths', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    before(() => {\n      createMockServer();\n    });\n\n    after(() => {\n      stopMockServer();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/specification.yml'])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.\\n');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/specification-avro.yml'])\n      .it('works when file path is passed and schema is avro', (ctx, done) => {\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification-avro.yml is valid but has (itself and/or referenced documents) governance issues.\\n');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/not-found.yml'])\n      .it('should throw error if file path is wrong', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal('error loading AsyncAPI document from file: ./test/fixtures/not-found.yml file does not exist.\\n');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', 'http://localhost:8080/dummySpec.yml'])\n      .it('works when url is passed', (ctx, done) => {\n        expect(ctx.stdout).to.contain('URL http://localhost:8080/dummySpec.yml is valid but has (itself and/or referenced documents) governance issues.\\n');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', 'http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080'])\n      .it('should throw error when url is passed with proxyHost and proxyPort with invalid host ', (ctx, done) => {\n        expect(ctx.stdout).to.contain('');\n        expect(ctx.stderr).to.equal('error loading AsyncAPI document from url: Failed to download http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080.\\n');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/valid-specification-latest.yml'])\n      .it('works when file path is passed', (ctx, done) => {\n        expect(ctx.stdout).to.include('File ./test/fixtures/valid-specification-latest.yml is valid! File ./test/fixtures/valid-specification-latest.yml and referenced documents don\\'t have governance issues.');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/external-refs/main.yaml'])\n      .it('should resolve external file references in same directory', (ctx, done) => {\n        expect(ctx.stdout).to.include('File ./test/fixtures/external-refs/main.yaml is valid');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with context names', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', 'code'])\n      .it('validates if context name exists', (ctx, done) => {\n        const fileName = path.resolve(__dirname, '../fixtures/specification.yml');\n        expect(ctx.stdout).to.include(`File ${fileName} is valid but has (itself and/or referenced documents) governance issues.`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', 'non-existing-context'])\n      .it('throws error if context name is not saved', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal('ContextError: Context \"non-existing-context\" does not exist.\\n');\n        done();\n      });\n  });\n\n  describe('with no arguments', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.setCurrentContext('home');\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate'])\n      .it('validates from current context', (ctx, done) => {\n        const fileName = path.resolve(__dirname, '../../test/fixtures/specification.yml');\n        expect(ctx.stdout).to.includes(`File ${fileName} is valid but has (itself and/or referenced documents) governance issues`);\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .do(() => {\n        testHelper.unsetCurrentContext();\n        testHelper.createDummyContextFile();\n      })\n      .command(['validate'])\n      .it('throws error message if no current context', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal('ContextError: No context is set as current, please set a current context.\\n');\n        done();\n      });\n  });\n\n  describe('with no context file', () => {\n    beforeEach(() => {\n      try {\n        testHelper.deleteDummyContextFile();\n      } catch (e: any) {\n        if (e.code !== 'ENOENT') {\n          throw e;\n        }\n      }\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate'])\n      .it('throws error message if no context file exists', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal(`error locating AsyncAPI document: ${NO_CONTEXTS_SAVED}\\n`);\n        done();\n      });\n  });\n\n  describe('with --log-diagnostics flag', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/specification.yml', '--log-diagnostics'])\n      .it('works with --log-diagnostics', (ctx, done) => {\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.\\n');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/specification.yml', '--no-log-diagnostics'])\n      .it('works with --no-log-diagnostics', (ctx, done) => {\n        expect(ctx.stdout).to.equal('');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with --diagnostics-format flag', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/specification.yml', '--diagnostics-format=text'])\n      .it('works with --diagnostics-format flag (with governance issues)', (ctx, done) => {\n        expect(ctx.stdout).to.match(new RegExp('File ./test/fixtures/specification.yml is valid but has \\\\(itself and\\\\/or referenced documents\\\\) governance issues.\\\\ntest\\\\/fixtures\\\\/specification.yml:1:1'));\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/valid-specification-latest.yml', '--diagnostics-format=text'])\n      .it('works with --diagnostics-format flag (without governance issues)', (ctx, done) => {\n        expect(ctx.stdout).to.include('\\nFile ./test/fixtures/valid-specification-latest.yml is valid! File ./test/fixtures/valid-specification-latest.yml and referenced documents don\\'t have governance issues.');\n        expect(ctx.stderr).to.equal('');\n        done();\n      });\n  });\n\n  describe('with --fail-severity flag', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command(['validate', './test/fixtures/specification.yml', '--fail-severity=warn'])\n      .it('works with --fail-severity', (ctx, done) => {\n        expect(ctx.stderr).to.contain('File ./test/fixtures/specification.yml and/or referenced documents have governance issues.');\n        expect(process.exitCode).to.equal(1);\n        done();\n      });\n  });\n  \n  describe('with --score flag',() => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stdout()\n      .command(['validate', './test/fixtures/asyncapiTestingScore.yml', '--score'])\n      .it('work with --score flag', (ctx, done) => {\n        expect(ctx.stdout).to.contains('The score of the asyncapi document is 100\\n');\n        expect(ctx.stdout).to.contains('File ./test/fixtures/asyncapiTestingScore.yml is valid! File ./test/fixtures/asyncapiTestingScore.yml and referenced documents don\\'t have governance issues.');\n        done();\n      });\n  });\n\n  describe('validate command and suppression of the single warning', () => {\n    test\n      .stdout()\n      .command([\n        'validate',\n        path.join('test', 'fixtures', 'asyncapi_v1.yml'),\n        '--suppressWarnings',\n        'asyncapi-id'\n      ])\n      .it('should suppress specified warnings and still validate correctly', (ctx, done) => {\n        expect(ctx.stdout).to.include('asyncapi_v1.yml');\n        expect(ctx.stdout).to.match(/is valid/i); // General validity check\n        expect(ctx.stdout).to.not.include('asyncapi-id'); // Ensure warning is suppressed\n        done();\n      });\n  });\n  describe('validate command and suppression of multiple warnings', () => {\n    test\n      .stdout()\n      .command([\n        'validate',\n        path.join('test', 'fixtures', 'asyncapi_v1.yml'),\n        '--suppressWarnings',\n        'asyncapi-id',\n        'suppressWarnings',\n        'asyncapi2-tags'\n      ])\n      .it('should suppress multiple specified warnings and still validate correctly', (ctx, done) => {\n        expect(ctx.stdout).to.not.include('asyncapi-id'); // Suppressed warning #1\n        expect(ctx.stdout).to.not.include('asyncapi2-tags'); // Suppressed warning #2\n        done();\n      });\n  });\n\n  describe('validate command without suppression', () => {\n    test\n      .stdout()\n      .command([\n        'validate',\n        path.join('test', 'fixtures', 'asyncapi_v1.yml'),\n      ])\n      .it('should include the asyncapi-id warning when not suppressed', (ctx, done) => {\n        expect(ctx.stdout).to.include('asyncapi-id'); // Should show up if not suppressed\n        done();\n      });\n  });\n  describe('validate command with an invalid suppression rule', () => {\n    test\n      .stdout()\n      .command([\n        'validate',\n        path.join('test', 'fixtures', 'asyncapi_v1.yml'),\n        '--suppressWarnings',\n        'non-existing-rule'\n      ])\n      .it('should not suppress anything', (ctx, done) => {\n        expect(ctx.stdout).to.include('asyncapi-id'); \n        done();\n      });\n  });\n  describe('validate command with mixed valid and invalid suppressed warnings', () => {\n    test\n      .stdout()\n      .command([\n        'validate',\n        path.join('test', 'fixtures', 'asyncapi_v1.yml'),\n        '--suppressWarnings',\n        'asyncapi-id',\n        '--suppressWarnings',\n        'foobar'\n      ])\n      .it('should suppress valid rules', (ctx, done) => {\n        expect(ctx.stdout).to.not.include('asyncapi-id'); \n        done();\n      });\n  });\n\n  describe('with --save-output flag', () => {\n    beforeEach(() => {\n      testHelper.createDummyContextFile();\n    });\n\n    afterEach(() => {\n      testHelper.deleteDummyContextFile();\n    });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/specification.yml',\n        '--log-diagnostics',\n        '--save-output=./test/fixtures/validate-output-stylish.txt'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output-stylish.txt');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save diagnostics output to file with default stylish format', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output-stylish.txt');\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output-stylish.txt', 'utf8');\n        expect(fileContent.length).to.be.greaterThan(0);\n        expect(fileContent).to.include('test/fixtures/specification.yml');\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/specification.yml',\n        '--log-diagnostics',\n        '--diagnostics-format=json',\n        '--save-output=./test/fixtures/validate-output.json'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output.json');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save diagnostics output to file with JSON format', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output.json');\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output.json', 'utf8');\n        expect(fileContent.length).to.be.greaterThan(0);\n        const jsonContent = JSON.parse(fileContent);\n        expect(jsonContent).to.be.an('array');\n        expect(jsonContent.length).to.be.greaterThan(0);\n        expect(jsonContent[0]).to.have.property('code');\n        expect(jsonContent[0]).to.have.property('message');\n        expect(jsonContent[0]).to.have.property('path');\n        expect(jsonContent[0]).to.have.property('severity');\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/specification.yml',\n        '--log-diagnostics',\n        '--diagnostics-format=text',\n        '--save-output=./test/fixtures/validate-output.txt'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output.txt');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save diagnostics output to file with text format', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output.txt');\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output.txt', 'utf8');\n        expect(fileContent.length).to.be.greaterThan(0);\n        expect(fileContent).to.include('test/fixtures/specification.yml');\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/specification.yml',\n        '--log-diagnostics',\n        '--diagnostics-format=html',\n        '--save-output=./test/fixtures/validate-output.html'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output.html');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save diagnostics output to file with HTML format', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output.html');\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output.html', 'utf8');\n        expect(fileContent.length).to.be.greaterThan(0);\n        expect(fileContent).to.include('<');\n        expect(fileContent).to.include('>');\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/valid-specification-latest.yml',\n        '--log-diagnostics',\n        '--save-output=./test/fixtures/validate-output-valid.txt'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output-valid.txt');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save empty diagnostics when document has no issues', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output-valid.txt');\n        expect(ctx.stdout).to.include('File ./test/fixtures/valid-specification-latest.yml is valid!');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output-valid.txt', 'utf8');\n        expect(fileContent.length).to.be.equal(0);\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/specification.yml',\n        '--log-diagnostics',\n        '--diagnostics-format=junit',\n        '--save-output=./test/fixtures/validate-output.xml'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output.xml');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save diagnostics output to file with JUnit format', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output.xml');\n        expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output.xml', 'utf8');\n        expect(fileContent.length).to.be.greaterThan(0);\n        expect(fileContent).to.include('<?xml');\n        expect(fileContent).to.include('<testsuites');\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/specification.yml',\n        '--log-diagnostics',\n        '--save-output=./test/fixtures/validate-output-with-suppression.txt',\n        '--suppressWarnings=asyncapi-id'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output-with-suppression.txt');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save diagnostics with suppressed warnings to file', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output-with-suppression.txt');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output-with-suppression.txt', 'utf8');\n        expect(fileContent.length).to.be.greaterThan(0);\n        expect(fileContent).to.not.include('asyncapi-id');\n      });\n\n    test\n      .stderr()\n      .stdout()\n      .command([\n        'validate',\n        './test/fixtures/specification.yml',\n        '--log-diagnostics',\n        '--save-output=./test/fixtures/validate-output-fail-severity.txt',\n        '--fail-severity=warn'\n      ])\n      .finally(async () => {\n        const fs = await import('fs');\n        try {\n          await fs.promises.unlink('./test/fixtures/validate-output-fail-severity.txt');\n        } catch (err) {\n          // Ignore error if file doesn't exist\n        }\n      })\n      .it('should save diagnostics with fail-severity to file', async (ctx) => {\n        const fs = await import('fs');\n        expect(ctx.stdout).to.contain('Diagnostics saved to ./test/fixtures/validate-output-fail-severity.txt');\n        const fileContent = await fs.promises.readFile('./test/fixtures/validate-output-fail-severity.txt', 'utf8');\n        expect(fileContent.length).to.be.greaterThan(0);\n        expect(fileContent).to.include('test/fixtures/specification.yml');\n      });\n  });\n});\n"
  },
  {
    "path": "test/jest.setup.ts",
    "content": "/* eslint-disable @typescript-eslint/ban-ts-comment */\n/* eslint-disable @typescript-eslint/no-namespace, @typescript-eslint/no-unused-vars */\n// @ts-nocheck\n\ndeclare namespace NodeJS {\n  interface Global {\n    oclif: any;\n  }\n}\n\nglobal.oclif = global.oclif || {};\nglobal.oclif.columns = 80;\n\n// in tests like model generation or template generation, there is need to have more listeners than default is available (10)\n// this is a problem with the dependencies that are used in the given tools (I mean generator/modelina) than with the code in the CLI\nrequire('events').EventEmitter.defaultMaxListeners = 30;\n"
  },
  {
    "path": "test/system/.gitkeep",
    "content": ""
  },
  {
    "path": "test/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig\",\n  \"compilerOptions\": {\n    \"noEmit\": true,\n  },\n}\n"
  },
  {
    "path": "test/unit/controllers/convert.controller.test.ts",
    "content": "import request from 'supertest';\n\nimport './setup.test';\nimport { App } from '../../../src/apps/api/app';\nimport { ProblemException } from '../../../src/apps/api/exceptions/problem.exception';\nimport { ConvertController } from '../../../src/apps/api/controllers/convert.controller';\n\ndescribe('ConvertController', () => {\n  describe('[POST] /convert', () => {\n    it('should convert a valid AsyncAPI document', async () => {\n      const app = new App([new ConvertController()]);\n      await app.init();\n\n      await request(app.getServer())\n        .post('/v1/convert')\n        .send({\n          source: {\n            asyncapi: '2.3.0',\n            info: {\n              title: 'Test API',\n              version: '1.0.0'\n            },\n            channels: {\n              'my-channel': {\n                publish: {\n                  message: {\n                    payload: {\n                      type: 'object'\n                    }\n                  }\n                }\n              }\n            }\n          },\n          format: 'asyncapi',\n          'target-version': '3.1.0',\n          perspective: 'server'\n        })\n        .expect(200)\n        .expect(res => {\n          if (!res.body.converted) {\n            throw new Error('Expected converted document in response');\n          }\n          if (res.body.sourceFormat !== 'asyncapi') {\n            throw new Error(`Expected sourceFormat to be asyncapi but got ${res.body.sourceFormat}`);\n          }\n        });\n    });\n\n    it('should return 422 when conversion fails', async () => {\n      const app = new App([new ConvertController()]);\n      await app.init();\n\n      await request(app.getServer())\n        .post('/v1/convert')\n        .send({\n          source: '{ \"asyncapi\": \"2.0.0\", \"info\": {} }', // deliberately malformed for conversion service\n          format: 'asyncapi'\n        })\n        .expect(422)\n        .expect(res => {\n          if (res.body.type !== ProblemException.createType('invalid-asyncapi-document')) {\n            throw new Error(`Expected error type conversion-error but got ${res.body.type}`);\n          }\n          if (!res.body.detail) {\n            throw new Error('Expected error detail in response');\n          }\n        });\n    });\n\n    it('should return 422 for invalid request body', async () => {\n      const app = new App([new ConvertController()]);\n      await app.init();\n\n      await request(app.getServer())\n        .post('/v1/convert')\n        .send({\n          format: 'asyncapi'\n          // missing `source`\n        })\n        .expect(422)\n        .expect(res => {\n          if (res.body.type !== ProblemException.createType('invalid-request-body')) {\n            throw new Error('Expected validation error for missing source');\n          }\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/controllers/diff.controller.test.ts",
    "content": "import request from 'supertest';\n\nimport './setup.test';\nimport { App } from '../../../src/apps/api/app';\nimport { ProblemException } from '../../../src/apps/api/exceptions/problem.exception';\n\nimport { DiffController } from '../../../src/apps/api/controllers/diff.controller';\n\ndescribe('DiffController', () => {\n  describe('[POST] /diff', () => {\n    it('should diff AsyncAPI documents', async () => {\n      const app = new App([new DiffController()]);\n      await app.init();\n\n      await request(app.getServer())\n        .post('/v1/diff')\n        .send({\n          asyncapis: [\n            {\n              asyncapi: '2.3.0',\n              info: {\n                title: 'Super test',\n                version: '1.0.0'\n              },\n              channels: {\n                'test-channel-1': {\n                  publish: {\n                    message: {\n                      payload: {\n                        type: 'object',\n                      },\n                    },\n                  }\n                },\n              },\n            },\n            {\n              asyncapi: '2.3.0',\n              info: {\n                title: 'Changed super test',\n                version: '1.1.0'\n              },\n              channels: {\n                'test-channel-1': {\n                  publish: {\n                    message: {\n                      payload: {\n                        type: 'object',\n                      },\n                    },\n                  }\n                },\n              },\n            }, \n          ],\n        })\n        .expect(200, {\n          diff: {\n            changes: [\n              {\n                action: 'edit',\n                path: '/info/version',\n                before: '1.0.0',\n                after: '1.1.0',\n                type: 'breaking',\n              },\n              {\n                action: 'edit',\n                path: '/info/title',\n                before: 'Super test',\n                after: 'Changed super test',\n                type: 'non-breaking',\n              },\n            ],\n          }\n        });\n    });\n\n    it('should throw error with invalid AsyncAPI document', async () => {\n      const app = new App([new DiffController()]);\n      await app.init();\n\n      await request(app.getServer())\n        .post('/v1/diff')\n        .send({\n          asyncapis: [\n            {\n              asyncapi: '2.2.0',\n              info: {\n                title: 'Test Service',\n                version: '1.0.0',\n              },\n              channels: {\n                'test-channel-2': {\n                  publish: {\n                    message: {\n                      payload: {\n                        type: 'object',\n                      },\n                    },\n                  }\n                },\n              },\n            },\n            {\n              asyncapi: '2.2.0',\n              info: {\n                tite: 'My API', // spelled wrong on purpose to throw an error in the test\n                version: '1.0.0'\n              },\n              channels: {},\n            }\n          ],\n        })\n        .expect(422, {\n          type: ProblemException.createType('invalid-asyncapi-document'),\n          title: 'Invalid AsyncAPI Document',\n          status: 422,\n          detail: 'The provided AsyncAPI document is invalid.',\n          diagnostics: [\n            {\n              code: 'asyncapi-defaultContentType',\n              message: 'AsyncAPI document should have \"defaultContentType\" field.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-id',\n              message: 'AsyncAPI document should have \"id\" field.',\n              path: [],\n              severity: 1,\n\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-servers',\n              message: 'AsyncAPI document should have non-empty \"servers\" object.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi2-tags',\n              message: 'AsyncAPI object should have non-empty \"tags\" array.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-latest-version',\n              message: 'The latest version of AsyncAPi is not used. It is recommended update to the \"3.1.0\" version.',\n              path: [\n                'asyncapi'\n              ],\n              severity: 2,\n              range: {\n                start: {\n                  line: 0,\n                  character: 12\n                },\n                end: {\n                  line: 0,\n                  character: 19\n                }\n              }\n            },\n            {\n              code: 'asyncapi-document-resolved',\n              message: '\"info\" property must have required property \"title\"',\n              path: [\n                'info'\n              ],\n              severity: 0,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-contact',\n              message: 'Info object should have \"contact\" object.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-description',\n              message: 'Info \"description\" should be present and non-empty string.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-license',\n              message: 'Info object should have \"license\" object.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-document-resolved',\n              message: 'Property \"tite\" is not expected to be here',\n              path: [\n                'info',\n                'tite'\n              ],\n              severity: 0,\n              range: {\n                start: {\n                  line: 0,\n                  character: 35\n                },\n                end: {\n                  line: 0,\n                  character: 43\n                }\n              }\n            }\n          ]\n        });\n    });\n  });    \n});\n"
  },
  {
    "path": "test/unit/controllers/parse.controller.test.ts",
    "content": "import request from 'supertest';\n\nimport './setup.test';\nimport { App } from '../../../src/apps/api/app';\nimport { ProblemException } from '../../../src/apps/api/exceptions/problem.exception';\n\nimport { ParseController } from '../../../src/apps/api/controllers/parse.controller';\n\nconst validJSONAsyncAPI = {\n  asyncapi: '2.0.0',\n  info: {\n    title: 'My API',\n    version: '1.0.0'\n  },\n  channels: {}\n};\nconst invalidJSONAsyncAPI = {\n  asyncapi: '2.0.0',\n  info: {\n    tite: 'My API', // spelled wrong on purpose to throw an error in the test\n    version: '1.0.0'\n  },\n  channels: {}\n};\n\ndescribe('ParseController', () => {\n  describe('[POST] /parse', () => {\n    it('should return stringified AsyncAPI document', async () => {\n      const app = new App([new ParseController()]);\n      await app.init();\n\n      return request(app.getServer())\n        .post('/v1/parse')\n        .send({\n          asyncapi: validJSONAsyncAPI\n        })\n        .expect(200, {\n          parsed: '{\"asyncapi\":\"2.0.0\",\"info\":{\"title\":\"My API\",\"version\":\"1.0.0\"},\"channels\":{},\"x-parser-spec-parsed\":true,\"x-parser-api-version\":3}',\n        });\n    });\n\n    it('should throw error when sent an invalid AsyncAPI document', async () => {\n      const app = new App([new ParseController()]);\n      await app.init();\n\n      return request(app.getServer())\n        .post('/v1/parse')\n        .send({\n          asyncapi: invalidJSONAsyncAPI\n        })\n        .expect(422, {\n          type: ProblemException.createType('invalid-asyncapi-document'),\n          title: 'Invalid AsyncAPI Document',\n          status: 422,\n          detail: 'The provided AsyncAPI document is invalid.',\n          diagnostics: [\n            {\n              code: 'asyncapi-defaultContentType',\n              message: 'AsyncAPI document should have \"defaultContentType\" field.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-id',\n              message: 'AsyncAPI document should have \"id\" field.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-servers',\n              message: 'AsyncAPI document should have non-empty \"servers\" object.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi2-tags',\n              message: 'AsyncAPI object should have non-empty \"tags\" array.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-latest-version',\n              message: 'The latest version of AsyncAPi is not used. It is recommended update to the \"3.1.0\" version.',\n              path: [\n                'asyncapi'\n              ],\n              severity: 2,\n              range: {\n                start: {\n                  line: 0,\n                  character: 12\n                },\n                end: {\n                  line: 0,\n                  character: 19\n                }\n              }\n            },\n            {\n              code: 'asyncapi-document-resolved',\n              message: '\"info\" property must have required property \"title\"',\n              path: [\n                'info'\n              ],\n              severity: 0,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-contact',\n              message: 'Info object should have \"contact\" object.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-description',\n              message: 'Info \"description\" should be present and non-empty string.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-license',\n              message: 'Info object should have \"license\" object.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-document-resolved',\n              message: 'Property \"tite\" is not expected to be here',\n              path: [\n                'info',\n                'tite'\n              ],\n              severity: 0,\n              range: {\n                start: {\n                  line: 0,\n                  character: 35\n                },\n                end: {\n                  line: 0,\n                  character: 43\n                }\n              }\n            }\n          ]\n        }\n        );\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/controllers/setup.test.ts",
    "content": "import { AsyncAPIDocument, ValidationResult } from '../../../src/interfaces/index';\n\ndeclare module 'express' {\n  export interface Request extends Express.Request {\n    asyncapi?: {\n      parsedDocument?: AsyncAPIDocument;\n      parsedDocuments?: Array<AsyncAPIDocument>;\n      validationResults?: Array<ValidationResult>;\n      validationResult?: ValidationResult;\n    };\n  }\n}\n"
  },
  {
    "path": "test/unit/controllers/validate.controller.test.ts",
    "content": "import request from 'supertest';\n\nimport './setup.test';\nimport { App } from '../../../src/apps/api/app';\nimport { ProblemException } from '../../../src/apps/api/exceptions/problem.exception';\n\nimport { ValidateController } from '../../../src/apps/api/controllers/validate.controller';\n\nconst validJSONAsyncAPI = {\n  asyncapi: '2.2.0',\n  info: {\n    title: 'Account Service',\n    version: '1.0.0',\n    description: 'This service is in charge of processing user signups'\n  },\n  channels: {\n    'user/signedup': {\n      subscribe: {\n        message: {\n          $ref: '#/components/messages/UserSignedUp'\n        }\n      }\n    }\n  },\n  components: {\n    messages: {\n      UserSignedUp: {\n        payload: {\n          type: 'object',\n          properties: {\n            displayName: {\n              type: 'string',\n              description: 'Name of the user'\n            },\n            email: {\n              type: 'string',\n              format: 'email',\n              description: 'Email of the user'\n            }\n          }\n        }\n      }\n    }\n  }\n};\nconst validYAMLAsyncAPI = `\nasyncapi: '2.2.0'\ninfo:\n  title: Account Service\n  version: 1.0.0\n  description: This service is in charge of processing user signups\nchannels:\n  user/signedup:\n    subscribe:\n      message:\n        $ref: '#/components/messages/UserSignedUp'\ncomponents:\n  messages:\n    UserSignedUp:\n      payload:\n        type: object\n        properties:\n          displayName:\n            type: string\n            description: Name of the user\n          email:\n            type: string\n            format: email\n            description: Email of the user\n`;\nconst invalidJSONAsyncAPI = {\n  asyncapi: '2.0.0',\n  info: {\n    tite: 'My API', // spelled wrong on purpose to throw an error in the test\n    version: '1.0.0'\n  },\n  channels: {}\n};\n\ndescribe('ValidateController', () => {\n  describe('[POST] /validate', () => {\n    it('should validate AsyncAPI document in JSON', async () => {\n      const app = new App([new ValidateController()]);\n      await app.init();\n\n      return request(app.getServer())\n        .post('/v1/validate')\n        .send({\n          asyncapi: validJSONAsyncAPI\n        })\n        .expect(200);\n    });\n\n    it('should validate AsyncAPI document in YAML', async () => {\n      const app = new App([new ValidateController()]);\n      await app.init();\n\n      return request(app.getServer())\n        .post('/v1/validate')\n        .send({\n          asyncapi: validYAMLAsyncAPI\n        })\n        .expect(200);\n    });\n\n    it('should throw error when sent an empty document', async () => {\n      const app = new App([new ValidateController()]);\n      await app.init();\n\n      return request(app.getServer())\n        .post('/v1/validate')\n        .send({})\n        .expect(422, {\n          type: ProblemException.createType('invalid-request-body'),\n          title: 'Invalid Request Body',\n          status: 422,\n          validationErrors: [\n            {\n              instancePath: '',\n              schemaPath: '#/required',\n              keyword: 'required',\n              params: {\n                missingProperty: 'asyncapi'\n              },\n              message: 'must have required property \\'asyncapi\\''\n            }\n          ]\n        });\n    });\n\n    it('should throw error when sent an invalid AsyncAPI document', async () => {\n      const app = new App([new ValidateController()]);\n      await app.init();\n\n      return request(app.getServer())\n        .post('/v1/validate')\n        .send({\n          asyncapi: invalidJSONAsyncAPI\n        })\n        .expect(422, {\n          type: ProblemException.createType('invalid-asyncapi-document'),\n          title: 'Invalid AsyncAPI Document',\n          status: 422,\n          detail: 'The provided AsyncAPI document is invalid.',\n          diagnostics: [\n            {\n              code: 'asyncapi-defaultContentType',\n              message: 'AsyncAPI document should have \"defaultContentType\" field.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-id',\n              message: 'AsyncAPI document should have \"id\" field.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-servers',\n              message: 'AsyncAPI document should have non-empty \"servers\" object.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi2-tags',\n              message: 'AsyncAPI object should have non-empty \"tags\" array.',\n              path: [],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 0\n                },\n                end: {\n                  line: 0,\n                  character: 76\n                }\n              }\n            },\n            {\n              code: 'asyncapi-latest-version',\n              message: 'The latest version of AsyncAPi is not used. It is recommended update to the \"3.1.0\" version.',\n              path: [\n                'asyncapi'\n              ],\n              severity: 2,\n              range: {\n                start: {\n                  line: 0,\n                  character: 12\n                },\n                end: {\n                  line: 0,\n                  character: 19\n                }\n              }\n            },\n            {\n              code: 'asyncapi-document-resolved',\n              message: '\"info\" property must have required property \"title\"',\n              path: [\n                'info'\n              ],\n              severity: 0,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-contact',\n              message: 'Info object should have \"contact\" object.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-description',\n              message: 'Info \"description\" should be present and non-empty string.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-info-license',\n              message: 'Info object should have \"license\" object.',\n              path: [\n                'info'\n              ],\n              severity: 1,\n              range: {\n                start: {\n                  line: 0,\n                  character: 27\n                },\n                end: {\n                  line: 0,\n                  character: 61\n                }\n              }\n            },\n            {\n              code: 'asyncapi-document-resolved',\n              message: 'Property \"tite\" is not expected to be here',\n              path: [\n                'info',\n                'tite'\n              ],\n              severity: 0,\n              range: {\n                start: {\n                  line: 0,\n                  character: 35\n                },\n                end: {\n                  line: 0,\n                  character: 43\n                }\n              }\n            }\n          ]\n        }\n        );\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/services/archiver.service.test.ts",
    "content": "import { expect } from 'chai';\nimport { ArchiverService } from '../../../src/domains/services/archiver.service';\nimport archiver from 'archiver';\n\ndescribe('ArchiverService', () => {\n  let archiverService: ArchiverService;\n\n  beforeEach(() => {\n    archiverService = new ArchiverService();\n  });\n\n  describe('createZip()', () => {\n    it('should create a zip archiver instance', () => {\n      const zip = archiverService.createZip();\n      \n      expect(zip).to.have.property('append');\n      expect(zip).to.have.property('directory');\n      expect(zip).to.have.property('finalize');\n    });\n\n    it('should create zip without response object', () => {\n      const zip = archiverService.createZip();\n      \n      expect(zip).to.be.an('object');\n      expect(typeof zip.append).to.equal('function');\n    });\n  });\n\n  describe('appendDirectory()', () => {\n    it('should append directory to archive without throwing', () => {\n      const zip = archiverService.createZip();\n      \n      expect(() => {\n        archiverService.appendDirectory(zip, '/source', 'destination');\n      }).to.not.throw();\n    });\n  });\n\n  describe('appendAsyncAPIDocument()', () => {\n    it('should append JSON AsyncAPI document with .json extension', () => {\n      const zip = archiverService.createZip();\n      const asyncapiDoc = {\n        asyncapi: '2.6.0',\n        info: {\n          title: 'Test API',\n          version: '1.0.0'\n        },\n        channels: {}\n      };\n\n      expect(() => {\n        archiverService.appendAsyncAPIDocument(zip, JSON.stringify(asyncapiDoc));\n      }).to.not.throw();\n    });\n\n    it('should append YAML AsyncAPI document with .yml extension', () => {\n      const zip = archiverService.createZip();\n      const yamlDoc = `asyncapi: 2.6.0\ninfo:\n  title: Test API\n  version: 1.0.0\nchannels: {}`;\n\n      expect(() => {\n        archiverService.appendAsyncAPIDocument(zip, yamlDoc);\n      }).to.not.throw();\n    });\n\n    it('should use custom filename when provided', () => {\n      const zip = archiverService.createZip();\n      const asyncapiDoc = {\n        asyncapi: '2.6.0',\n        info: { title: 'Test', version: '1.0.0' },\n        channels: {}\n      };\n\n      expect(() => {\n        archiverService.appendAsyncAPIDocument(zip, JSON.stringify(asyncapiDoc), 'custom-name');\n      }).to.not.throw();\n    });\n  });\n\n  describe('createTempDirectory()', () => {\n    it('should create a temporary directory', async () => {\n      const tempDir = await archiverService.createTempDirectory();\n      \n      expect(tempDir).to.be.a('string');\n      expect(tempDir.length).to.be.greaterThan(0);\n      \n      // Clean up\n      await archiverService.removeTempDirectory(tempDir);\n    });\n  });\n\n  describe('removeTempDirectory()', () => {\n    it('should remove temporary directory', async () => {\n      const tempDir = await archiverService.createTempDirectory();\n      \n      expect(() => {\n        return archiverService.removeTempDirectory(tempDir);\n      }).to.not.throw();\n    });\n\n    it('should handle removal of non-existent directory gracefully', async () => {\n      const nonExistentPath = '/non/existent/path';\n      \n      try {\n        await archiverService.removeTempDirectory(nonExistentPath);\n        // Should either succeed silently or throw a specific error\n        expect(true).to.equal(true);\n      } catch (error) {\n        // Expected behavior for non-existent paths\n        expect(error).to.be.an('error');\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/services/convert.service.test.ts",
    "content": "import { expect } from 'chai';\nimport { ConversionService } from '../../../src/domains/services/convert.service';\nimport { Specification } from '../../../src/domains/models/SpecificationFile';\nimport { AsyncAPIConvertVersion } from '@asyncapi/converter';\n\nconst validJsonAsyncAPI2_0_0 = `{\n  \"asyncapi\": \"2.0.0\",\n  \"info\": {\n    \"title\": \"Super test\",\n    \"version\": \"1.0.0\"\n  },\n  \"channels\": {}\n}`;\n\nconst validJsonAsyncAPI2_1_0 = `{\n  \"asyncapi\": \"2.1.0\",\n  \"info\": {\n    \"title\": \"Super test\",\n    \"version\": \"1.0.0\"\n  },\n  \"channels\": {}\n}`;\n\nconst validJsonAsyncAPI3_0_0 = `{\n  \"asyncapi\": \"3.0.0\",\n  \"info\": {\n    \"title\": \"Super test\",\n    \"version\": \"1.0.0\"\n  },\n  \"channels\": {}\n}`;\n\nconst invalidAsyncAPI = `{\n  \"asyncapi\": \"invalid\",\n  \"info\": {\n    \"title\": \"Super test\"\n  }\n}`;\n\ndescribe('ConversionService', () => {\n  let conversionService: ConversionService;\n\n  beforeEach(() => {\n    conversionService = new ConversionService();\n  });\n\n  describe('convertDocument()', () => {\n    it('should successfully convert AsyncAPI 2.0.0 to 2.4.0', async () => {\n      const specFile = new Specification(validJsonAsyncAPI2_0_0);\n      const options = {\n        format: 'asyncapi' as const,\n        'target-version': '2.4.0' as AsyncAPIConvertVersion\n      };\n\n      const result = await conversionService.convertDocument(specFile, options);\n\n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('convertedDocument');\n        expect(result.data).to.have.property('originalFormat', 'asyncapi');\n        expect(result.data?.convertedDocument).to.be.a('string');\n      }\n    });\n\n    it('should successfully convert AsyncAPI 2.0.0 to latest version (3.1.0)', async () => {\n      const specFile = new Specification(validJsonAsyncAPI2_0_0);\n      const options = {\n        format: 'asyncapi' as const\n      };\n\n      const result = await conversionService.convertDocument(specFile, options);\n\n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('convertedDocument');\n        expect(result.data).to.have.property('originalFormat', 'asyncapi');\n        // Should convert to 3.1.0 as default\n        expect(result.data?.convertedDocument).to.contain('3.1.0');\n      }\n    });\n\n    it('should handle conversion errors gracefully', async () => {\n      const invalidSpec = new Specification(invalidAsyncAPI);\n      const options = {\n        format: 'asyncapi' as const,\n        'target-version': '2.4.0' as AsyncAPIConvertVersion\n      };\n\n      try {\n        const result = await conversionService.convertDocument(invalidSpec, options);\n        \n        // If it returns a result, it should be unsuccessful\n        expect(result.success).to.equal(false);\n        expect(result.error).to.be.a('string');\n      } catch (error) {\n        // If it throws, that's also acceptable error handling\n        expect(error).to.be.an('error');\n      }\n    });\n\n    it('should return error for unsupported conversion format', async () => {\n      const specFile = new Specification(validJsonAsyncAPI2_0_0);\n      const options = {\n        format: 'unsupported' as any,\n        'target-version': '2.4.0' as AsyncAPIConvertVersion\n      };\n\n      const result = await conversionService.convertDocument(specFile, options);\n\n      expect(result.success).to.equal(false);\n      expect(result.error).to.contain('Unsupported conversion format');\n    });\n\n    it('should handle OpenAPI conversion', async () => {\n      const openApiSpec = `{\n        \"openapi\": \"3.0.0\",\n        \"info\": {\n          \"title\": \"Test API\",\n          \"version\": \"1.0.0\"\n        },\n        \"paths\": {}\n      }`;\n      \n      const specFile = new Specification(openApiSpec);\n      const options = {\n        format: 'openapi' as const,\n        perspective: 'client' as const\n      };\n\n      const result = await conversionService.convertDocument(specFile, options);\n\n      // Should either succeed or fail gracefully\n      expect(result).to.have.property('success');\n      if (!result.success) {\n        expect(result.error).to.be.a('string');\n      }\n    });\n  });\n\n  describe('handleLogging()', () => {\n    it('should return appropriate logging message for AsyncAPI conversion', () => {\n      const specFile = new Specification(validJsonAsyncAPI2_0_0);\n      specFile.getFilePath = () => '/test/spec.yaml';\n      \n      const options = {\n        format: 'asyncapi' as const,\n        'target-version': '2.4.0' as AsyncAPIConvertVersion\n      };\n\n      const message = conversionService.handleLogging(specFile, options);\n\n      expect(message).to.be.a('string');\n      expect(message).to.contain('AsyncAPI document');\n      expect(message).to.contain('/test/spec.yaml');\n      expect(message).to.contain('2.4.0');\n    });\n\n    it('should return appropriate logging message for OpenAPI conversion', () => {\n      const specFile = new Specification('{\"openapi\": \"3.0.0\"}');\n      specFile.getFilePath = () => '/test/openapi.yaml';\n      \n      const options = {\n        format: 'openapi' as const\n      };\n\n      const message = conversionService.handleLogging(specFile, options);\n\n      expect(message).to.be.a('string');\n      expect(message).to.contain('OpenAPI document');\n      expect(message).to.contain('/test/openapi.yaml');\n    });\n\n    it('should handle target-version as latest', () => {\n      const specFile = new Specification(validJsonAsyncAPI2_0_0);\n      specFile.getFilePath = () => '/test/spec.yaml';\n      \n      const options = {\n        format: 'asyncapi' as const\n      };\n\n      const message = conversionService.handleLogging(specFile, options);\n\n      expect(message).to.be.a('string');\n      expect(message).to.contain('latest');\n    });\n  });\n\n  describe('handleOutput()', () => {\n    it('should handle file output without throwing errors', async () => {\n      const tempPath = '/tmp/test-output.json';\n      const content = '{\"test\": \"content\"}';\n\n      try {\n        await conversionService.handleOutput(tempPath, content);\n        // If no error is thrown, the test passes\n        expect(true).to.equal(true);\n      } catch (error) {\n        // Expected to potentially fail due to file system permissions\n        expect(error).to.be.an('error');\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/services/validation.service.test.ts",
    "content": "import { expect } from 'chai';\nimport { ValidationService } from '../../../src/domains/services/validation.service';\nimport { Specification } from '../../../src/domains/models/SpecificationFile';\nimport { ConfigService } from '../../../src/domains/services/config.service';\nimport { promises as fs } from 'fs';\nimport path from 'path';\nimport os from 'os';\n\nconst validAsyncAPI = `{\n  \"asyncapi\": \"2.6.0\",\n  \"info\": {\n    \"title\": \"Test Service\",\n    \"version\": \"1.0.0\"\n  },\n  \"channels\": {}\n}`;\n\nconst invalidAsyncAPI = `{\n  \"asyncapi\": \"2.6.0\",\n  \"info\": {\n    \"title\": \"Test Service\"\n  },\n  \"channels\": {}\n}`;\n\nconst completelyInvalidDocument = `{\n  \"not\": \"asyncapi\",\n  \"document\": true\n}`;\n\n// Test AsyncAPI documents with external references\nconst asyncAPIWithPrivateGitHubRef = `{\n  \"asyncapi\": \"2.6.0\",\n  \"info\": {\n    \"title\": \"Test Service with Private GitHub Ref\",\n    \"version\": \"1.0.0\"\n  },\n  \"channels\": {\n    \"user/private\": {\n      \"publish\": {\n        \"message\": {\n          \"payload\": {\n            \"$ref\": \"https://github.com/private-org/private-repo/blob/main/schema.yaml#/payload\"\n          }\n        }\n      }\n    }\n  }\n}`;\n\nconst asyncAPIWithPublicHTTPRef = `{\n  \"asyncapi\": \"2.6.0\",\n  \"info\": {\n    \"title\": \"Test Service with Public HTTP Ref\",\n    \"version\": \"1.0.0\"\n  },\n  \"channels\": {\n    \"user/event\": {\n      \"publish\": {\n        \"message\": {\n          \"payload\": {\n            \"$ref\": \"https://raw.githubusercontent.com/asyncapi/spec/master/examples/streetlights.yml#/channels/light/measured/message/payload\"\n          }\n        }\n      }\n    }\n  }\n}`;\n\ndescribe('ValidationService', () => {\n  let validationService: ValidationService;\n\n  beforeEach(() => {\n    validationService = new ValidationService();\n  });\n\n  describe('validateDocument()', () => {\n    it('should validate a valid AsyncAPI document', async () => {\n      const specFile = new Specification(validAsyncAPI);\n      const options = {\n        'diagnostics-format': 'stylish' as const\n      };\n\n      const result = await validationService.validateDocument(specFile, options);\n\n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('status');\n        expect(result.data).to.have.property('diagnostics');\n        expect(result.data?.diagnostics).to.be.an('array');\n      }\n    });\n\n    it('should detect errors in invalid AsyncAPI document', async () => {\n      const specFile = new Specification(invalidAsyncAPI);\n      const options = {\n        'diagnostics-format': 'stylish' as const\n      };\n\n      const result = await validationService.validateDocument(specFile, options);\n\n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('status');\n        expect(result.data).to.have.property('diagnostics');\n        expect(result.data?.diagnostics).to.be.an('array');\n        \n        // Should have diagnostics for missing required fields\n        if (result.data?.diagnostics && result.data.diagnostics.length > 0) {\n          expect(result.data.diagnostics.some((d: any) => d.message)).to.equal(true);\n        }\n      }\n    });\n\n    it('should handle completely invalid documents', async () => {\n      const specFile = new Specification(completelyInvalidDocument);\n      const options = {\n        'diagnostics-format': 'stylish' as const\n      };\n\n      const result = await validationService.validateDocument(specFile, options);\n      \n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('diagnostics');\n        expect(result.data?.diagnostics).to.be.an('array');\n      }\n    });\n\n    it('should handle different output formats', async () => {\n      const specFile = new Specification(validAsyncAPI);\n      const formats = ['json', 'junit', 'html', 'text', 'teamcity', 'pretty'] as const;\n\n      for (const format of formats) {\n        const options = { 'diagnostics-format': format };\n        const result = await validationService.validateDocument(specFile, options);\n        \n        expect(result.success).to.equal(true);\n        if (result.success) {\n          expect(result.data).to.have.property('diagnostics');\n        }\n      }\n    });\n\n    it('should handle validation with output file', async () => {\n      const specFile = new Specification(validAsyncAPI);\n      const options = {\n        'diagnostics-format': 'json' as const,\n        output: '/tmp/validation-output.json'\n      };\n\n      const result = await validationService.validateDocument(specFile, options);\n\n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('diagnostics');\n      }\n    });\n\n    it('should handle malformed JSON', async () => {\n      const specFile = new Specification('{ invalid json }');\n      const options = {\n        'diagnostics-format': 'stylish' as const\n      };\n\n      const result = await validationService.validateDocument(specFile, options);\n\n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('diagnostics');\n        expect(result.data?.diagnostics).to.be.an('array');\n      }\n    });\n  });\n\n  describe('validateDocument() with external URLs', () => {\n    let originalConfig: any;\n    const CONFIG_DIR = path.join(os.homedir(), '.asyncapi');\n    const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\n\n    beforeEach(async () => {\n      // Backup original config\n      try {\n        const content = await fs.readFile(CONFIG_FILE, 'utf8');\n        originalConfig = JSON.parse(content);\n      } catch (err) {\n        originalConfig = null;\n      }\n    });\n\n    afterEach(async () => {\n      // Restore original config\n      if (originalConfig) {\n        await fs.writeFile(CONFIG_FILE, JSON.stringify(originalConfig, null, 2), 'utf8');\n      } else {\n        try {\n          await fs.unlink(CONFIG_FILE);\n        } catch (err) {\n          // File doesn't exist, ignore\n        }\n      }\n    });\n\n    it('should fail to validate document with private GitHub reference when not properly configured', async () => {\n      // Ensure no auth config exists for private GitHub repository\n      try {\n        await fs.unlink(CONFIG_FILE);\n      } catch (err) {\n        // File doesn't exist, ignore\n      }\n\n      const specFile = new Specification(asyncAPIWithPrivateGitHubRef);\n      const options = {\n        'diagnostics-format': 'stylish' as const\n      };\n\n      const result = await validationService.validateDocument(specFile, options);\n\n      // The validation succeeds means the validation command is successfully executed it is independent whether \n      // the document is valid or not \n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('status');\n        expect(result.data?.status).to.equal('invalid');\n        expect(result.data).to.have.property('diagnostics');\n        expect(result.data?.diagnostics).to.be.an('array');\n        \n        // Should have an invalid-ref diagnostic for the private GitHub URL\n        const invalidRefDiagnostic = result.data?.diagnostics?.find((d: any) => d.code === 'invalid-ref');\n        // eslint-disable-next-line no-unused-expressions\n        expect(invalidRefDiagnostic).to.exist;\n        // Error message varies by platform - macOS shows FetchError, Linux/Windows show \"Page not found\"\n        expect(invalidRefDiagnostic?.message).to.satisfy((msg: string) => \n          msg.includes('Page not found') || msg.includes('FetchError')\n        );\n        expect(invalidRefDiagnostic?.message).to.include('https://github.com/private-org/private-repo/blob/main/schema.yaml');\n      }\n    });\n\n    it('should validate document with public HTTP reference', async () => {\n      const specFile = new Specification(asyncAPIWithPublicHTTPRef);\n      const options = {\n        'diagnostics-format': 'stylish' as const\n      };\n\n      const result = await validationService.validateDocument(specFile, options);\n      // The validation succeeds means the validation command is successfully executed it is independent whether \n      // the document is valid or not \n      expect(result.success).to.equal(true);\n      if (result.success) {\n        expect(result.data).to.have.property('status');\n        expect(result.data).to.have.property('diagnostics');\n        expect(result.data?.diagnostics).to.be.an('array');\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/utils/ajv.test.ts",
    "content": "/* eslint-disable no-unused-expressions */\nimport { expect } from 'chai';\nimport { createAjvInstance } from '../../../src/utils/ajv';\n\ndescribe('createAjvInstance()', () => {\n  it('should create an AJV instance', () => {\n    const ajv = createAjvInstance();\n    expect(ajv).to.not.be.undefined;\n    expect(ajv.compile).to.be.a('function');\n  });\n\n  it('should support formats', () => {\n    const ajv = createAjvInstance();\n    const schema = {\n      type: 'string',\n      format: 'email'\n    };\n    \n    const validate = ajv.compile(schema);\n    expect(validate('test@example.com')).to.be.true;\n    expect(validate('invalid-email')).to.be.false;\n  });\n\n  it('should return validation errors when validation fails', () => {\n    const ajv = createAjvInstance();\n    const schema = {\n      type: 'object',\n      properties: {\n        name: { type: 'string' },\n        age: { type: 'number' }\n      },\n      required: ['name', 'age']\n    };\n    \n    const validate = ajv.compile(schema);\n    const result = validate({ name: 'John' }); // missing age\n    \n    expect(result).to.be.false;\n    expect(validate.errors).to.not.be.null;\n    expect(validate.errors).to.have.length.greaterThan(0);\n  });\n});\n"
  },
  {
    "path": "test/unit/utils/app-openapi.test.ts",
    "content": "import { expect } from 'chai';\nimport { getAppOpenAPI } from '../../../src/utils/app-openapi';\n\ndescribe('getAppOpenAPI()', () => {\n  it('should return OpenAPI document as JSON', async () => {\n    const openapi = await getAppOpenAPI();\n    expect(openapi.openapi).to.equal('3.1.0');\n    expect(openapi.info.title).to.equal('AsyncAPI Server API');\n  });\n\n  it('should return always this same instance of JSON', async () => {\n    const openapi1 = await getAppOpenAPI();\n    const openapi2 = await getAppOpenAPI();\n    // assert references\n    expect(openapi1 === openapi2).to.equal(true);\n  });\n});\n"
  },
  {
    "path": "test/unit/utils/registry.test.ts",
    "content": "import { expect } from 'chai';\nimport {\n  registryURLParser,\n  registryValidation,\n} from '../../../src/utils/generate/registry';\n\ndescribe('registryURLParser()', () => {\n  it('allows undefined', () => {\n    expect(registryURLParser(undefined)).to.equal(undefined);\n  });\n\n  it('rejects non-http(s) url', () => {\n    expect(() => registryURLParser('ftp://x')).to.throw(/Invalid --registry-url/);\n  });\n});\n\ndescribe('registryValidation()', () => {\n  let originalFetch: typeof globalThis.fetch;\n\n  beforeEach(() => {\n    originalFetch = globalThis.fetch;\n  });\n\n  afterEach(() => {\n    globalThis.fetch = originalFetch;\n  });\n\n  it('no-ops when registry url is missing', async () => {\n    await registryValidation(undefined);\n  });\n\n  it('succeeds on HEAD 200', async () => {\n    globalThis.fetch = async () =>\n      new Response(null, { status: 200 }) as unknown as Response;\n    await registryValidation('https://registry.npmjs.org');\n  });\n\n  it('retries with GET when HEAD returns 405', async () => {\n    let calls = 0;\n    globalThis.fetch = async (_url, init) => {\n      calls += 1;\n      const method = (init as RequestInit)?.method ?? 'GET';\n      if (calls === 1) {\n        expect(method).to.equal('HEAD');\n        return new Response(null, { status: 405 }) as unknown as Response;\n      }\n      expect(method).to.equal('GET');\n      return new Response(null, { status: 200 }) as unknown as Response;\n    };\n    await registryValidation('https://example.com/npm');\n    expect(calls).to.equal(2);\n  });\n\n  it('throws when 401 and no credentials', async () => {\n    globalThis.fetch = async () =>\n      new Response(null, { status: 401 }) as unknown as Response;\n    try {\n      await registryValidation('https://private.registry/npm');\n      expect.fail('expected throw');\n    } catch (e: unknown) {\n      expect((e as Error).message).to.match(/registryAuth|registryToken/);\n    }\n  });\n\n  it('does not throw on 401 when token is set', async () => {\n    globalThis.fetch = async () =>\n      new Response(null, { status: 401 }) as unknown as Response;\n    await registryValidation('https://private.registry/npm', undefined, 'tok');\n  });\n\n  it('surfaces timeout as a clear error', async () => {\n    process.env.ASYNCAPI_REGISTRY_CHECK_TIMEOUT_MS = '50';\n    try {\n      globalThis.fetch = async (_url, init) => {\n        const signal = (init as RequestInit).signal;\n        return await new Promise<Response>((_resolve, reject) => {\n          signal?.addEventListener('abort', () => {\n            reject(Object.assign(new Error('Aborted'), { name: 'AbortError' }));\n          });\n        });\n      };\n      try {\n        await registryValidation('http://10.255.255.1');\n        expect.fail('expected throw');\n      } catch (e: unknown) {\n        expect((e as Error).message).to.match(/timed out|unreachable/);\n        expect((e as Error).message).to.include('10.255.255.1');\n      }\n    } finally {\n      delete process.env.ASYNCAPI_REGISTRY_CHECK_TIMEOUT_MS;\n    }\n  });\n\n  it('includes underlying message on network failure', async () => {\n    globalThis.fetch = async () => {\n      throw new TypeError('fetch failed');\n    };\n    try {\n      await registryValidation('https://registry.npmjs.org');\n      expect.fail('expected throw');\n    } catch (e: unknown) {\n      expect((e as Error).message).to.include('Can\\'t fetch registryURL');\n      expect((e as Error).message).to.include('fetch failed');\n    }\n  });\n});\n"
  },
  {
    "path": "test/unit/utils/retrieve-language.test.ts",
    "content": "import { expect } from 'chai';\nimport { retrieveLangauge } from '../../../src/utils/retrieve-language';\n\ndescribe('retrieveLangauge()', () => {\n  it('should check that content is yaml', () => {\n    const result = retrieveLangauge('asyncapi: 2.2.0\\nfoobar: barfoo\\n');\n    expect(result).to.equal('yaml');\n  });\n\n  it('should check that content is json', () => {\n    const result = retrieveLangauge('{\"asyncapi\": \"2.2.0\", \"foobar\": \"barfoo\"}');\n    expect(result).to.equal('json');\n  });\n\n  it('should check that content is yaml - fallback for non json content', () => {\n    const result = retrieveLangauge('');\n    expect(result).to.equal('yaml');\n  });\n});\n"
  },
  {
    "path": "test/unit/utils/temp-dir.test.ts",
    "content": "import { expect } from 'chai';\nimport fs from 'fs';\nimport { createTempDirectory, removeTempDirectory } from '../../../src/utils/temp-dir';\nimport { closeStudioServer } from '../../helpers/index';\n\ndescribe('createTempDirectory() & removeTempDirectory()', () => {\n  after(async () => {\n    await closeStudioServer();\n    await closeStudioServer(4321);\n  });\n  it('should create and then remove temp folder', async () => {\n    // create dir\n    const tempDir = await createTempDirectory();\n    expect(fs.existsSync(tempDir)).to.equal(true);\n\n    // remove dir\n    await removeTempDirectory(tempDir);\n    expect(fs.existsSync(tempDir)).to.equal(false);\n  });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"outDir\": \"./lib\",\n    \"baseUrl\": \"./src\",\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"lib\": [\n      \"esnext\",\n    ],\n    \"paths\": {\n      \"@/*\": [\"*\"],\n      \"@src/*\": [\"*\"],\n      \"@cli/*\": [\"apps/cli/*\"],\n      \"@api/*\": [\"apps/api/*\"],\n      \"@services/*\": [\"domains/services/*\"],\n      \"@models/*\": [\"domains/models/*\"],\n      \"@utils/*\": [\"utils/*\"],\n      \"@errors/*\": [\"errors/*\"],\n      \"@interfaces/*\": [\"interfaces/*\"],\n    },\n    \"declaration\": true,\n    \"importHelpers\": true,\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n  },\n  \"include\": [\n    \"src\",\n  ],\n}\n"
  }
]