diff --git a/.azure-pipelines/NetworkValidation.yml b/.azure-pipelines/NetworkValidation.yml deleted file mode 100644 index 8b3ed7dd40c0..000000000000 --- a/.azure-pipelines/NetworkValidation.yml +++ /dev/null @@ -1,39 +0,0 @@ -jobs: -- job: "NetworkValidation" - condition: and(startsWith(variables['System.PullRequest.TargetBranch'], 'network-'), endsWith(variables['System.PullRequest.TargetBranch'], '-release')) - continueOnError: true - pool: - vmImage: vs2017-win2016 - variables: - NRP_SWAGGER_VALIDATION_OVERRIDE_PS_BRANCH: 'true' - NRP_BUILD_TOOLS_SKIP_GENERATE_PS1: 'true' - NRP_BUILD_TOOLS_OUTPUT_PATH: '$(System.DefaultWorkingDirectory)\Out' - steps: - - powershell: | - & "C:/Program Files (x86)/Microsoft SDKs/Windows/v10.0A/bin/NETFX 4.6.1 Tools/sn.exe" -Vr *,31bf3856ad364e35 - displayName: Bypass Strong Name validation - - task: NodeTool@0 - inputs: - versionSpec: '10.16.3' - displayName: Install Node.js - - script: npm install autorest -g - displayName: Install autorest - - script: git clone --depth 1 -- https://dev.azure.com/nrp-swagger/swagger-stack/_git/automation-scripts ..\BuildScripts - displayName: Clone build tools - - powershell: | - $swgrPath = (Get-Item ".\").FullName - $outputPath = $env:NRP_BUILD_TOOLS_OUTPUT_PATH - New-Item -Path $outputPath -ItemType "Directory" -Force | Out-Null - ..\BuildScripts\Test-SwaggerRelease.ps1 -Component Network -SwaggerRepoPath $swgrPath -OutputFolder $outputPath - failOnStderr: false - displayName: Test Swagger updates - - powershell: | - Copy-Item "..\BuildScripts\.azure-pipelines\.artifactignore" -Destination "$($env:NRP_BUILD_TOOLS_OUTPUT_PATH)\.artifactignore" -Force -ErrorAction "SilentlyContinue" - failOnStderr: false - condition: always() - displayName: Copy .artifactignore - - task: PublishPipelineArtifact@0 - condition: always() - inputs: - artifactName: SwaggerTestOutput - targetPath: $(NRP_BUILD_TOOLS_OUTPUT_PATH) \ No newline at end of file diff --git a/.azure-pipelines/SwaggerToSDK.yml b/.azure-pipelines/SwaggerToSDK.yml deleted file mode 100644 index 050b16bd3709..000000000000 --- a/.azure-pipelines/SwaggerToSDK.yml +++ /dev/null @@ -1,31 +0,0 @@ -jobs: -- job: "SDK" - timeoutInMinutes: 180 - strategy: - matrix: - java: - AZURE_SDK_REPO: azure-sdk-for-java - AZURE_SDK_PARAMS: '' - javascript: - AZURE_SDK_REPO: azure-sdk-for-js - AZURE_SDK_PARAMS: '' - python: - AZURE_SDK_REPO: azure-sdk-for-python - AZURE_SDK_PARAMS: '' - cliextension: - AZURE_SDK_REPO: azure-cli-extensions - AZURE_SDK_PARAMS: '' - trenton: - AZURE_SDK_REPO: azure-sdk-for-trenton - AZURE_SDK_PARAMS: '' - go: - AZURE_SDK_REPO: azure-sdk-for-go - AZURE_SDK_PARAMS: '-o latest' - pool: - vmImage: 'Ubuntu 16.04' - variables: - NODE_OPTIONS: '--max-old-space-size=8192' - steps: - - script: echo $(NODE_OPTIONS) - - script: "scripts/swagger-to-sdk.sh Azure/$(AZURE_SDK_REPO) -v $(AZURE_SDK_PARAMS)" - displayName: "Swagger to SDK script" diff --git a/.azure-pipelines/azure-pipelines-data-container-windows.yml b/.azure-pipelines/azure-pipelines-data-container-windows.yml deleted file mode 100644 index 9f410f1b7bb3..000000000000 --- a/.azure-pipelines/azure-pipelines-data-container-windows.yml +++ /dev/null @@ -1,41 +0,0 @@ -schedules: -- cron: "0 0 * * *" - displayName: Daily build - branches: - include: - - main - always: true - -pool: - vmImage: 'windows-latest' - -variables: - IMAGE_TAG_PREFIX: $[format('1.0.{0:HHm}', pipeline.startTime)] - -steps: -- task: Docker@2 - displayName: Login to production ACR - inputs: - command: login - containerRegistry: $(CONTAINER_REGISTRY_SERVICE_CONNECTION) - -- task: Docker@2 - displayName: Login to dogfood ACR - inputs: - command: login - containerRegistry: $(DOGFOOD_CONTAINER_REGISTRY_SERVICE_CONNECTION) - -- task: CopyFiles@2 - inputs: - SourceFolder: $(Build.SourcesDirectory) - contents: .git/** - targetFolder: $(Build.SourcesDirectory)\scripts\datacontainer-windows - -- task: Docker@2 - displayName: Build and Push - inputs: - command: buildAndPush - Dockerfile: scripts\datacontainer-windows\Dockerfile - repository: $(CONTAINER_REGISTRY_REPOSITORY_NAME) - tags: | - latest \ No newline at end of file diff --git a/.azure-pipelines/azure-pipelines-data-container.yml b/.azure-pipelines/azure-pipelines-data-container.yml deleted file mode 100644 index 1b27baf4137f..000000000000 --- a/.azure-pipelines/azure-pipelines-data-container.yml +++ /dev/null @@ -1,43 +0,0 @@ -schedules: -- cron: "0 0 * * *" - displayName: Daily build - branches: - include: - - main - always: true - -pool: - name: azsdk-pool - demands: ImageOverride -equals ubuntu-24.04 - -variables: - IMAGE_TAG_PREFIX: $[format('1.0.{0:HHm}', pipeline.startTime)] - -steps: -- task: Docker@2 - displayName: Login to production ACR - inputs: - command: login - containerRegistry: $(CONTAINER_REGISTRY_SERVICE_CONNECTION) - -- task: Docker@2 - displayName: Login to dogfood ACR - inputs: - command: login - containerRegistry: $(DOGFOOD_CONTAINER_REGISTRY_SERVICE_CONNECTION) - -- task: CopyFiles@2 - inputs: - SourceFolder: $(Build.SourcesDirectory) - contents: .git/** - targetFolder: $(Build.SourcesDirectory)/scripts/datacontainer - -- task: Docker@2 - displayName: Build and Push - inputs: - command: buildAndPush - Dockerfile: scripts/datacontainer/Dockerfile - repository: $(CONTAINER_REGISTRY_REPOSITORY_NAME) - tags: | - latest - $(Build.BuildNumber) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6b55ada71f6f..00e98dad13e1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -134,13 +134,13 @@ /specification/iothub/ @rkmanda # PRLabel: %KeyVault -/specification/keyvault/resource-manager/ @vickm @chen-karen @cheathamb36 @lgonsoulin +/specification/keyvault/resource-manager/ @vickm @chen-karen @cheathamb36 @Azure/azure-sdk-write-keyvault # PRLabel: %KeyVault -/specification/keyvault/Security.*/ @vickm @chen-karen @cheathamb36 @lgonsoulin @heaths @chlowell +/specification/keyvault/Security.*/ @vickm @chen-karen @cheathamb36 @Azure/azure-sdk-write-keyvault @heaths @chlowell # PRLabel: %KeyVault -/specification/keyvault/data-plane/ @vickm @chen-karen @cheathamb36 @lgonsoulin @heaths +/specification/keyvault/data-plane/ @vickm @chen-karen @cheathamb36 @Azure/azure-sdk-write-keyvault @heaths # PRLabel: %Logic App /specification/logic/ @pankajsn @tonytang-microsoft-com @@ -208,10 +208,10 @@ /specification/scheduler/ @pinwang81 # PRLabel: %Search -/specification/search/data-plane/ @arv100kri @bleroy @BevLoh @giulianob +/specification/search/data-plane/ @kuanlu95 @BevLoh @giulianob # PRLabel: %Search -/specification/search/resource-manager/ @efrainretana @conor-joplin @BevLoh @xiong-qiao @jonathanserbent @Draconicida @kuanlu95 @admayber +/specification/search/resource-manager/ @efrainretana @BevLoh @xiong-qiao @jonathanserbent @Draconicida @kuanlu95 @admayber /specification/serialconsole/ @amitchat @craigw @asinn826 @@ -225,7 +225,7 @@ /specification/servicefabric/ @juhacket @samedder # PRLabel: %SQL -/specification/sql/ @jamestao @ericshape @jeremyfrosti +/specification/sql/ @jamestao @ericshape @jeremyfrosti @mitesh-pv @achyuth-ms # PRLabel: %Storage /specification/storage/resource-manager/ @blueww @yifanz7 @@ -287,9 +287,11 @@ /eng/tools/typespec-migration-validation @pshao25 @live1206 /eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts @wanlwanl @raych1 @maririos /scripts/ @weshaggard @mikeharder -/specification/suppressions.yaml @weshaggard @mikeharder @benbp @raych1 @wanlwanl @maririos +/specification/suppressions.yaml @weshaggard @mikeharder @benbp @raych1 @wanlwanl @maririos @qiaozha /.github/CODEOWNERS @Azure/azure-sdk-eng ## Copilot /.github/copilot-instructions.md @praveenkuttappan @maririos -/.github/prompts/ @praveenkuttappan @maririos +/.github/prompts/ @praveenkuttappan @maririos +/.github/instructions/ @praveenkuttappan @maririos +/.github/chatmodes/ @praveenkuttappan @maririos diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1f05ffe67549..7c584b8e0a10 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -120,7 +120,6 @@ Examples of common errors and warnings that should be addressed after running th /** Service api versions **/ enum Versions { /** The 2023-11-01 api version **/ - @useDependency(Azure.Core.Versions.v1_0_Preview_2) v2023_11_01: "2023-11-01", } @@ -154,9 +153,10 @@ the context. If there are no TypeSpec project paths in the context, then prompt from the list of paths. If user does not have a TypeSpec project, then prompt user to create a new TypeSpec project. -### Pre-requisites +### Prerequisites - User should have a GitHub account and should be logged in to GitHub account using GitHub CLI `gh auth login`. - run `npm ci` to install the dependencies +- To use Azure MCP tool calls, the user must have PowerShell installed. Provide [PowerShell installation instructions](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) if not installed, and recommend restarting the IDE to start the MCP server. ### Basic Rules for SDK Generation from TypeSpec @@ -184,10 +184,10 @@ from the list of paths. If user does not have a TypeSpec project, then prompt us - Confirm with the user if they want to change the repo owner or target branch, and prompt for new values if needed. 6. **Exclusions**: - - Exclude changes in `.github` and `.vscode` folders from API spec and SDK pull requests. + - Exclude changes to the `.gitignore` file and contents within the `.github` and `.vscode` folders from API spec and SDK pull requests. 7. **Working Branch Rule**: - - Ensure the TypeSpec project repository and the current working repository are not on the `main` branch: + - If the typespec pull request already exists or is merged stay on the `main` branch, otherwise ensure the TypeSpec project repository and the current working repository are not on the `main` branch: - Check the current branch name for the cloned GitHub repository: - If the current branch is `main`, prompt the user to create a new branch using `git checkout -b `. @@ -201,8 +201,8 @@ from the list of paths. If user does not have a TypeSpec project, then prompt us By following these rules, the SDK release process will remain clear, structured, and user-friendly. ## Steps to generate SDK from TypeSpec API specification -Follow `/typespec-to-sdk` prompt to generate and release SDK from TypeSpec API specification. The process is divided into several steps, each with specific actions to ensure a smooth SDK generation and release process. -Do not skip the step that choose SDK generation method to ensure the user selects the appropriate method for SDK generation, either locally or using the SDK generation pipeline. Do not repeat the steps. +Follow [typespec to sdk](..\eng\common\instructions\azsdk-tools\typespec-to-sdk.instructions.md) to generate and release SDK from TypeSpec API specification. The process is divided into several steps, each with specific actions to ensure a smooth SDK generation and release process. +Do not skip the step that choose SDK generation method to ensure the user selects the appropriate method for SDK generation, either locally or using the SDK generation pipeline. Do not repeat the steps. Before using tools, check if user has Powershell installed. 1. **Identify TypeSpec Project**: Locate the TypeSpec project root path by checking for `tspconfig.yaml` or `main.tsp` files. 2. **Validate TypeSpec Specification**: Ensure the TypeSpec specification compiles without errors. @@ -210,12 +210,25 @@ Do not skip the step that choose SDK generation method to ensure the user select 4. **Review and Commit Changes**: Stage and commit TypeSpec modifications, ensuring the current branch is not "main". Do not create pull request yet. 5. **Create Specification Pull Request**: Create a pull request for TypeSpec changes if not already created. This is required only if there are TypeSpec changes in current branch. 6. **Choose SDK Generation Method**: Determine how to generate SDKs (locally or via pipeline). Only Python is supported for local SDK generation at this time. -7. **Generate SDKs via Pipeline**: Generate SDKs using `/run-sdk-gen-pipeline` prompt, monitor the pipeline status and displaying generated SDK PR links. +7. **Generate SDKs via Pipeline**: Generate SDKs using [run sdk gen pipeline](..\eng\common\instructions\azsdk-tools\run-sdk-gen-pipeline.instructions.md), monitor the pipeline status and displaying generated SDK PR links. 8. **Show generated SDK PR**: Display the generated SDK pull request links for review. -9. **Create a release plan**: To create a release plan refer to #file:.\prompts\create-release-plan.prompt.md -10. **Prompt user to change spec pull request to ready for review from draft status**: Update spec pull request to change it to ready for review. -11. **Release package**: Release the SDK package using `ReleaseSdkPackage` tool. - +9. **Validate Label and then Codeowners**: Validate the service label and codeowners for the created SDK service using both [validate service label](..\eng\common\instructions\azsdk-tools\validate-service-label.instructions.md) and [validate codeowners](..\eng\common\instructions\azsdk-tools\validate-codeowners.instructions.md). +10. **Create a release plan**: To create a release plan refer to [create release plan](..\eng\common\instructions\azsdk-tools\create-release-plan.instructions.md) +11. **Prompt user to change spec pull request to ready for review from draft status**: Update spec pull request to change it to ready for review. +12. **Release package**: Release the SDK package using `azsdk_release_sdk` tool. ## Release readiness of SDK and information about the release pipeline -Run `/check-package-readiness` prompt to check the release readiness of an SDK package. This prompt will collect the required information from the user, execute the readiness check, and present the results. \ No newline at end of file +Run [check package readiness](..\eng\common\instructions\azsdk-tools\check-package-readiness.instructions.md) to check the release readiness of an SDK package. This prompt will collect the required information from the user, execute the readiness check, and present the results. + +## Instructions for GitHub coding agent to generate SDKs using GitHub.com +Run only SDK generation step and Update SDK details back in issue description and release plan. Do not run TypeSpec validation, or service label check. This is applicable only when GitHub copilot coding agent is used to generate SDK and if a Release Plan is available in the context. +Coding agent must wait until all SDK generation pipeline are in completed status and then fetch the SDK details from pipeline run. This is applicable only when a GitHub issue is assigned to GitHub coding agent to generate SDK from a TypeSpec project. This is not applicable when running Copilot from Visual Studio and VS Code. + + steps: + - Run SDK generation pipeline for the given TypeSpec path for all languages mentioned in the description. + - Keep checking the pipeline status until pipeline is in completed or failed status. + - Find SDK pull request links from the pipeline run. + constraints: + - Do not invoke other steps. + - Do not modify main.tsp file or any files in TypeSpec project. + - Do not add API version to an existing main.tsp if input API version is not present. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 01a6589f7173..78b44a1972ce 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,13 +1,10 @@ version: 2 updates: - # For monorepos, only the root folder should be listed (not the sub-folders). - # We may also need to set "versioning-strategy" to "increase" rather than "increase-if-necessary", - # to update both package.json and package-lock.json files. In our case, we don't really care - # if the package.json files are updated or not, just that package-lock.json is update. - # https://github.com/dependabot/dependabot-core/issues/4993#issuecomment-1289133027 - package-ecosystem: "npm" directories: - "/" + - "/eng/tools" + - "/eng/tools/**" schedule: interval: "daily" ignore: @@ -20,9 +17,6 @@ updates: - dependency-name: "typescript" # Updated manually by the Liftr team - dependency-name: "@azure-tools/typespec-liftr-base" - - dependency-name: "@types/js-yaml" - - dependency-name: "autorest" - - dependency-name: "js-yaml" # Only allow patch updates for spec-gen-sdk - dependency-name: "@azure-tools/spec-gen-sdk" update-types: ["version-update:semver-minor", "version-update:semver-major"] @@ -41,8 +35,7 @@ updates: eslint: patterns: - "*eslint*" - # I prefer "increase-if-necessary", but I believe we may need to use "increase" since we're a monorepo - versioning-strategy: increase + versioning-strategy: increase-if-necessary - package-ecosystem: "npm" directories: - "/.github" @@ -63,5 +56,4 @@ updates: eslint: patterns: - "*eslint*" - # I prefer "increase-if-necessary", but we should align with the root package.json - versioning-strategy: increase + versioning-strategy: increase-if-necessary diff --git a/.github/instructions/openapi-review.instructions.md b/.github/instructions/openapi-review.instructions.md new file mode 100644 index 000000000000..b96bd1d9462c --- /dev/null +++ b/.github/instructions/openapi-review.instructions.md @@ -0,0 +1,36 @@ +--- +applyTo: "**/*.json" +--- + +# Copilot Review Instructions for reviewing OpenAPI v2 + +Please review OpenAPI v2 (Swagger) definition files with the following in mind: + +## API Guidelines Alignment + +- Ensure the API follows https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md. +- Validate naming conventions for paths, parameters, and schemas. +- Confirm consistent use of `x-ms-*` extensions where applicable. +- Check for proper use of `operationId`, `description`, and `summary` fields. + +## Versioning + +- Ensure the API version is clearly defined and follows the `YYYY-MM-DD` format. +- Confirm that breaking changes are not introduced in minor version updates. + +## Security + +- Validate that security definitions (e.g., OAuth2 scopes) are present and correctly applied. +- Ensure no unsecured endpoints are exposed unless explicitly documented. + +## Documentation + +- Flag missing or vague descriptions for operations, parameters, and responses. +- Recommend examples for request/response bodies where missing. + +## Style + +- Prefer camelCase for property names. +- Avoid abbreviations unless industry-standard. + +Respond in markdown format with clear suggestions and highlight any violations of the guidelines. diff --git a/.github/package-lock.json b/.github/package-lock.json index f7d36cf703b0..43f7958b9853 100644 --- a/.github/package-lock.json +++ b/.github/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "name": ".github", "dependencies": { "@apidevtools/json-schema-ref-parser": "^14.1.1", "debug": "^4.4.0", @@ -16,22 +17,25 @@ "devDependencies": { "@actions/github-script": "github:actions/github-script", "@eslint/js": "^9.22.0", + "@octokit/endpoint": "^11.0.0", "@octokit/rest": "^22.0.0", + "@octokit/types": "^14.1.0", "@octokit/webhooks-types": "^7.5.1", "@tsconfig/node20": "^20.1.4", "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", + "@vitest/coverage-v8": "^3.1.2", "cross-env": "^7.0.3", "eslint": "^9.22.0", + "fflate": "0.8.2", "globals": "^16.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", "typescript-eslint": "^8.38.0", - "vitest": "^3.0.7" + "vitest": "^3.1.2" } }, "node_modules/@actions/core": { @@ -135,12 +139,11 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.1.1.tgz", - "integrity": "sha512-uGF1YGOzzD50L7HLNWclXmsEhQflw8/zZHIz0/AzkJrKL5r9PceUipZxR/cp/8veTk4TVfdDJLyIwXLjaP5ePg==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.2.0.tgz", + "integrity": "sha512-NaGMMWwppbByagq+LwQMq6PMXHFWVu6kSwwx+eJfYTJ5zdpOvb9TIk6ZWxEEeXMUvGdVOZq3JalYsjsTZDvtkA==", "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" }, "engines": { @@ -148,6 +151,9 @@ }, "funding": { "url": "https://github.com/sponsors/philsturgeon" + }, + "peerDependencies": { + "@types/json-schema": "^7.0.15" } }, "node_modules/@babel/helper-string-parser": { @@ -171,13 +177,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -211,9 +217,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -228,9 +234,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -245,9 +251,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -262,9 +268,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -279,9 +285,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -296,9 +302,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -313,9 +319,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -330,9 +336,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -347,9 +353,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -364,9 +370,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -381,9 +387,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -398,9 +404,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -415,9 +421,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -432,9 +438,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -449,9 +455,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -466,9 +472,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -483,9 +489,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -500,9 +506,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -517,9 +523,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -534,9 +540,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -551,9 +557,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -568,9 +574,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", "cpu": [ "arm64" ], @@ -585,9 +591,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -602,9 +608,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -619,9 +625,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -636,9 +642,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -710,9 +716,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -720,9 +726,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -770,9 +776,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, "license": "MIT", "engines": { @@ -793,13 +799,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -911,9 +917,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -932,16 +938,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1031,20 +1037,44 @@ "node": ">= 18" } }, + "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@octokit/endpoint": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", - "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^13.1.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, + "node_modules/@octokit/endpoint/node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" + }, "node_modules/@octokit/graphql": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", @@ -1060,13 +1090,30 @@ "node": ">= 18" } }, - "node_modules/@octokit/openapi-types": { + "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { "version": "24.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "dev": true, "license": "MIT" }, + "node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "dev": true, + "license": "MIT" + }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", @@ -1164,6 +1211,23 @@ "@octokit/core": "5" } }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@octokit/request": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", @@ -1195,6 +1259,54 @@ "node": ">= 18" } }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@octokit/rest": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.0.tgz", @@ -1240,20 +1352,6 @@ "node": ">= 20" } }, - "node_modules/@octokit/rest/node_modules/@octokit/endpoint": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", - "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^14.0.0", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 20" - } - }, "node_modules/@octokit/rest/node_modules/@octokit/graphql": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", @@ -1269,13 +1367,6 @@ "node": ">= 20" } }, - "node_modules/@octokit/rest/node_modules/@octokit/openapi-types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", - "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", - "dev": true, - "license": "MIT" - }, "node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": { "version": "13.1.1", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz", @@ -1351,16 +1442,6 @@ "node": ">= 20" } }, - "node_modules/@octokit/rest/node_modules/@octokit/types": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", - "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^25.1.0" - } - }, "node_modules/@octokit/rest/node_modules/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", @@ -1376,13 +1457,13 @@ "license": "ISC" }, "node_modules/@octokit/types": { - "version": "13.10.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", - "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^24.2.0" + "@octokit/openapi-types": "^25.1.0" } }, "node_modules/@octokit/webhooks-types": { @@ -1404,9 +1485,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.1.tgz", + "integrity": "sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw==", "cpu": [ "arm" ], @@ -1418,9 +1499,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.1.tgz", + "integrity": "sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ==", "cpu": [ "arm64" ], @@ -1432,9 +1513,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.1.tgz", + "integrity": "sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A==", "cpu": [ "arm64" ], @@ -1446,9 +1527,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz", + "integrity": "sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ==", "cpu": [ "x64" ], @@ -1460,9 +1541,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.1.tgz", + "integrity": "sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ==", "cpu": [ "arm64" ], @@ -1474,9 +1555,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.1.tgz", + "integrity": "sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ==", "cpu": [ "x64" ], @@ -1488,9 +1569,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.1.tgz", + "integrity": "sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ==", "cpu": [ "arm" ], @@ -1502,9 +1583,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.1.tgz", + "integrity": "sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q==", "cpu": [ "arm" ], @@ -1516,9 +1597,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.1.tgz", + "integrity": "sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ==", "cpu": [ "arm64" ], @@ -1530,9 +1611,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.1.tgz", + "integrity": "sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ==", "cpu": [ "arm64" ], @@ -1544,9 +1625,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.1.tgz", + "integrity": "sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ==", "cpu": [ "loong64" ], @@ -1557,10 +1638,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.1.tgz", + "integrity": "sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ==", "cpu": [ "ppc64" ], @@ -1572,9 +1653,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.1.tgz", + "integrity": "sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw==", "cpu": [ "riscv64" ], @@ -1586,9 +1667,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.1.tgz", + "integrity": "sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA==", "cpu": [ "riscv64" ], @@ -1600,9 +1681,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.1.tgz", + "integrity": "sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ==", "cpu": [ "s390x" ], @@ -1614,9 +1695,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.1.tgz", + "integrity": "sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA==", "cpu": [ "x64" ], @@ -1628,9 +1709,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.1.tgz", + "integrity": "sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg==", "cpu": [ "x64" ], @@ -1642,9 +1723,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.1.tgz", + "integrity": "sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ==", "cpu": [ "arm64" ], @@ -1656,9 +1737,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.1.tgz", + "integrity": "sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ==", "cpu": [ "ia32" ], @@ -1670,9 +1751,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.1.tgz", + "integrity": "sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg==", "cpu": [ "x64" ], @@ -1745,9 +1826,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", - "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", "dev": true, "license": "MIT", "dependencies": { @@ -1755,17 +1836,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", + "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/type-utils": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1779,9 +1860,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.38.0", + "@typescript-eslint/parser": "^8.41.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -1795,16 +1876,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz", + "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4" }, "engines": { @@ -1816,18 +1897,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", + "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/tsconfig-utils": "^8.41.0", + "@typescript-eslint/types": "^8.41.0", "debug": "^4.3.4" }, "engines": { @@ -1838,18 +1919,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", + "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1860,9 +1941,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", + "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==", "dev": true, "license": "MIT", "engines": { @@ -1873,19 +1954,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", + "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1898,13 +1979,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz", + "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==", "dev": true, "license": "MIT", "engines": { @@ -1916,16 +1997,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", + "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/project-service": "8.41.0", + "@typescript-eslint/tsconfig-utils": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1941,7 +2022,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -1971,16 +2052,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1991,17 +2072,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", + "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/types": "8.41.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2202,9 +2283,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -2247,13 +2328,13 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.29", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } @@ -2324,9 +2405,9 @@ } }, "node_modules/chai": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -2491,9 +2572,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2504,32 +2585,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escape-string-regexp": { @@ -2546,20 +2627,20 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2798,6 +2879,13 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3119,9 +3207,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3236,9 +3324,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", - "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, "license": "MIT" }, @@ -3250,13 +3338,13 @@ "license": "ISC" }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { @@ -3298,9 +3386,9 @@ } }, "node_modules/marked": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.1.tgz", - "integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.1.tgz", + "integrity": "sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -3582,9 +3670,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -3667,9 +3755,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.1.tgz", + "integrity": "sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg==", "dev": true, "license": "MIT", "dependencies": { @@ -3683,26 +3771,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.48.1", + "@rollup/rollup-android-arm64": "4.48.1", + "@rollup/rollup-darwin-arm64": "4.48.1", + "@rollup/rollup-darwin-x64": "4.48.1", + "@rollup/rollup-freebsd-arm64": "4.48.1", + "@rollup/rollup-freebsd-x64": "4.48.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.48.1", + "@rollup/rollup-linux-arm-musleabihf": "4.48.1", + "@rollup/rollup-linux-arm64-gnu": "4.48.1", + "@rollup/rollup-linux-arm64-musl": "4.48.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.48.1", + "@rollup/rollup-linux-ppc64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-musl": "4.48.1", + "@rollup/rollup-linux-s390x-gnu": "4.48.1", + "@rollup/rollup-linux-x64-gnu": "4.48.1", + "@rollup/rollup-linux-x64-musl": "4.48.1", + "@rollup/rollup-win32-arm64-msvc": "4.48.1", + "@rollup/rollup-win32-ia32-msvc": "4.48.1", + "@rollup/rollup-win32-x64-msvc": "4.48.1", "fsevents": "~2.3.2" } }, @@ -4024,14 +4112,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -4041,11 +4129,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -4162,16 +4253,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", - "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", + "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.38.0", - "@typescript-eslint/parser": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0" + "@typescript-eslint/eslint-plugin": "8.41.0", + "@typescript-eslint/parser": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4182,7 +4273,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/undici": { @@ -4223,18 +4314,18 @@ } }, "node_modules/vite": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", - "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", + "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -4321,11 +4412,14 @@ } }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -4593,9 +4687,9 @@ } }, "node_modules/zod": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.8.tgz", - "integrity": "sha512-+MSh9cZU9r3QKlHqrgHMTSr3QwMGv4PLfR0M4N/sYWV5/x67HgXEhIGObdBkpnX8G78pTgWnIrBL2lZcNJOtfg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz", + "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/.github/package.json b/.github/package.json index 65cb98057057..ce72dfece6b1 100644 --- a/.github/package.json +++ b/.github/package.json @@ -17,22 +17,25 @@ "devDependencies": { "@actions/github-script": "github:actions/github-script", "@eslint/js": "^9.22.0", - "@octokit/webhooks-types": "^7.5.1", + "@octokit/endpoint": "^11.0.0", "@octokit/rest": "^22.0.0", + "@octokit/types": "^14.1.0", + "@octokit/webhooks-types": "^7.5.1", "@tsconfig/node20": "^20.1.4", "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", + "@vitest/coverage-v8": "^3.1.2", "cross-env": "^7.0.3", "eslint": "^9.22.0", + "fflate": "0.8.2", "globals": "^16.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", "typescript-eslint": "^8.38.0", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "scripts": { "lint": "npm run lint:eslint && npm run lint:tsc", @@ -42,6 +45,7 @@ "format:check": "prettier . --check", "format:check:ci": "prettier . --check --log-level debug", "test": "vitest", - "test:ci": "vitest run --coverage --reporter=verbose" + "test:ci": "vitest run --coverage --reporter=verbose", + "validate": "npm run lint && npm run format:check && npm run test:ci" } } diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index c83f90dcd345..fdf177296046 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -2533,6 +2533,12 @@ configuration: description: - if: - payloadType: Pull_Request + # If a PR changes no files, "includesModifiedFiles()" incorrectly returns "true" instead of "false", + # which adds all labels to the PR. + # Workaround by first checking "filesMatchPattern()", which correctly returns "false". + - filesMatchPattern: + pattern: ".*" + matchAny: true then: - if: - includesModifiedFiles: @@ -2611,6 +2617,12 @@ configuration: description: - if: - payloadType: Pull_Request + # If a PR changes no files, "includesModifiedFiles()" incorrectly returns "true" instead of "false", + # which adds all labels to the PR. + # Workaround by first checking "filesMatchPattern()", which correctly returns "false". + - filesMatchPattern: + pattern: ".*" + matchAny: true then: - if: - includesModifiedFiles: @@ -3301,6 +3313,12 @@ configuration: description: - if: - payloadType: Pull_Request + # If a PR changes no files, "includesModifiedFiles()" incorrectly returns "true" instead of "false", + # which adds all labels to the PR. + # Workaround by first checking "filesMatchPattern()", which correctly returns "false". + - filesMatchPattern: + pattern: ".*" + matchAny: true then: - if: - includesModifiedFiles: diff --git a/.github/prompts/tspconfig_add_missing_languages.prompt.md b/.github/prompts/tspconfig_add_missing_languages.prompt.md new file mode 100644 index 000000000000..83a9d0e858bd --- /dev/null +++ b/.github/prompts/tspconfig_add_missing_languages.prompt.md @@ -0,0 +1,14 @@ +--- +mode: agent +model: Claude Sonnet 4 +--- + +Add a language that is missing (for instance, adding Go, Python, Javascript, or C# support), using these rules: + +- New emitter configurations should be added as a child of the "options:" section, after any other emitters and their options. +- Do NOT search the files in the repository for examples, as these can be out of date. +- Determine which tspconfig.yaml template to use: + - If the currently open tspconfig.yaml contains @azure-tools/typespec-azure-rulesets/resource-manager, then use this template: [tspconfig template](https://raw.githubusercontent.com/Azure/azure-rest-api-specs/refs/heads/main/specification/contosowidgetmanager/Contoso.Management/tspconfig.yaml) + - If the currently open tspconfig.yaml contains @azure-tools/typespec-azure-rulesets/data-plane, then use this template: [tspconfig template](https://raw.githubusercontent.com/Azure/azure-rest-api-specs/refs/heads/main/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml) +- Using the appropriate template file as a guide, add in any emitter configurations, like @azure-tools/typespec-go, that do not have emitters configured in our tspconfig.yaml. + diff --git a/.github/shared/package-lock.json b/.github/shared/package-lock.json index 9cbc1f0f1c22..2e0b9901f3d3 100644 --- a/.github/shared/package-lock.json +++ b/.github/shared/package-lock.json @@ -13,7 +13,6 @@ "simple-git": "^3.27.0" }, "bin": { - "api-doc-preview": "cmd/api-doc-preview.js", "spec-model": "cmd/spec-model.js" }, "devDependencies": { @@ -22,15 +21,15 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", + "@vitest/coverage-v8": "^3.1.2", "cross-env": "^7.0.3", "eslint": "^9.22.0", "globals": "^16.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" } }, "node_modules/@ampproject/remapping": { @@ -48,12 +47,11 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.1.1.tgz", - "integrity": "sha512-uGF1YGOzzD50L7HLNWclXmsEhQflw8/zZHIz0/AzkJrKL5r9PceUipZxR/cp/8veTk4TVfdDJLyIwXLjaP5ePg==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.2.0.tgz", + "integrity": "sha512-NaGMMWwppbByagq+LwQMq6PMXHFWVu6kSwwx+eJfYTJ5zdpOvb9TIk6ZWxEEeXMUvGdVOZq3JalYsjsTZDvtkA==", "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" }, "engines": { @@ -61,6 +59,9 @@ }, "funding": { "url": "https://github.com/sponsors/philsturgeon" + }, + "peerDependencies": { + "@types/json-schema": "^7.0.15" } }, "node_modules/@babel/helper-string-parser": { @@ -84,13 +85,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -124,9 +125,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -141,9 +142,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -158,9 +159,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -175,9 +176,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -192,9 +193,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -209,9 +210,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -226,9 +227,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -243,9 +244,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -260,9 +261,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -277,9 +278,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -294,9 +295,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -311,9 +312,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -328,9 +329,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -345,9 +346,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -362,9 +363,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -379,9 +380,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -396,9 +397,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -413,9 +414,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -430,9 +431,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -447,9 +448,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -464,9 +465,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -481,9 +482,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", "cpu": [ "arm64" ], @@ -498,9 +499,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -515,9 +516,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -532,9 +533,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -549,9 +550,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -623,9 +624,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -633,9 +634,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -683,9 +684,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, "license": "MIT", "engines": { @@ -706,13 +707,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -814,9 +815,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -835,16 +836,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -879,9 +880,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.1.tgz", + "integrity": "sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw==", "cpu": [ "arm" ], @@ -893,9 +894,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.1.tgz", + "integrity": "sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ==", "cpu": [ "arm64" ], @@ -907,9 +908,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.1.tgz", + "integrity": "sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A==", "cpu": [ "arm64" ], @@ -921,9 +922,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz", + "integrity": "sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ==", "cpu": [ "x64" ], @@ -935,9 +936,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.1.tgz", + "integrity": "sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ==", "cpu": [ "arm64" ], @@ -949,9 +950,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.1.tgz", + "integrity": "sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ==", "cpu": [ "x64" ], @@ -963,9 +964,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.1.tgz", + "integrity": "sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ==", "cpu": [ "arm" ], @@ -977,9 +978,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.1.tgz", + "integrity": "sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q==", "cpu": [ "arm" ], @@ -991,9 +992,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.1.tgz", + "integrity": "sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ==", "cpu": [ "arm64" ], @@ -1005,9 +1006,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.1.tgz", + "integrity": "sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ==", "cpu": [ "arm64" ], @@ -1019,9 +1020,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.1.tgz", + "integrity": "sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ==", "cpu": [ "loong64" ], @@ -1032,10 +1033,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.1.tgz", + "integrity": "sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ==", "cpu": [ "ppc64" ], @@ -1047,9 +1048,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.1.tgz", + "integrity": "sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw==", "cpu": [ "riscv64" ], @@ -1061,9 +1062,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.1.tgz", + "integrity": "sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA==", "cpu": [ "riscv64" ], @@ -1075,9 +1076,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.1.tgz", + "integrity": "sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ==", "cpu": [ "s390x" ], @@ -1089,9 +1090,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.1.tgz", + "integrity": "sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA==", "cpu": [ "x64" ], @@ -1103,9 +1104,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.1.tgz", + "integrity": "sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg==", "cpu": [ "x64" ], @@ -1117,9 +1118,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.1.tgz", + "integrity": "sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ==", "cpu": [ "arm64" ], @@ -1131,9 +1132,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.1.tgz", + "integrity": "sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ==", "cpu": [ "ia32" ], @@ -1145,9 +1146,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.1.tgz", + "integrity": "sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg==", "cpu": [ "x64" ], @@ -1220,9 +1221,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", - "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", "dev": true, "license": "MIT", "dependencies": { @@ -1419,9 +1420,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -1464,13 +1465,13 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.29", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } @@ -1514,9 +1515,9 @@ } }, "node_modules/chai": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -1674,9 +1675,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1687,32 +1688,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escape-string-regexp": { @@ -1729,20 +1730,20 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -1925,11 +1926,14 @@ "license": "MIT" }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -2230,9 +2234,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2347,9 +2351,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", - "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, "license": "MIT" }, @@ -2361,13 +2365,13 @@ "license": "ISC" }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { @@ -2399,9 +2403,9 @@ } }, "node_modules/marked": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.1.tgz", - "integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.1.tgz", + "integrity": "sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -2649,9 +2653,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -2702,9 +2706,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.1.tgz", + "integrity": "sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg==", "dev": true, "license": "MIT", "dependencies": { @@ -2718,26 +2722,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.48.1", + "@rollup/rollup-android-arm64": "4.48.1", + "@rollup/rollup-darwin-arm64": "4.48.1", + "@rollup/rollup-darwin-x64": "4.48.1", + "@rollup/rollup-freebsd-arm64": "4.48.1", + "@rollup/rollup-freebsd-x64": "4.48.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.48.1", + "@rollup/rollup-linux-arm-musleabihf": "4.48.1", + "@rollup/rollup-linux-arm64-gnu": "4.48.1", + "@rollup/rollup-linux-arm64-musl": "4.48.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.48.1", + "@rollup/rollup-linux-ppc64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-musl": "4.48.1", + "@rollup/rollup-linux-s390x-gnu": "4.48.1", + "@rollup/rollup-linux-x64-gnu": "4.48.1", + "@rollup/rollup-linux-x64-musl": "4.48.1", + "@rollup/rollup-win32-arm64-msvc": "4.48.1", + "@rollup/rollup-win32-ia32-msvc": "4.48.1", + "@rollup/rollup-win32-x64-msvc": "4.48.1", "fsevents": "~2.3.2" } }, @@ -3035,14 +3039,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -3126,18 +3130,18 @@ } }, "node_modules/vite": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", - "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", + "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" diff --git a/.github/shared/package.json b/.github/shared/package.json index b82abb44c7aa..4be4dba5e740 100644 --- a/.github/shared/package.json +++ b/.github/shared/package.json @@ -6,25 +6,28 @@ "./array": "./src/array.js", "./breaking-change": "./src/breaking-change.js", "./changed-files": "./src/changed-files.js", - "./equality": "./src/equality.js", "./error-reporting": "./src/error-reporting.js", "./exec": "./src/exec.js", + "./git": "./src/git.js", + "./github": "./src/github.js", "./logger": "./src/logger.js", + "./math": "./src/math.js", "./path": "./src/path.js", "./readme": "./src/readme.js", "./sdk-types": "./src/sdk-types.js", + "./set": "./src/set.js", + "./simple-git": "./src/simple-git.js", "./sleep": "./src/sleep.js", "./sort": "./src/sort.js", "./spec-model-error": "./src/spec-model-error.js", "./spec-model": "./src/spec-model.js", "./swagger": "./src/swagger.js", "./tag": "./src/tag.js", - "./simple-git": "./src/simple-git.js", + "./time": "./src/time.js", "./test/examples": "./test/examples.js" }, "bin": { - "spec-model": "./cmd/spec-model.js", - "api-doc-preview": "./cmd/api-doc-preview.js" + "spec-model": "./cmd/spec-model.js" }, "_comments": { "dependencies": "Runtime dependencies must be kept to an absolute minimum for performance, ideally with no transitive dependencies", @@ -43,15 +46,15 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", + "@vitest/coverage-v8": "^3.1.2", "cross-env": "^7.0.3", "eslint": "^9.22.0", "globals": "^16.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "scripts": { "lint": "npm run lint:eslint && npm run lint:tsc", diff --git a/.github/shared/src/equality.js b/.github/shared/src/equality.js deleted file mode 100644 index 31521d49e123..000000000000 --- a/.github/shared/src/equality.js +++ /dev/null @@ -1,26 +0,0 @@ -// @ts-check - -/** - * - * @param {Set} set1 - * @param {Set} set2 - * @returns {boolean} True if sets are the same size and have the same elements - */ -export function setEquals(set1, set2) { - if (!(set1 instanceof Set) || !(set2 instanceof Set)) { - return false; - } - - if (set1.size !== set2.size) { - return false; - } - - // Should be O(N), since set lookup should be O(1) - for (const item of set1) { - if (!set2.has(item)) { - return false; - } - } - - return true; -} diff --git a/.github/shared/src/git.js b/.github/shared/src/git.js new file mode 100644 index 000000000000..9ab7cab1d2d6 --- /dev/null +++ b/.github/shared/src/git.js @@ -0,0 +1,11 @@ +// @ts-check + +/** + * Returns true if a string is a possible full git SHA (40 hex chars, case insensitive) + * + * @param {string} string + * @returns {boolean} + */ +export function isFullGitSha(string) { + return /^[0-9a-f]{40}$/i.test(string); +} diff --git a/.github/shared/src/github.js b/.github/shared/src/github.js new file mode 100644 index 000000000000..4c5dafaf3589 --- /dev/null +++ b/.github/shared/src/github.js @@ -0,0 +1,104 @@ +/* v8 ignore start */ + +// @ts-check + +export const PER_PAGE_MAX = 100; + +/** + * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions + * + * @readonly + * @enum {"completed" | "expected" | "failure" | "in_progress" | "pending" | "queued" | "requested" | "startup_failure" | "waiting" } + */ +export const CheckStatus = Object.freeze({ + /** + * @description The check run completed and has a conclusion. + */ + COMPLETED: "completed", + /** + * @description The check run is waiting for a status to be reported. + */ + EXPECTED: "expected", + /** + * @description The check run failed. + */ + FAILURE: "failure", + /** + * @description The check run is in progress. + */ + IN_PROGRESS: "in_progress", + /** + * @description The check run is at the front of the queue but the group-based concurrency limit has been reached. + */ + PENDING: "pending", + /** + * @description The check run has been queued. + */ + QUEUED: "queued", + /** + * @description The check run has been created but has not been queued. + */ + REQUESTED: "requested", + /** + * @description The check suite failed during startup. This status is not applicable to check runs. + */ + STARTUP_FAILURE: "startup_failure", + /** + * @description The check run is waiting for a deployment protection rule to be satisfied. + */ + WAITING: "waiting", +}); + +/** + * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions + * + * @readonly + * @enum {"action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" } + */ +export const CheckConclusion = Object.freeze({ + /** + * @description The check run provided required actions upon its completion. For more information, see Using the REST API to interact with checks. + */ + ACTION_REQUIRED: "action_required", + /** + * @description The check run was cancelled before it completed. + */ + CANCELLED: "cancelled", + /** + * @description The check run failed. + */ + FAILURE: "failure", + /** + * @description The check run completed with a neutral result. This is treated as a success for dependent checks in GitHub Actions. + */ + NEUTRAL: "neutral", + /** + * @description The check run was skipped. This is treated as a success for dependent checks in GitHub Actions. + */ + SKIPPED: "skipped", + /** + * @description The check run was marked stale by GitHub because it took too long. + */ + STALE: "stale", + /** + * @description The check run completed successfully. + */ + SUCCESS: "success", + /** + * @description The check run timed out. + */ + TIMED_OUT: "timed_out", +}); + +/** + * https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#create-a-commit-status--parameters + * + * @readonly + * @enum {"error" | "failure" | "pending" | "success"} + */ +export const CommitStatusState = Object.freeze({ + ERROR: "error", + FAILURE: "failure", + PENDING: "pending", + SUCCESS: "success", +}); diff --git a/.github/shared/src/logger.js b/.github/shared/src/logger.js index 8ccccd1ace86..0ae39e22d443 100644 --- a/.github/shared/src/logger.js +++ b/.github/shared/src/logger.js @@ -5,6 +5,7 @@ * @property {(message:string) => void} debug * @property {(message:string) => void} error * @property {(message:string) => void} info + * @property {(message:string) => void} warning * @property {() => boolean} isDebug */ @@ -51,6 +52,13 @@ export class ConsoleLogger { isDebug() { return this.#isDebug; } + + /** + * @param {string} message + */ + warning(message) { + console.warn(message); + } } // Singleton loggers diff --git a/.github/shared/src/math.js b/.github/shared/src/math.js new file mode 100644 index 000000000000..c02cd428b8f5 --- /dev/null +++ b/.github/shared/src/math.js @@ -0,0 +1,12 @@ +// @ts-check + +/** + * Convert a float [0,1] to a percentage string. + * + * @param {number} value - A number between 0 and 1. + * @param {number} [decimals=0] - How many decimal places to include. Default: 0 + * @returns {string} + */ +export function toPercent(value, decimals = 0) { + return `${(value * 100).toFixed(decimals)}%`; +} diff --git a/.github/shared/src/set.js b/.github/shared/src/set.js new file mode 100644 index 000000000000..87fbdeaab360 --- /dev/null +++ b/.github/shared/src/set.js @@ -0,0 +1,38 @@ +// @ts-check + +/** + * @param {Set} set1 + * @param {Set} set2 + * @returns {boolean} True if sets are the same size and have the same elements + */ +export function equals(set1, set2) { + if (!(set1 instanceof Set) || !(set2 instanceof Set)) { + return false; + } + + if (set1.size !== set2.size) { + return false; + } + + // Should be O(N), since set lookup should be O(1) + // Sets are same size, so iterating over either set has the same perf + for (const item of set1) { + if (!set2.has(item)) { + return false; + } + } + + return true; +} + +/** + * @template T + * @param {Set} a + * @param {Set} b + * @returns {Set} + */ +export function intersect(a, b) { + // Since set lookup is O(1), iterate over the smaller set for better perf: O(small) vs O(large) + const [small, large] = a.size < b.size ? [a, b] : [b, a]; + return new Set([...small].filter((value) => large.has(value))); +} diff --git a/.github/shared/src/spec-model.js b/.github/shared/src/spec-model.js index 9d3281f951dd..f1fac09e7eb3 100644 --- a/.github/shared/src/spec-model.js +++ b/.github/shared/src/spec-model.js @@ -5,6 +5,7 @@ import { resolve } from "path"; import { flatMapAsync, mapAsync } from "./array.js"; import { readme } from "./changed-files.js"; import { Readme } from "./readme.js"; +import { SpecModelError } from "./spec-model-error.js"; /** @type {Map} */ const specModelCache = new Map(); @@ -161,7 +162,12 @@ export class SpecModel { // The swagger file supplied does not exist in the given specModel if (affectedSwaggers.size === 0) { - throw new Error(`No affected swaggers found in specModel for ${swaggerPath}`); + throw new SpecModelError( + `Swagger file ${swaggerPath} not found in specModel.\n` + + `It must be referenced in the "input-file" section of a tag in a readme.md file ` + + `or in a swagger JSON file using $ref.`, + { source: swaggerPath }, + ); } return affectedSwaggers; diff --git a/.github/shared/src/swagger.js b/.github/shared/src/swagger.js index 0e204c4a613a..5a4bece6ccf9 100644 --- a/.github/shared/src/swagger.js +++ b/.github/shared/src/swagger.js @@ -5,6 +5,7 @@ import { readFile } from "fs/promises"; import { dirname, relative, resolve } from "path"; import { mapAsync } from "./array.js"; import { example } from "./changed-files.js"; +import { includesFolder } from "./path.js"; import { SpecModelError } from "./spec-model-error.js"; /** @@ -191,7 +192,7 @@ export class Swagger { * @returns {string} version kind (stable or preview) */ get versionKind() { - return dirname(this.#path).includes("/preview/") + return includesFolder(this.#path, "preview") ? API_VERSION_LIFECYCLE_STAGES.PREVIEW : API_VERSION_LIFECYCLE_STAGES.STABLE; } diff --git a/.github/shared/src/time.js b/.github/shared/src/time.js new file mode 100644 index 000000000000..b77c6be4bc82 --- /dev/null +++ b/.github/shared/src/time.js @@ -0,0 +1,77 @@ +// @ts-check + +/** + * @typedef {Object} DurationType + * @property {number} Millisecond - 1 millisecond + * @property {number} Second - 1 second in milliseconds + * @property {number} Minute - 1 minute in milliseconds + * @property {number} Hour - 1 hour in milliseconds + * @property {number} Day - 1 day in milliseconds + * @property {number} Week - 1 week in milliseconds + */ + +/** + * Common time duration constants in milliseconds. + * + * @readonly + * @type {DurationType} + */ +export const Duration = Object.freeze({ + Millisecond: 1, + Second: 1000, + Minute: 60 * 1000, + Hour: 60 * 60 * 1000, + Day: 24 * 60 * 60 * 1000, + Week: 7 * 24 * 60 * 60 * 1000, +}); + +/** + * Add milliseconds to a date. + * @param {Date} date + * @param {number} ms + * @returns {Date} + */ +export function add(date, ms) { + return new Date(date.getTime() + ms); +} + +/** + * Formats a duration of milliseconds as hh:mm:ss (always zero-padded). + * + * @param {number} ms + * @returns {string} + */ +export function formatDuration(ms) { + let totalSeconds = Math.floor(ms / Duration.Second); + + const hours = Math.floor(totalSeconds / 3600); + totalSeconds %= 3600; + + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + + const pad = (/** @type {number} */ n) => String(n).padStart(2, "0"); + + return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`; +} + +/** + * Returns the number of milliseconds between two dates (always non-negative) + * + * @param {Date} from + * @param {Date} to + * @returns {number} + */ +export function getDuration(from, to) { + return Math.abs(from.getTime() - to.getTime()); +} + +/** + * Subtract milliseconds from a date. + * @param {Date} date + * @param {number} ms + * @returns {Date} + */ +export function subtract(date, ms) { + return new Date(date.getTime() - ms); +} diff --git a/.github/shared/test/changed-files.test.js b/.github/shared/test/changed-files.test.js index e98dadff5d9d..2af62f98b84b 100644 --- a/.github/shared/test/changed-files.test.js +++ b/.github/shared/test/changed-files.test.js @@ -41,6 +41,20 @@ describe("changedFiles", () => { vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(files.join("\n")); await expect(getChangedFiles(options)).resolves.toEqual(files); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith(["--name-only", "HEAD^", "HEAD"]); + + const specFiles = files.filter((f) => f.startsWith("specification")); + vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(specFiles.join("\n")); + await expect(getChangedFiles({ ...options, paths: ["specification"] })).resolves.toEqual( + specFiles, + ); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith([ + "--name-only", + "HEAD^", + "HEAD", + "--", + "specification", + ]); }); const files = [ @@ -174,6 +188,7 @@ describe("changedFiles", () => { "should categorize files correctly with all types of changes (%o)", async (options) => { const gitOutput = [ + "M\t.github/src/changed-files.js", "A\tspecification/new-service/readme.md", "M\tspecification/existing-service/main.tsp", "D\tspecification/old-service/contoso.json", @@ -183,7 +198,31 @@ describe("changedFiles", () => { ].join("\n"); vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(gitOutput); - const result = await getChangedFilesStatuses(options); + let result = await getChangedFilesStatuses(options); + expect(result).toEqual({ + additions: ["specification/new-service/readme.md", "specification/service/derived.json"], + modifications: [ + ".github/src/changed-files.js", + "specification/existing-service/main.tsp", + "specification/service/type-changed.json", + ], + deletions: ["specification/old-service/contoso.json"], + renames: [ + { + from: "specification/service/old-name.json", + to: "specification/service/new-name.json", + }, + ], + total: 7, + }); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith(["--name-status", "HEAD^", "HEAD"]); + + const specGitOutput = gitOutput + .split("\n") + .filter((f) => f.includes("specification/")) + .join("\n"); + vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(specGitOutput); + result = await getChangedFilesStatuses({ ...options, paths: ["specification"] }); expect(result).toEqual({ additions: ["specification/new-service/readme.md", "specification/service/derived.json"], modifications: [ @@ -199,6 +238,13 @@ describe("changedFiles", () => { ], total: 6, }); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith([ + "--name-status", + "HEAD^", + "HEAD", + "--", + "specification", + ]); }, ); diff --git a/.github/shared/test/examples.js b/.github/shared/test/examples.js index 12336f56be2f..d8924c0183d7 100644 --- a/.github/shared/test/examples.js +++ b/.github/shared/test/examples.js @@ -1,5 +1,7 @@ // @ts-check +export const fullGitSha = "abc123abc123abc123abc123abc123abc123abc1"; + export const swaggerHandWritten = JSON.stringify("foo"); export const swaggerTypeSpecGenerated = JSON.stringify({ diff --git a/.github/shared/test/git.test.js b/.github/shared/test/git.test.js new file mode 100644 index 000000000000..d5e08121e2a4 --- /dev/null +++ b/.github/shared/test/git.test.js @@ -0,0 +1,25 @@ +// @ts-check + +import { describe, expect, it } from "vitest"; +import { isFullGitSha } from "../src/git"; +import { fullGitSha } from "./examples"; + +describe("git", () => { + it.each([ + [undefined, false], + [null, false], + ["", false], + // Short SHAs of 7 chars are not valid + ["abc1234", false], + // Invalid hex chars + ["aBcDeG0189".repeat(4), false], + ["aBcDe 0189".repeat(4), false], + ["aBcDe_0189".repeat(4), false], + // Valid + ["aBcDeF0189".repeat(4), true], + [fullGitSha, true], + ])("isFullGitSha(%o) => %o", (string, result) => { + // @ts-expect-error Testing invalid input types + expect(isFullGitSha(string)).toBe(result); + }); +}); diff --git a/.github/shared/test/logger.test.js b/.github/shared/test/logger.test.js index f474d44a12b5..644eaa3517e8 100644 --- a/.github/shared/test/logger.test.js +++ b/.github/shared/test/logger.test.js @@ -4,18 +4,20 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ConsoleLogger, debugLogger, defaultLogger } from "../src/logger"; describe("logger", () => { - let debugSpy, errorSpy, logSpy; + let debugSpy, errorSpy, logSpy, warnSpy; beforeEach(() => { debugSpy = vi.spyOn(console, "debug"); errorSpy = vi.spyOn(console, "error"); logSpy = vi.spyOn(console, "log"); + warnSpy = vi.spyOn(console, "warn"); }); afterEach(() => { debugSpy.mockRestore(); errorSpy.mockRestore(); logSpy.mockRestore(); + warnSpy.mockRestore(); }); it.each([ @@ -29,6 +31,9 @@ describe("logger", () => { logger.info("test info"); expect(logSpy).toBeCalledWith("test info"); + logger.warning("test warning"); + expect(warnSpy).toBeCalledWith("test warning"); + logger.error("test error"); expect(errorSpy).toBeCalledWith("test error"); diff --git a/.github/shared/test/math.test.js b/.github/shared/test/math.test.js new file mode 100644 index 000000000000..cf7098cbc295 --- /dev/null +++ b/.github/shared/test/math.test.js @@ -0,0 +1,29 @@ +// @ts-check + +import { describe, expect, it } from "vitest"; +import { toPercent } from "../src/math.js"; + +describe("math", () => { + it.each([ + [0, "0%"], + [0, "0.0%", 1], + [0, "0.00%", 2], + [0.0001, "0%"], + [0.0001, "0.0%", 1], + [0.0001, "0.01%", 2], + [0.001, "0%"], + [0.001, "0.1%", 1], + [0.01, "1%"], + [0.25, "25%"], + [1.0 / 3, "33%"], + [1.0 / 3, "33.3%", 1], + [1.0 / 3, "33.33%", 2], + [1.0 / 3, "33.333%", 3], + [0.5, "50%"], + [0.99, "99%"], + [0.999, "100%"], + [1, "100%"], + ])("toPercent(%f)=%s", (value, expected, decimals = 0) => { + expect(toPercent(value, decimals)).toEqual(expected); + }); +}); diff --git a/.github/shared/test/readme.test.js b/.github/shared/test/readme.test.js index 4602546d189b..5fee3ef35e65 100644 --- a/.github/shared/test/readme.test.js +++ b/.github/shared/test/readme.test.js @@ -26,8 +26,6 @@ describe("readme", () => { expect(readme.path).toBe(resolve("/specs/foo/readme.md")); }); - // TODO: Test that path is resolved against backpointer - it("can be created with string content", async () => { const folder = "/fake"; const readme = new Readme(resolve(folder, "readme.md"), { @@ -51,6 +49,59 @@ describe("readme", () => { expect(swaggerPaths.sort()).toEqual(expectedPaths.sort()); }); + // If tag names are duplicated (with no disambiguating conditionals), it's almost certainly a bug + // in the readme that should be fixed. + it("throws if duplicate tags", async () => { + let content = ` +\`\`\`yaml $(tag) == 'package-2025-01-01' +input-file: + - foo.json +\`\`\` +\`\`\`yaml $(tag) == 'package-2025-01-01' +input-file: + - bar.json +\`\`\` +`; + + let readme = new Readme("foo", { ...options, content }); + + expect(readme.getTags()).rejects.toThrowError( + "Multiple input-file definitions for tag package-2025-01-01", + ); + }); + + // A few existing specs use duplicate tags, but with conditionals to disambiguate at runtime. + // + // While this may work for generating the SDK itself (where you can specify the additional values), + // it doesn't work for static analysis tools like LintDiff, since LintDiff wouldn't know what + // additional values to pass to autorest. + // + // It may be possible to support this in spec-model, by allowing duplicate tags with different conditionals. + // However, downstream users of the tags for static analysis would still need to throw. Also, we only + // found two specs using this pattern (and only in the private repo). So I think it's better to continue + // disallowing this, and instead rename the conflicting tags in the readme. Rather than adding + // complexity to spec-model for an edge case (with workarounds). + // + // More details: https://github.com/Azure/azure-rest-api-specs/issues/37003 + it("throws if duplicate tags with different conditionals", async () => { + let content = ` +\`\`\`yaml $(tag) == 'package-2025-01-01' +input-file: + - foo.json +\`\`\` +\`\`\`yaml $(tag) == 'package-2025-01-01' && $(test-condition) +input-file: + - bar.json +\`\`\` +`; + + let readme = new Readme("foo", { ...options, content }); + + expect(readme.getTags()).rejects.toThrowError( + "Multiple input-file definitions for tag package-2025-01-01", + ); + }); + it("can be created with empty content", async () => { const folder = "/fake"; const readme = new Readme(resolve(folder, "readme.md"), { diff --git a/.github/shared/test/equality.test.js b/.github/shared/test/set.test.js similarity index 53% rename from .github/shared/test/equality.test.js rename to .github/shared/test/set.test.js index b4ab178f4e6e..82712fa89ebe 100644 --- a/.github/shared/test/equality.test.js +++ b/.github/shared/test/set.test.js @@ -1,9 +1,9 @@ // @ts-check import { describe, expect, it } from "vitest"; -import { setEquals } from "../src/equality.js"; +import { equals, intersect } from "../src/set"; -describe("equality", () => { +describe("set", () => { it.each([ // not instanceof Set: null, undefined [null, null, false], @@ -30,10 +30,46 @@ describe("equality", () => { [new Set([1]), new Set([1]), true], [new Set([1, 2]), new Set([1, 2]), true], [new Set([1, 2, 3]), new Set([1, 2, 3]), true], - ])("setEquals(%s, %s, %s)", (set1, set2, expected) => { + ])("equals(%o, %o) = %o", (set1, set2, expected) => { // @ts-expect-error testing runtime behavior of invalid types - expect(setEquals(set1, set2)).toBe(expected); + expect(equals(set1, set2)).toBe(expected); // @ts-expect-error testing runtime behavior of invalid types - expect(setEquals(set2, set1)).toBe(expected); + expect(equals(set2, set1)).toBe(expected); }); + + it.each([ + [[], [], []], + [[1, 2, 3], [], []], + [ + [1, 2, 3], + [2, 3, 4], + [2, 3], + ], + [[1, 2, 3], [4, 5, 6], []], + [ + [1, 2, 3], + [1, 2, 3], + [1, 2, 3], + ], + [ + ["a", "b", "c"], + ["b", "c", "d"], + ["b", "c"], + ], + ])( + "intersect(%o, %o) = %o", + async ( + /** @type {(string|number)[]} */ a, + /** @type {(string|number)[]} */ b, + /** @type {(string|number)[]} */ result, + ) => { + const setA = new Set(a); + const setB = new Set(b); + const setResult = new Set(result); + + // Check both orders, result should be same + expect(intersect(setA, setB)).toEqual(setResult); + expect(intersect(setB, setA)).toEqual(setResult); + }, + ); }); diff --git a/.github/shared/test/spec-model.test.js b/.github/shared/test/spec-model.test.js index e159719055a9..cf38669521d7 100644 --- a/.github/shared/test/spec-model.test.js +++ b/.github/shared/test/spec-model.test.js @@ -7,6 +7,7 @@ import { describe, expect, it } from "vitest"; import { mapAsync } from "../src/array.js"; import { ConsoleLogger } from "../src/logger.js"; import { SpecModel } from "../src/spec-model.js"; +import { Duration } from "../src/time.js"; import { repoRoot } from "./repo.js"; const options = { logger: new ConsoleLogger(/*debug*/ true) }; @@ -232,7 +233,7 @@ describe("SpecModel", () => { const swaggerPath = resolve(folder, "data-plane/not-found.json"); await expect(specModel.getAffectedSwaggers(swaggerPath)).rejects.toThrowError( - /no affected swaggers/i, + /not found in specModel/i, ); }); @@ -305,52 +306,48 @@ describe("SpecModel", () => { // the '.skip' from the describe block. Put '.skip' back in when done or this // test may fail unexpectedly in the future. describe.skip("Parse readmes", () => { - it( - "Does not produce exceptions", - { timeout: 30 * 60 * 1000 /* 30 minutes */ }, - async ({ expect }) => { - const excludeFolders = [ - "authorization", // specification/authorization/resource-manager/readme.md defines has duplicate tags including 'package-2020-10-01' - "azureactivedirectory", // specification/azureactivedirectory/resource-manager/readme.md has duplicate tags including 'package-preview-2020-07' - "cost-management", // specification/cost-management/resource-manager/readme.md has duplicate tags including 'package-2019-01' - "migrate", // specification/migrate/resource-manager/readme.md has duplicate tags including 'package-migrate-2023-04' - "quota", // specification/quota/resource-manager/readme.md has duplicate tags including 'package-2023-02-01' - "redisenterprise", // specification/redisenterprise/resource-manager/readme.md has duplicate tags including 'package-2024-02' - "security", // specification/security/resource-manager/readme.md has duplicate tags including 'package-2021-07-preview-only' - "confidentialledger", // data-plane/readme.md tag 'package-2022-04-20-preview-ledger' points to a swagger file that doesn't exist - "network", // network takes a long time to evaluate - "servicenetworking", // servicenetworking includes a swagger file which references a file that doesn't exist - ]; - - // List all folders under specification/ - const folders = await readdir(join(repoRoot, "specification"), { - withFileTypes: true, - }); - const services = folders - .filter((f) => f.isDirectory() && !excludeFolders.includes(f.name)) - .map((f) => f.name); - for (const folder of services) { - // Folders are listed in alphabetical order, when running this function - // iteratively over all service folders, a value can be placed in in this - // condition to skip folders that appear before a given folder. This means - // you won't have to wait for tests to run over all folders that have - // previously passed. - if (folder < "000") { - console.log(`Skipping service: ${folder}`); - continue; - } + it("Does not produce exceptions", { timeout: 30 * Duration.Minute }, async ({ expect }) => { + const excludeFolders = [ + "authorization", // specification/authorization/resource-manager/readme.md defines has duplicate tags including 'package-2020-10-01' + "azureactivedirectory", // specification/azureactivedirectory/resource-manager/readme.md has duplicate tags including 'package-preview-2020-07' + "cost-management", // specification/cost-management/resource-manager/readme.md has duplicate tags including 'package-2019-01' + "migrate", // specification/migrate/resource-manager/readme.md has duplicate tags including 'package-migrate-2023-04' + "quota", // specification/quota/resource-manager/readme.md has duplicate tags including 'package-2023-02-01' + "redisenterprise", // specification/redisenterprise/resource-manager/readme.md has duplicate tags including 'package-2024-02' + "security", // specification/security/resource-manager/readme.md has duplicate tags including 'package-2021-07-preview-only' + "confidentialledger", // data-plane/readme.md tag 'package-2022-04-20-preview-ledger' points to a swagger file that doesn't exist + "network", // network takes a long time to evaluate + "servicenetworking", // servicenetworking includes a swagger file which references a file that doesn't exist + ]; + + // List all folders under specification/ + const folders = await readdir(join(repoRoot, "specification"), { + withFileTypes: true, + }); + const services = folders + .filter((f) => f.isDirectory() && !excludeFolders.includes(f.name)) + .map((f) => f.name); + for (const folder of services) { + // Folders are listed in alphabetical order, when running this function + // iteratively over all service folders, a value can be placed in in this + // condition to skip folders that appear before a given folder. This means + // you won't have to wait for tests to run over all folders that have + // previously passed. + if (folder < "000") { + console.log(`Skipping service: ${folder}`); + continue; + } - console.log(`Testing service: ${folder}`); - const specModel = new SpecModel(`specification/${folder}`, options); + console.log(`Testing service: ${folder}`); + const specModel = new SpecModel(`specification/${folder}`, options); - expect(specModel).toBeDefined(); - } - }, - ); + expect(specModel).toBeDefined(); + } + }); it( "runs properly against specific services", - { timeout: 30 * 60 * 1000 /* 30 minutes */ }, + { timeout: 30 * Duration.Minute }, async ({ expect }) => { const folders = [ // Fill in services to test here @@ -441,22 +438,27 @@ describe("getSwaggers", () => { }); it("should work with swagger fixtures", async () => { - const folder = resolve( - __dirname, - "fixtures/swagger/specification/servicelinker/resource-manager", - ); + const folder = resolve(__dirname, "fixtures/swagger"); const specModel = new SpecModel(folder, options); const swaggers = await specModel.getSwaggers(); - // Should return an array (may be empty if no valid readmes in this fixture) - expect(swaggers.length).toBe(9); - // If swaggers are found, they should have the expected structure - for (const swagger of swaggers) { - expect(swagger.path).toBeDefined(); - expect(swagger.versionKind).toBeDefined(); - } - expect(swaggers[0].path).contains(folder); - expect(swaggers[0].versionKind).toBe("stable"); + // Expected paths as complete normalized paths + const expectedSwaggerPaths = [ + "specification/common-types/resource-management/v2/types.json", + "specification/common-types/resource-management/v3/types.json", + "specification/common-types/resource-management/v3/types.json", + "specification/common-types/resource-management/v3/types.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/preview/2023-04-01-preview/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/preview/2024-07-01-preview/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/stable/2022-05-01/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/stable/2024-04-01/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/stable/2024-04-01/test.json", + ]; + + // Sort swaggers by path to ensure consistent order + expect(swaggers.sort((a, b) => a.path.localeCompare(b.path)).map((s) => s.path)).toEqual( + expectedSwaggerPaths.map((p) => resolve(folder, p)), + ); }); }); diff --git a/.github/shared/test/swagger.test.js b/.github/shared/test/swagger.test.js index abca25fe9ffb..7704a5ba099a 100644 --- a/.github/shared/test/swagger.test.js +++ b/.github/shared/test/swagger.test.js @@ -2,7 +2,7 @@ import { dirname, join, resolve } from "path"; import { describe, expect, it } from "vitest"; -import { Swagger } from "../src/swagger.js"; +import { API_VERSION_LIFECYCLE_STAGES, Swagger } from "../src/swagger.js"; import { fileURLToPath } from "url"; import { ConsoleLogger } from "../src/logger.js"; @@ -71,6 +71,14 @@ describe("Swagger", () => { ); }); + it("computes versionKind from path", () => { + let swagger = new Swagger(resolve("foo/preview/2025-01-01-preview/foo.json")); + expect(swagger.versionKind).toEqual(API_VERSION_LIFECYCLE_STAGES.PREVIEW); + + swagger = new Swagger(resolve("foo/stable/2025-01-01/foo.json")); + expect(swagger.versionKind).toEqual(API_VERSION_LIFECYCLE_STAGES.STABLE); + }); + describe("getOperations", () => { it("should return normal operations", async () => { const testFixturePath = join(__dirname, "fixtures", "swagger", "specification"); diff --git a/.github/shared/test/time.test.js b/.github/shared/test/time.test.js new file mode 100644 index 000000000000..6b157cc004c8 --- /dev/null +++ b/.github/shared/test/time.test.js @@ -0,0 +1,21 @@ +// @ts-check + +import { describe, expect, it } from "vitest"; + +import { add, Duration, formatDuration, getDuration, subtract } from "../src/time.js"; + +describe("time", () => { + // Cover Duration, formatDuration, getDuration, add, and subtract in a single test + it.each([ + [Duration.Millisecond, "00:00:00"], + [Duration.Second, "00:00:01"], + [Duration.Minute, "00:01:00"], + [Duration.Hour, "01:00:00"], + [Duration.Day, "24:00:00"], + [Duration.Week, "168:00:00"], + ])("formatDuration(%i)=%s", (duration, expected) => { + const now = new Date(); + expect(formatDuration(getDuration(now, add(now, duration)))).toEqual(expected); + expect(formatDuration(getDuration(now, subtract(now, duration)))).toEqual(expected); + }); +}); diff --git a/.github/workflows/SDK-Suppressions-Label.yaml b/.github/workflows/SDK-Suppressions-Label.yaml index 920b5bffe4cf..c8212c9a1cbe 100644 --- a/.github/workflows/SDK-Suppressions-Label.yaml +++ b/.github/workflows/SDK-Suppressions-Label.yaml @@ -2,6 +2,13 @@ name: SDK Suppressions on: pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited branches: - main - RPSaaSMaster diff --git a/.github/workflows/_reusable-set-check-status.yaml b/.github/workflows/_reusable-set-check-status.yaml index 9798dce2a0a2..1f4c1779f964 100644 --- a/.github/workflows/_reusable-set-check-status.yaml +++ b/.github/workflows/_reusable-set-check-status.yaml @@ -68,6 +68,13 @@ jobs: '${{ inputs.overriding_label }}' ); + - if: ${{ always() && steps.set-status.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.set-status.outputs.head_sha }} + - if: ${{ always() && steps.set-status.outputs.issue_number }} name: Upload artifact with issue number uses: ./.github/actions/add-empty-artifact diff --git a/.github/workflows/arm-auto-signoff.yaml b/.github/workflows/arm-auto-signoff.yaml index ca099c309749..d4f46fdb1eb5 100644 --- a/.github/workflows/arm-auto-signoff.yaml +++ b/.github/workflows/arm-auto-signoff.yaml @@ -1,36 +1,39 @@ name: ARM Auto SignOff on: - issue_comment: - types: - - edited # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on # labels from bot accounts in fork PRs. pull_request_target is also more similar to the other - # triggers "issue_comment" and "workflow_run" -- they are all privileged# and run in the target - # branch and repo -- which simplifies implementation. + # trigger "workflow_run" -- they are both privileged and run in the target branch and repo -- + # which simplifies implementation. pull_request_target: types: # Depends on labels, so must re-evaluate whenever a relevant label is manually added or removed. - labeled - unlabeled workflow_run: - workflows: ["ARM Incremental TypeSpec"] + workflows: + ["ARM Incremental TypeSpec", "Swagger Avocado - Set Status", "Swagger LintDiff - Set Status"] types: [completed] permissions: + # actions.listWorkflowRunsForRepo + # actions.listWorkflowRunArtifacts actions: read - checks: read + # default contents: read + # issues.listLabelsOnIssue issues: read + # issues.listLabelsOnIssue pull-requests: read + # repos.listCommitStatusesForRef + statuses: read jobs: arm-auto-signoff: name: ARM Auto SignOff + # workflow_run - already filtered by triggers above # pull_request_target:labeled - filter to only the input and output labels - # issue_comment:edited - filter to only PR comments containing "next steps to merge", - # a signal that checks like "Swagger LintDiff" or "Swagger Avocado" status may have changed if: | github.event_name == 'workflow_run' || (github.event_name == 'pull_request_target' && @@ -41,10 +44,7 @@ jobs: github.event.label.name == 'ARMReview' || github.event.label.name == 'ARMSignedOff' || github.event.label.name == 'NotReadyForARMReview' || - github.event.label.name == 'SuppressionReviewRequired')) || - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - contains(github.event.comment.body, 'next steps to merge')) + github.event.label.name == 'SuppressionReviewRequired')) runs-on: ubuntu-24.04 @@ -97,15 +97,28 @@ jobs: # Convert "add/remove" to "true/false" value: "${{ fromJson(steps.get-label-action.outputs.result).labelAction == 'add' }}" + # Required for consumers to identify the head SHA associated with this workflow run. + # Output can be trusted, because it was uploaded from a workflow that is trusted, + # because "issue_comment", and "workflow_run" only trigger on workflows in the default branch. + # Consumers should verify the "event_name" before attempting to extract from the artifact name. + - if: | + always() && + (github.event_name == 'issue_comment' || github.event_name == 'workflow_run') && + fromJson(steps.get-label-action.outputs.result).headSha + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: "head-sha" + value: "${{ fromJson(steps.get-label-action.outputs.result).headSha }}" + # Required for consumers to identify the PR associated with this workflow run. # Output can be trusted, because it was uploaded from a workflow that is trusted, # because "issue_comment", and "workflow_run" only trigger on workflows in the default branch. # Consumers should verify the "event_name" before attempting to extract from the artifact name. - if: | - (github.event_name == 'issue_comment' || - github.event_name == 'workflow_run') && - (fromJson(steps.get-label-action.outputs.result).labelAction == 'add' || - fromJson(steps.get-label-action.outputs.result).labelAction == 'remove') + always() && + (github.event_name == 'issue_comment' || github.event_name == 'workflow_run') && + fromJson(steps.get-label-action.outputs.result).issueNumber > 0 name: Upload artifact with issue number uses: ./.github/actions/add-empty-artifact with: diff --git a/.github/workflows/arm-incremental-typespec.yaml b/.github/workflows/arm-incremental-typespec.yaml index a0055862fc99..b84d1017ff43 100644 --- a/.github/workflows/arm-incremental-typespec.yaml +++ b/.github/workflows/arm-incremental-typespec.yaml @@ -2,6 +2,13 @@ name: ARM Incremental TypeSpec on: pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited paths: # Always trigger on changes to WF itself - ".github/**" diff --git a/.github/workflows/avocado-code.yaml b/.github/workflows/avocado-code.yaml index b46dbf169349..15adf2d98823 100644 --- a/.github/workflows/avocado-code.yaml +++ b/.github/workflows/avocado-code.yaml @@ -1,6 +1,14 @@ name: "Swagger Avocado - Analyze Code" -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read diff --git a/.github/workflows/avocado-status.yaml b/.github/workflows/avocado-status.yaml index 80942c1c2544..eaf2316c30b6 100644 --- a/.github/workflows/avocado-status.yaml +++ b/.github/workflows/avocado-status.yaml @@ -15,7 +15,9 @@ on: - labeled - unlabeled workflow_run: - workflows: ["Swagger Avocado - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + ["Swagger Avocado - Analyze Code", "\\[TEST-IGNORE\\] Swagger Avocado - Analyze Code"] types: [completed] permissions: diff --git a/.github/workflows/breaking-change-add-label-artifacts.yaml b/.github/workflows/breaking-change-add-label-artifacts.yaml new file mode 100644 index 000000000000..29b698d66f19 --- /dev/null +++ b/.github/workflows/breaking-change-add-label-artifacts.yaml @@ -0,0 +1,80 @@ +name: "Breaking Change - Add Label Artifacts" + +on: + workflow_run: + workflows: + - "Swagger BreakingChange - Analyze Code" + - "Breaking Change(Cross-Version) - Analyze Code" + types: [completed] + +permissions: + # Required for github.rest.actions.listWorkflowRunsForRepo() + actions: read + contents: read + # Required for github.rest.repos.listPullRequestsAssociatedWithCommit() + issues: read + pull-requests: read + +jobs: + breaking-change-add-label-artifacts: + name: "Breaking Change - Add Label Artifacts" + + runs-on: ubuntu-24.04 + + steps: + # *** IMPORTANT *** + # For workflows that are triggered by the pull_request_target event, the workflow runs in the + # context of the base of the pull request. You should make sure that you do not check out, + # build, or run untrusted code from the head of the pull request. + - uses: actions/checkout@v4 + with: + # Only needs .github folder for automation, not the files in the PR (analyzed in a + # separate workflow). + # + # Uses the .github folder from the PR base branch (pull_request_target trigger), + # or the repo default branch (other triggers). + sparse-checkout: | + .github + + - id: get-label-actions + name: Get Label Actions + uses: actions/github-script@v7 + with: + script: | + const { default: getLabelActions } = + await import('${{ github.workspace }}/.github/workflows/src/breaking-change-add-label-artifacts.js'); + return await getLabelActions({ github, context, core }); + + # Upload artifact for 'BreakingChangeReviewRequired' label + - if: | + always() && + (steps.get-label-actions.outputs.breakingChangeReviewLabelName != '') + name: Upload artifact with BreakingChangeReviewRequiredLabel label + uses: ./.github/actions/add-label-artifact + with: + name: "${{ steps.get-label-actions.outputs.breakingChangeReviewLabelName }}" + value: "${{ steps.get-label-actions.outputs.breakingChangeReviewLabelValue == 'true' }}" + + # Upload artifact for 'VersioningReviewRequired' label + - if: | + always() && + (steps.get-label-actions.outputs.versioningReviewLabelName != '') + name: Upload artifact with VersioningReviewRequiredLabel label + uses: ./.github/actions/add-label-artifact + with: + name: "${{ steps.get-label-actions.outputs.versioningReviewLabelName }}" + value: "${{ steps.get-label-actions.outputs.versioningReviewLabelValue == 'true' }}" + + - if: ${{ always() && steps.get-label-actions.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.get-label-actions.outputs.head_sha }} + + - if: ${{ always() && steps.get-label-actions.outputs.issue_number > 0 }} + name: Upload artifact with issue number + uses: ./.github/actions/add-empty-artifact + with: + name: issue-number + value: ${{ steps.get-label-actions.outputs.issue_number }} diff --git a/.github/workflows/breaking-change-code.yaml b/.github/workflows/breaking-change-code.yaml index d3fe1d9b5522..33b8cfe49ce9 100644 --- a/.github/workflows/breaking-change-code.yaml +++ b/.github/workflows/breaking-change-code.yaml @@ -1,13 +1,21 @@ -name: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code" +name: "Swagger BreakingChange - Analyze Code" -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read jobs: validateBreakingChange: - name: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code" + name: "Swagger BreakingChange - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -27,6 +35,7 @@ jobs: - name: Swagger Breaking Change - Analyze Code id: swagger-breaking-change-analyze-code run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- openapi-diff-runner \ --spec-repo-path "$GITHUB_WORKSPACE" \ --pr-source-branch "$GITHUB_HEAD_REF" \ @@ -40,8 +49,8 @@ jobs: - if: | always() && (steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelName != '') - name: Upload artifact with BreakingChangeReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with BreakingChangeReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelName }}" value: "${{ steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelValue == 'true' }}" @@ -50,21 +59,18 @@ jobs: - if: | always() && (steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelName != '') - name: Upload artifact with VersioningReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with VersioningReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelName }}" - # Convert "add/remove" to "true/false" value: "${{ steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelValue == 'true' }}" - # Upload artifact with issue number if labels are present and PR number is valid - - if: | - always() && - (steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelName != '' || - steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelName != '') && - github.event.pull_request.number > 0 - name: Upload artifact with issue number - uses: ./.github/actions/add-empty-artifact + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.swagger-breaking-change-analyze-code.outputs.summary }} + uses: actions/upload-artifact@v4 with: - name: "issue-number" - value: "${{ github.event.pull_request.number }}" + name: job-summary + path: ${{ steps.swagger-breaking-change-analyze-code.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/breaking-change-cross-version-code.yaml b/.github/workflows/breaking-change-cross-version-code.yaml index 3c44f1ad3184..79c8ff9276c4 100644 --- a/.github/workflows/breaking-change-cross-version-code.yaml +++ b/.github/workflows/breaking-change-cross-version-code.yaml @@ -1,13 +1,21 @@ -name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Analyze Code" +name: "Breaking Change(Cross-Version) - Analyze Code" -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read jobs: validateBreakingChange: - name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Analyze Code" + name: "Breaking Change(Cross-Version) - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -27,6 +35,7 @@ jobs: - name: Breaking Change(Cross-Version) - Analyze Code id: breaking-change-cross-version-analyze-code run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- openapi-diff-runner \ --run-type "CrossVersion" \ --spec-repo-path "$GITHUB_WORKSPACE" \ @@ -41,8 +50,8 @@ jobs: - if: | always() && (steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelName != '') - name: Upload artifact with BreakingChangeReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with BreakingChangeReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelName }}" value: "${{ steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelValue == 'true' }}" @@ -51,20 +60,18 @@ jobs: - if: | always() && (steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelName != '') - name: Upload artifact with VersioningReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with VersioningReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelName }}" value: "${{ steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelValue == 'true' }}" - # Upload artifact with issue number if labels are present and PR number is valid - - if: | - always() && - (steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelName != '' || - steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelName != '') && - github.event.pull_request.number > 0 - name: Upload artifact with issue number - uses: ./.github/actions/add-empty-artifact + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.breaking-change-cross-version-analyze-code.outputs.summary }} + uses: actions/upload-artifact@v4 with: - name: "issue-number" - value: "${{ github.event.pull_request.number }}" + name: job-summary + path: ${{ steps.breaking-change-cross-version-analyze-code.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/breaking-change-cross-version-status.yaml b/.github/workflows/breaking-change-cross-version-status.yaml index fda112c1ef41..17a7b7b2d606 100644 --- a/.github/workflows/breaking-change-cross-version-status.yaml +++ b/.github/workflows/breaking-change-cross-version-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Set Status" +name: "Breaking Change(Cross-Version) - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Breaking Change(Cross-Version) - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Breaking Change(Cross-Version) - Analyze Code", + "\\[TEST-IGNORE\\] Breaking Change(Cross-Version) - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set Breaking Change(Cross-Version) Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Analyze Code" - required_check_name: "[TEST-IGNORE] Breaking Change(Cross-Version)" - overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired" + monitored_workflow_name: "Breaking Change(Cross-Version) - Analyze Code" + required_check_name: "Breaking Change(Cross-Version)" + overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,BreakingChange-Approved-Retired,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired,Versioning-Approved-NotReleased" diff --git a/.github/workflows/breaking-change-status.yaml b/.github/workflows/breaking-change-status.yaml index 3b3d77c4dcce..0ced53a9e937 100644 --- a/.github/workflows/breaking-change-status.yaml +++ b/.github/workflows/breaking-change-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger BreakingChange - Set Status" +name: "Swagger BreakingChange - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Swagger BreakingChange - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Swagger BreakingChange - Analyze Code", + "\\[TEST-IGNORE\\] Swagger BreakingChange - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set BreakingChange Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code" - required_check_name: "[TEST-IGNORE] Swagger BreakingChange" - overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired" + monitored_workflow_name: "Swagger BreakingChange - Analyze Code" + required_check_name: "Swagger BreakingChange" + overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,BreakingChange-Approved-Retired,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired,Versioning-Approved-NotReleased" diff --git a/.github/shared/cmd/api-doc-preview.js b/.github/workflows/cmd/api-doc-preview.js similarity index 94% rename from .github/shared/cmd/api-doc-preview.js rename to .github/workflows/cmd/api-doc-preview.js index d3ba8a05a372..9b32d436e109 100755 --- a/.github/shared/cmd/api-doc-preview.js +++ b/.github/workflows/cmd/api-doc-preview.js @@ -6,7 +6,7 @@ import { dirname, join, resolve } from "path"; import { fileURLToPath } from "url"; import { parseArgs } from "util"; -import { getChangedFilesStatuses, swagger } from "../src/changed-files.js"; +import { getChangedFilesStatuses, swagger } from "../../shared/src/changed-files.js"; import { getSwaggersToProcess, @@ -117,6 +117,11 @@ if (swaggerPaths.length === 0) { const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(swaggerPaths); +if (swaggersToProcess.length === 0) { + console.log("No swagger files to process. No documentation artifacts will be written."); + process.exit(0); +} + const repoName = specRepoName; const prNumber = specRepoPrNumber; diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 840384194b83..304610d0ed48 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -11,6 +11,7 @@ jobs: checks: read statuses: write id-token: write + pull-requests: write steps: - name: Checkout code @@ -22,3 +23,8 @@ jobs: client-id: "936c56f0-298b-467f-b702-3ad5bf4b15c1" tenant-id: "72f988bf-86f1-41af-91ab-2d7cd011db47" allow-no-subscriptions: true + + - name: Install azsdk mcp server + shell: pwsh + run: | + ./eng/common/mcp/azure-sdk-mcp.ps1 -InstallDirectory $HOME/bin diff --git a/.github/workflows/lintdiff-code.yaml b/.github/workflows/lintdiff-code.yaml index 11e0e9c187df..58485f0ba440 100644 --- a/.github/workflows/lintdiff-code.yaml +++ b/.github/workflows/lintdiff-code.yaml @@ -1,6 +1,14 @@ name: "Swagger LintDiff - Analyze Code" -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read diff --git a/.github/workflows/lintdiff-status.yaml b/.github/workflows/lintdiff-status.yaml index a938b9c8d534..a769ffe49021 100644 --- a/.github/workflows/lintdiff-status.yaml +++ b/.github/workflows/lintdiff-status.yaml @@ -15,7 +15,9 @@ on: - labeled - unlabeled workflow_run: - workflows: ["Swagger LintDiff - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + ["Swagger LintDiff - Analyze Code", "\\[TEST-IGNORE\\] Swagger LintDiff - Analyze Code"] types: [completed] permissions: diff --git a/.github/workflows/protected-files.yaml b/.github/workflows/protected-files.yaml index 186160bf93e8..5b52fd62107e 100644 --- a/.github/workflows/protected-files.yaml +++ b/.github/workflows/protected-files.yaml @@ -1,6 +1,14 @@ name: Protected Files -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read @@ -33,9 +41,16 @@ jobs: . eng/scripts/ChangedFiles-Functions.ps1 $protectedFiles = @("cspell.json", "cspell.yaml", "package.json", "package-lock.json", ".github/*", "eng/*") + $excludedFiles = @(".github/CODEOWNERS") $changedFiles = @(Get-ChangedFiles -baseCommitish HEAD^ -headCommitish HEAD -diffFilter "") - $matchedFiles = @($changedFiles | Where-Object { $changedFile = $_; $protectedFiles | Where-Object { $changedFile -like $_ } }) + $matchedFiles = @( + $changedFiles | Where-Object { + $changedFile = $_; + ($protectedFiles | Where-Object { $changedFile -like $_ }) -and + -not ($excludedFiles | Where-Object { $changedFile -like $_ }) + } + ) if ($matchedFiles.Count -gt 0) { foreach ($file in $matchedFiles) { diff --git a/.github/workflows/sdk-breaking-change-labels.yaml b/.github/workflows/sdk-breaking-change-labels.yaml index cd8c226aa196..44e82c6b3abe 100644 --- a/.github/workflows/sdk-breaking-change-labels.yaml +++ b/.github/workflows/sdk-breaking-change-labels.yaml @@ -58,10 +58,14 @@ jobs: # Convert "add/remove" to "true/false" value: "${{ fromJson(steps.get-label-and-action.outputs.result).labelAction == 'add' }}" - - if: | - ((fromJson(steps.get-label-and-action.outputs.result).labelAction == 'add' || - fromJson(steps.get-label-and-action.outputs.result).labelAction == 'remove') && - fromJson(steps.get-label-and-action.outputs.result).issueNumber > 0) + - if: ${{ always() && fromJson(steps.get-label-and-action.outputs.result).headSha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: "head-sha" + value: "${{ fromJson(steps.get-label-and-action.outputs.result).headSha }}" + + - if: ${{ always() && fromJson(steps.get-label-and-action.outputs.result).issueNumber > 0 }} name: Upload artifact with issue number uses: ./.github/actions/add-empty-artifact with: diff --git a/.github/workflows/spec-gen-sdk-status.yml b/.github/workflows/spec-gen-sdk-status.yml index e1bb91ab37ae..faaaf71bbbf7 100644 --- a/.github/workflows/spec-gen-sdk-status.yml +++ b/.github/workflows/spec-gen-sdk-status.yml @@ -48,6 +48,13 @@ jobs: (await import('${{ github.workspace }}/.github/workflows/src/spec-gen-sdk-status.js')).default; return await setStatus({ github, context, core }); + - if: ${{ always() && steps.sdk-validation-status.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.sdk-validation-status.outputs.head_sha }} + - if: ${{ always() && steps.sdk-validation-status.outputs.issue_number }} name: Upload artifact with issue number uses: ./.github/actions/add-empty-artifact diff --git a/.github/workflows/spelling.yaml b/.github/workflows/spelling.yaml index 8410d19ec91e..d844de6ca2d6 100644 --- a/.github/workflows/spelling.yaml +++ b/.github/workflows/spelling.yaml @@ -1,6 +1,14 @@ name: SpellCheck -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read diff --git a/.github/workflows/src/arm-auto-signoff.js b/.github/workflows/src/arm-auto-signoff.js index 05f825557223..c48360dedab9 100644 --- a/.github/workflows/src/arm-auto-signoff.js +++ b/.github/workflows/src/arm-auto-signoff.js @@ -1,8 +1,9 @@ // @ts-check -import { setEquals } from "../../shared/src/equality.js"; +import { CommitStatusState, PER_PAGE_MAX } from "../../shared/src/github.js"; +import { equals } from "../../shared/src/set.js"; +import { byDate, invert } from "../../shared/src/sort.js"; import { extractInputs } from "./context.js"; -import { PER_PAGE_MAX } from "./github.js"; import { LabelAction } from "./label.js"; // TODO: Add tests @@ -33,25 +34,29 @@ export default async function getLabelAction({ github, context, core }) { * @param {string} params.head_sha * @param {(import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api & { paginate: import("@octokit/plugin-paginate-rest").PaginateInterface; })} params.github * @param {typeof import("@actions/core")} params.core - * @returns {Promise<{labelAction: LabelAction, issueNumber: number}>} + * @returns {Promise<{labelAction: LabelAction, headSha: string, issueNumber: number}>} */ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, github, core }) { const labelActions = { [LabelAction.None]: { labelAction: LabelAction.None, + headSha: head_sha, issueNumber: issue_number, }, [LabelAction.Add]: { labelAction: LabelAction.Add, + headSha: head_sha, issueNumber: issue_number, }, [LabelAction.Remove]: { labelAction: LabelAction.Remove, + headSha: head_sha, issueNumber: issue_number, }, }; // TODO: Try to extract labels from context (when available) to avoid unnecessary API call + // permissions: { issues: read, pull-requests: read } const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, { owner: owner, repo: repo, @@ -68,6 +73,7 @@ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, core.info(`Labels: ${labelNames}`); + // permissions: { actions: read } const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { owner, repo, @@ -102,6 +108,7 @@ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, return removeAction; } + // permissions: { actions: read } const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { owner, repo, @@ -139,52 +146,64 @@ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, return removeAction; } - const checkRuns = await github.paginate(github.rest.checks.listForRef, { + // permissions: { statuses: read } + const statuses = await github.paginate(github.rest.repos.listCommitStatusesForRef, { owner: owner, repo: repo, ref: head_sha, per_page: PER_PAGE_MAX, }); - const requiredCheckNames = ["Swagger LintDiff", "Swagger Avocado"]; + core.info("Statuses:"); + statuses.forEach((status) => { + core.info(`- ${status.context}: ${status.state}`); + }); + + const requiredStatusNames = ["Swagger LintDiff", "Swagger Avocado"]; /** - * @type {typeof checkRuns.check_runs} + * @type {typeof statuses} */ - let requiredCheckRuns = []; - - for (const checkName of requiredCheckNames) { - const matchingRuns = checkRuns.filter((run) => run.name === checkName); - - if (matchingRuns.length > 1) { - throw new Error(`Unexpected number of checks named '${checkName}': ${matchingRuns.length}`); - } - - const matchingRun = matchingRuns.length === 1 ? matchingRuns[0] : undefined; - - core.info( - `${checkName}: Status='${matchingRun?.status}', Conclusion='${matchingRun?.conclusion}'`, - ); - - if (matchingRun && matchingRun.status === "completed" && matchingRun.conclusion !== "success") { - core.info(`Check '${checkName}' did not succeed`); + let requiredStatuses = []; + + for (const statusName of requiredStatusNames) { + // The "statuses" array may contain multiple statuses with the same "context" (aka "name"), + // but different states and update times. We only care about the latest. + const matchingStatuses = statuses + .filter((status) => status.context.toLowerCase() === statusName.toLowerCase()) + .sort(invert(byDate((status) => status.updated_at))); + + // undefined if matchingStatuses.length === 0 (which is OK) + const matchingStatus = matchingStatuses[0]; + + core.info(`${statusName}: State='${matchingStatus?.state}'`); + + if ( + matchingStatus && + (matchingStatus.state === CommitStatusState.ERROR || + matchingStatus.state === CommitStatusState.FAILURE) + ) { + core.info(`Status '${matchingStatus}' did not succeed`); return removeAction; } - if (matchingRun) { - requiredCheckRuns.push(matchingRun); + if (matchingStatus) { + requiredStatuses.push(matchingStatus); } } if ( - setEquals(new Set(requiredCheckRuns.map((run) => run.name)), new Set(requiredCheckNames)) && - requiredCheckRuns.every((run) => run.status === "completed" && run.conclusion === "success") + equals( + new Set(requiredStatuses.map((status) => status.context)), + new Set(requiredStatusNames), + ) && + requiredStatuses.every((status) => status.state === CommitStatusState.SUCCESS) ) { core.info("All requirements met for auto-signoff"); return labelActions[LabelAction.Add]; } - // If any checks are missing or not completed, no-op to prevent frequent remove/add label as checks re-run - core.info("One or more checks are still in-progress"); + // If any statuses are missing or pending, no-op to prevent frequent remove/add label as checks re-run + core.info("One or more statuses are still pending"); return labelActions[LabelAction.None]; } diff --git a/.github/workflows/src/breaking-change-add-label-artifacts.js b/.github/workflows/src/breaking-change-add-label-artifacts.js new file mode 100644 index 000000000000..b057ce661d2c --- /dev/null +++ b/.github/workflows/src/breaking-change-add-label-artifacts.js @@ -0,0 +1,109 @@ +// @ts-check + +import { REVIEW_REQUIRED_LABELS } from "../../shared/src/breaking-change.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { byDate, invert } from "../../shared/src/sort.js"; +import { extractInputs } from "./context.js"; + +export const SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME = "Swagger BreakingChange - Analyze Code"; +export const CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME = + "Breaking Change(Cross-Version) - Analyze Code"; + +/** + * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments + * @returns {Promise} + */ +export default async function getLabelActions({ github, context, core }) { + const { owner, repo, head_sha, issue_number } = await extractInputs(github, context, core); + + core.setOutput("head_sha", head_sha); + core.setOutput("issue_number", issue_number); + + const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { + owner, + repo, + event: "pull_request", + head_sha, + per_page: PER_PAGE_MAX, + }); + + core.info("Workflow Runs:"); + workflowRuns.forEach((wf) => { + core.info(`- ${wf.name}: ${wf.conclusion || wf.status}`); + }); + + const latestBreakingChangesRun = workflowRuns + .filter((wf) => wf.name === SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME) + // Sort by "updated_at" descending + .sort(invert(byDate((wf) => wf.updated_at)))[0]; + + const latestCrossVersionBreakingChangesRun = workflowRuns + .filter((wf) => wf.name === CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME) + // Sort by "updated_at" descending + .sort(invert(byDate((wf) => wf.updated_at)))[0]; + + // Add null checks before accessing artifacts + if (!latestBreakingChangesRun || latestBreakingChangesRun.status !== "completed") { + core.info("No completed breaking changes workflow run found"); + return; + } + + if ( + !latestCrossVersionBreakingChangesRun || + latestCrossVersionBreakingChangesRun.status !== "completed" + ) { + core.info("No completed cross-version breaking changes workflow run found"); + return; + } + + core.info(`breaking change workflow run: ${latestBreakingChangesRun.html_url}`); + core.info( + `cross-version breaking change workflow run: ${latestCrossVersionBreakingChangesRun.html_url}`, + ); + const breakingChangesArtifactNames = ( + await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { + owner: owner, + repo: repo, + run_id: latestBreakingChangesRun.id, + per_page: PER_PAGE_MAX, + }) + ).map((a) => a.name); + + const crossVersionBreakingChangesArtifactNames = ( + await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { + owner: owner, + repo: repo, + run_id: latestCrossVersionBreakingChangesRun.id, + per_page: PER_PAGE_MAX, + }) + ).map((a) => a.name); + + if ( + breakingChangesArtifactNames.length === 0 && + crossVersionBreakingChangesArtifactNames.length === 0 + ) { + core.info("No artifacts found for breaking changes or cross-version breaking changes"); + return; + } + const breakingChangeKey = `${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`; + const versioningKey = `${REVIEW_REQUIRED_LABELS.VERSIONING}=true`; + + // Check for labels in both artifact collections + const hasBreakingChangeReviewLabel = + breakingChangesArtifactNames.includes(breakingChangeKey) || + crossVersionBreakingChangesArtifactNames.includes(breakingChangeKey); + + const hasVersioningReviewLabel = + breakingChangesArtifactNames.includes(versioningKey) || + crossVersionBreakingChangesArtifactNames.includes(versioningKey); + + // Apply precedence rule: breaking change takes precedence over versioning, only one label should be added + const breakingChangeReviewLabelValue = hasBreakingChangeReviewLabel; + const versioningReviewLabelValue = hasVersioningReviewLabel && !hasBreakingChangeReviewLabel; + core.info(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}: ${breakingChangeReviewLabelValue}`); + core.info(`${REVIEW_REQUIRED_LABELS.VERSIONING}: ${versioningReviewLabelValue}`); + core.setOutput("breakingChangeReviewLabelName", REVIEW_REQUIRED_LABELS.BREAKING_CHANGE); + core.setOutput("breakingChangeReviewLabelValue", breakingChangeReviewLabelValue); + core.setOutput("versioningReviewLabelName", REVIEW_REQUIRED_LABELS.VERSIONING); + core.setOutput("versioningReviewLabelValue", versioningReviewLabelValue); +} diff --git a/.github/workflows/src/comment.js b/.github/workflows/src/comment.js index e6f8ae63b73d..48d10f54539d 100644 --- a/.github/workflows/src/comment.js +++ b/.github/workflows/src/comment.js @@ -1,4 +1,4 @@ -import { PER_PAGE_MAX } from "./github.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; /** * @typedef {Object} IssueComment @@ -63,50 +63,38 @@ export async function commentOrUpdate( body, commentIdentifier, ) { - try { - // Get the authenticated user to know who we are - const { data: user } = await github.rest.users.getAuthenticated(); - const authenticatedUsername = user.login; - const computedBody = body + `\n`; + const computedBody = body + `\n`; - /** @type {IssueComment[]} */ - const comments = await github.paginate(github.rest.issues.listComments, { + /** @type {IssueComment[]} */ + const comments = await github.paginate(github.rest.issues.listComments, { + owner, + repo, + issue_number, + per_page: PER_PAGE_MAX, + }); + + const [commentId, commentBody] = parseExistingComments(comments, commentIdentifier); + + if (commentId) { + if (commentBody === computedBody) { + core.info(`No update needed for comment ${commentId}.`); + return; // No-op if the body is the same + } + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: commentId, + body: computedBody, + }); + core.info(`Updated existing comment ${commentId}.`); + } else { + // Create a new comment + const { data: newComment } = await github.rest.issues.createComment({ owner, repo, issue_number, - per_page: PER_PAGE_MAX, + body: computedBody, }); - - // only examine the comments from user in our current GITHUB_TOKEN context - const existingComments = comments.filter( - (comment) => comment.user?.login === authenticatedUsername, - ); - - const [commentId, commentBody] = parseExistingComments(existingComments, commentIdentifier); - - if (commentId) { - if (commentBody === computedBody) { - core.info(`No update needed for comment ${commentId} by ${authenticatedUsername}`); - return; // No-op if the body is the same - } - await github.rest.issues.updateComment({ - owner, - repo, - comment_id: commentId, - body: computedBody, - }); - core.info(`Updated existing comment ${commentId} by ${authenticatedUsername}`); - } else { - // Create a new comment - const { data: newComment } = await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body: computedBody, - }); - core.info(`Created new comment #${newComment.id}`); - } - } catch (/** @type {any} */ error) { - core.error(`Failed to comment or update: ${error.message}`); + core.info(`Created new comment #${newComment.id}`); } } diff --git a/.github/workflows/src/context.js b/.github/workflows/src/context.js index aa3d7dbb4aa9..111cdc760f30 100644 --- a/.github/workflows/src/context.js +++ b/.github/workflows/src/context.js @@ -1,6 +1,9 @@ // @ts-check -import { PER_PAGE_MAX } from "./github.js"; +import { isFullGitSha } from "../../shared/src/git.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { CoreLogger } from "./core-logger.js"; +import { createLogHook, createRateLimitHook } from "./github.js"; import { getIssueNumber } from "./issues.js"; /** @@ -20,7 +23,7 @@ import { getIssueNumber } from "./issues.js"; export async function extractInputs(github, context, core) { core.info("extractInputs()"); core.info(` eventName: ${context.eventName}`); - core.info(` payload.action: ${context.eventName}`); + core.info(` payload.action: ${context.payload.action}`); core.info(` payload.workflow_run.event: ${context.payload.workflow_run?.event || "undefined"}`); // Log full context when debug is enabled. Most workflows should be idempotent and can be re-run @@ -29,6 +32,10 @@ export async function extractInputs(github, context, core) { core.debug(`context: ${JSON.stringify(context)}`); } + const coreLogger = new CoreLogger(core); + github.hook.before("request", createLogHook(github.request.endpoint, coreLogger)); + github.hook.after("request", createRateLimitHook(coreLogger)); + /** @type {{ owner: string, repo: string, head_sha: string, issue_number: number, run_id: number, details_url?: string }} */ let inputs; @@ -41,7 +48,10 @@ export async function extractInputs(github, context, core) { context.payload.action === "synchronize" || context.payload.action === "reopened" || context.payload.action === "labeled" || - context.payload.action === "unlabeled")) + context.payload.action === "unlabeled" || + context.payload.action === "edited" || + context.payload.action === "ready_for_review" || + context.payload.action === "converted_to_draft")) ) { // Most properties on payload should be the same for both pull_request and pull_request_target @@ -95,11 +105,14 @@ export async function extractInputs(github, context, core) { ); let issue_number = NaN; + let head_sha = ""; if ( payload.workflow_run.event === "pull_request" || payload.workflow_run.event == "pull_request_target" ) { + head_sha = payload.workflow_run.head_sha; + // Other properties on payload.workflow_run should be the same for both pull_request and pull_request_target. // Extract the issue number from the payload itself, or by passing the head_sha to an API. @@ -113,6 +126,9 @@ export async function extractInputs(github, context, core) { const pull_requests = payload.workflow_run.pull_requests; if (pull_requests && pull_requests.length > 0) { + // TODO: Only include PRs to the same repo as the triggering workflow (existing filter below) + // TODO: Throw if more than one open PR to our repo + // For non-fork PRs, we should be able to extract the PR number from the payload, which avoids an // unnecessary API call. The listPullRequestsAssociatedWithCommit() API also seems to return // empty for non-fork PRs. This should be the same for pull_request and pull_request_target. @@ -123,7 +139,6 @@ export async function extractInputs(github, context, core) { // Owner and repo for the PR head (at least one should differ from base for fork PRs) const head_owner = payload.workflow_run.head_repository.owner.login; const head_repo = payload.workflow_run.head_repository.name; - const head_sha = payload.workflow_run.head_sha; /** @type {PullRequest[]} */ let pullRequests = []; @@ -165,12 +180,14 @@ export async function extractInputs(github, context, core) { // // In any case, the solution is to fall back to the (lower-rate-limit) search API. // The search API is confirmed to work in case #1, but has not been tested in #2 or #3. - issue_number = (await getIssueNumber({ head_sha, github, core })).issueNumber; + issue_number = (await getIssueNumber(github, head_sha, coreLogger)).issueNumber; } else if (pullRequests.length === 1) { issue_number = pullRequests[0].number; } else { throw new Error( - `Unexpected number of pull requests associated with commit '${head_sha}'. Expected: '1'. Actual: '${pullRequests.length}'.`, + `Unexpected number of pull requests associated with commit '${head_sha}'. ` + + `Expected: '1'. Actual: '${pullRequests.length}'. PRs:\n` + + pullRequests.map((pr) => pr.html_url).join("\n"), ); } if (!issue_number) { @@ -198,25 +215,44 @@ export async function extractInputs(github, context, core) { core.info(`artifactNames: ${JSON.stringify(artifactNames)}`); for (const artifactName of artifactNames) { - // If artifactName has format "issue-number=number", set issue_number - // Else, if artifactName has format "issue-number=other-string", throw an error - // Else, if artifactName does not start with "issue-number=", ignore it + // If artifactName has format "head-sha=valid-full-sha", set head_sha=value + // Else, if artifactName has format "head-sha=other-string", warn and set head_sha="" + // Else, if artifactName has format "issue-number=positive-integer", set issue_number=value + // Else, if artifactName has format "issue-number=other-string", warn and set issue_number=NaN + // - Workflows should probably only set "issue-number" to positive integers, but sometimes set it to "null" + // Else, if artifactName does not start with "head-sha=" or "issue-number=", ignore it const firstEquals = artifactName.indexOf("="); if (firstEquals !== -1) { const key = artifactName.substring(0, firstEquals); - const value = artifactName.substring(firstEquals + 1); - - if (key === "issue-number") { + if (key === "head-sha") { + const value = artifactName.substring(firstEquals + 1); + if (isFullGitSha(value)) { + head_sha = value; + } else { + // Producers must ensure they only set head-sha to valid full git SHA + throw new Error(`head-sha is not a valid full git SHA: '${value}'`); + } + continue; + } else if (key === "issue-number") { + const value = artifactName.substring(firstEquals + 1); const parsedValue = Number.parseInt(value); - if (parsedValue) { + if (parsedValue > 0) { issue_number = parsedValue; - continue; } else { - throw new Error(`Invalid issue-number: '${value}' parsed to '${parsedValue}'`); + // TODO: Consider throwing instead of warning. May need to handle `issue-number=null|undefined`, + // but invalid integers should throw. + core.info(`Invalid issue-number: '${value}' parsed to '${parsedValue}'`); + issue_number = NaN; } + continue; } } } + if (!head_sha) { + core.info( + `Could not find 'head-sha' artifact, which is required to associate the triggering workflow run with the head SHA of a PR`, + ); + } if (!issue_number) { core.info( `Could not find 'issue-number' artifact, which is required to associate the triggering workflow run with a PR`, @@ -231,8 +267,8 @@ export async function extractInputs(github, context, core) { inputs = { owner: payload.workflow_run.repository.owner.login, repo: payload.workflow_run.repository.name, - head_sha: payload.workflow_run.head_sha, - issue_number: issue_number, + head_sha, + issue_number, run_id: payload.workflow_run.id, }; } else if (context.eventName === "check_run") { diff --git a/.github/workflows/src/core-logger.js b/.github/workflows/src/core-logger.js index 47de0bf70a96..5bc8763d693f 100644 --- a/.github/workflows/src/core-logger.js +++ b/.github/workflows/src/core-logger.js @@ -45,4 +45,11 @@ export class CoreLogger { isDebug() { return this.#core.isDebug(); } + + /** + * @param {string} message + */ + warning(message) { + this.#core.warning(message); + } } diff --git a/.github/shared/src/doc-preview.js b/.github/workflows/src/doc-preview.js similarity index 92% rename from .github/shared/src/doc-preview.js rename to .github/workflows/src/doc-preview.js index 1ac67eb7ee36..d6dde743cad6 100644 --- a/.github/shared/src/doc-preview.js +++ b/.github/workflows/src/doc-preview.js @@ -136,11 +136,21 @@ us in the [Docs Support Teams Channel](https://aka.ms/ci-fix/api-docs-help)`; * @param {string[]} swaggerFiles **/ export function getSwaggersToProcess(swaggerFiles) { - const swaggerFileObjs = swaggerFiles.map(parseSwaggerFilePath); + const swaggerFileObjs = []; + for (const file of swaggerFiles) { + try { + const parsed = parseSwaggerFilePath(file); + swaggerFileObjs.push(parsed); + } catch (error) { + console.log(`Skipping file "${file}" due to parsing error: ${error}`); + continue; + } + } const versions = swaggerFileObjs.map((obj) => obj.apiVersion).filter(Boolean); if (versions.length === 0) { - throw new Error("No new API versions found in eligible swagger files."); + console.log("No API versions found in eligible swagger files."); + return { selectedVersion: null, swaggersToProcess: [] }; } const uniqueVersions = Array.from(new Set(versions)); diff --git a/.github/workflows/src/github.js b/.github/workflows/src/github.js index 503b2ce66d32..b192457b42d3 100644 --- a/.github/workflows/src/github.js +++ b/.github/workflows/src/github.js @@ -1,6 +1,9 @@ // @ts-check +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { toPercent } from "../../shared/src/math.js"; import { byDate, invert } from "../../shared/src/sort.js"; +import { Duration, formatDuration, getDuration, subtract } from "../../shared/src/time.js"; /** * @typedef {import('@octokit/plugin-rest-endpoint-methods').RestEndpointMethodTypes} RestEndpointMethodTypes @@ -9,127 +12,6 @@ import { byDate, invert } from "../../shared/src/sort.js"; * @typedef {RestEndpointMethodTypes["repos"]["listCommitStatusesForRef"]["response"]["data"]} CommitStatuses */ -export const PER_PAGE_MAX = 100; - -/** - * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions - */ -export const CheckStatus = { - /** - * @type {"completed"} - * @description The check run completed and has a conclusion. - */ - COMPLETED: "completed", - /** - * @type {"expected"} - * @description The check run is waiting for a status to be reported. - */ - EXPECTED: "expected", - /** - * @type {"failure"} - * @description The check run failed. - */ - FAILURE: "failure", - /** - * @type {"in_progress"} - * @description The check run is in progress. - */ - IN_PROGRESS: "in_progress", - /** - * @type {"pending"} - * @description The check run is at the front of the queue but the group-based concurrency limit has been reached. - */ - PENDING: "pending", - /** - * @type {"queued"} - * @description The check run has been queued. - */ - QUEUED: "queued", - /** - * @type {"requested"} - * @description The check run has been created but has not been queued. - */ - REQUESTED: "requested", - /** - * @type {"startup_failure"} - * @description The check suite failed during startup. This status is not applicable to check runs. - */ - STARTUP_FAILURE: "startup_failure", - /** - * @type {"waiting"} - * @description The check run is waiting for a deployment protection rule to be satisfied. - */ - WAITING: "waiting", -}; - -/** - * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions - */ -export const CheckConclusion = { - /** - * @type {"action_required"} - * @description The check run provided required actions upon its completion. For more information, see Using the REST API to interact with checks. - */ - ACTION_REQUIRED: "action_required", - /** - * @type {"cancelled"} - * @description The check run was cancelled before it completed. - */ - CANCELLED: "cancelled", - /** - * @type {"failure"} - * @description The check run failed. - */ - FAILURE: "failure", - /** - * @type {"neutral"} - * @description The check run completed with a neutral result. This is treated as a success for dependent checks in GitHub Actions. - */ - NEUTRAL: "neutral", - /** - * @type {"skipped"} - * @description The check run was skipped. This is treated as a success for dependent checks in GitHub Actions. - */ - SKIPPED: "skipped", - /** - * @type {"stale"} - * @description The check run was marked stale by GitHub because it took too long. - */ - STALE: "stale", - /** - * @type {"success"} - * @description The check run completed successfully. - */ - SUCCESS: "success", - /** - * @type {"timed_out"} - * @description The check run timed out. - */ - TIMED_OUT: "timed_out", -}; - -/** - * https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#create-a-commit-status--parameters - */ -export const CommitStatusState = { - /** - * @type {"error"} - */ - ERROR: "error", - /** - * @type {"failure"} - */ - FAILURE: "failure", - /** - * @type {"pending"} - */ - PENDING: "pending", - /** - * @type {"success"} - */ - SUCCESS: "success", -}; - /** * Writes content to the GitHub Actions summary * @param {string} content - Markdown content to add to the summary @@ -220,3 +102,69 @@ export async function getWorkflowRuns(github, context, workflowName, ref) { .filter((run) => run.name === workflowName) .sort(invert(byDate((run) => run.updated_at))); } + +/** + * @param {import("@octokit/endpoint").endpoint} endpoint + * @param {import('../../shared/src/logger.js').ILogger} logger + * @returns {(options: import("@octokit/types").RequestParameters & {url: string, method: string}) => void} + */ +export function createLogHook(endpoint, logger) { + /** + * @param {import("@octokit/types").RequestParameters & {url: string, method: string}} options + */ + function logHook(options) { + const request = endpoint(options); + const { method, url, body } = request; + logger.info(`[github] ${method.toUpperCase()} ${url} ${body ? JSON.stringify(body) : ""}`); + } + + return logHook; +} + +/** + * @param {import('../../shared/src/logger.js').ILogger} logger + * @returns {(response: import("@octokit/types").OctokitResponse) => void} + */ +export function createRateLimitHook(logger) { + /** + * @param {import("@octokit/types").OctokitResponse} response + */ + function rateLimitHook(response) { + const { + "x-ratelimit-limit": limitHeader, + "x-ratelimit-remaining": remainingHeader, + "x-ratelimit-reset": resetHeader, + } = response.headers; + + if (!limitHeader || !remainingHeader || !resetHeader) { + logger.debug(`[github] missing ratelimit header(s) in response`); + return; + } + + const limit = parseInt(limitHeader); + const remaining = parseInt(remainingHeader); + const used = limit - remaining; + + const reset = new Date(parseInt(resetHeader) * Duration.Second); + const start = subtract(reset, Duration.Hour); + const elapsedMs = new Date().getTime() - start.getTime(); + const elapsedFraction = elapsedMs / Duration.Hour; + + // Example: If limit is 1000, and 6 minutes have elapsed (10% of 1 hour), + // availableLimit will be 100 (10% of total). + const availableLimit = limit * elapsedFraction; + + // If load is > 100%, we are "running hot" and predicted to hit limit before reset + // Keep load < 50% for a safety margin. If regularly > 50%, optimize. + const load = used / availableLimit; + + // const resource = headers["x-ratelimit-resource"]; + + logger.info( + `[github] load: ${toPercent(load)}, used: ${used}, remaining: ${remaining}` + + `, reset: ${formatDuration(getDuration(new Date(), reset))}`, + ); + } + + return rateLimitHook; +} diff --git a/.github/workflows/src/issues.js b/.github/workflows/src/issues.js index 98ae32234616..158c54a833f3 100644 --- a/.github/workflows/src/issues.js +++ b/.github/workflows/src/issues.js @@ -2,20 +2,19 @@ /** * Retrieves the PR number associated with a specific commit SHA - * @param {Object} params - * @param {String} params.head_sha - The head_sha - * @param {typeof import("@actions/core")} params.core - GitHub Actions core for logging - * @param {import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api} params.github - GitHub API client + * @param {import('@actions/github-script').AsyncFunctionArguments["github"]} github - GitHub API client + * @param {string} head_sha - The head_sha + * @param {import('../../shared/src/logger.js').ILogger} [logger] * @returns {Promise<{issueNumber: number}>} - The PR number or NaN if not found */ -export async function getIssueNumber({ head_sha, core, github }) { +export async function getIssueNumber(github, head_sha, logger) { let issueNumber = NaN; if (!head_sha) { throw new Error("head_sha is required when trying to search a PR."); } - core.info(`Searching for PRs with commit SHA: ${head_sha}`); + logger?.info(`Searching for PRs with commit SHA: ${head_sha}`); try { const searchResponse = await github.rest.search.issuesAndPullRequests({ @@ -25,23 +24,23 @@ export async function getIssueNumber({ head_sha, core, github }) { const totalCount = searchResponse.data.total_count; const itemsCount = searchResponse.data.items.length; - core.info(`Search results: ${totalCount} total matches, ${itemsCount} items returned`); + logger?.info(`Search results: ${totalCount} total matches, ${itemsCount} items returned`); if (itemsCount > 0) { const firstItem = searchResponse.data.items[0]; issueNumber = firstItem.number; - core.info(`Found the first matched PR #${issueNumber}: ${firstItem.html_url}`); + logger?.info(`Found the first matched PR #${issueNumber}: ${firstItem.html_url}`); if (itemsCount > 1) { - core.warning( + logger?.warning( `Multiple PRs found for commit ${head_sha}: ${searchResponse.data.items.map((item) => `#${item.html_url}`).join(", ")}`, ); } } else { - core.info(`No open PRs found for commit ${head_sha}`); + logger?.info(`No open PRs found for commit ${head_sha}`); } } catch (error) { - core.error(`Error searching for PRs with commit ${head_sha}: ${error}`); + logger?.error(`Error searching for PRs with commit ${head_sha}: ${error}`); throw error; } diff --git a/.github/workflows/src/label.js b/.github/workflows/src/label.js index cb8a6a2b189d..3d1d29dc3f8a 100644 --- a/.github/workflows/src/label.js +++ b/.github/workflows/src/label.js @@ -9,10 +9,3 @@ export const LabelAction = Object.freeze({ Add: "add", Remove: "remove", }); - -export const Label = { - /** - * @type {"Approved-Avocado"} - */ - APPROVED_AVOCADO: "Approved-Avocado", -}; diff --git a/.github/workflows/src/sdk-breaking-change-labels.js b/.github/workflows/src/sdk-breaking-change-labels.js index f62b79b3d634..35dffaae6356 100644 --- a/.github/workflows/src/sdk-breaking-change-labels.js +++ b/.github/workflows/src/sdk-breaking-change-labels.js @@ -39,12 +39,13 @@ export async function getLabelAndAction({ github, context, core }) { * @param {string} params.details_url * @param {typeof import("@actions/core")} params.core * @param {import('./retries.js').RetryOptions} [params.retryOptions] - * @returns {Promise<{labelName: string | undefined, labelAction: LabelAction, issueNumber: number}>} + * @returns {Promise<{labelName: string | undefined, labelAction: LabelAction, headSha: string, issueNumber: number}>} */ export async function getLabelAndActionImpl({ details_url, core, retryOptions = {} }) { // Override default logger from console.log to core.info retryOptions = { logger: core.info, ...retryOptions }; + let head_sha = ""; let issue_number = NaN; let labelAction; /** @type {String | undefined} */ @@ -74,6 +75,9 @@ export async function getLabelAndActionImpl({ details_url, core, retryOptions = // Parse the JSON data const specGenSdkArtifactInfo = JSON.parse(result.artifactData); const labelActionText = specGenSdkArtifactInfo.labelAction; + + head_sha = specGenSdkArtifactInfo.headSha; + issue_number = parseInt(specGenSdkArtifactInfo.prNumber, 10); if (!issue_number) { core.warning( @@ -95,10 +99,10 @@ export async function getLabelAndActionImpl({ details_url, core, retryOptions = } } - if (!labelAction) { - core.info("No label action found, defaulting to None"); + if (!labelAction || !labelName) { + core.info("No label action or name found, defaulting to None"); labelAction = LabelAction.None; } - return { labelName, labelAction, issueNumber: issue_number }; + return { labelName, labelAction, headSha: head_sha, issueNumber: issue_number }; } diff --git a/.github/workflows/src/set-status.js b/.github/workflows/src/set-status.js index 7fad49b6153f..c49f9399d365 100644 --- a/.github/workflows/src/set-status.js +++ b/.github/workflows/src/set-status.js @@ -1,7 +1,14 @@ // @ts-check +import { isFullGitSha } from "../../shared/src/git.js"; +import { + CheckConclusion, + CheckStatus, + CommitStatusState, + PER_PAGE_MAX, +} from "../../shared/src/github.js"; +import { byDate, invert } from "../../shared/src/sort.js"; import { extractInputs } from "./context.js"; -import { CheckConclusion, CheckStatus, CommitStatusState, PER_PAGE_MAX } from "./github.js"; // TODO: Add tests /* v8 ignore start */ @@ -66,6 +73,14 @@ export async function setStatusImpl({ requiredStatusName, overridingLabel, }) { + if (!isFullGitSha(head_sha)) { + throw new Error(`head_sha is not a valid full git SHA: '${head_sha}'`); + } + core.setOutput("head_sha", head_sha); + + if (!Number.isInteger(issue_number) || issue_number <= 0) { + throw new Error(`issue_number must be a positive integer: ${issue_number}`); + } core.setOutput("issue_number", issue_number); // TODO: Try to extract labels from context (when available) to avoid unnecessary API call @@ -124,9 +139,13 @@ export async function setStatusImpl({ }); const targetRuns = workflowRuns - .filter((wf) => wf.name == monitoredWorkflowName) + .filter( + (wf) => + // Match both old and new names, to handle the rename more gracefully + wf.name === monitoredWorkflowName || wf.name === `[TEST-IGNORE] ${monitoredWorkflowName}`, + ) // Sort by "updated_at" descending - .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()); + .sort(invert(byDate((r) => r.updated_at))); // Sorted by "updated_at" descending, so most recent run is at index 0. // If "targetRuns.length === 0", run will be "undefined", which the following @@ -134,7 +153,9 @@ export async function setStatusImpl({ const run = targetRuns[0]; if (!run) { - console.log(`No workflow runs found for '${monitoredWorkflowName}'.`); + console.log( + `No workflow runs found for '${monitoredWorkflowName}' or '[TEST-IGNORE] ${monitoredWorkflowName}'.`, + ); } if (run) { @@ -205,7 +226,8 @@ export async function setStatusImpl({ }); } else { core.info( - `No workflow runs found for '${monitoredWorkflowName}'. Setting status to ${CommitStatusState.PENDING} for required status: ${requiredStatusName}.`, + `No workflow runs found for '${monitoredWorkflowName}' or '[TEST-IGNORE] ${monitoredWorkflowName}'.` + + ` Setting status to ${CommitStatusState.PENDING} for required status: ${requiredStatusName}.`, ); // Run was not found (not started), or not completed await github.rest.repos.createCommitStatus({ diff --git a/.github/workflows/src/spec-gen-sdk-status.js b/.github/workflows/src/spec-gen-sdk-status.js index 90632d2b03d1..3a176e57bef9 100644 --- a/.github/workflows/src/spec-gen-sdk-status.js +++ b/.github/workflows/src/spec-gen-sdk-status.js @@ -1,7 +1,8 @@ // @ts-check +import { CheckStatus, CommitStatusState, PER_PAGE_MAX } from "../../shared/src/github.js"; import { getAdoBuildInfoFromUrl, getAzurePipelineArtifact } from "./artifacts.js"; import { extractInputs } from "./context.js"; -import { CheckStatus, CommitStatusState, PER_PAGE_MAX, writeToActionsSummary } from "./github.js"; +import { writeToActionsSummary } from "./github.js"; /** * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments @@ -56,6 +57,7 @@ export async function setSpecGenSdkStatusImpl({ issue_number, }) { const statusName = "SDK Validation Status"; + core.setOutput("head_sha", head_sha); core.setOutput("issue_number", issue_number); const checks = await github.paginate(github.rest.checks.listForRef, { owner, @@ -123,10 +125,10 @@ export async function setSpecGenSdkStatusImpl({ * @param {Object} params * @param {Array} params.checkRuns * @param {typeof import("@actions/core")} params.core - * @returns {Promise<{state: import("./github.js").CommitStatusState, description: string}>} + * @returns {Promise<{state: CommitStatusState, description: string}>} */ async function processResult({ checkRuns, core }) { - /** @type {import("./github.js").CommitStatusState} */ + /** @type {CommitStatusState} */ let state = CommitStatusState.SUCCESS; let specGenSdkFailedRequiredLanguages = ""; let description = "SDK Validation CI checks succeeded"; @@ -199,7 +201,9 @@ async function processResult({ checkRuns, core }) { if (state === CommitStatusState.FAILURE) { summaryContent += "\n### Next Steps\n\n" + - `Please fix any issues in the the SDK Validation CI checks for languages: ${specGenSdkFailedRequiredLanguages}.`; + `Address the issues reported in the the SDK Validation CI checks for language(s): ${specGenSdkFailedRequiredLanguages}.` + + `\n### More Information\n\n` + + `Refer to the [SDK Validation Wiki](https://github.com/Azure/azure-rest-api-specs/wiki/SDK-Validation).`; } // Write to the summary page diff --git a/.github/workflows/src/summarize-checks/dump-trigger-metadata.js b/.github/workflows/src/summarize-checks/dump-trigger-metadata.js new file mode 100644 index 000000000000..efb2e006af57 --- /dev/null +++ b/.github/workflows/src/summarize-checks/dump-trigger-metadata.js @@ -0,0 +1,55 @@ +// @ts-check + +/** + * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments + * @returns {Promise} + */ +export default async function dumpTriggerMetadata({ context, core }) { + core.info(`Event name: ${context.eventName}`); + core.info(`Action: ${context.payload.action}`); + + if ( + context.eventName === "workflow_run" && + context.payload.action === "completed" && + context.payload.workflow_run + ) { + const payload = /** @type {import("@octokit/webhooks-types").WorkflowRunCompletedEvent} */ ( + context.payload + ); + + const run = payload.workflow_run; + core.info(`Triggering workflow: ${run.name}`); + core.info(`Triggering workflow ID: ${run.id}`); + core.info(`Triggering run ID: ${run.run_number}`); + core.info(`Triggering event: ${run.event}`); + core.info(`Triggering status: ${run.status}`); + core.info(`Triggering conclusion: ${run.conclusion}`); + core.info(`Triggering branch: ${run.head_branch}`); + core.info(`Triggering SHA: ${run.head_sha}`); + core.info(`Triggering actor: ${run.actor?.login || "unknown"}`); + + // Create clickable URL to the triggering workflow run + const triggeringRunUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${run.id}`; + core.info(`🔗 Triggering workflow run URL: ${triggeringRunUrl}`); + + // If there's a pull request associated, show that too + if (run.pull_requests && run.pull_requests.length > 0) { + run.pull_requests.forEach((pr) => { + const prUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${pr.number}`; + core.info(`🔗 Associated PR: ${prUrl}`); + }); + } + } else if (context.eventName === "pull_request_target" && context.payload.pull_request) { + const payload = /** @type {import("@octokit/webhooks-types").PullRequestEvent} */ ( + context.payload + ); + + const pr = payload.pull_request; + core.info(`PR number: ${pr.number}`); + core.info(`PR title: ${pr.title}`); + core.info(`PR state: ${pr.state}`); + core.info(`PR head SHA: ${pr.head.sha}`); + core.info(`PR base branch: ${pr.base.ref}`); + core.info(`🔗 PR URL: ${pr.html_url}`); + } +} diff --git a/.github/workflows/src/summarize-checks/labelling.js b/.github/workflows/src/summarize-checks/labelling.js index efe97a9ef30c..3a1050885255 100644 --- a/.github/workflows/src/summarize-checks/labelling.js +++ b/.github/workflows/src/summarize-checks/labelling.js @@ -48,6 +48,7 @@ import { /** * @typedef {Object} ImpactAssessment * @property {boolean} resourceManagerRequired - Whether a resource manager review is required. + * @property {boolean} dataPlaneRequired - Whether a data plane review is required. * @property {boolean} suppressionReviewRequired - Whether a suppression review is required. * @property {boolean} isNewApiVersion - Whether this PR introduces a new API version. * @property {boolean} rpaasExceptionRequired - Whether an RPaaS exception is required. @@ -57,7 +58,6 @@ import { * @property {boolean} rpaasRPMissing - Whether the RPaaS RP label is missing. * @property {boolean} typeSpecChanged - Whether a TypeSpec file has changed. * @property {boolean} isDraft - Whether the PR is a draft. - * @property {LabelContext} labelContext - The context containing present, to-add, and to-remove labels. * @property {string} targetBranch - The name of the target branch for the PR. */ @@ -411,33 +411,36 @@ export const breakingChangesCheckType = { * @returns {void} */ export function processArmReviewLabels(context, existingLabels) { - // the important part about how this will work depends how the users use it - // EG: if they add the "ARMSignedOff" label, we will remove the "ARMChangesRequested" and "WaitForARMFeedback" labels. - // if they add the "ARMChangesRequested" label, we will remove the "WaitForARMFeedback" label. - // if they remove the "ARMChangesRequested" label, we will add the "WaitForARMFeedback" label. - // so if the user or ARM team actually unlabels `ARMChangesRequested`, then we're actually ok - // if we are signed off, we should remove the "ARMChangesRequested" and "WaitForARMFeedback" labels - if (includesEvery(existingLabels, ["ARMSignedOff"])) { - if (existingLabels.includes("ARMChangesRequested")) { - context.toRemove.add("ARMChangesRequested"); - } - if (existingLabels.includes("WaitForARMFeedback")) { - context.toRemove.add("WaitForARMFeedback"); + // only kick this off if the ARMReview label is present and NotReadyForARMReview is not present + if (existingLabels.includes("ARMReview") && !existingLabels.includes("NotReadyForARMReview")) { + // the important part about how this will work depends how the users use it + // EG: if they add the "ARMSignedOff" label, we will remove the "ARMChangesRequested" and "WaitForARMFeedback" labels. + // if they add the "ARMChangesRequested" label, we will remove the "WaitForARMFeedback" label. + // if they remove the "ARMChangesRequested" label, we will add the "WaitForARMFeedback" label. + // so if the user or ARM team actually unlabels `ARMChangesRequested`, then we're actually ok + // if we are signed off, we should remove the "ARMChangesRequested" and "WaitForARMFeedback" labels + if (includesEvery(existingLabels, ["ARMSignedOff"])) { + if (existingLabels.includes("ARMChangesRequested")) { + context.toRemove.add("ARMChangesRequested"); + } + if (existingLabels.includes("WaitForARMFeedback")) { + context.toRemove.add("WaitForARMFeedback"); + } } - } - // if there are ARM changes requested, we should remove the "WaitForARMFeedback" label as the presence indicates that ARM has reviewed - else if ( - includesEvery(existingLabels, ["ARMChangesRequested"]) && - includesNone(existingLabels, ["ARMSignedOff"]) - ) { - if (existingLabels.includes("WaitForARMFeedback")) { - context.toRemove.add("WaitForARMFeedback"); + // if there are ARM changes requested, we should remove the "WaitForARMFeedback" label as the presence indicates that ARM has reviewed + else if ( + includesEvery(existingLabels, ["ARMChangesRequested"]) && + includesNone(existingLabels, ["ARMSignedOff"]) + ) { + if (existingLabels.includes("WaitForARMFeedback")) { + context.toRemove.add("WaitForARMFeedback"); + } } - } - // finally, if ARMChangesRequested are not present, and we've gotten here by lac;k of signoff, we should add the "WaitForARMFeedback" label - else if (includesNone(existingLabels, ["ARMChangesRequested"])) { - if (!existingLabels.includes("WaitForARMFeedback")) { - context.toAdd.add("WaitForARMFeedback"); + // finally, if ARMChangesRequested are not present, and we've gotten here by lac;k of signoff, we should add the "WaitForARMFeedback" label + else if (includesNone(existingLabels, ["ARMChangesRequested"])) { + if (!existingLabels.includes("WaitForARMFeedback")) { + context.toAdd.add("WaitForARMFeedback"); + } } } } @@ -460,40 +463,59 @@ This function does the following, **among other things**: - Calls the "processARMReviewWorkflowLabels" function if "ARMReview" label applies. */ -// todo: refactor to take context: PRContext as input instead of IValidatorContext. -// All downstream usage appears to be using "context.contextConfig() as PRContext". /** - * @param {string} targetBranch * @param {LabelContext} labelContext - * @param {boolean} resourceManagerLabelShouldBePresent - * @param {boolean} ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent - * @param {boolean} rpaasExceptionLabelShouldBePresent - * @param {boolean} ciRpaasRPNotInPrivateRepoLabelShouldBePresent - * @param {boolean} isNewApiVersion - * @param {boolean} isDraft + * @param {ImpactAssessment} impactAssessment * @returns {Promise<{armReviewLabelShouldBePresent: boolean}>} */ -export async function processImpactAssessment( - targetBranch, - labelContext, - resourceManagerLabelShouldBePresent, - ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, - rpaasExceptionLabelShouldBePresent, - ciRpaasRPNotInPrivateRepoLabelShouldBePresent, - isNewApiVersion, - isDraft, -) { +export async function processImpactAssessment(labelContext, impactAssessment) { console.log("ENTER definition processARMReview"); + // By default these two should not be present. We may determine later in this function that they should be present after all. const armReviewLabel = new Label("ARMReview", labelContext.present); - // By default this label should not be present. We may determine later in this function that it should be present after all. armReviewLabel.shouldBePresent = false; - const newApiVersionLabel = new Label("new-api-version", labelContext.present); - // By default this label should not be present. We may determine later in this function that it should be present after all. newApiVersionLabel.shouldBePresent = false; - const branch = targetBranch; + const resourceManagerLabel = new Label("resource-manager", labelContext.present); + resourceManagerLabel.shouldBePresent = impactAssessment.resourceManagerRequired || false; + + const dataplaneLabel = new Label("data-plane", labelContext.present); + dataplaneLabel.shouldBePresent = impactAssessment.dataPlaneRequired || false; + + const typeSpecLabel = new Label("TypeSpec", labelContext.present); + typeSpecLabel.shouldBePresent = impactAssessment.typeSpecChanged || false; + + const suppressionReviewRequiredLabel = new Label( + "SuppressionReviewRequired", + labelContext.present, + ); + suppressionReviewRequiredLabel.shouldBePresent = + impactAssessment.suppressionReviewRequired || false; + + const rpassReviewRequiredLabel = new Label("RPaaS", labelContext.present); + rpassReviewRequiredLabel.shouldBePresent = impactAssessment.rpaasChange || false; + + const newRPNamespaceLabel = new Label("new-rp-namespace", labelContext.present); + newRPNamespaceLabel.shouldBePresent = impactAssessment.newRP || false; + + const ciNewRPNamespaceWithoutRpaaSLabel = new Label( + "CI-NewRPNamespaceWithoutRPaaS", + labelContext.present, + ); + ciNewRPNamespaceWithoutRpaaSLabel.shouldBePresent = impactAssessment.rpaasRPMissing || false; + + const rpaasExceptionLabel = new Label("RPaaSException", labelContext.present); + rpaasExceptionLabel.shouldBePresent = impactAssessment.rpaasExceptionRequired || false; + + const ciRpaasRPNotInPrivateRepoLabel = new Label( + "CI-RpaaSRPNotInPrivateRepo", + labelContext.present, + ); + ciRpaasRPNotInPrivateRepoLabel.shouldBePresent = + impactAssessment.rpaasRpNotInPrivateRepo || false; + + const branch = impactAssessment.targetBranch; const isReleaseBranchVal = isReleaseBranch(branch); // we used to also calculate if the branch name was from ShiftLeft, in which case we would or that @@ -501,38 +523,54 @@ export async function processImpactAssessment( // so we only check if the branch is a release branch. // const prTitle = await getPrTitle(owner, repo, prNumber) // const isShiftLeftPRWithRPSaaSDevVal = isShiftLeftPRWithRPSaaSDev(prTitle, branch) - const isBranchInScopeOfSpecReview = isReleaseBranchVal; // || isShiftLeftPRWithRPSaaSDevVal + const isBranchInScopeOfSpecReview = isReleaseBranchVal; // 'specReviewApplies' means that either ARM or data-plane review applies. Downstream logic // determines which kind of review exactly we need. - let specReviewApplies = !isDraft && isBranchInScopeOfSpecReview; + let specReviewApplies = !impactAssessment.isDraft && isBranchInScopeOfSpecReview; if (specReviewApplies) { - if (isNewApiVersion) { + if (impactAssessment.isNewApiVersion) { // Note that in case of data-plane PRs, the addition of this label will result // in API stewardship board review being required. // See requiredLabelsRules.ts. newApiVersionLabel.shouldBePresent = true; } - armReviewLabel.shouldBePresent = resourceManagerLabelShouldBePresent; + armReviewLabel.shouldBePresent = impactAssessment.resourceManagerRequired; await processARMReviewWorkflowLabels( labelContext, armReviewLabel.shouldBePresent, - ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, - rpaasExceptionLabelShouldBePresent, - ciRpaasRPNotInPrivateRepoLabelShouldBePresent, + impactAssessment.rpaasRPMissing, + impactAssessment.rpaasExceptionRequired, + impactAssessment.rpaasRpNotInPrivateRepo, ); } + dataplaneLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + resourceManagerLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); newApiVersionLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); armReviewLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + typeSpecLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + suppressionReviewRequiredLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + rpassReviewRequiredLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + newRPNamespaceLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + ciNewRPNamespaceWithoutRpaaSLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + rpaasExceptionLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + ciRpaasRPNotInPrivateRepoLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + + // this is the only labelling that was part of original pipelinebot logic, it handles the rotation of + // ARMChangesRequested, WaitForArmFeedback, and ARMSignedOff labels. The thing is, we only want to make + // these changes if the review is actually an ARM review. So we're going to place this after processing an impactAssessment + // which will tell us if a the ARMReview label should be present or not. + processArmReviewLabels(labelContext, [...labelContext.present, ...labelContext.toAdd]); console.log( `RETURN definition processARMReview. ` + `isReleaseBranch: ${isReleaseBranchVal}, ` + + `isArmReview: ${armReviewLabel.shouldBePresent}, ` + `isBranchInScopeOfArmReview: ${isBranchInScopeOfSpecReview}, ` + - `isNewApiVersion: ${isNewApiVersion}, ` + - `isDraft: ${isDraft}, ` + + `isNewApiVersion: ${impactAssessment.isNewApiVersion}, ` + + `isDraft: ${impactAssessment.isDraft}, ` + `newApiVersionLabel.shouldBePresent: ${newApiVersionLabel.shouldBePresent}, ` + `armReviewLabel.shouldBePresent: ${armReviewLabel.shouldBePresent}.`, ); @@ -604,13 +642,15 @@ async function processARMReviewWorkflowLabels( const armSignedOffLabel = new Label("ARMSignedOff", labelContext.present); + const blockedOnVersioningPolicy = getBlockedOnVersioningPolicy(labelContext); + const blockedOnRpaas = getBlockedOnRpaas( ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, rpaasExceptionLabelShouldBePresent, ciRpaasRPNotInPrivateRepoLabelShouldBePresent, ); - const blocked = blockedOnRpaas; + const blocked = blockedOnRpaas || blockedOnVersioningPolicy; // If given PR is in scope of ARM review and it is blocked for any reason, // the "NotReadyForARMReview" label should be present, to the exclusion @@ -672,6 +712,23 @@ async function processARMReviewWorkflowLabels( return; } +/** + * @param {LabelContext} labelContext + * @returns {boolean} + */ +function getBlockedOnVersioningPolicy(labelContext) { + const pendingVersioningReview = + labelContext.present.has("VersioningReviewRequired") && + !anyApprovalLabelPresent("SameVersion", [...labelContext.present]); + + const pendingBreakingChangeReview = + labelContext.present.has("BreakingChangeReviewRequired") && + !anyApprovalLabelPresent("CrossVersion", [...labelContext.present]); + + const blockedOnVersioningPolicy = pendingVersioningReview || pendingBreakingChangeReview; + return blockedOnVersioningPolicy; +} + /** * @param {boolean} ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent * @param {boolean} rpaasExceptionLabelShouldBePresent diff --git a/.github/workflows/src/summarize-checks/summarize-checks.js b/.github/workflows/src/summarize-checks/summarize-checks.js index c04829a20e65..b2c9454b326c 100644 --- a/.github/workflows/src/summarize-checks/summarize-checks.js +++ b/.github/workflows/src/summarize-checks/summarize-checks.js @@ -19,14 +19,15 @@ */ // #region imports/constants -import { extractInputs } from "../context.js"; -// import { commentOrUpdate } from "../comment.js"; import { execFile } from "../../../shared/src/exec.js"; -import { PER_PAGE_MAX } from "../github.js"; +import { CheckConclusion, PER_PAGE_MAX } from "../../../shared/src/github.js"; +import { intersect } from "../../../shared/src/set.js"; +import { byDate, invert } from "../../../shared/src/sort.js"; +import { commentOrUpdate } from "../comment.js"; +import { extractInputs } from "../context.js"; import { brChRevApproval, getViolatedRequiredLabelsRules, - processArmReviewLabels, processImpactAssessment, verRevApproval, } from "./labelling.js"; @@ -57,7 +58,7 @@ import path from "path"; * @typedef {Object} CheckRunData * @property {string} name * @property {string} status - * @property {string} conclusion + * @property {string | null} conclusion * @property {CheckMetadata} checkInfo */ @@ -125,6 +126,14 @@ import path from "path"; * @typedef {import("./labelling.js").RequiredLabelRule} RequiredLabelRule */ +/** + * @typedef {Object} CheckRunResult + * @property {string} name + * @property {string} summary + * @property {"pending" | keyof typeof CheckConclusion} result + * @property {string} [target_url] + */ + // Placing these configuration items here until we decide another way to pull them in. const FYI_CHECK_NAMES = [ "Swagger LintDiff", @@ -133,6 +142,7 @@ const FYI_CHECK_NAMES = [ "Swagger PrettierCheck", ]; const AUTOMATED_CHECK_NAME = "Automated merging requirements met"; +const IMPACT_CHECK_NAME = "Summarize PR Impact"; const NEXT_STEPS_COMMENT_ID = "NextStepsToMerge"; /** @type {CheckMetadata[]} */ @@ -280,19 +290,20 @@ const EXCLUDED_CHECK_NAMES = []; */ export default async function summarizeChecks({ github, context, core }) { let { owner, repo, issue_number, head_sha } = await extractInputs(github, context, core); - const targetBranch = context.payload.pull_request?.base?.ref; - core.info(`PR target branch: ${targetBranch}`); if (!issue_number) { - core.info( - "This summarize-checks was triggered off a workflow that doesn't provide the issue-number artifact, early exiting.", - ); + core.warning(`No issue number found for this event. Exiting summarize-checks.js early.`); return; } + const targetBranch = context.payload.pull_request?.base?.ref; + core.info(`PR target branch: ${targetBranch}`); + + // Default target is this run itself + const target_url = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + await summarizeChecksImpl( github, - context, core, owner, repo, @@ -300,12 +311,34 @@ export default async function summarizeChecks({ github, context, core }) { head_sha, context.eventName, targetBranch, + target_url, ); } +/** + * @param {typeof import("@actions/core")} core + * @param {CheckRunData[]} requiredCheckRuns + * @param {CheckRunData[]} fyiCheckRuns + */ +export function outputRunDetails(core, requiredCheckRuns, fyiCheckRuns) { + core.info( + `Observed ${requiredCheckRuns.length} required check runs ${requiredCheckRuns.length > 0 ? ":" : "."}`, + ); + requiredCheckRuns.forEach((x) => { + core.info( + `Required check "${x.name}" with status "${x.status}" and conclusion "${x.conclusion}"`, + ); + }); + core.info( + `Observed ${fyiCheckRuns.length} FYI check runs ${fyiCheckRuns.length > 0 ? ":" : "."}`, + ); + fyiCheckRuns.forEach((x) => { + core.info(`FYI check "${x.name}" with status "${x.status}" and conclusion "${x.conclusion}"`); + }); +} + /** * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github - * @param {import('@actions/github').context } context * @param {typeof import("@actions/core")} core * @param {string} owner * @param {string} repo @@ -313,11 +346,11 @@ export default async function summarizeChecks({ github, context, core }) { * @param {string} head_sha * @param {string} event_name * @param {string} targetBranch + * @param {string} target_url * @returns {Promise} */ export async function summarizeChecksImpl( github, - context, core, owner, repo, @@ -325,18 +358,17 @@ export async function summarizeChecksImpl( head_sha, event_name, targetBranch, + target_url, ) { - core.info( - `Handling ${event_name} event for PR #${issue_number} in ${owner}/${repo}@${head_sha}.`, - ); + core.info(`Handling ${event_name} event for PR #${issue_number} in ${owner}/${repo}.`); - // retrieve latest labels state - const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, { - owner, - repo, - issue_number: issue_number, - per_page: PER_PAGE_MAX, - }); + const prUrl = `https://github.com/${owner}/${repo}/pull/${issue_number}`; + core.summary.addRaw("PR: "); + core.summary.addLink(prUrl, prUrl); + core.summary.write(); + core.setOutput("summary", process.env.GITHUB_STEP_SUMMARY); + + let labelNames = await getExistingLabels(github, owner, repo, issue_number); /** @type {[CheckRunData[], CheckRunData[], import("./labelling.js").ImpactAssessment | undefined]} */ const [requiredCheckRuns, fyiCheckRuns, impactAssessment] = await getCheckRunTuple( @@ -349,39 +381,48 @@ export async function summarizeChecksImpl( EXCLUDED_CHECK_NAMES, ); - /** @type {string[]} */ - let labelNames = labels.map((/** @type {{ name: string; }} */ label) => label.name); + outputRunDetails(core, requiredCheckRuns, fyiCheckRuns); + + if (impactAssessment) { + core.info(`ImpactAssessment: ${JSON.stringify(impactAssessment)}`); + targetBranch = impactAssessment.targetBranch; + } else { + core.info( + `No impact assessment found for ${owner}/${repo}#${issue_number}. ` + + `No labels will be added or removed in this run, and only "pending" status check will be set.`, + ); + } let labelContext = await updateLabels(labelNames, impactAssessment); core.info( `Summarize checks label actions against ${owner}/${repo}#${issue_number}: \n` + - `The following labels were present: [${Array.from(labelContext.present).join(", ")}] \n` + - `Removing labels: [${Array.from(labelContext.toRemove).join(", ")}] \n` + - `Adding labels: [${Array.from(labelContext.toAdd).join(", ")}]`, + `The following labels were present: [${Array.from(labelContext.present).join(", ")}]` + + `Removing labels [${Array.from(labelContext.toRemove).join(", ")}] then \n` + + `Adding labels [${Array.from(labelContext.toAdd).join(", ")}]`, ); - // for (const label of labelContext.toRemove) { - // core.info(`Removing label: ${label} from ${owner}/${repo}#${issue_number}.`); - // await github.rest.issues.removeLabel({ - // owner: owner, - // repo: repo, - // issue_number: issue_number, - // name: label, - // }); - // } - - // if (labelContext.toAdd.size > 0) { - // core.info( - // `Adding labels: ${Array.from(labelContext.toAdd).join(", ")} to ${owner}/${repo}#${issue_number}.`, - // ); - // await github.rest.issues.addLabels({ - // owner: owner, - // repo: repo, - // issue_number: issue_number, - // labels: Array.from(labelContext.toAdd), - // }); - // } + for (const label of labelContext.toRemove) { + core.info(`Removing label: ${label} from ${owner}/${repo}#${issue_number}.`); + await github.rest.issues.removeLabel({ + owner: owner, + repo: repo, + issue_number: issue_number, + name: label, + }); + } + + if (labelContext.toAdd.size > 0) { + core.info( + `Adding labels: ${Array.from(labelContext.toAdd).join(", ")} to ${owner}/${repo}#${issue_number}.`, + ); + await github.rest.issues.addLabels({ + owner: owner, + repo: repo, + issue_number: issue_number, + labels: Array.from(labelContext.toAdd), + }); + } // adjust labelNames based on labelsToAdd/labelsToRemove labelNames = labelNames.filter((name) => !labelContext.toRemove.has(name)); @@ -398,93 +439,96 @@ export async function summarizeChecksImpl( targetBranch, requiredCheckRuns, fyiCheckRuns, + impactAssessment !== undefined, + target_url, ); + automatedChecksMet.target_url = target_url; + core.info( `Updating comment '${NEXT_STEPS_COMMENT_ID}' on ${owner}/${repo}#${issue_number} with body: ${commentBody}`, ); + core.summary.addRaw(`\n${commentBody}\n\n`); + core.summary.write(); + // this will remain commented until we're comfortable with the change. - // await commentOrUpdate( - // { github, context, core }, - // owner, - // repo, - // issue_number, - // commentName, - // commentBody - // ) + await commentOrUpdate( + github, + core, + owner, + repo, + issue_number, + commentBody, + NEXT_STEPS_COMMENT_ID, + ); + + // finally, update the "Automated merging requirements met" commit status + await updateCommitStatus(github, core, owner, repo, head_sha, automatedChecksMet); core.info( - `Summarize checks has identified that status of "Automated merging requirements met" check should be updated to: ${automatedChecksMet}.`, + `Summarize checks has identified that status of "${AUTOMATED_CHECK_NAME}" commit status should be updated to: ${JSON.stringify(automatedChecksMet)}.`, ); + core.summary.addHeading("Automated Checks Met", 2); + core.summary.addCodeBlock(JSON.stringify(automatedChecksMet, null, 2)); + core.summary.write(); } /** - * A GraphQL query to GitHub API that returns all check runs for given commit, with "isRequired" field for given PR. - * - * If you want to see example response, copy the query body into this: - * https://docs.github.com/en/graphql/overview/explorer - * Example inputs: - * resourceUrl: "https://github.com/test-repo-billy/azure-rest-api-specs/commit/c2789c5bde1b3f4fa34f76a8eeaaed479df23c4d" - * prNumber: 2996 - * - * Reference: - * https://docs.github.com/en/graphql/reference/queries#resource - * https://docs.github.com/en/graphql/guides/using-global-node-ids#3-do-a-direct-node-lookup-in-graphql - * https://docs.github.com/en/graphql/reference/objects#checkrun - * Rate limit: - * https://docs.github.com/en/graphql/overview/resource-limitations#rate-limit - * https://docs.github.com/en/graphql/reference/objects#ratelimit - * - * Note: here, for "checkRuns(first: ..)", maybe we should add a filter that filters to LATEST, per: - * https://docs.github.com/en/graphql/reference/input-objects#checkrunfilter - * https://docs.github.com/en/graphql/reference/enums#checkruntype - **/ + * Updates or creates a commit status with the given status + * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github + * @param {typeof import("@actions/core")} core + * @param {string} owner + * @param {string} repo + * @param {string} head_sha + * @param {CheckRunResult} checkResult + * @returns {Promise} + */ +export async function updateCommitStatus(github, core, owner, repo, head_sha, checkResult) { + // Map CheckRunResult status to commit status state + /** @type {"pending" | "success" | "failure" | "error"} */ + let state; + + const validStates = [CheckConclusion.SUCCESS, CheckConclusion.FAILURE, "pending"]; + if (validStates.includes(checkResult.result.toLowerCase())) { + state = /** @type {"pending" | "success" | "failure"} */ (checkResult.result.toLowerCase()); + } else { + state = "error"; // fallback for unexpected values + } + + // Create commit status instead of check run + await github.rest.repos.createCommitStatus({ + owner, + repo, + sha: head_sha, + state: state, + description: + checkResult.summary.length > 140 + ? checkResult.summary.substring(0, 137) + "..." + : checkResult.summary, + context: checkResult.name, + target_url: checkResult.target_url, + }); + + core.info( + `Created commit status for ${checkResult.name} with state: ${state} and description: ${checkResult.summary}`, + ); +} + /** - * Returns a GraphQL query string for the given resource URL and PR number. - * - * @param {string} owner - The URL of the GitHub resource (commit). - * @param {string} repo - The URL of the GitHub resource (commit). - * @param {string} sha - targeted commit. context.pr!.headInfo.sha - * @param {number} prNumber - The pull request number. - * @returns {string} The GraphQL query string. + * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github + * @param {string} owner + * @param {string} repo + * @param {number} issue_number + * @return {Promise} */ -function getGraphQLQuery(owner, repo, sha, prNumber) { - const resourceUrl = `https://github.com/${owner}/${repo}/commit/${sha}`; - - return ` - { - resource(url: "${resourceUrl}") { - ... on Commit { - checkSuites(first: 20) { - nodes { - workflowRun { - id - databaseId - workflow { - name - } - } - checkRuns(first: 30) { - nodes { - name - status - conclusion - isRequired(pullRequestNumber: ${prNumber}) - } - } - } - } - } - } - rateLimit { - limit - cost - used - remaining - resetAt - } - } - `; +export async function getExistingLabels(github, owner, repo, issue_number) { + const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, { + owner, + repo, + issue_number: issue_number, + per_page: PER_PAGE_MAX, + }); + return labels.map((label) => label.name); } // #endregion @@ -494,7 +538,7 @@ function getGraphQLQuery(owner, repo, sha, prNumber) { * @param {Set} labelsToRemove */ function warnIfLabelSetsIntersect(labelsToAdd, labelsToRemove) { - const intersection = Array.from(labelsToAdd).filter((label) => labelsToRemove.has(label)); + const intersection = [...intersect(labelsToAdd, labelsToRemove)]; if (intersection.length > 0) { console.warn( "ASSERTION VIOLATION! The intersection of labelsToRemove and labelsToAdd is non-empty! " + @@ -525,32 +569,9 @@ export function updateLabels(existingLabels, impactAssessment) { toRemove: new Set(), }; - if (impactAssessment) { - console.log(`Downloaded impact assessment: ${JSON.stringify(impactAssessment)}`); - // Merge impact assessment labels into the main labelContext - impactAssessment.labelContext.toAdd.forEach((label) => { - labelContext.toAdd.add(label); - }); - impactAssessment.labelContext.toRemove.forEach((label) => { - labelContext.toRemove.add(label); - }); - } - - // this is the only labelling that was part of original pipelinebot logic - processArmReviewLabels(labelContext, existingLabels); - if (impactAssessment) { // will further update the label context if necessary - processImpactAssessment( - impactAssessment.targetBranch, - labelContext, - impactAssessment.resourceManagerRequired, - impactAssessment.rpaasRPMissing, - impactAssessment.rpaasExceptionRequired, - impactAssessment.rpaasRpNotInPrivateRepo, - impactAssessment.isNewApiVersion, - impactAssessment.isDraft, - ); + processImpactAssessment(labelContext, impactAssessment); } warnIfLabelSetsIntersect(labelContext.toAdd, labelContext.toRemove); @@ -559,6 +580,26 @@ export function updateLabels(existingLabels, impactAssessment) { // #endregion // #region checks +/** + * Extracts required status check context names from GitHub branch rules response. + * @param {import('@octokit/rest').RestEndpointMethodTypes['repos']['getBranchRules']['response']} checkResponseObj - The GitHub branch rules API response object + * @returns {string[]} Array of required status check context names (e.g., ["license/cla", "Swagger LintDiff"]) + */ +export function getRequiredChecksFromBranchRuleOutput(checkResponseObj) { + const requiredChecks = []; + + // Look through all rules for required_status_checks type + for (const rule of checkResponseObj.data) { + if (rule.type === "required_status_checks" && rule.parameters?.required_status_checks) { + for (const statusCheck of rule.parameters.required_status_checks) { + requiredChecks.push(statusCheck.context); + } + } + } + + return requiredChecks; +} + /** * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github * @param {typeof import("@actions/core")} core @@ -580,10 +621,8 @@ export async function getCheckRunTuple( ) { // This function was originally a version of getRequiredAndFyiAndAutomatedMergingRequirementsMetCheckRuns // but has been simplified for clarity and purpose. - /** @type {CheckRunData[]} */ - let reqCheckRuns = []; - /** @type {CheckRunData[]} */ - let fyiCheckRuns = []; + /** @type {string[]} */ + let requiredCheckNames = []; /** @type {number | undefined} */ let impactAssessmentWorkflowRun = undefined; @@ -591,11 +630,150 @@ export async function getCheckRunTuple( /** @type { import("./labelling.js").ImpactAssessment | undefined } */ let impactAssessment = undefined; - const response = await github.graphql(getGraphQLQuery(owner, repo, head_sha, prNumber)); - core.info(`GraphQL Rate Limit Information: ${JSON.stringify(response.rateLimit)}`); + const allCheckRuns = await github.paginate(github.rest.checks.listForRef, { + owner: owner, + repo: repo, + ref: head_sha, + per_page: PER_PAGE_MAX, + }); + + const allCommitStatuses = await github.paginate(github.rest.repos.listCommitStatusesForRef, { + owner: owner, + repo: repo, + ref: head_sha, + per_page: PER_PAGE_MAX, + }); + + // Process allCheckRuns and allCommitStatuses into unified CheckRunData array + // all checks will be considered as "FYI" until we have an impact assessment, so we can + // determine the target branch, and from there pull branch protect rulesets to ensure we + // are marking the required checks correctly. + /** @type {Array} */ + const allChecks = []; + + allCheckRuns.forEach((checkRun) => { + allChecks.push({ + name: checkRun.name, + status: checkRun.status, + conclusion: checkRun.conclusion || null, + checkInfo: getCheckInfo(checkRun.name), + // Store original object for date sorting + _originalData: checkRun, + _source: "checkRun", + }); + }); + + allCommitStatuses.forEach((status) => { + // Map commit status state to check run conclusion + let conclusion = null; + let checkStatus = "completed"; + + switch (status.state) { + case "success": + conclusion = "success"; + break; + case "failure": + conclusion = "failure"; + break; + case "error": + conclusion = "failure"; + break; + case "pending": + checkStatus = "in_progress"; + conclusion = null; + break; + } + + allChecks.push({ + name: status.context, + status: checkStatus, + conclusion: conclusion, + checkInfo: getCheckInfo(status.context), + // Store original object for date sorting and data access + _originalData: status, + _source: "commitStatus", + }); + }); + + // Group by name and take the latest for each + const checksByName = new Map(); + + allChecks.forEach((check) => { + const name = check.name; + if (!checksByName.has(name)) { + checksByName.set(name, []); + } + checksByName.get(name).push(check); + }); + + // For each group, sort by date (newest first) and take the first one + const unifiedCheckRuns = []; + for (const [, checks] of checksByName) { + // Sort by date - newest first using invert(byDate(...)) + const sortedChecks = checks.sort( + invert( + byDate((check) => { + if (check._source === "checkRun") { + // Check runs have started_at, completed_at, etc. Use the most recent available date + return ( + check._originalData.completed_at || + check._originalData.started_at || + check._originalData.created_at + ); + } else { + // Commit statuses have created_at and updated_at + return check._originalData.updated_at || check._originalData.created_at; + } + }), + ), + ); + + const latestCheck = sortedChecks[0]; + + if ( + latestCheck.name === IMPACT_CHECK_NAME && + latestCheck.status === "completed" && + latestCheck.conclusion === "success" + ) { + const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { + owner, + repo, + head_sha: head_sha, + check_suite_id: latestCheck._originalData.check_suite.id, + per_page: PER_PAGE_MAX, + }); + + if (workflowRuns.length === 0) { + core.warning( + `No workflow runs found for check suite ID: ${latestCheck._originalData.check_suite.id}`, + ); + } else { + // Sort by updated_at to get the most recent run + const sortedRuns = workflowRuns.sort( + (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), + ); + impactAssessmentWorkflowRun = sortedRuns[0].id; + + if (workflowRuns.length > 1) { + core.info( + `Found ${workflowRuns.length} workflow runs for check suite ID: ${latestCheck._originalData.check_suite.id}, using most recent: ${sortedRuns[0].id}`, + ); + } + } + } + + // Create clean CheckRunData without temporary properties + unifiedCheckRuns.push({ + name: latestCheck.name, + status: latestCheck.status, + conclusion: latestCheck.conclusion, + checkInfo: latestCheck.checkInfo, + }); + } - [reqCheckRuns, fyiCheckRuns, impactAssessmentWorkflowRun] = - extractRunsFromGraphQLResponse(response); + core.info( + `Processed ${allCheckRuns.length} check runs and ${allCommitStatuses.length} commit statuses into ${unifiedCheckRuns.length} unified checks`, + ); if (impactAssessmentWorkflowRun) { core.info( @@ -608,24 +786,34 @@ export async function getCheckRunTuple( repo, impactAssessmentWorkflowRun, ); + + const branchRules = await github.rest.repos.getBranchRules({ + owner: owner, + repo: repo, + branch: impactAssessment.targetBranch, + }); + + if (branchRules) { + requiredCheckNames = getRequiredChecksFromBranchRuleOutput(branchRules).filter( + // "Automated merging requirements met" may be required in repo settings, to ensure PRs cannot be merged unless + // it's passing. However, it must be excluded from our list of requiredCheckNames, since it's status is set + // by our own workflow. If this check isn't excluded, it creates a deadlock where it can never be set. + (checkName) => checkName !== AUTOMATED_CHECK_NAME, + ); + } + } else { + requiredCheckNames = [IMPACT_CHECK_NAME]; } - core.info( - `RequiredCheckRuns: ${JSON.stringify(reqCheckRuns)}, ` + - `FyiCheckRuns: ${JSON.stringify(fyiCheckRuns)}, ` + - `ImpactAssessment: ${JSON.stringify(impactAssessment)}`, - ); - const filteredReqCheckRuns = reqCheckRuns.filter( - /** - * @param {CheckRunData} checkRun - */ - (checkRun) => !excludedCheckNames.includes(checkRun.name), + const filteredReqCheckRuns = unifiedCheckRuns.filter( + (checkRun) => + !excludedCheckNames.includes(checkRun.name) && requiredCheckNames.includes(checkRun.name), ); - const filteredFyiCheckRuns = fyiCheckRuns.filter( - /** - * @param {CheckRunData} checkRun - */ - (checkRun) => !excludedCheckNames.includes(checkRun.name), + const filteredFyiCheckRuns = unifiedCheckRuns.filter( + (checkRun) => + !excludedCheckNames.includes(checkRun.name) && + !requiredCheckNames.includes(checkRun.name) && + FYI_CHECK_NAMES.includes(checkRun.name), ); return [filteredReqCheckRuns, filteredFyiCheckRuns, impactAssessment]; @@ -653,86 +841,22 @@ export function checkRunIsSuccessful(checkRun) { } /** - * @param {any} response - GraphQL response data - * @returns {[CheckRunData[], CheckRunData[], number | undefined]} + * Get metadata for a specific check from our index. + * @param {string} checkName + * @returns {CheckMetadata} */ -function extractRunsFromGraphQLResponse(response) { - /** @type {CheckRunData[]} */ - const reqCheckRuns = []; - /** @type {CheckRunData[]} */ - const fyiCheckRuns = []; - - /** @type {number | undefined} */ - let impactAssessmentWorkflowRun = undefined; - - // Define the automated merging requirements check name - - if (response.resource?.checkSuites?.nodes) { - response.resource.checkSuites.nodes.forEach( - /** @param {{ workflowRun?: WorkflowRunInfo, checkRuns?: { nodes?: any[] } }} checkSuiteNode */ - (checkSuiteNode) => { - if (checkSuiteNode.checkRuns?.nodes) { - checkSuiteNode.checkRuns.nodes.forEach((checkRunNode) => { - // We have some specific guidance for some of the required checks. - const checkInfo = - CHECK_METADATA.find((metadata) => metadata.name === checkRunNode.name) || - /** @type {CheckMetadata} */ ({ - precedence: 1000, - name: checkRunNode.name, - suppressionLabels: [], - troubleshootingGuide: defaultTsg, - }); - - if (checkRunNode.isRequired) { - reqCheckRuns.push({ - name: checkRunNode.name, - status: checkRunNode.status, - conclusion: checkRunNode.conclusion, - checkInfo: checkInfo, - }); - } - // Note the "else" here. It means that: - // A GH check will be bucketed into "failing FYI check run" if: - // - It is failing - // - AND it is is NOT marked as 'required' in GitHub branch policy - // - AND it is marked as 'FYI' in this file's FYI_CHECK_NAMES array - else if (FYI_CHECK_NAMES.includes(checkRunNode.name)) { - fyiCheckRuns.push({ - name: checkRunNode.name, - status: checkRunNode.status, - conclusion: checkRunNode.conclusion, - checkInfo: checkInfo, - }); - } - }); - } - }, - ); - } - - // extract the ImpactAssessment check run if it is completed and successful - if (response.resource?.checkSuites?.nodes) { - response.resource.checkSuites.nodes.forEach( - /** @param {{ workflowRun?: WorkflowRunInfo, checkRuns?: { nodes?: any[] } }} checkSuiteNode */ - (checkSuiteNode) => { - if (checkSuiteNode.checkRuns?.nodes) { - checkSuiteNode.checkRuns.nodes.forEach((checkRunNode) => { - if ( - checkRunNode.name === "[TEST-IGNORE] Summarize PR Impact" && - checkRunNode.status?.toLowerCase() === "completed" && - checkRunNode.conclusion?.toLowerCase() === "success" - ) { - // Assign numeric databaseId, not the string node ID - impactAssessmentWorkflowRun = checkSuiteNode.workflowRun?.databaseId; - } - }); - } - }, - ); - } - - return [reqCheckRuns, fyiCheckRuns, impactAssessmentWorkflowRun]; +export function getCheckInfo(checkName) { + return ( + CHECK_METADATA.find((metadata) => metadata.name === checkName) || + /** @type {CheckMetadata} */ ({ + precedence: 1000, + name: checkName, + suppressionLabels: [], + troubleshootingGuide: defaultTsg, + }) + ); } + // #endregion // #region next steps /** @@ -743,7 +867,9 @@ function extractRunsFromGraphQLResponse(response) { * @param {string} targetBranch * @param {CheckRunData[]} requiredRuns * @param {CheckRunData[]} fyiRuns - * @returns {Promise<[string, string]>} + * @param {boolean} assessmentCompleted + * @param {string} target_url + * @returns {Promise<[string, CheckRunResult]>} */ export async function createNextStepsComment( core, @@ -752,17 +878,24 @@ export async function createNextStepsComment( targetBranch, requiredRuns, fyiRuns, + assessmentCompleted, + target_url, ) { // select just the metadata that we need about the runs. - const requiredCheckInfos = requiredRuns + const failingCheckInfos = requiredRuns .filter((run) => checkRunIsSuccessful(run) === false) .map((run) => run.checkInfo); - // determine if required runs have any successful runs. - const requiredCheckInfosPresent = requiredRuns.some((run) => { + // determine if required runs have any in-progress or queued runs + // if there are any, we consider the requirements not met. + // if there are NO required runs, we also consider this to be a "requirements met" situation. + // there is a possibility that this will be a false positive, but it is better than + // assuming that the requirements are not met when they actually are. + const requiredCheckInfosPresent = requiredRuns.every((run) => { const status = run.status.toLowerCase(); - return status !== "queued" && status !== "in_progress"; + return status === "completed"; }); + const fyiCheckInfos = fyiRuns .filter((run) => checkRunIsSuccessful(run) === false) .map((run) => run.checkInfo); @@ -772,8 +905,10 @@ export async function createNextStepsComment( labels, `${repo}/${targetBranch}`, requiredCheckInfosPresent, - requiredCheckInfos, + failingCheckInfos, fyiCheckInfos, + assessmentCompleted, + target_url, ); return [commentBody, automatedChecksMet]; @@ -786,7 +921,9 @@ export async function createNextStepsComment( * @param {boolean} requiredCheckInfosPresent * @param {CheckMetadata[]} failingReqChecksInfo * @param {CheckMetadata[]} failingFyiChecksInfo - * @returns {Promise<[string, string]>} + * @param {boolean} assessmentCompleted + * @param {string} target_url + * @returns {Promise<[string, CheckRunResult]>} */ async function buildNextStepsToMergeCommentBody( core, @@ -795,21 +932,24 @@ async function buildNextStepsToMergeCommentBody( requiredCheckInfosPresent, failingReqChecksInfo, failingFyiChecksInfo, + assessmentCompleted, + target_url, ) { // Build the comment header const commentTitle = `

Next Steps to Merge

`; const violatedReqLabelsRules = await getViolatedRequiredLabelsRules(core, labels, targetBranch); - // this is the first place of adjusted logic. I am treating `requirementsMet` as `no failed required checks`. - // I do this because the `automatedMergingRequirementsMetCheckRun` WILL NOT BE PRESENT in the new world. - // The new world we will simply pull all the required checks and if any are failing then we are blocked. If there are - // no failed checks we can't yet say that everything is met, because a check MIGHT run in the future. To prevent - // this "no checks run" accidentally evaluating as success, we need to ensure that we have at least one failing check - // in the required checks to consider the requirements met + // we are "blocked" if we have any violated labelling rules OR if we have any failing required checks const anyBlockerPresent = failingReqChecksInfo.length > 0 || violatedReqLabelsRules.length > 0; const anyFyiPresent = failingFyiChecksInfo.length > 0; - const requirementsMet = !anyBlockerPresent && requiredCheckInfosPresent; + // we consider requirements met if there are: + // - no blockers (which includes violated labelling rules in its definition) (anyBlockerPresent) + // - that none of the required checks are in_progress or queued (requiredCheckInfosPresent) + // - and that the assessment is completed. If it is not, we assume we are still evaluating the requirements. Not having + // the assessment completed is a blocker, as we may end up having violated labelling rules that would be detected only after + // it is completed. + const requirementsMet = !anyBlockerPresent && requiredCheckInfosPresent && assessmentCompleted; // Compose the body based on the current state const [commentBody, automatedChecksMet] = getCommentBody( @@ -819,6 +959,7 @@ async function buildNextStepsToMergeCommentBody( failingReqChecksInfo, failingFyiChecksInfo, violatedReqLabelsRules, + target_url, ); return [commentTitle + commentBody, automatedChecksMet]; @@ -832,7 +973,8 @@ async function buildNextStepsToMergeCommentBody( * @param {CheckMetadata[]} failingReqChecksInfo - Failing required checks info * @param {CheckMetadata[]} failingFyiChecksInfo - Failing FYI checks info * @param {RequiredLabelRule[]} violatedRequiredLabelsRules - Violated required label rules - * @returns {[string, string]} The body content HTML and the status that automated checks met should be set to. + * @param {string} target_url - The target URL for the automated checks met run which will be set at the outset of summarize-checks + * @returns {[string, CheckRunResult]} The body content HTML and the CheckRunResult that automated checks met should be set to. */ function getCommentBody( requirementsMet, @@ -841,14 +983,21 @@ function getCommentBody( failingReqChecksInfo, failingFyiChecksInfo, violatedRequiredLabelsRules, + target_url, ) { + /** @type {"pending" | keyof typeof CheckConclusion} */ + let status = "pending"; + let summaryData = "The requirements for merging this PR are still being evaluated. Please wait."; + + // Generate the comment body using the original logic for backwards compatibility let bodyProper = ""; - let automatedChecksMet = "pending"; if (anyBlockerPresent || anyFyiPresent) { if (anyBlockerPresent) { bodyProper += getBlockerPresentBody(failingReqChecksInfo, violatedRequiredLabelsRules); - automatedChecksMet = "blocked"; + summaryData = + "❌ This PR cannot be merged because some requirements are not met. See the details."; + status = "FAILURE"; } if (anyBlockerPresent && anyFyiPresent) { @@ -857,19 +1006,42 @@ function getCommentBody( if (anyFyiPresent) { bodyProper += getFyiPresentBody(failingFyiChecksInfo); - if (!anyBlockerPresent) { + if (!anyBlockerPresent && requirementsMet) { bodyProper += `If you still want to proceed merging this PR without addressing the above failures, ${diagramTsg(4, false)}.`; + summaryData = + `⚠️ Some important automated merging requirements have failed. As of today you can still merge this PR, ` + + `but soon these requirements will be blocking.` + + `
See Next Steps to merge comment on this PR for details on how to address them.` + + `
If you want to proceed with merging this PR without fixing them, refer to ` + + `aka.ms/azsdk/specreview/merge.`; + status = "SUCCESS"; } } } else if (requirementsMet) { - automatedChecksMet = "success"; bodyProper = `✅ All automated merging requirements have been met! ` + `To get your PR merged, see aka.ms/azsdk/specreview/merge.`; + summaryData = + `✅ All automated merging requirements have been met.` + + `
To merge this PR, refer to ` + + `aka.ms/azsdk/specreview/merge.` + + "
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help)."; + status = "SUCCESS"; } else { bodyProper = "⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛"; + // dont need to update the status of the check, as pending is the default state. } + + bodyProper += `

Comment generated by summarize-checks workflow run.`; + + /** @type {CheckRunResult} */ + const automatedChecksMet = { + name: AUTOMATED_CHECK_NAME, + summary: summaryData, + result: status, + }; + return [bodyProper, automatedChecksMet]; } @@ -964,55 +1136,57 @@ function buildViolatedLabelRulesNextStepsText(violatedRequiredLabelsRules) { * @param {string} owner * @param {string} repo * @param {number} runId - The workflow run databaseId - * @returns {Promise} The parsed job summary data + * @returns {Promise} The parsed job summary data */ export async function getImpactAssessment(github, core, owner, repo, runId) { - try { - // List artifacts for provided workflow run - const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner, - repo, - run_id: runId, - }); + // List artifacts for provided workflow run + const jobSummaryArtifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { + owner, + repo, + run_id: runId, + name: "job-summary", + per_page: PER_PAGE_MAX, + }); + + // If multiple artifacts with same name, select latest updated + const jobSummaryArtifact = jobSummaryArtifacts.sort( + invert(byDate((a) => a.updated_at || "1970")), + )[0]; - // Find the job-summary artifact - const jobSummaryArtifact = artifacts.data.artifacts.find( - (artifact) => artifact.name === "job-summary", + if (!jobSummaryArtifact) { + throw new Error( + `Unable to find job-summary artifact for run ID: ${runId}. This should never happen, as this section of code should only run with a valid runId.`, ); + } - if (!jobSummaryArtifact) { - core.info("No job-summary artifact found"); - return undefined; - } + // Download the artifact as a zip archive + const download = await github.rest.actions.downloadArtifact({ + owner, + repo, + artifact_id: jobSummaryArtifact.id, + archive_format: "zip", + }); - // Download the artifact as a zip archive - const download = await github.rest.actions.downloadArtifact({ - owner, - repo, - artifact_id: jobSummaryArtifact.id, - archive_format: "zip", - }); + core.info(`Successfully downloaded job-summary artifact ID: ${jobSummaryArtifact.id}`); - core.info(`Successfully downloaded job-summary artifact ID: ${jobSummaryArtifact.id}`); - - // Write zip buffer to temp file and extract JSON - const tmpZip = path.join(process.env.RUNNER_TEMP || os.tmpdir(), `job-summary-${runId}.zip`); - // Convert ArrayBuffer to Buffer - // Convert ArrayBuffer (download.data) to Node Buffer - const arrayBuffer = /** @type {ArrayBuffer} */ (download.data); - const zipBuffer = Buffer.from(new Uint8Array(arrayBuffer)); - await fs.writeFile(tmpZip, zipBuffer); - // Extract JSON content from zip archive - const { stdout: jsonContent } = await execFile("unzip", ["-p", tmpZip]); - await fs.unlink(tmpZip); - - /** @type {import("./labelling.js").ImpactAssessment} */ - // todo: we need to zod this to ensure the structure is correct, however we do not have zod installed at time of run - const impact = JSON.parse(jsonContent); - return impact; - } catch (/** @type {any} */ error) { - core.error(`Failed to download job summary artifact: ${error.message}`); - return undefined; - } + // Write zip buffer to temp file and extract JSON + const tmpZip = path.join(process.env.RUNNER_TEMP || os.tmpdir(), `job-summary-${runId}.zip`); + // Convert ArrayBuffer to Buffer + // Convert ArrayBuffer (download.data) to Node Buffer + const arrayBuffer = /** @type {ArrayBuffer} */ (download.data); + const zipBuffer = Buffer.from(new Uint8Array(arrayBuffer)); + await fs.writeFile(tmpZip, zipBuffer); + + // Extract JSON content from zip archive + // Could replace with library like 'fflate' instead of 'exec unzip', but + // this would require 'npm i', while 'unzip' is pre-installed. + const { stdout: jsonContent } = await execFile("unzip", ["-p", tmpZip]); + + await fs.unlink(tmpZip); + + /** @type {import("./labelling.js").ImpactAssessment} */ + // todo: we need to zod this to ensure the structure is correct, however we do not have zod installed at time of run + const impact = JSON.parse(jsonContent); + return impact; } // #endregion diff --git a/.github/workflows/src/update-labels.js b/.github/workflows/src/update-labels.js index a7664d21fb6f..05b450cea962 100644 --- a/.github/workflows/src/update-labels.js +++ b/.github/workflows/src/update-labels.js @@ -1,26 +1,52 @@ // @ts-check +import { isFullGitSha } from "../../shared/src/git.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; import { extractInputs } from "../src/context.js"; -import { PER_PAGE_MAX } from "./github.js"; /** * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */ export default async function updateLabels({ github, context, core }) { - const { owner, repo, issue_number, run_id } = await extractInputs(github, context, core); - await updateLabelsImpl({ owner, repo, issue_number, run_id, github, core }); + const { owner, repo, head_sha, issue_number, run_id } = await extractInputs( + github, + context, + core, + ); + await updateLabelsImpl({ owner, repo, head_sha, issue_number, run_id, github, core }); } /** * @param {Object} params * @param {string} params.owner * @param {string} params.repo + * @param {string} params.head_sha * @param {number} params.issue_number * @param {number} params.run_id * @param {(import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api & { paginate: import("@octokit/plugin-paginate-rest").PaginateInterface; })} params.github * @param {typeof import("@actions/core")} params.core */ -export async function updateLabelsImpl({ owner, repo, issue_number, run_id, github, core }) { +export async function updateLabelsImpl({ + owner, + repo, + head_sha, + issue_number, + run_id, + github, + core, +}) { + if (isFullGitSha(head_sha)) { + core.setOutput("head_sha", head_sha); + } else { + core.info(`head_sha is not a valid full git SHA: '${head_sha}'`); + } + + if (Number.isInteger(issue_number) && issue_number > 0) { + core.setOutput("issue_number", issue_number); + } else { + core.info(`issue_number must be a positive integer: ${issue_number}`); + } + /** @type {string[]} */ let artifactNames = []; @@ -59,6 +85,11 @@ export async function updateLabelsImpl({ owner, repo, issue_number, run_id, gith if (key.startsWith("label-")) { const name = key.substring("label-".length); + + if (!name) { + throw new Error(`Invalid value for label name: '${name}'`); + } + if (value === "true") { labelsToAdd.push(name); } else if (value === "false") { diff --git a/.github/workflows/summarize-checks.yaml b/.github/workflows/summarize-checks.yaml index 4e39bd8fb5a4..9cadb9dfd50d 100644 --- a/.github/workflows/summarize-checks.yaml +++ b/.github/workflows/summarize-checks.yaml @@ -1,15 +1,16 @@ -name: "[TEST-IGNORE] Summarize Checks" +name: "Summarize Checks" on: workflow_run: - # todo, any others need to be added here? workflows: - - "\\[TEST-IGNORE\\] Swagger SemanticValidation - Set Status" - - "\\[TEST-IGNORE\\] Swagger ModelValidation - Set Status" - - "\\[TEST-IGNORE\\] Summarize PR Impact" + - "Swagger SemanticValidation - Set Status" + - "Swagger ModelValidation - Set Status" + - "Summarize PR Impact" - "Swagger Avocado - Set Status" - "Swagger LintDiff - Set Status" - "SDK Validation Status" + - "TypeSpec Validation" + - "Update Labels" types: - completed pull_request_target: # when a PR is labeled. NOT pull_request, because that would run on the PR branch, not the base branch. @@ -19,18 +20,22 @@ on: - opened - synchronize - reopened + - edited + - ready_for_review + - converted_to_draft permissions: actions: read # to inspect workflow_run metadata contents: read # for actions/checkout checks: read # for octokit.rest.checks.listForRef - statuses: read # for octokit.rest.repos.getCombinedStatusForRef + statuses: write # for octokit.rest.repos.getCombinedStatusForRef and octokit.rest.repos.createCommitStatus issues: write # to post comments via the Issues API pull-requests: write # to post comments via the Pull Requests API jobs: run-summarize-checks: - name: "[TEST-IGNORE] Summarize Checks" + if: ${{ github.event_name == 'pull_request_target' || github.event.workflow_run.conclusion != 'skipped' }} + name: "Summarize Checks" runs-on: ubuntu-24.04 steps: @@ -50,6 +55,15 @@ jobs: # todo: add the npm install ci here for the zod usage in summarize-checks (not currently there) + - id: dump-trigger-metadata + name: Dump Trigger Metadata + uses: actions/github-script@v7 + with: + script: | + const { default: dumpTriggerMetadata } = + await import('${{ github.workspace }}/.github/workflows/src/summarize-checks/dump-trigger-metadata.js'); + return await dumpTriggerMetadata({ context, core }); + - id: summarize-checks name: Summarize Checks uses: actions/github-script@v7 @@ -58,3 +72,13 @@ jobs: const { default: summarizeChecks } = await import('${{ github.workspace }}/.github/workflows/src/summarize-checks/summarize-checks.js'); return await summarizeChecks({ github, context, core }); + + # May be used by downstream workflows + - name: Set job-summary artifact + if: ${{ always() && steps.summarize-checks.outputs.summary }} + uses: actions/upload-artifact@v4 + with: + name: job-summary + path: ${{ steps.summarize-checks.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/summarize-impact-test.yaml b/.github/workflows/summarize-impact-test.yaml new file mode 100644 index 000000000000..ba274bf6cbf6 --- /dev/null +++ b/.github/workflows/summarize-impact-test.yaml @@ -0,0 +1,28 @@ +name: Summarize Impact - Test + +on: + push: + branches: + - main + pull_request: + paths: + - package-lock.json + - package.json + - tsconfig.json + - .github/shared + - .github/workflows/_reusable-eng-tools-test.yaml + - .github/workflows/summarize-impact-test.yaml + - eng/tools/package.json + - eng/tools/tsconfig.json + - eng/tools/summarize-impact/** + workflow_dispatch: + +permissions: + contents: read + +jobs: + summarize-impact: + uses: ./.github/workflows/_reusable-eng-tools-test.yaml + with: + package: summarize-impact + lint: false diff --git a/.github/workflows/summarize-impact.yaml b/.github/workflows/summarize-impact.yaml index 373f091ed470..6bafe8e84454 100644 --- a/.github/workflows/summarize-impact.yaml +++ b/.github/workflows/summarize-impact.yaml @@ -1,6 +1,14 @@ -name: "[TEST-IGNORE] Summarize PR Impact" +name: "Summarize PR Impact" -on: pull_request +on: + pull_request: + types: + - opened + - synchronize + - reopened + - edited + - ready_for_review + - converted_to_draft permissions: contents: read @@ -8,7 +16,7 @@ permissions: jobs: impact: - name: "[TEST-IGNORE] Summarize PR Impact" + name: "Summarize PR Impact" runs-on: ubuntu-24.04 steps: @@ -40,7 +48,7 @@ jobs: --sha "${{ github.event.pull_request.head.sha }}" \ --repo "${{ github.event.repository.name }}" \ --owner "${{ github.repository_owner }}" \ - --isDraft ${{ github.event.pull_request.draft }} + ${{ github.event.pull_request.draft && '--isDraft' || '' }} env: # We absolutely need to avoid OOM errors due to certain inherited types from openapi-alps NODE_OPTIONS: "--max-old-space-size=8192" @@ -55,12 +63,3 @@ jobs: path: ${{ steps.summarize-impact.outputs.summary }} # If the file doesn't exist, just don't add the artifact if-no-files-found: ignore - - - if: | - always() && - github.event.pull_request.number > 0 - name: Upload artifact with issue number - uses: ./after/.github/actions/add-empty-artifact - with: - name: "issue-number" - value: "${{ github.event.pull_request.number }}" diff --git a/.github/workflows/swagger-modelvalidation-code.yaml b/.github/workflows/swagger-modelvalidation-code.yaml index 7ee3c19b8571..9eaf3053dfa7 100644 --- a/.github/workflows/swagger-modelvalidation-code.yaml +++ b/.github/workflows/swagger-modelvalidation-code.yaml @@ -1,13 +1,21 @@ -name: "[TEST-IGNORE] Swagger ModelValidation" +name: "Swagger ModelValidation - Analyze Code" -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read jobs: oav: - name: "[TEST-IGNORE] Swagger ModelValidation" + name: "Swagger ModelValidation - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -20,5 +28,17 @@ jobs: uses: ./.github/actions/setup-node-install-deps - name: Swagger Model Validation + id: swagger-model-validation run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- oav-runner examples + + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.swagger-model-validation.outputs.summary }} + uses: actions/upload-artifact@v4 + with: + name: job-summary + path: ${{ steps.swagger-model-validation.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/swagger-modelvalidation-status.yaml b/.github/workflows/swagger-modelvalidation-status.yaml index 5dc54b4caa8c..ac178380a2a9 100644 --- a/.github/workflows/swagger-modelvalidation-status.yaml +++ b/.github/workflows/swagger-modelvalidation-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger ModelValidation - Set Status" +name: "Swagger ModelValidation - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Swagger ModelValidation"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Swagger ModelValidation - Analyze Code", + "\\[TEST-IGNORE\\] Swagger ModelValidation - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set ModelValidation Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Swagger ModelValidation" - required_check_name: "[TEST-IGNORE] Swagger ModelValidation" + monitored_workflow_name: "Swagger ModelValidation - Analyze Code" + required_check_name: "Swagger ModelValidation" overriding_label: "Approved-ModelValidation" diff --git a/.github/workflows/swagger-semanticvalidation-code.yaml b/.github/workflows/swagger-semanticvalidation-code.yaml index 532de1554d88..1f533e2197f9 100644 --- a/.github/workflows/swagger-semanticvalidation-code.yaml +++ b/.github/workflows/swagger-semanticvalidation-code.yaml @@ -1,13 +1,21 @@ -name: "[TEST-IGNORE] Swagger SemanticValidation" +name: "Swagger SemanticValidation - Analyze Code" -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read jobs: oav: - name: "[TEST-IGNORE] Swagger SemanticValidation" + name: "Swagger SemanticValidation - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -20,5 +28,17 @@ jobs: uses: ./.github/actions/setup-node-install-deps - name: Swagger Semantic Validation + id: swagger-semantic-validation run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- oav-runner specs + + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.swagger-semantic-validation.outputs.summary }} + uses: actions/upload-artifact@v4 + with: + name: job-summary + path: ${{ steps.swagger-semantic-validation.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/swagger-semanticvalidation-status.yaml b/.github/workflows/swagger-semanticvalidation-status.yaml index 637866d22004..930533c6d4ad 100644 --- a/.github/workflows/swagger-semanticvalidation-status.yaml +++ b/.github/workflows/swagger-semanticvalidation-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger SemanticValidation - Set Status" +name: "Swagger SemanticValidation - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Swagger SemanticValidation"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Swagger SemanticValidation - Analyze Code", + "\\[TEST-IGNORE\\] Swagger SemanticValidation - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set SemanticValidation Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Swagger SemanticValidation" - required_check_name: "[TEST-IGNORE] Swagger SemanticValidation" + monitored_workflow_name: "Swagger SemanticValidation - Analyze Code" + required_check_name: "Swagger SemanticValidation" overriding_label: "Approved-SemanticValidation" diff --git a/.github/workflows/test/arm-auto-signoff.test.js b/.github/workflows/test/arm-auto-signoff.test.js index c27801369f36..d354f08b54bf 100644 --- a/.github/workflows/test/arm-auto-signoff.test.js +++ b/.github/workflows/test/arm-auto-signoff.test.js @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import { CommitStatusState } from "../../shared/src/github.js"; import { getLabelActionImpl } from "../src/arm-auto-signoff.js"; import { LabelAction } from "../src/label.js"; import { createMockCore, createMockGithub as createMockGithubBase } from "./mocks.js"; @@ -78,7 +79,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); }); it("removes label if not incremental typespec", async () => { @@ -96,7 +97,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it("no-ops if incremental typespec in progress", async () => { @@ -123,7 +124,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); }); it("removes label if no runs of incremental typespec", async () => { @@ -146,7 +147,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it("uses latest run of incremental typespec", async () => { @@ -184,7 +185,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it.each([ @@ -207,7 +208,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it.each(["Swagger Avocado", "Swagger LintDiff"])( @@ -218,16 +219,13 @@ describe("getLabelActionImpl", () => { github.rest.issues.listLabelsOnIssue.mockResolvedValue({ data: [{ name: "ARMAutoSignedOff" }, { name: "ARMReview" }], }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: check, - status: "completed", - conclusion: "failure", - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [ + { + context: check, + state: CommitStatusState.FAILURE, + }, + ], }); await expect( @@ -239,33 +237,58 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }, ); - it("thows if multiple runs for same check", async () => { + it.each([ + [CommitStatusState.ERROR, ["ARMReview", "ARMAutoSignedOff"]], + [CommitStatusState.FAILURE, ["ARMReview", "ARMAutoSignedOff"]], + [CommitStatusState.PENDING, ["ARMReview"]], + [CommitStatusState.SUCCESS, ["ARMReview"]], + ])("uses latest status if multiple (%o)", async (state, labels) => { const github = createMockGithub({ incrementalTypeSpec: true }); github.rest.issues.listLabelsOnIssue.mockResolvedValue({ - data: [{ name: "ARMReview" }], + data: labels.map((l) => ({ + name: l, + })), }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: "Swagger LintDiff", - status: "in_progress", - conclusion: null, - }, - { - name: "Swagger LintDiff", - status: "in_progress", - conclusion: null, - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [ + { + context: "Swagger Avocado", + state: CommitStatusState.SUCCESS, + updated_at: "2025-01-01", + }, + { + context: "Swagger LintDiff", + state: CommitStatusState.PENDING, + updated_at: "2025-01-01", + }, + { + context: "Swagger LintDiff", + state, + updated_at: "2025-01-02", + }, + ], }); + + let expectedAction; + switch (state) { + case CommitStatusState.ERROR: + case CommitStatusState.FAILURE: + expectedAction = LabelAction.Remove; + break; + case CommitStatusState.SUCCESS: + expectedAction = LabelAction.Add; + break; + case CommitStatusState.PENDING: + expectedAction = LabelAction.None; + break; + } + await expect( getLabelActionImpl({ owner: "TestOwner", @@ -275,7 +298,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).rejects.toThrow(); + ).resolves.toEqual({ labelAction: expectedAction, headSha: "abc123", issueNumber: 123 }); }); it("no-ops if check not found or not completed", async () => { @@ -285,10 +308,8 @@ describe("getLabelActionImpl", () => { data: [{ name: "ARMReview" }], }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [], }); await expect( getLabelActionImpl({ @@ -299,18 +320,10 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: "Swagger LintDiff", - status: "in_progress", - conclusion: null, - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [{ context: "Swagger LintDiff", state: CommitStatusState.PENDING }], }); await expect( getLabelActionImpl({ @@ -321,7 +334,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); }); it("adds label if incremental tsp, labels match, and check succeeded", async () => { @@ -330,21 +343,17 @@ describe("getLabelActionImpl", () => { github.rest.issues.listLabelsOnIssue.mockResolvedValue({ data: [{ name: "ARMReview" }], }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: "Swagger LintDiff", - status: "completed", - conclusion: "success", - }, - { - name: "Swagger Avocado", - status: "completed", - conclusion: "success", - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [ + { + context: "Swagger LintDiff", + state: CommitStatusState.SUCCESS, + }, + { + context: "Swagger Avocado", + state: CommitStatusState.SUCCESS, + }, + ], }); await expect( @@ -356,6 +365,6 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Add, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Add, headSha: "abc123", issueNumber: 123 }); }); }); diff --git a/.github/workflows/test/breaking-change-add-label-artifacts.test.js b/.github/workflows/test/breaking-change-add-label-artifacts.test.js new file mode 100644 index 000000000000..a36b9fc4b56c --- /dev/null +++ b/.github/workflows/test/breaking-change-add-label-artifacts.test.js @@ -0,0 +1,474 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { REVIEW_REQUIRED_LABELS } from "../../shared/src/breaking-change.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import getLabelActions, { + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, +} from "../src/breaking-change-add-label-artifacts.js"; +import { createMockContext, createMockCore, createMockGithub } from "./mocks.js"; + +// Mock dependencies +vi.mock("../src/context.js", () => ({ + extractInputs: vi.fn(), +})); + +describe("breaking-change-add-label-artifacts", () => { + let mockGithub; + let mockContext; + let mockCore; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + + // Create fresh mock instances for each test + mockGithub = createMockGithub(); + mockContext = createMockContext(); + mockCore = createMockCore(); + }); + + const mockInputs = { + owner: "Azure", + repo: "azure-rest-api-specs", + head_sha: "abc123def456", + issue_number: 12345, + }; + + const createMockWorkflowRun = ( + name, + status = "completed", + conclusion = "success", + id = 1, + updated_at = "2024-01-01T12:00:00Z", + ) => ({ + id, + name, + status, + conclusion, + updated_at, + }); + + const createMockArtifact = (name) => ({ name }); + + // Shared setup helpers + const setupMockInputs = async () => { + const { extractInputs } = await import("../src/context.js"); + extractInputs.mockResolvedValue(mockInputs); + }; + + const setupWorkflowRunsMock = (workflowRuns) => { + mockGithub.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ + data: { workflow_runs: workflowRuns }, + }); + }; + + const setupArtifactsMock = (breakingChangeArtifacts, crossVersionArtifacts) => { + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: breakingChangeArtifacts } }) + .mockResolvedValueOnce({ data: { artifacts: crossVersionArtifacts } }); + }; + + const createStandardWorkflowRuns = () => [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 1), + createMockWorkflowRun(CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 2), + ]; + + const expectRequiredOutputs = () => { + expect(mockCore.setOutput).toHaveBeenCalledWith("head_sha", mockInputs.head_sha); + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + }; + + const expectStandardOutputs = (breakingChangeValue, versioningValue) => { + expectRequiredOutputs(); + + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelName", + REVIEW_REQUIRED_LABELS.BREAKING_CHANGE, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelValue", + breakingChangeValue, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "versioningReviewLabelName", + REVIEW_REQUIRED_LABELS.VERSIONING, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", versioningValue); + }; + + const expectEarlyReturn = (infoMessage) => { + expectRequiredOutputs(); + + // Ensure setOutput was *only* called with the two required outputs + expect(mockCore.setOutput).toHaveBeenCalledTimes(2); + + expect(mockCore.info).toHaveBeenCalledWith(infoMessage); + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).not.toHaveBeenCalled(); + }; + + describe("successful execution with both workflows completed", () => { + it("should process breaking change and versioning labels correctly", async () => { + // Setup mocks + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun( + SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 1, + "2024-01-01T12:00:00Z", + ), + createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 2, + "2024-01-01T11:30:00Z", + ), + createMockWorkflowRun("Other Workflow", "completed", "success", 3, "2024-01-01T11:00:00Z"), + ]; + + const breakingChangeArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`), + createMockArtifact("other-artifact"), + ]; + + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.VERSIONING}=true`), + createMockArtifact("another-artifact"), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + setupArtifactsMock(breakingChangeArtifacts, crossVersionArtifacts); + + // Execute the function + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Verify API calls + expect(mockGithub.rest.actions.listWorkflowRunsForRepo).toHaveBeenCalledWith({ + owner: "Azure", + repo: "azure-rest-api-specs", + event: "pull_request", + head_sha: "abc123def456", + per_page: PER_PAGE_MAX, + }); + + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).toHaveBeenCalledTimes(2); + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).toHaveBeenNthCalledWith(1, { + owner: "Azure", + repo: "azure-rest-api-specs", + run_id: 1, + per_page: PER_PAGE_MAX, + }); + + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).toHaveBeenNthCalledWith(2, { + owner: "Azure", + repo: "azure-rest-api-specs", + run_id: 2, + per_page: PER_PAGE_MAX, + }); + + // Verify outputs - breaking change takes precedence over versioning + expectStandardOutputs(true, false); // breaking change takes precedence + }); + + it("should set versioning label when only versioning artifacts are present", async () => { + await setupMockInputs(); + + const breakingChangeArtifacts = [createMockArtifact("other-artifact")]; + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.VERSIONING}=true`), + ]; + + setupWorkflowRunsMock(createStandardWorkflowRuns()); + setupArtifactsMock(breakingChangeArtifacts, crossVersionArtifacts); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectStandardOutputs(false, true); // only versioning label should be true + }); + + it("should set no labels when no relevant artifacts are present", async () => { + await setupMockInputs(); + + const breakingChangeArtifacts = [createMockArtifact("other-artifact")]; + const crossVersionArtifacts = [createMockArtifact("another-artifact")]; + + setupWorkflowRunsMock(createStandardWorkflowRuns()); + setupArtifactsMock(breakingChangeArtifacts, crossVersionArtifacts); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectStandardOutputs(false, false); // no labels should be true + }); + }); + + describe("workflow run status handling", () => { + it("should return early when breaking changes workflow is not completed", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "in_progress", null, 1), + createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 2, + ), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectEarlyReturn("No completed breaking changes workflow run found"); + }); + + it("should return early when cross-version workflow is not completed", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 1), + createMockWorkflowRun(CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, "queued", null, 2), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectEarlyReturn("No completed cross-version breaking changes workflow run found"); + }); + + it("should return early when breaking changes workflow is not found", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 2, + ), + createMockWorkflowRun("Other Workflow", "completed", "success", 3), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.info).toHaveBeenCalledWith( + "No completed breaking changes workflow run found", + ); + }); + + it("should return early when cross-version workflow is not found", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 1), + createMockWorkflowRun("Other Workflow", "completed", "success", 3), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.info).toHaveBeenCalledWith( + "No completed cross-version breaking changes workflow run found", + ); + }); + }); + + describe("artifact processing", () => { + it("should find labels in cross-version artifacts when not in breaking change artifacts", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + const breakingChangeArtifacts = [createMockArtifact("other-artifact")]; + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: breakingChangeArtifacts } }) + .mockResolvedValueOnce({ data: { artifacts: crossVersionArtifacts } }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.setOutput).toHaveBeenCalledWith("breakingChangeReviewLabelValue", true); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", false); + }); + + it("should handle both labels present in different artifact collections", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + const breakingChangeArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.VERSIONING}=true`), + ]; + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: breakingChangeArtifacts } }) + .mockResolvedValueOnce({ data: { artifacts: crossVersionArtifacts } }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Breaking change should take precedence + expect(mockCore.setOutput).toHaveBeenCalledWith("breakingChangeReviewLabelValue", true); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", false); + }); + }); + + describe("edge cases", () => { + it("should handle empty workflow runs list", async () => { + await setupMockInputs(); + + mockGithub.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ + data: { workflow_runs: [] }, + }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.info).toHaveBeenCalledWith( + "No completed breaking changes workflow run found", + ); + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).not.toHaveBeenCalled(); + }); + + it("should return early when both artifact lists are empty", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: [] } }) // Empty breaking change artifacts + .mockResolvedValueOnce({ data: { artifacts: [] } }); // Empty cross-version artifacts + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Should log the info message about no artifacts found + expect(mockCore.info).toHaveBeenCalledWith( + "No artifacts found for breaking changes or cross-version breaking changes", + ); + + // Should only set issue_number output, but not the label outputs since it returns early + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + expect(mockCore.setOutput).not.toHaveBeenCalledWith( + "breakingChangeReviewLabelValue", + expect.anything(), + ); + expect(mockCore.setOutput).not.toHaveBeenCalledWith( + "versioningReviewLabelValue", + expect.anything(), + ); + }); + + it("should return early when one workflow has empty artifacts and other has only non-label artifacts", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: [] } }) // Empty breaking change artifacts + .mockResolvedValueOnce({ + data: { artifacts: [createMockArtifact("some-other-artifact")] }, + }); // Non-label artifacts + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Should NOT return early because there are artifacts (even though they're not label artifacts) + // The function should continue and set outputs with false values + expect(mockCore.info).not.toHaveBeenCalledWith( + "No artifacts found for breaking changes or cross-version breaking changes", + ); + + // Should set all outputs including the label values + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + expect(mockCore.setOutput).toHaveBeenCalledWith("breakingChangeReviewLabelValue", false); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", false); + }); + + it("should handle workflow runs with status conclusion pairs", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + { + ...createMockWorkflowRun( + SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "failure", + 1, + ), + }, + { + ...createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "in_progress", + null, + 2, + ), + }, + ]; + + mockGithub.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ + data: { workflow_runs: mockWorkflowRuns }, + }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Should log status for in_progress and conclusion for completed + expect(mockCore.info).toHaveBeenCalledWith( + `- ${SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME}: failure`, + ); + expect(mockCore.info).toHaveBeenCalledWith( + `- ${CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME}: in_progress`, + ); + }); + }); + + describe("output validation", () => { + it("should always set all required outputs", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + const artifacts = [createMockArtifact("test-artifact")]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts } }) + .mockResolvedValueOnce({ data: { artifacts } }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Verify all required outputs are set + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelName", + REVIEW_REQUIRED_LABELS.BREAKING_CHANGE, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelValue", + expect.any(Boolean), + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "versioningReviewLabelName", + REVIEW_REQUIRED_LABELS.VERSIONING, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "versioningReviewLabelValue", + expect.any(Boolean), + ); + }); + }); +}); diff --git a/.github/workflows/test/context.test.js b/.github/workflows/test/context.test.js index baedbdbcfa8b..ffc7ca11ba84 100644 --- a/.github/workflows/test/context.test.js +++ b/.github/workflows/test/context.test.js @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { fullGitSha } from "../../shared/test/examples.js"; import { extractInputs } from "../src/context.js"; -import { PER_PAGE_MAX } from "../src/github.js"; import { createMockCore, createMockGithub } from "./mocks.js"; describe("extractInputs", () => { @@ -12,7 +13,11 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).rejects.toThrow(); + await expect( + extractInputs(createMockGithub(), context, createMockCore()), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Context 'unsupported_event:unsupported_action' is not yet supported.]`, + ); }); it("pull_request", async () => { @@ -34,7 +39,7 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual({ + await expect(extractInputs(createMockGithub(), context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", head_sha: "abc123", @@ -44,6 +49,8 @@ describe("extractInputs", () => { }); it("pull_request_target", async () => { + const github = createMockGithub(); + const context = { eventName: "pull_request_target", payload: { @@ -71,23 +78,36 @@ describe("extractInputs", () => { run_id: NaN, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "unlabeled"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "opened"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "synchronize"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "reopened"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); + + context.payload.action = "ready_for_review"; + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); + + context.payload.action = "edited"; + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); + + context.payload.action = "converted_to_draft"; + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); // Action not yet supported context.payload.action = "assigned"; - await expect(extractInputs(null, context, createMockCore())).rejects.toThrow(); + await expect( + extractInputs(github, context, createMockCore()), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Context 'pull_request_target:assigned' is not yet supported.]`, + ); }); it("issue_comment:edited", async () => { @@ -140,7 +160,7 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual({ + await expect(extractInputs(createMockGithub(), context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", head_sha: "", @@ -157,7 +177,11 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).rejects.toThrow(); + await expect( + extractInputs(createMockGithub(), context, createMockCore()), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Context 'workflow_run:unsupported_action' is not yet supported.]`, + ); }); it("workflow_run:completed:pull_request (same repo)", async () => { @@ -180,7 +204,7 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual({ + await expect(extractInputs(createMockGithub(), context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", head_sha: "abc123", @@ -273,6 +297,32 @@ describe("extractInputs", () => { }); expect(github.rest.search.issuesAndPullRequests).toHaveBeenCalled(); + + // Simulate REST API throwing error, which should be handled, log a warning, and then + // treat like any other scenario with no pull requests. + github.rest.repos.listPullRequestsAssociatedWithCommit.mockRejectedValue( + new Error("test-error"), + ); + + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "abc123", + issue_number: 789, + run_id: 456, + }); + + // Simulate REST API throwing object, which should be handled, log a warning, and then + // treat like any other scenario with no pull requests. + github.rest.repos.listPullRequestsAssociatedWithCommit.mockRejectedValue("test-error"); + + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "abc123", + issue_number: 789, + run_id: 456, + }); } else if (numPullRequests === 1 || numPullRequests === 2) { // Second PR is to a different repo, so expect same behavior with or without it await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ @@ -295,13 +345,17 @@ describe("extractInputs", () => { commit_sha: "abc123", per_page: PER_PAGE_MAX, }); + + // For pull_request, do NOT attempt to extract the issue number from an artifact, since this could be modified + // in a fork PR. + expect(github.rest.actions.listWorkflowRunArtifacts).toHaveBeenCalledTimes(0); }, ); it("workflow_run:completed:workflow_run", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [{ name: "issue-number=123" }] }, + data: { artifacts: [{ name: "issue-number=123" }, { name: `head-sha=${fullGitSha}` }] }, }); const context = { @@ -310,7 +364,7 @@ describe("extractInputs", () => { action: "completed", workflow_run: { event: "workflow_run", - head_sha: "abc123", + head_sha: "def456", id: 456, repository: { name: "TestRepoName", @@ -325,25 +379,49 @@ describe("extractInputs", () => { await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", - head_sha: "abc123", + head_sha: fullGitSha, issue_number: 123, run_id: 456, }); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [{ name: "issue-number=not-a-number" }] }, + data: { artifacts: [{ name: "head-sha=not-full-git-sha" }] }, }); - await expect(extractInputs(github, context, createMockCore())).rejects.toThrow( - /invalid issue-number/i, + await expect( + extractInputs(github, context, createMockCore()), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: head-sha is not a valid full git SHA: 'not-full-git-sha']`, ); + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { artifacts: [{ name: "issue-number=not-a-number" }] }, + }); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "", + issue_number: NaN, + run_id: 456, + }); + + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { artifacts: [{ name: "issue-number=null" }] }, + }); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "", + issue_number: NaN, + run_id: 456, + }); + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ data: { artifacts: [] }, }); await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", - head_sha: "abc123", + head_sha: "", issue_number: NaN, run_id: 456, }); @@ -360,13 +438,17 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).rejects.toThrow(); + await expect( + extractInputs(createMockGithub(), context, createMockCore()), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Context 'workflow_run:completed' with 'workflow_run.event=unsupported is not yet supported.]`, + ); }); it("workflow_run:completed:check_run", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [] }, + data: { artifacts: [{ name: `head-sha=${fullGitSha}` }] }, }); const context = { @@ -375,7 +457,7 @@ describe("extractInputs", () => { action: "completed", workflow_run: { event: "check_run", - head_sha: "abc123", + head_sha: "def456", id: 456, repository: { name: "TestRepoName", @@ -387,13 +469,10 @@ describe("extractInputs", () => { }, }; - github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [] }, - }); await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", - head_sha: "abc123", + head_sha: fullGitSha, issue_number: NaN, run_id: 456, }); diff --git a/.github/workflows/test/core-logger.test.js b/.github/workflows/test/core-logger.test.js index 47c63bdae0f5..da0c10f9a0da 100644 --- a/.github/workflows/test/core-logger.test.js +++ b/.github/workflows/test/core-logger.test.js @@ -40,4 +40,13 @@ describe("CoreLogger", () => { expect(core.isDebug).toBeCalled(); }); + + it("warning", async () => { + const core = createMockCore(); + const logger = new CoreLogger(core); + + logger.warning("test warning"); + + expect(core.warning).toBeCalledWith("test warning"); + }); }); diff --git a/.github/shared/test/doc-preview.test.js b/.github/workflows/test/doc-preview.test.js similarity index 82% rename from .github/shared/test/doc-preview.test.js rename to .github/workflows/test/doc-preview.test.js index b0425c4f8b36..315632be4d59 100644 --- a/.github/shared/test/doc-preview.test.js +++ b/.github/workflows/test/doc-preview.test.js @@ -32,8 +32,25 @@ describe("parseSwaggerFilePath", () => { }); describe("getSwaggersToProcess", () => { - test("throws when swagger paths do not properly parse", () => { - expect(() => getSwaggersToProcess(["specification/inscrutable/path/swagger.json"])).toThrow(); + test("returns empty arrays when no files match the expected pattern", () => { + const files = ["invalid/path/to/swagger.json", "another/invalid/path/swagger.json"]; + const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(files); + expect(selectedVersion).toBeNull(); + expect(swaggersToProcess).toEqual([]); + }); + + test("skips files that do not match the expected pattern", () => { + const files = [ + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + "invalid/path/to/swagger.json", + ]; + + const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(files); + + expect(selectedVersion).toEqual("2024-07-01.20.0"); + expect(swaggersToProcess).toEqual([ + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + ]); }); test("returns swaggers to process for valid files", () => { diff --git a/.github/workflows/test/github.test.js b/.github/workflows/test/github.test.js index 3cc5770e4e60..825c8691d696 100644 --- a/.github/workflows/test/github.test.js +++ b/.github/workflows/test/github.test.js @@ -1,6 +1,14 @@ -import { describe, expect, it, vi } from "vitest"; -import { getCheckRuns, getWorkflowRuns, writeToActionsSummary } from "../src/github.js"; -import { createMockContext, createMockCore, createMockGithub } from "./mocks.js"; +import { afterEach } from "node:test"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { add, Duration } from "../../shared/src/time.js"; +import { + createLogHook, + createRateLimitHook, + getCheckRuns, + getWorkflowRuns, + writeToActionsSummary, +} from "../src/github.js"; +import { createMockContext, createMockCore, createMockGithub, createMockLogger } from "./mocks.js"; const mockCore = createMockCore(); @@ -209,3 +217,141 @@ describe("writeToActionsSummary function", () => { ); }); }); + +describe("createLogHook", () => { + it("logs request info with body", () => { + const mockLogger = createMockLogger(); + const logHook = createLogHook((r) => r, mockLogger); + + expect( + logHook({ + method: "GET", + url: "https://test-url.com", + body: { "test-key": "test-value" }, + }), + ).toBeUndefined(); + + expect(mockLogger.info).toBeCalledTimes(1); + expect(mockLogger.info.mock.calls[0]).toMatchInlineSnapshot(` + [ + "[github] GET https://test-url.com {"test-key":"test-value"}", + ] + `); + }); + + it("logs request info without body", () => { + const mockLogger = createMockLogger(); + const logHook = createLogHook((r) => r, mockLogger); + + expect( + logHook({ + method: "GET", + url: "https://test-url.com", + }), + ).toBeUndefined(); + + expect(mockLogger.info).toBeCalledTimes(1); + expect(mockLogger.info.mock.calls[0]).toMatchInlineSnapshot(` + [ + "[github] GET https://test-url.com ", + ] + `); + }); +}); + +describe("createRateLimitHook", () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2025-01-01T00:00:00Z")); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("logs to debug if missing rate limit header", () => { + const mockLogger = createMockLogger(); + const ratelimitHook = createRateLimitHook(mockLogger); + + expect(ratelimitHook({ headers: {} })).toBeUndefined(); + + expect(mockLogger.info).toBeCalledTimes(0); + expect(mockLogger.debug).toBeCalledTimes(1); + expect(mockLogger.debug.mock.calls[0]).toMatchInlineSnapshot(` + [ + "[github] missing ratelimit header(s) in response", + ] + `); + }); + + it("logs to info if all rate limit headers", () => { + const mockLogger = createMockLogger(); + const ratelimitHook = createRateLimitHook(mockLogger); + + const reset = (add(new Date(), 30 * Duration.Minute).getTime() / Duration.Second).toFixed(0); + + expect( + ratelimitHook({ + headers: { + "x-ratelimit-limit": "100", + "x-ratelimit-remaining": "50", + "x-ratelimit-reset": reset, + }, + }), + ).toBeUndefined(); + expect(mockLogger.info).toBeCalledTimes(1); + expect(mockLogger.info.mock.calls[0]).toMatchInlineSnapshot(` + [ + "[github] load: 100%, used: 50, remaining: 50, reset: 00:30:00", + ] + `); + + expect( + ratelimitHook({ + headers: { + "x-ratelimit-limit": "100", + "x-ratelimit-remaining": "75", + "x-ratelimit-reset": reset, + }, + }), + ).toBeUndefined(); + expect(mockLogger.info).toBeCalledTimes(2); + expect(mockLogger.info.mock.calls[1]).toMatchInlineSnapshot(` + [ + "[github] load: 50%, used: 25, remaining: 75, reset: 00:30:00", + ] + `); + + expect( + ratelimitHook({ + headers: { + "x-ratelimit-limit": "100", + "x-ratelimit-remaining": "25", + "x-ratelimit-reset": reset, + }, + }), + ).toBeUndefined(); + expect(mockLogger.info).toBeCalledTimes(3); + expect(mockLogger.info.mock.calls[2]).toMatchInlineSnapshot(` + [ + "[github] load: 150%, used: 75, remaining: 25, reset: 00:30:00", + ] + `); + + expect( + ratelimitHook({ + headers: { + "x-ratelimit-limit": "100", + "x-ratelimit-remaining": "0", + "x-ratelimit-reset": reset, + }, + }), + ).toBeUndefined(); + expect(mockLogger.info).toBeCalledTimes(4); + expect(mockLogger.info.mock.calls[3]).toMatchInlineSnapshot(` + [ + "[github] load: 200%, used: 100, remaining: 0, reset: 00:30:00", + ] + `); + }); +}); diff --git a/.github/workflows/test/issues.test.js b/.github/workflows/test/issues.test.js index 03facfee03ea..0f5c2b5cda90 100644 --- a/.github/workflows/test/issues.test.js +++ b/.github/workflows/test/issues.test.js @@ -1,9 +1,13 @@ +// @ts-check + import { beforeEach, describe, expect, it, vi } from "vitest"; +import { defaultLogger } from "../../shared/src/logger.js"; import { getIssueNumber } from "../src/issues.js"; -import { createMockCore, createMockGithub } from "./mocks.js"; +import { asGitHub, createMockGithub, createMockLogger } from "./mocks.js"; + +/** @typedef {import('@actions/github-script').AsyncFunctionArguments["github"]} GitHub */ const mockGithub = createMockGithub(); -const mockCore = createMockCore(); describe("getIssueNumber", () => { beforeEach(() => { @@ -13,11 +17,15 @@ describe("getIssueNumber", () => { it("should return NaN when head_sha is missing", async () => { // Call function and expect it to throw await expect( - getIssueNumber({ head_sha: "", github: mockGithub, core: mockCore }), - ).rejects.toThrow(); + getIssueNumber(asGitHub(mockGithub), ""), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: head_sha is required when trying to search a PR.]`, + ); }); it("should handle multiple PRs for the same commit", async () => { + const mockLogger = createMockLogger(); + // Mock multiple PRs found mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ data: { @@ -30,17 +38,13 @@ describe("getIssueNumber", () => { }); // Call function - const result = await getIssueNumber({ - head_sha: "abc", - github: mockGithub, - core: mockCore, - }); + const result = await getIssueNumber(asGitHub(mockGithub), "abc123", mockLogger); // Verify result uses first PR expect(result.issueNumber).toBe(123); // Verify warning was logged - expect(mockCore.warning).toHaveBeenCalledWith( + expect(mockLogger.warning).toHaveBeenCalledWith( expect.stringContaining("Multiple PRs found for commit"), ); }); @@ -55,11 +59,7 @@ describe("getIssueNumber", () => { }); // Call function - const result = await getIssueNumber({ - head_sha: "abc", - github: mockGithub, - core: mockCore, - }); + const result = await getIssueNumber(asGitHub(mockGithub), "abc123", defaultLogger); // Verify result expect(result.issueNumber).toBeNaN(); @@ -71,8 +71,6 @@ describe("getIssueNumber", () => { mockGithub.rest.search.issuesAndPullRequests.mockRejectedValue(searchError); // Call function and expect it to throw - await expect( - getIssueNumber({ head_sha: "abc", github: mockGithub, core: mockCore }), - ).rejects.toThrow(); + await expect(getIssueNumber(asGitHub(mockGithub), "abc123", defaultLogger)).rejects.toThrow(); }); }); diff --git a/.github/workflows/test/mocks.js b/.github/workflows/test/mocks.js index 555bcb3d295a..1a1a094fbd0c 100644 --- a/.github/workflows/test/mocks.js +++ b/.github/workflows/test/mocks.js @@ -1,9 +1,19 @@ +// @ts-check + import { RequestError } from "@octokit/request-error"; import { vi } from "vitest"; +/** + * @typedef {import('@actions/github-script').AsyncFunctionArguments["github"]} GitHub + */ + // Partial mock of `github` parameter passed into github-script actions export function createMockGithub() { return { + hook: { + after: vi.fn(), + before: vi.fn(), + }, paginate: async (func, params) => { // Assume all test data fits in single page const data = (await func(params)).data; @@ -13,6 +23,7 @@ export function createMockGithub() { }, rest: { actions: { + downloadArtifact: vi.fn().mockResolvedValue({ data: new ArrayBuffer(0) }), listJobsForWorkflowRun: vi.fn().mockResolvedValue({ data: [] }), listWorkflowRunArtifacts: vi.fn().mockResolvedValue({ data: { artifacts: [] } }), listWorkflowRunsForRepo: vi.fn().mockResolvedValue({ data: { workflow_runs: [] } }), @@ -30,6 +41,7 @@ export function createMockGithub() { }, repos: { createCommitStatus: vi.fn(), + listCommitStatusesForRef: vi.fn().mockResolvedValue({ data: [] }), listPullRequestsAssociatedWithCommit: vi.fn().mockResolvedValue({ data: [], }), @@ -38,9 +50,20 @@ export function createMockGithub() { issuesAndPullRequests: vi.fn(), }, }, + request: { + endpoint: vi.fn(), + }, }; } +/** + * @returns {GitHub} + * @param {any} mockGithub + */ +export function asGitHub(mockGithub) { + return /** @type {GitHub} */ mockGithub; +} + // Partial mock of `core` parameter passed into to github-script actions export function createMockCore() { return { @@ -62,10 +85,14 @@ export function createMockCore() { }; } +/** + * @param {number} status + * @returns {RequestError} + */ export function createMockRequestError(status) { return new RequestError(`mock RequestError with status '${status}'`, status, { // request properties "url" and "headers" must be defined to prevent errors - request: { url: "test url", headers: {} }, + request: { method: "GET", url: "test url", headers: {} }, }); } @@ -79,3 +106,13 @@ export function createMockContext() { }, }; } + +export function createMockLogger() { + return { + debug: vi.fn(), + error: vi.fn(), + info: vi.fn(), + isDebug: vi.fn().mockReturnValue(false), + warning: vi.fn(), + }; +} diff --git a/.github/workflows/test/sdk-breaking-change-labels.test.js b/.github/workflows/test/sdk-breaking-change-labels.test.js index 244eb4dd6b1f..a2dca7492c61 100644 --- a/.github/workflows/test/sdk-breaking-change-labels.test.js +++ b/.github/workflows/test/sdk-breaking-change-labels.test.js @@ -138,6 +138,62 @@ describe("sdk-breaking-change-labels", () => { issueNumber: 123, }); }); + it("should correctly set labelAction to none when label name is empty", async () => { + // Setup inputs + const inputs = { + details_url: "https://dev.azure.com/project/_build/results?buildId=12345", + head_sha: "abc123", + }; + + // Setup mock for extractInputs + const { extractInputs } = await import("../src/context.js"); + extractInputs.mockResolvedValue(inputs); + + // Mock artifact responses with 'remove' action + const mockArtifactResponse = { + ok: true, + json: vi.fn().mockResolvedValue({ + resource: { + downloadUrl: "https://dev.azure.com/download?format=zip", + }, + }), + }; + + const language = "azure-sdk-for-java"; + const mockContentResponse = { + ok: true, + text: vi.fn().mockResolvedValue( + JSON.stringify({ + labelAction: false, + language, + prNumber: "123", + }), + ), + }; + + // Setup fetch to return different responses for each call + global.fetch.mockImplementation((url) => { + if (url.includes("artifacts?artifactName=")) { + return mockArtifactResponse; + } else { + return mockContentResponse; + } + }); + + // Call function + const result = await getLabelAndAction({ + github: mockGithub, + context: mockContext, + core: mockCore, + }); + + // Verify result has none action + expect(result).toEqual({ + labelName: sdkLabels[language].breakingChange, + labelAction: LabelAction.None, + issueNumber: 123, + }); + }); it("should throw error with invalid inputs", async () => { // Setup inputs const inputs = { @@ -188,6 +244,7 @@ describe("sdk-breaking-change-labels", () => { expect(result).toEqual({ labelName: "", labelAction: LabelAction.None, + headSha: "", issueNumber: NaN, }); @@ -258,6 +315,7 @@ describe("sdk-breaking-change-labels", () => { expect(result).toEqual({ labelName: "", labelAction: LabelAction.None, + headSha: "", issueNumber: NaN, }); }); diff --git a/.github/workflows/test/set-status.test.js b/.github/workflows/test/set-status.test.js index b2d22575ae54..f1d0c6341f76 100644 --- a/.github/workflows/test/set-status.test.js +++ b/.github/workflows/test/set-status.test.js @@ -1,9 +1,9 @@ // @ts-check import { beforeEach, describe, expect, it } from "vitest"; +import { CheckConclusion, CheckStatus, CommitStatusState } from "../../shared/src/github.js"; +import { fullGitSha } from "../../shared/test/examples.js"; import { setStatusImpl } from "../src/set-status.js"; - -import { CheckConclusion, CheckStatus, CommitStatusState } from "../src/github.js"; import { createMockCore, createMockGithub } from "./mocks.js"; describe("setStatusImpl", () => { @@ -16,7 +16,46 @@ describe("setStatusImpl", () => { }); it("throws if inputs null", async () => { - await expect(setStatusImpl({})).rejects.toThrow(); + // @ts-expect-error Testing invalid input type + await expect(setStatusImpl({})).rejects.toMatchInlineSnapshot( + `[Error: head_sha is not a valid full git SHA: 'undefined']`, + ); + }); + + it.each([null, undefined, "", "abc123"])("throws when head_sha is %o", async (head_sha) => { + await expect( + setStatusImpl({ + owner: "test-owner", + repo: "test-repo", + // @ts-expect-error - Testing invalid input + head_sha, + issue_number: 123, + target_url: "https://test.com/set_status_url", + github, + core, + monitoredWorkflowName: "test-workflow", + requiredStatusName: "test-status", + overridingLabel: "test-label", + }), + ).rejects.toThrow("head_sha is not a valid full git SHA"); + }); + + it.each([null, undefined, NaN, 0, -1])("throws when issue_number is %o", async (issue_number) => { + await expect( + setStatusImpl({ + owner: "test-owner", + repo: "test-repo", + head_sha: fullGitSha, + // @ts-expect-error - Testing invalid input + issue_number, + target_url: "https://test.com/set_status_url", + github, + core, + monitoredWorkflowName: "test-workflow", + requiredStatusName: "test-status", + overridingLabel: "test-label", + }), + ).rejects.toThrow("issue_number must be a positive integer"); }); it("sets success if approved by label", async () => { @@ -28,13 +67,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger Avocado - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger Avocado", + monitoredWorkflowName: "Swagger Avocado - Analyze Code", + requiredStatusName: "Swagger Avocado", overridingLabel: "Approved-Avocado", }), ).resolves.toBeUndefined(); @@ -42,9 +81,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger Avocado", + context: "Swagger Avocado", description: "Found label 'Approved-Avocado'", target_url: "https://test.com/set_status_url", }); @@ -59,13 +98,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact", }), @@ -74,12 +113,15 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", description: "Found label 'BreakingChange-Approved-Benign'", target_url: "https://test.com/set_status_url", }); + + expect(core.setOutput).toBeCalledWith("head_sha", fullGitSha); + expect(core.setOutput).toBeCalledWith("issue_number", 123); }); it("handles comma-separated labels with whitespace", async () => { @@ -91,13 +133,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign, BreakingChange-Approved-BugFix , BreakingChange-Approved-UserImpact", }), @@ -106,9 +148,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", description: "Found label 'BreakingChange-Approved-UserImpact'", target_url: "https://test.com/set_status_url", }); @@ -123,13 +165,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign,,BreakingChange-Approved-Security,", }), ).resolves.toBeUndefined(); @@ -137,9 +179,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", description: "Found label 'BreakingChange-Approved-Security'", target_url: "https://test.com/set_status_url", }); @@ -158,13 +200,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact", }), @@ -173,9 +215,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.PENDING, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", target_url: "https://test.com/set_status_url", }); }); @@ -193,13 +235,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "", }), ).resolves.toBeUndefined(); @@ -207,9 +249,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.PENDING, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", target_url: "https://test.com/set_status_url", }); }); @@ -252,7 +294,7 @@ describe("setStatusImpl", () => { github.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ data: [ { - name: "[TEST-IGNORE] Swagger Avocado - Analyze Code", + name: "Swagger Avocado - Analyze Code", status: checkStatus, conclusion: checkConclusion, updated_at: "2025-01-01", @@ -286,13 +328,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger Avocado - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger Avocado", + monitoredWorkflowName: "Swagger Avocado - Analyze Code", + requiredStatusName: "Swagger Avocado", overridingLabel: "Approved-Avocado", }), ).resolves.toBeUndefined(); @@ -300,9 +342,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: commitStatusState, - context: "[TEST-IGNORE] Swagger Avocado", + context: "Swagger Avocado", target_url: targetUrl, }); }, diff --git a/.github/workflows/test/spec-gen-sdk-status.test.js b/.github/workflows/test/spec-gen-sdk-status.test.js index 6ac0ad303da9..edb3f10be70e 100644 --- a/.github/workflows/test/spec-gen-sdk-status.test.js +++ b/.github/workflows/test/spec-gen-sdk-status.test.js @@ -79,6 +79,7 @@ describe("spec-gen-sdk-status", () => { target_url: "https://example.com", github: mockGithub, core: mockCore, + issue_number: 123, }); // Verify the right status was set @@ -90,6 +91,9 @@ describe("spec-gen-sdk-status", () => { state: "pending", }), ); + + expect(mockCore.setOutput).toBeCalledWith("head_sha", "testSha"); + expect(mockCore.setOutput).toBeCalledWith("issue_number", 123); }); it("should set success status when all checks are completed successfully", async () => { diff --git a/.github/workflows/test/summarize-checks.test.js b/.github/workflows/test/summarize-checks.test.js deleted file mode 100644 index 058963c37c28..000000000000 --- a/.github/workflows/test/summarize-checks.test.js +++ /dev/null @@ -1,592 +0,0 @@ -import { Octokit } from "@octokit/rest"; -import { describe, expect, it } from "vitest"; -import { - createNextStepsComment, - summarizeChecksImpl, - updateLabels, -} from "../src/summarize-checks/summarize-checks.js"; -import { createMockCore } from "./mocks.js"; - -const mockCore = createMockCore(); - -describe("Summarize Checks Tests", () => { - describe("next steps comment rendering", () => { - it("Should generate summary for a mockdata PR scenario", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = [ - "Cognitive Services", - "data-plane", - "TypeSpec", - "VersioningReviewRequired", - ]; - const fyiCheckRuns = []; - const expectedComment = - "

Next Steps to Merge

Next steps that must be taken to merge this PR:
    " + - "
  • ❌ This PR targets either the main branch of the public specs repo or the RPSaaSMaster branch of the private specs repo. " + - "These branches are not intended for iterative development. Therefore, you must acknowledge you understand that after this PR is merged, the APIs are considered " + - "shipped to Azure customers. Any further attempts at in-place modifications to the APIs will be subject to Azure's versioning " + - "and breaking change policies. Additionally, for control plane APIs, you must acknowledge that you are following all " + - 'the best practices documented by ARM at aka.ms/armapibestpractices. ' + - "If you do intend to release the APIs to your customers by merging this PR, add the PublishToCustomers label " + - "to your PR in acknowledgement of the above. Otherwise, retarget this PR onto a feature branch, i.e. with prefix release- " + - '(see aka.ms/azsdk/api-versions#release--branches).
  • ' + - "
  • ❌ This PR has at least one change violating Azure versioning policy (label: VersioningReviewRequired).
    To unblock this PR, either a) " + - 'introduce a new API version with these changes instead of modifying an existing API version, or b) follow the process at aka.ms/brch.' + - "
  • ❌ The required check named TypeSpec Validation has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult " + - 'the aka.ms/ci-fix guide
'; - const expectedOutput = [expectedComment, "blocked"]; - - const requiredCheckRuns = [ - { - name: "SpellCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "SpellCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Requirement", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "TypeSpec Requirement", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Protected Files", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "Protected Files", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Validation", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 0, - name: "TypeSpec Validation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger BreakingChange", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 4, - name: "Swagger BreakingChange", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Breaking Change(Cross-Version)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 4, - name: "Breaking Change(Cross-Version)", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Swagger Avocado", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger Avocado", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger ModelValidation", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 3, - name: "Swagger ModelValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger SemanticValidation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 2, - name: "Swagger SemanticValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger Lint(RPaaS)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 5, - name: "Swagger Lint(RPaaS)", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Automated merging requirements met", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 10, - name: "Automated merging requirements met", - suppressionLabels: [], - troubleshootingGuide: - 'This is the final check that must pass. Refer to the check in the PR\'s \'Checks\' tab for details on how to fix it and consult the aka.ms/ci-fix guide. In addition, refer to step 4 in the PR workflow diagram', - }, - }, - { - name: "license/cla", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 0, - name: "license/cla", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger PrettierCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger PrettierCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - ]; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it("should generate pending summary for no matched check suites", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = []; - const fyiCheckRuns = []; - const requiredCheckRuns = []; - const expectedOutput = [ - "

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛", - "pending", - ]; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it("should generate success summary for all completed check suites", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = []; - const fyiCheckRuns = []; - const expectedOutput = [ - '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.', - "success", - ]; - - const requiredCheckRuns = [ - { - name: "SpellCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "SpellCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Requirement", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "TypeSpec Requirement", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Protected Files", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "Protected Files", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Validation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 0, - name: "TypeSpec Validation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger BreakingChange", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 4, - name: "Swagger BreakingChange", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Breaking Change(Cross-Version)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 4, - name: "Breaking Change(Cross-Version)", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Swagger Avocado", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger Avocado", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger ModelValidation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 3, - name: "Swagger ModelValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger SemanticValidation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 2, - name: "Swagger SemanticValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger Lint(RPaaS)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 5, - name: "Swagger Lint(RPaaS)", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Automated merging requirements met", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 10, - name: "Automated merging requirements met", - suppressionLabels: [], - troubleshootingGuide: - 'This is the final check that must pass. Refer to the check in the PR\'s \'Checks\' tab for details on how to fix it and consult the aka.ms/ci-fix guide. In addition, refer to step 4 in the PR workflow diagram', - }, - }, - { - name: "license/cla", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 0, - name: "license/cla", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger PrettierCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger PrettierCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - ]; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it("should generate pending summary when checks are in progress", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = []; - const fyiCheckRuns = []; - const expectedOutput = [ - "

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛", - "pending", - ]; - - const requiredCheckRuns = [ - { - name: "TypeSpec Validation", - status: "IN_PROGRESS", - conclusion: null, - checkInfo: { - precedence: 0, - name: "TypeSpec Validation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger Avocado", - status: "QUEUED", - conclusion: null, - checkInfo: { - precedence: 1, - name: "Swagger Avocado", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "license/cla", - status: "IN_PROGRESS", - conclusion: null, - checkInfo: { - precedence: 0, - name: "license/cla", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - ]; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it.skipIf(!process.env.GITHUB_TOKEN || !process.env.INTEGRATION_TEST)( - "Should fetch real PR data when GITHUB_TOKEN is available and when integration testing is enabled.", - async () => { - const github = new Octokit({ - auth: process.env.GITHUB_TOKEN, - }); - - const owner = "Azure"; - const repo = "azure-rest-api-specs"; - const issue_number = 35629; - const head_sha = "c12f0191c34212c4e6be88121d132ccb0a7f560c"; - const event_name = "pull_request"; - const mockContext = { - repo: { - owner: owner, - repo: repo, - }, - payload: { - action: "opened", - pull_request: { - number: issue_number, - head: { - sha: head_sha, - }, - }, - }, - eventName: event_name, - }; - - await expect( - summarizeChecksImpl( - github, - mockContext, - mockCore, - owner, - repo, - issue_number, - head_sha, - event_name, - "main", - ), - ).resolves.not.toThrow(); - }, - 600000, - ); - }); - - describe("label add and remove", () => { - const testCases = [ - { - existingLabels: ["WaitForARMFeedback", "ARMChangesRequested", "other-label"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: ["WaitForARMFeedback"], - }, - { - existingLabels: ["other-label", "ARMChangesRequested"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: [], - }, - { - existingLabels: [ - "WaitForARMFeedback", - "ARMSignedOff", - "ARMChangesRequested", - "other-label", - ], - expectedLabelsToAdd: [], - expectedLabelsToRemove: ["WaitForARMFeedback", "ARMChangesRequested"], - }, - { - existingLabels: ["WaitForARMFeedback", "ARMSignedOff", "other-label"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: ["WaitForARMFeedback"], - }, - { - existingLabels: ["ARMChangesRequested", "ARMSignedOff", "other-label"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: ["ARMChangesRequested"], - }, - { - existingLabels: ["other-label", "ARMSignedOff"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: [], - }, - { - existingLabels: ["WaitForARMFeedback", "other-label"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: [], - }, - { - existingLabels: ["other-label"], - expectedLabelsToAdd: ["WaitForARMFeedback"], - expectedLabelsToRemove: [], - }, - { - existingLabels: ["WaitForARMFeedback", "ARMChangesRequested"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: ["WaitForARMFeedback"], - }, - { - existingLabels: ["WaitForARMFeedback", "ARMChangesRequested"], - expectedLabelsToAdd: [], - expectedLabelsToRemove: ["WaitForARMFeedback"], - }, - ]; - - it.each(testCases)( - "$description", - async ({ existingLabels, expectedLabelsToAdd, expectedLabelsToRemove }) => { - const labelContext = await updateLabels(existingLabels, undefined); - - expect([...labelContext.toAdd].sort()).toEqual(expectedLabelsToAdd.sort()); - expect([...labelContext.toRemove].sort()).toEqual(expectedLabelsToRemove.sort()); - }, - ); - }); -}); diff --git a/.github/workflows/test/summarize-checks/labelling.test.js b/.github/workflows/test/summarize-checks/labelling.test.js new file mode 100644 index 000000000000..3e81694bfed7 --- /dev/null +++ b/.github/workflows/test/summarize-checks/labelling.test.js @@ -0,0 +1,308 @@ +import { describe, expect, it } from "vitest"; +import { processArmReviewLabels } from "../../src/summarize-checks/labelling.js"; +import { updateLabels } from "../../src/summarize-checks/summarize-checks.js"; + +describe("update labels", () => { + const testCases = [ + { + description: "Add ARMReview and resource-manager labels when existing labels are empty", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["ARMReview", "resource-manager", "TypeSpec", "WaitForARMFeedback"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Shouldn't add ARM review if resourceManagerRequired is false", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Shouldn't add ARM review if not targeting a release branch", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec", "resource-manager"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "a-test-branch", + }, + }, + { + description: "Shouldn't add add new-api-version if not targeting a release branch", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec", "resource-manager"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "a-test-branch", + }, + }, + { + description: "Should add new-api-version correctly", + existingLabels: ["other-label"], + expectedLabelsToAdd: [ + "TypeSpec", + "resource-manager", + "new-api-version", + "ARMReview", + "WaitForARMFeedback", + ], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should add CI-NewRPNamespaceWithoutRPaaS label properly", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec", "resource-manager", "CI-NewRPNamespaceWithoutRPaaS"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: true, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "a-test-branch", + }, + }, + { + description: "Shouldn't add any ARM process updates when not ready for ARM review", + existingLabels: ["other-label"], + expectedLabelsToAdd: [ + "TypeSpec", + "resource-manager", + "CI-NewRPNamespaceWithoutRPaaS", + "NotReadyForARMReview", + "ARMReview", + "new-api-version", + ], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: true, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: + "Shouldn't remove NotReadyForARMReview label when not ready for ARM review due to being blocked on versioning or breaking change review.", + existingLabels: [ + "ARMReview", + "NotReadyForARMReview", + "PipelineBotTrigger", + "PublishToCustomers", + "resource-manager", + "RPaaS", + "TypeSpec", + "VersioningReviewRequired", + ], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: true, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "RPSaaSMaster", + }, + }, + { + description: + "Should remove NotReadyForARMReview and add WaitForARMFeedback when VersioningReviewRequired is approved.", + existingLabels: [ + "ARMReview", + "NotReadyForARMReview", + "PipelineBotTrigger", + "PublishToCustomers", + "resource-manager", + "RPaaS", + "TypeSpec", + "VersioningReviewRequired", + "Versioning-Approved-Benign", + ], + expectedLabelsToAdd: ["WaitForARMFeedback"], + expectedLabelsToRemove: ["NotReadyForARMReview"], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: true, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "RPSaaSMaster", + }, + }, + ]; + it.each(testCases)( + "$description", + async ({ existingLabels, expectedLabelsToAdd, expectedLabelsToRemove, impactAssessment }) => { + const labelContext = await updateLabels(existingLabels, impactAssessment); + + expect([...labelContext.toAdd].sort()).toEqual(expectedLabelsToAdd.sort()); + expect([...labelContext.toRemove].sort()).toEqual(expectedLabelsToRemove.sort()); + }, + ); +}); + +describe("ARM review process labelling", () => { + const testCases = [ + { + existingLabels: ["WaitForARMFeedback", "ARMChangesRequested", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + { + existingLabels: ["other-label", "ARMChangesRequested"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + }, + { + existingLabels: [ + "WaitForARMFeedback", + "ARMSignedOff", + "ARMChangesRequested", + "other-label", + "ARMReview", + ], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback", "ARMChangesRequested"], + }, + { + existingLabels: ["WaitForARMFeedback", "ARMSignedOff", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + { + existingLabels: ["ARMChangesRequested", "ARMSignedOff", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["ARMChangesRequested"], + }, + { + existingLabels: ["other-label", "ARMSignedOff"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + }, + { + existingLabels: ["WaitForARMFeedback", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + }, + { + existingLabels: ["other-label", "ARMReview"], + expectedLabelsToAdd: ["WaitForARMFeedback"], + expectedLabelsToRemove: [], + }, + { + existingLabels: ["WaitForARMFeedback", "ARMChangesRequested", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + { + existingLabels: ["WaitForARMFeedback", "ARMChangesRequested", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + ]; + + it.each(testCases)( + "$description", + async ({ existingLabels, expectedLabelsToAdd, expectedLabelsToRemove }) => { + /** @type {import("./labelling.js").LabelContext} */ + const labelContext = { + present: new Set(), + toAdd: new Set(), + toRemove: new Set(), + }; + await processArmReviewLabels(labelContext, existingLabels); + + expect([...labelContext.toAdd].sort()).toEqual(expectedLabelsToAdd.sort()); + expect([...labelContext.toRemove].sort()).toEqual(expectedLabelsToRemove.sort()); + }, + ); +}); diff --git a/.github/workflows/test/summarize-checks/summarize-checks.test.js b/.github/workflows/test/summarize-checks/summarize-checks.test.js new file mode 100644 index 000000000000..830a4879a72c --- /dev/null +++ b/.github/workflows/test/summarize-checks/summarize-checks.test.js @@ -0,0 +1,1237 @@ +import { Octokit } from "@octokit/rest"; +import { strToU8, zipSync } from "fflate"; +import { describe, expect, it } from "vitest"; +import { execFile } from "../../../shared/src/exec.js"; +import { + createNextStepsComment, + getCheckInfo, + getCheckRunTuple, + getExistingLabels, + getImpactAssessment, + updateLabels, +} from "../../src/summarize-checks/summarize-checks.js"; +import { createMockCore, createMockGithub } from "../mocks.js"; + +const mockCore = createMockCore(); +const WORKFLOW_URL = "http://github.com/a/fake/workflowrun/url"; + +/** + * Find and extract the "Next Steps to Merge" existing comment on a PR. + * Used in the integration test. + */ +async function getNextStepsComment(github, owner, repo, prNumber) { + try { + const { data: comments } = await github.rest.issues.listComments({ + owner: owner, + repo: repo, + issue_number: prNumber, + }); + + // Find comment containing "Next Steps to Merge" + const nextStepsComment = comments.find((comment) => + comment.body.includes("

Next Steps to Merge

"), + ); + + return nextStepsComment ? nextStepsComment.body : null; + } catch (error) { + console.error(`Error getting comments for PR #${prNumber}:`, error.message); + return null; + } +} + +describe("Summarize Checks Integration Tests", () => { + describe("Repro a PR summarize-checks invocation", () => { + it.skipIf(!process.env.GITHUB_TOKEN || !process.env.INTEGRATION_TEST)( + "Should fetch real pr data and check the next steps to merge and final labels against what is actually there.", + async () => { + const issue_number = 24021; + const owner = "Azure"; + const repo = "azure-rest-api-specs-pr"; + + const ignorableLabels = [ + "VersioningReviewRequired", + "BreakingChangeReviewRequired", + "customer-reported", + "dependencies", + "javascript", + "Monitor", + ]; + + const github = new Octokit({ + auth: process.env.GITHUB_TOKEN, + }); + + const { data: pr } = await github.rest.pulls.get({ + owner: owner, + repo: repo, + pull_number: issue_number, + }); + + // Get current PR labels and Next Steps comment + const expectedNextStepsComment = await getNextStepsComment( + github, + owner, + repo, + issue_number, + ); + + const head_sha = "961faf0dd048e0b846026bc84fdd795f4b46e9e8"; + const expectedLabels = await getExistingLabels(github, owner, repo, issue_number); + + const [requiredCheckRuns, fyiCheckRuns, impactAssessment] = await getCheckRunTuple( + github, + mockCore, + owner, + repo, + head_sha, + issue_number, + [], + ); + + let adjustedStartLabels = expectedLabels.filter((x) => ignorableLabels.includes(x)); + let labelContext = await updateLabels(adjustedStartLabels, impactAssessment); + + adjustedStartLabels = adjustedStartLabels.filter( + (name) => !labelContext.toRemove.has(name), + ); + for (const label of labelContext.toAdd) { + if (!adjustedStartLabels.includes(label)) { + adjustedStartLabels.push(label); + } + } + + const [commentBody, automatedChecksMet] = await createNextStepsComment( + mockCore, + repo, + adjustedStartLabels, + pr.base.ref, + requiredCheckRuns, + fyiCheckRuns, + impactAssessment !== undefined, + WORKFLOW_URL, + ); + + const actualLabels = [...labelContext.toAdd, ...labelContext.present]; + expect(actualLabels.sort()).toEqual(expectedLabels.sort()); + expect(commentBody).toEqual(expectedNextStepsComment); + expect(automatedChecksMet).toBeTruthy(); + }, + 600000, + ); + }); +}); + +describe("Summarize Checks Unit Tests", () => { + describe("check result processing", () => { + it("should generate success summary for no matched check suites (completed impactAssessment)", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const requiredCheckRuns = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate success summary for all completed check suites", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "Swagger Lint(RPaaS)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Lint(RPaaS)"), + }, + { + name: "Automated merging requirements met", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Automated merging requirements met"), + }, + { + name: "license/cla", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate success summary with completed required checks but in-progress FYI", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + ]; + + const fyiCheckRuns = [ + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: { + precedence: 1, + name: "Swagger Avocado", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "license/cla", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + // this case should NEVER occur in practice, due to Summarize PR Impact being made a "required" check, but in cases + // where the user is targeting a branch that is not the main branch, we may have no required checks + // but still have FYI checks in progress. This is a regression test to ensure we handle this case correctly. + it("should generate success summary with 0 required checks but in-progress FYI", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const requiredCheckRuns = []; + + const fyiCheckRuns = [ + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: { + precedence: 1, + name: "Swagger Avocado", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "license/cla", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate success with warning for error FYI", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedOutput = [ + `

Next Steps to Merge

Important checks have failed. As of today they are not blocking this PR, but in near future they may.
Addressing the following failures is highly recommended:
  • ⚠️ The check named license/cla has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide
If you still want to proceed merging this PR without addressing the above failures, refer to step 4 in the PR workflow diagram.

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `⚠️ Some important automated merging requirements have failed. As of today you can still merge this PR, but soon these requirements will be blocking.
See Next Steps to merge comment on this PR for details on how to address them.
If you want to proceed with merging this PR without fixing them, refer to aka.ms/azsdk/specreview/merge.`, + }, + ]; + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + ]; + + const fyiCheckRuns = [ + { + name: "license/cla", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("license/cla"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks are partially in progress", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("license/cla"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks are in progress", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("license/cla"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when impact assessment is not completed", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "Swagger Lint(RPaaS)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Lint(RPaaS)"), + }, + { + name: "Automated merging requirements met", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Automated merging requirements met"), + }, + { + name: "license/cla", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + false, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks are in progress", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "TypeSpec Validation", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: { + precedence: 1, + name: "Swagger Avocado", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "license/cla", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks there are no required checks blocking or completed, but successful FYI checks", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = [ + "ARMReview", + "ARMAutoSignedOff", + "resource-manager", + "TypeSpec", + "RPaaS", + "ARMSignedOff", + "PublishToCustomers", + ]; + const expectedOutput = [ + `

Next Steps to Merge

Important checks have failed. As of today they are not blocking this PR, but in near future they may.
Addressing the following failures is highly recommended:
  • ⚠️ The check named Swagger LintDiff has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide


Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const fyiCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Swagger LintDiff", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger LintDiff"), + }, + ]; + + const requiredCheckRuns = [ + { + name: "Summarize PR Impact", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("Summarize PR Impact"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + false, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("Should generate error summary for a PR scenario where NotReadyForARMReview is present", async () => { + const repo = "azure-rest-api-specs-pr"; + const targetBranch = "RPSaaSMaster"; + const labelNames = [ + "ARMReview", + "NotReadyForARMReview", + "PipelineBotTrigger", + "PublishToCustomers", + "resource-manager", + "RPaaS", + "TypeSpec", + "VersioningReviewRequired", + ]; + const fyiCheckRuns = []; + const expectedComment = + `

Next Steps to Merge

Next steps that must be taken to merge this PR:
  • ❌ ` + + `This PR is in purview of the ARM review (label: ARMReview). This PR must get ARMSignedOff label ` + + `from an ARM reviewer.
    This PR is not ready for ARM review (label: NotReadyForARMReview). This PR will not ` + + `be reviewed by ARM until relevant problems are fixed. Consult the rest of this Next Steps to Merge comment ` + + `for details.
    Once the blocking problems are addressed, add to the PR a comment with contents /azp run. ` + + `Automation will re-evaluate this PR and if everything looks good, it will add WaitForARMFeedback label which ` + + `will put this PR on the ARM review queue.
    For details of the ARM review, see ` + + `aka.ms/azsdk/pr-arm-review
  • ❌ This PR is NotReadyForARMReview because it has the VersioningReviewRequired ` + + `label.
  • ❌ This PR has at least one change violating Azure versioning policy ` + + `(label: VersioningReviewRequired).
    To unblock this PR, either a) introduce a new API version with these ` + + `changes instead of modifying an existing API version, or b) follow the process at aka.ms/brch.
  • ` + + `
  • ❌ The required check named Swagger BreakingChange has failed. To unblock this PR, follow the process at ` + + `aka.ms/brch.


Comment generated by summarize-checks workflow run.`; + + const expectedOutput = [ + expectedComment, + { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "license/cla", + status: "QUEUED", + conclusion: null, + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "SDK Validation Status", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SDK Validation Status"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger LintDiff", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger LintDiff"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("Should generate error summary for a PR scenario with labeling issues", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = [ + "Cognitive Services", + "data-plane", + "TypeSpec", + "VersioningReviewRequired", + ]; + const fyiCheckRuns = []; + const expectedComment = + "

Next Steps to Merge

Next steps that must be taken to merge this PR:
    " + + "
  • ❌ This PR targets either the main branch of the public specs repo or the RPSaaSMaster branch of the private specs repo. " + + "These branches are not intended for iterative development. Therefore, you must acknowledge you understand that after this PR is merged, the APIs are considered " + + "shipped to Azure customers. Any further attempts at in-place modifications to the APIs will be subject to Azure's versioning " + + "and breaking change policies. Additionally, for control plane APIs, you must acknowledge that you are following all " + + 'the best practices documented by ARM at aka.ms/armapibestpractices. ' + + "If you do intend to release the APIs to your customers by merging this PR, add the PublishToCustomers label " + + "to your PR in acknowledgement of the above. Otherwise, retarget this PR onto a feature branch, i.e. with prefix release- " + + '(see aka.ms/azsdk/api-versions#release--branches).
  • ' + + "
  • ❌ This PR has at least one change violating Azure versioning policy (label: VersioningReviewRequired).
    To unblock this PR, either a) " + + 'introduce a new API version with these changes instead of modifying an existing API version, or b) follow the process at aka.ms/brch.' + + "
  • ❌ The required check named TypeSpec Validation has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult " + + 'the aka.ms/ci-fix guide


Comment generated by summarize-checks workflow run.'; + const expectedOutput = [ + expectedComment, + { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "Swagger Lint(RPaaS)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Lint(RPaaS)"), + }, + { + name: "Automated merging requirements met", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Automated merging requirements met"), + }, + { + name: "license/cla", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate error summary with a failed required check, and failed FYI checks", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedCheckOutput = { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }; + const expectedCommentOutput = `

Next Steps to Merge

Next steps that must be taken to merge this PR:
  • ❌ The required check named Swagger BreakingChange has failed. To unblock this PR, follow the process at aka.ms/brch.

Important checks have failed. As of today they are not blocking this PR, but in near future they may.
Addressing the following failures is highly recommended:
  • ⚠️ The check named TypeSpec Validation has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide


Comment generated by summarize-checks workflow run.`; + + const fyiCheckRuns = [ + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + ]; + + const requiredCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + ]; + + const [commentOutput, automatedCheckOutput] = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(automatedCheckOutput).toEqual(expectedCheckOutput); + expect(commentOutput).toEqual(expectedCommentOutput); + }); + + it("should generate error summary with a failed required check, and in-progress FYI checks", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedCheckOutput = { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }; + + const fyiCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + ]; + + const requiredCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + ]; + + const [, automatedCheckOutput] = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + ); + + expect(automatedCheckOutput).toEqual(expectedCheckOutput); + }); + + it("should generate error summary when checks are in error state", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedCheckOutput = { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }; + + const requiredCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + ]; + + const [, automatedCheckOutput] = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + ); + + expect(automatedCheckOutput).toEqual(expectedCheckOutput); + }); + }); + + describe("getImpactAssessment", async () => { + const unzipExists = await execFile("unzip") + .then(() => true) + .catch(() => false); + + // Runtime code currently requires "unzip" executable to exist + it.runIf(unzipExists)("unzips and extracts artifact", async () => { + /** @type {import("../../src/summarize-checks/labelling.js").ImpactAssessment} */ + const impactAssessment = { + // Set booleans arbitrarily to false|true + resourceManagerRequired: false, + dataPlaneRequired: true, + suppressionReviewRequired: false, + isNewApiVersion: true, + rpaasExceptionRequired: false, + rpaasRpNotInPrivateRepo: true, + rpaasChange: false, + newRP: true, + rpaasRPMissing: false, + typeSpecChanged: true, + isDraft: false, + targetBranch: "test-target-branch", + }; + + const zip = zipSync({ + "summary.json": strToU8(JSON.stringify(impactAssessment)), + }); + + const github = createMockGithub(); + + github.rest.actions.downloadArtifact.mockResolvedValue({ + data: Buffer.from(zip), + }); + + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { + artifacts: [{ id: 1, name: "job-summary" }], + }, + }); + + await expect( + getImpactAssessment(github, mockCore, "test-owner", "test-repo", 123), + ).resolves.toEqual(impactAssessment); + + expect(github.rest.actions.downloadArtifact).toHaveBeenCalledWith({ + owner: "test-owner", + repo: "test-repo", + artifact_id: 1, + archive_format: "zip", + }); + + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { + artifacts: [ + { id: 1, name: "job-summary" /* updated_at: "1970" (default) */ }, + { id: 2, name: "job-summary", updated_at: "2025" }, + { id: 3, name: "job-summary", updated_at: "2024" }, + ], + }, + }); + + await expect( + getImpactAssessment(github, mockCore, "test-owner", "test-repo", 123), + ).resolves.toEqual(impactAssessment); + + expect(github.rest.actions.downloadArtifact).toHaveBeenCalledWith({ + owner: "test-owner", + repo: "test-repo", + artifact_id: 2, + archive_format: "zip", + }); + }); + + it("throws if no job-summary artifact", async () => { + const github = createMockGithub(); + + await expect( + getImpactAssessment(github, mockCore, "test-owner", "test-repo", 123), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Unable to find job-summary artifact for run ID: 123. This should never happen, as this section of code should only run with a valid runId.]`, + ); + }); + }); +}); diff --git a/.github/workflows/test/update-labels.test.js b/.github/workflows/test/update-labels.test.js index 4c14438888ea..e9cfdd97c869 100644 --- a/.github/workflows/test/update-labels.test.js +++ b/.github/workflows/test/update-labels.test.js @@ -1,10 +1,13 @@ import { describe, expect, it } from "vitest"; -import { PER_PAGE_MAX } from "../src/github.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { fullGitSha } from "../../shared/test/examples.js"; import updateLabels, { updateLabelsImpl } from "../src/update-labels.js"; import { createMockCore, createMockGithub, createMockRequestError } from "./mocks.js"; describe("updateLabels", () => { it("loads inputs from context", async () => { + const core = createMockCore(); + const github = createMockGithub(); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ data: { @@ -18,7 +21,7 @@ describe("updateLabels", () => { action: "completed", workflow_run: { event: "pull_request", - head_sha: "abc123", + head_sha: fullGitSha, id: 456, repository: { name: "TestRepoName", @@ -31,13 +34,10 @@ describe("updateLabels", () => { }, }; - await expect( - updateLabels({ - github: github, - context: context, - core: createMockCore(), - }), - ).resolves.toBeUndefined(); + await expect(updateLabels({ github, context, core })).resolves.toBeUndefined(); + + expect(core.setOutput).toBeCalledWith("head_sha", fullGitSha); + expect(core.setOutput).toBeCalledWith("issue_number", 123); expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ owner: "TestRepoOwnerLogin", @@ -68,7 +68,9 @@ describe("updateLabelsImpl", () => { github: github, core: createMockCore(), }), - ).rejects.toThrow(); + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Required input 'run_id' not found in env or context]`, + ); expect(github.rest.issues.addLabels).toBeCalledTimes(0); expect(github.rest.issues.removeLabel).toBeCalledTimes(0); @@ -192,7 +194,44 @@ describe("updateLabelsImpl", () => { github: github, core: createMockCore(), }), - ).rejects.toThrow(); + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Invalid value for label 'baz': invalid. Expected "true" or "false".]`, + ); + + expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ + owner: "owner", + repo: "repo", + run_id: 456, + per_page: PER_PAGE_MAX, + }); + + // Ensure no labels are added or removed if any are invalid + expect(github.rest.issues.addLabels).toBeCalledTimes(0); + expect(github.rest.issues.removeLabel).toBeCalledTimes(0); + }); + + it("throws for invalid label name", async () => { + const github = createMockGithub(); + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { + artifacts: [ + { name: "label-foo=true" }, + { name: "label-bar=false" }, + { name: "label-=true" }, + ], + }, + }); + + await expect( + updateLabelsImpl({ + owner: "owner", + repo: "repo", + issue_number: 123, + run_id: 456, + github: github, + core: createMockCore(), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Invalid value for label name: '']`); expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ owner: "owner", diff --git a/.github/workflows/typespec-migration-validation.yaml b/.github/workflows/typespec-migration-validation.yaml index 2536989fe082..15cdb80f57b2 100644 --- a/.github/workflows/typespec-migration-validation.yaml +++ b/.github/workflows/typespec-migration-validation.yaml @@ -2,7 +2,7 @@ name: TypeSpec Migration Validation on: pull_request: - types: [labeled, unlabeled, opened, reopened, synchronize] + types: [edited, labeled, unlabeled, opened, reopened, synchronize] permissions: contents: read @@ -10,7 +10,7 @@ permissions: jobs: typespec-migration-validation: name: TypeSpec Migration Validation - if: contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w1') || contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w2') + if: contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w1') || contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w2') || contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w3') runs-on: ubuntu-24.04 steps: @@ -23,5 +23,5 @@ jobs: - name: Run TypeSpec Migration Validation run: | - ./eng/tools/typespec-migration-validation/scripts/download-main.ps1 -Verbose -callValidation $true + ./eng/tools/typespec-migration-validation/scripts/download-main.ps1 -Verbose -reportFile $env:GITHUB_STEP_SUMMARY shell: pwsh diff --git a/.github/workflows/typespec-requirement.yaml b/.github/workflows/typespec-requirement.yaml index 47eafcb8970a..9101d527f6af 100644 --- a/.github/workflows/typespec-requirement.yaml +++ b/.github/workflows/typespec-requirement.yaml @@ -1,6 +1,14 @@ name: TypeSpec Requirement -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read diff --git a/.github/workflows/typespec-validation.yaml b/.github/workflows/typespec-validation.yaml index 7a38bb20870d..2cda8ad8000d 100644 --- a/.github/workflows/typespec-validation.yaml +++ b/.github/workflows/typespec-validation.yaml @@ -1,6 +1,14 @@ name: TypeSpec Validation -on: pull_request +on: + pull_request: + types: + # default + - opened + - synchronize + - reopened + # re-run if base branch is changed, since previous merge commit may generate incorrect diff + - edited permissions: contents: read diff --git a/.github/workflows/update-labels.yaml b/.github/workflows/update-labels.yaml index 54a3c0576a18..e9fc28c9cc99 100644 --- a/.github/workflows/update-labels.yaml +++ b/.github/workflows/update-labels.yaml @@ -7,7 +7,13 @@ on: # If an upstream workflow if completed, get only the artifacts from that workflow, and update labels workflow_run: workflows: - ["ARM Auto SignOff", "SDK Breaking Change Labels", "SDK Suppressions", "TypeSpec Requirement"] + [ + "ARM Auto SignOff", + "SDK Breaking Change Labels", + "SDK Suppressions", + "TypeSpec Requirement", + "Breaking Change - Add Label Artifacts", + ] types: [completed] permissions: @@ -19,9 +25,6 @@ jobs: update-labels: name: Update Labels - # Only process labels from successful workflows - if: ${{ github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-24.04 steps: @@ -31,9 +34,24 @@ jobs: .github - name: Update Labels + id: update-labels uses: actions/github-script@v7 with: script: | const { default: updateLabels } = await import('${{ github.workspace }}/.github/workflows/src/update-labels.js'); await updateLabels({ github, context, core }); + + - if: ${{ always() && steps.update-labels.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.update-labels.outputs.head_sha }} + + - if: ${{ always() && steps.update-labels.outputs.issue_number }} + name: Upload artifact with issue number + uses: ./.github/actions/add-empty-artifact + with: + name: issue-number + value: ${{ steps.update-labels.outputs.issue_number }} diff --git a/.github/workflows/watch-breakingchange-crossversion.yaml b/.github/workflows/watch-breakingchange-crossversion.yaml deleted file mode 100644 index 41fa9752c676..000000000000 --- a/.github/workflows/watch-breakingchange-crossversion.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - Breaking Change(Cross-Version)" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Breaking Change(Cross-Version) - Set Status" - -permissions: - actions: read - checks: read - contents: read - statuses: read - -jobs: - BreakingChangeCrossVersion: - name: Watch Breaking Change(Cross-Version) - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Breaking Change(Cross-Version)" - commit_status_name: "[TEST-IGNORE] Breaking Change(Cross-Version)" diff --git a/.github/workflows/watch-breakingchange.yaml b/.github/workflows/watch-breakingchange.yaml deleted file mode 100644 index 50978f8a475b..000000000000 --- a/.github/workflows/watch-breakingchange.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - BreakingChange" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Swagger BreakingChange - Set Status" - -permissions: - actions: read - checks: read - contents: read - statuses: read - -jobs: - BreakingChange: - name: Watch BreakingChange - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Swagger BreakingChange" - commit_status_name: "[TEST-IGNORE] Swagger BreakingChange" diff --git a/.github/workflows/watch-modelvalidation.yaml b/.github/workflows/watch-modelvalidation.yaml deleted file mode 100644 index ba3ff8165018..000000000000 --- a/.github/workflows/watch-modelvalidation.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - Swagger ModelValidation" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Swagger ModelValidation" - -permissions: - actions: read - checks: read - contents: read - statuses: read - -jobs: - ModelValidationWatch: - name: Watch ModelValidation - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Swagger ModelValidation" - workflow_name: "[TEST-IGNORE] Swagger ModelValidation" diff --git a/.github/workflows/watch-semanticvalidation.yaml b/.github/workflows/watch-semanticvalidation.yaml deleted file mode 100644 index 757e55e367d6..000000000000 --- a/.github/workflows/watch-semanticvalidation.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - Swagger SemanticValidation" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Swagger SemanticValidation" - -permissions: - actions: read - checks: read - contents: read - statuses: read - -jobs: - SemanticValidationWatch: - name: Watch SemanticValidation - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Swagger SemanticValidation" - workflow_name: "[TEST-IGNORE] Swagger SemanticValidation" diff --git a/.prettierrc.json b/.prettierrc.json index c1d4aa50bb5a..93b4f718c039 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -8,7 +8,7 @@ ], "options": { "plugins": [ - "./scripts/prettier-swagger-plugin.js" + "./eng/scripts/prettier-swagger-plugin.js" ], "parser": "json-swagger", "printWidth": 20, diff --git a/documentation/onboard-dpg-in-sdkautomation/java/README.md b/documentation/onboard-dpg-in-sdkautomation/java/README.md index 0001e6f4b622..fa6661dd09de 100644 --- a/documentation/onboard-dpg-in-sdkautomation/java/README.md +++ b/documentation/onboard-dpg-in-sdkautomation/java/README.md @@ -8,11 +8,9 @@ See [Use SDK Automation from REST API specifications](https://github.com/Azure/a `namespace` always starts with `com.azure` for Azure Java data-plane SDK. [Review process](https://azure.github.io/azure-sdk/policies_reviewprocess.html). -`package-dir` should follow the pattern of the `namespace`. - ```yaml "@azure-tools/typespec-java": - package-dir: "azure-ai-openai" + emitter-output-dir: "{output-dir}/{service-dir}/azure-ai-openai" flavor: azure namespace: "com.azure.ai.openai" ``` @@ -21,13 +19,11 @@ See [Use SDK Automation from REST API specifications](https://github.com/Azure/a `namespace` always starts with `com.azure.resourcemanager` for Azure Java management-plane SDK. [Review process](https://eng.ms/docs/products/azure-developer-experience/develop/namespace-review). -`package-dir` should follow the pattern of the `namespace`. Remove the "com." and replace the dot with hyphen. - `service-name` is the name of the service. It is used for SDK class name and documentations. ```yaml "@azure-tools/typespec-java": - package-dir: "azure-resourcemanager-standbypool" + emitter-output-dir: "{output-dir}/{service-dir}/azure-resourcemanager-standbypool" flavor: "azure" namespace: "com.azure.resourcemanager.standbypool" service-name: "Standby Pool" diff --git a/eng/common/TestResources/New-TestResources.ps1 b/eng/common/TestResources/New-TestResources.ps1 index 078991250e64..1acc02fe42fb 100755 --- a/eng/common/TestResources/New-TestResources.ps1 +++ b/eng/common/TestResources/New-TestResources.ps1 @@ -129,6 +129,11 @@ param ( $wellKnownTMETenants = @('70a036f6-8e4d-4615-bad6-149c02e7720d') +# People keep passing this legacy parameter. Throw an error to save them future keystrokes +if ($NewTestResourcesRemainingArguments -like '*UserAuth*') { + throw "The -UserAuth parameter is deprecated and is now the default behavior" +} + if (!$ServicePrincipalAuth) { # Clear secrets if not using Service Principal auth. This prevents secrets # from being passed to pre- and post-scripts. @@ -353,15 +358,19 @@ try { # Make sure the provisioner OID is set so we can pass it through to the deployment. if (!$ProvisionerApplicationId -and !$ProvisionerApplicationOid) { if ($context.Account.Type -eq 'User') { - # Support corp tenant and TME tenant user id lookups - $user = Get-AzADUser -Mail $context.Account.Id - if ($null -eq $user -or !$user.Id) { - $user = Get-AzADUser -UserPrincipalName $context.Account.Id + # Calls to graph API in corp tenant get blocked by conditional access policy now + # but not in TME. For corp tenant we get the user's id from the login context + # but for TME it is different so we have to source it from graph + $userAccountId = if ($wellKnownTMETenants.Contains($TenantId)) { + (Get-AzADUser -SignedIn).Id + } else { + # HomeAccountId format is '.' + (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] } - if ($null -eq $user -or !$user.Id) { + if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } - $ProvisionerApplicationOid = $user.Id + $ProvisionerApplicationOid = $userAccountId } elseif ($context.Account.Type -eq 'ServicePrincipal') { $sp = Get-AzADServicePrincipal -ApplicationId $context.Account.Id $ProvisionerApplicationOid = $sp.Id @@ -428,20 +437,25 @@ try { if (!$CI -and !$ServicePrincipalAuth) { if ($TestApplicationId) { - Write-Warning "The specified TestApplicationId '$TestApplicationId' will be ignored when -ServicePrincipalAutth is not set." + Write-Warning "The specified TestApplicationId '$TestApplicationId' will be ignored when -ServicePrincipalAuth is not set." } - # Support corp tenant and TME tenant user id lookups - $userAccount = (Get-AzADUser -Mail (Get-AzContext).Account.Id) - if ($null -eq $userAccount -or !$userAccount.Id) { - $userAccount = (Get-AzADUser -UserPrincipalName (Get-AzContext).Account) + $userAccountName = (Get-AzContext).Account.Id + # HomeAccountId format is '.' + # Calls to graph API in corp tenant get blocked by conditional access policy now + # but not in TME. For corp tenant we get the user's id from the login context + # but for TME it is different so we have to source it from graph + $userAccountId = if ($wellKnownTMETenants.Contains($TenantId)) { + (Get-AzADUser -SignedIn).Id + } else { + # HomeAccountId format is '.' + (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] } - if ($null -eq $userAccount -or !$userAccount.Id) { + if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } - $TestApplicationOid = $userAccount.Id + $TestApplicationOid = $userAccountId $TestApplicationId = $testApplicationOid - $userAccountName = $userAccount.UserPrincipalName Log "User authentication with user '$userAccountName' ('$TestApplicationId') will be used." } # If user has specified -ServicePrincipalAuth diff --git a/eng/common/TestResources/TestResources-Helpers.ps1 b/eng/common/TestResources/TestResources-Helpers.ps1 index cbe047ebc5f1..ce16b7767907 100644 --- a/eng/common/TestResources/TestResources-Helpers.ps1 +++ b/eng/common/TestResources/TestResources-Helpers.ps1 @@ -264,9 +264,10 @@ function SetDeploymentOutputs( ) { $deploymentEnvironmentVariables = $environmentVariables.Clone() $deploymentOutputs = BuildDeploymentOutputs $serviceName $azContext $deployment $deploymentEnvironmentVariables + $isBicep = $templateFile.originalFilePath -and $templateFile.originalFilePath.EndsWith(".bicep") - if ($OutFile) { - if ($IsWindows -and $Language -eq 'dotnet') { + # Azure SDK for .NET on Windows uses DPAPI-encrypted, JSON-encoded environment variables. + if ($OutFile -and $IsWindows -and $Language -eq 'dotnet') { $outputFile = "$($templateFile.originalFilePath).env" $environmentText = $deploymentOutputs | ConvertTo-Json; @@ -276,29 +277,29 @@ function SetDeploymentOutputs( Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force Write-Host "Test environment settings`n$environmentText`nstored into encrypted $outputFile" + } + # Any Bicep template in a repo that has opted into .env files. + elseif ($OutFile -and $isBicep) { + $bicepTemplateFile = $templateFile.originalFilePath + + # Make sure the file would not write secrets to .env file. + if (!(LintBicepFile $bicepTemplateFile)) { + Write-Error "$bicepTemplateFile may write secrets. No file written." } - elseif ($templateFile.originalFilePath -and $templateFile.originalFilePath.EndsWith(".bicep")) { - $bicepTemplateFile = $templateFile.originalFilePath + $outputFile = $bicepTemplateFile | Split-Path | Join-Path -ChildPath '.env' - # Make sure the file would not write secrets to .env file. - if (!(LintBicepFile $bicepTemplateFile)) { - Write-Error "$bicepTemplateFile may write secrets. No file written." + # Make sure the file would be ignored. + git check-ignore -- "$outputFile" > $null + if ($?) { + $environmentText = foreach ($kv in $deploymentOutputs.GetEnumerator()) { + "$($kv.Key)=`"$($kv.Value)`"" } - $outputFile = $bicepTemplateFile | Split-Path | Join-Path -ChildPath '.env' - # Make sure the file would be ignored. - git check-ignore -- "$outputFile" > $null - if ($?) { - $environmentText = foreach ($kv in $deploymentOutputs.GetEnumerator()) { - "$($kv.Key)=`"$($kv.Value)`"" - } - - Set-Content $outputFile -Value $environmentText -Force - Write-Host "Test environment settings`n$environmentText`nstored in $outputFile" - } - else { - Write-Error "$outputFile is not ignored by .gitignore. No file written." - } + Set-Content $outputFile -Value $environmentText -Force + Write-Host "Test environment settings`n$environmentText`nstored in $outputFile" + } + else { + Write-Error "$outputFile is not ignored by .gitignore. No file written." } } else { diff --git a/.github/prompts/check-api-readiness.prompt.md b/eng/common/instructions/azsdk-tools/check-api-readiness.instructions.md similarity index 75% rename from .github/prompts/check-api-readiness.prompt.md rename to eng/common/instructions/azsdk-tools/check-api-readiness.instructions.md index 9db18c3967f0..1daeeadf261f 100644 --- a/.github/prompts/check-api-readiness.prompt.md +++ b/eng/common/instructions/azsdk-tools/check-api-readiness.instructions.md @@ -1,6 +1,4 @@ --- -mode: 'agent' -tools: ['CheckApiReadyForSDKGeneration', 'codebase', 'GetPullRequest', 'GetGitHubUserDetails', 'GetPullRequestForCurrentBranch'] description: 'Check API Readiness for SDK Generation' --- Your goal is to check if API spec pull request is ready for SDK generation. Identify the next action required from user based on the comments on spec pull request if spec is not ready and notify the user. diff --git a/.github/prompts/check-package-readiness.prompt.md b/eng/common/instructions/azsdk-tools/check-package-readiness.instructions.md similarity index 89% rename from .github/prompts/check-package-readiness.prompt.md rename to eng/common/instructions/azsdk-tools/check-package-readiness.instructions.md index cd30574baa12..bb4334dc586f 100644 --- a/.github/prompts/check-package-readiness.prompt.md +++ b/eng/common/instructions/azsdk-tools/check-package-readiness.instructions.md @@ -1,6 +1,4 @@ --- -mode: 'agent' -tools: ['CheckPackageReleaseReadiness'] description: 'This prompt is designed to check the release readiness of a SDK package.' --- ## Goal @@ -17,7 +15,7 @@ Check the release readiness of an SDK package by collecting the required informa - Go 2. **Execute Readiness Check**: - - Use the `CheckPackageReleaseReadiness` tool with the provided package name and selected language + - Use the `azsdk_check_package_release_readiness` tool with the provided package name and selected language - Do not check for existing pull requests to run this step. - Do not ask the user to create a release plan to run this step. diff --git a/.github/prompts/create-release-plan.prompt.md b/eng/common/instructions/azsdk-tools/create-release-plan.instructions.md similarity index 78% rename from .github/prompts/create-release-plan.prompt.md rename to eng/common/instructions/azsdk-tools/create-release-plan.instructions.md index 9c760583d9ec..fa55d8edb0ff 100644 --- a/.github/prompts/create-release-plan.prompt.md +++ b/eng/common/instructions/azsdk-tools/create-release-plan.instructions.md @@ -1,8 +1,3 @@ ---- -mode: 'agent' -tools: ['CreateReleasePlan', 'GetReleasePlanForPullRequest', 'GetReleasePlan', 'LinkSdkPullRequestToReleasePlan'] ---- - # Release Plan Creation Process You goal is to create a valid release plan. You must prompt user to provide all required information and all input must match the format and requirement mentioned in step 3 below. Follow these steps in order to create or manage a release plan for an API specification pull request: @@ -13,7 +8,7 @@ Follow these steps in order to create or manage a release plan for an API specif - Validate that the provided pull request link is accessible and valid ## Step 2: Check Existing Release Plan -- Use `GetReleasePlanForPullRequest` to check if a release plan already exists for the API spec pull request +- Use `azsdk_get_release_plan_for_spec_pr` to check if a release plan already exists for the API spec pull request - If a release plan exists: - Display the existing release plan details to the user - Skip to Step 5 (Link SDK Pull Requests) @@ -38,19 +33,19 @@ If any details are missing, prompt the user accordingly: ## Step 4: Create Release Plan - If the user doesn't know the required details, direct them to create a release plan using the release planner - Provide this resource: [Release Plan Creation Guide](https://eng.ms/docs/products/azure-developer-experience/plan/release-plan-create) -- Once all information is gathered, use `CreateReleasePlan` to create the release plan +- Once all information is gathered, use `azsdk_create_release_plan` to create the release plan - Display the newly created release plan details to the user for confirmation -- Run `/sdk-details-in-release-plan` to identify languages configured in the TypeSpec project and add them to the release plan +- Refer to #file:sdk-details-in-release-plan.instructions.md to identify languages configured in the TypeSpec project and add them to the release plan ## Step 5: Update SDK Details in Release Plan -- Run `/sdk-details-in-release-plan.prompt.md` to add languages and package names to the release plan -- If the TypeSpec project is for a management plane, run `/verify-namespace-approval` if this is first release of SDK. +- Refer to #file:sdk-details-in-release-plan.instructions.md to add languages and package names to the release plan +- If the TypeSpec project is for a management plane, refer to #file:verify-namespace-approval.instructions.md if this is first release of SDK. ## Step 6: Link SDK Pull Requests (if applicable) - Ask the user if they have already created SDK pull requests locally for any programming language - If SDK pull requests exist: - Collect the pull request links from the user - - Use `LinkSdkPullRequestToReleasePlan` to link each SDK pull request to the release plan + - Use `azsdk_link_sdk_pull_request_to_release_plan` to link each SDK pull request to the release plan - Confirm successful linking for each SDK pull request ## Step 7: Summary diff --git a/.github/prompts/create-sdk-locally.prompt.md b/eng/common/instructions/azsdk-tools/create-sdk-locally.instructions.md similarity index 95% rename from .github/prompts/create-sdk-locally.prompt.md rename to eng/common/instructions/azsdk-tools/create-sdk-locally.instructions.md index 34a1a76c5f08..20cc2f31bd5d 100644 --- a/.github/prompts/create-sdk-locally.prompt.md +++ b/eng/common/instructions/azsdk-tools/create-sdk-locally.instructions.md @@ -1,7 +1,3 @@ ---- -mode: 'agent' -tools: ['codebase'] ---- Your goal is to help guide the user to create SDK locally for TypeSpec changes. This is currently supported for **Python** only. User can generate SDK for other languages using SDK generation pipeline. ## Steps to create Python SDK locally from TypeSpec ### Step 1: Check for existing azure-sdk-for-python repository @@ -29,7 +25,7 @@ Your goal is to help guide the user to create SDK locally for TypeSpec changes. "Help me generate SDK for Python from TypeSpec API specification for project ." ``` ### Step 7: Inform user about SDK generation -- Inform user to provide link to SDK pull request if they generate DSK locally and created a pull request for it. SDK generation +- Inform user to provide link to SDK pull request if they generate SDK locally and created a pull request for it. SDK generation step below will skip it for the language and reuse the pull request link provided by the user. - In some cases, user will come back and make more changes to TypeSpec so start the process from step 1 again. - If user provides a link to SDK pull request then link SDK pull request to release plan if a release plan already exists and skip SDK generation for that language. diff --git a/.github/prompts/create-spec-pullrequest.prompt.md b/eng/common/instructions/azsdk-tools/create-spec-pullrequest.instructions.md similarity index 63% rename from .github/prompts/create-spec-pullrequest.prompt.md rename to eng/common/instructions/azsdk-tools/create-spec-pullrequest.instructions.md index b3284f0db417..dca8369a8b5a 100644 --- a/.github/prompts/create-spec-pullrequest.prompt.md +++ b/eng/common/instructions/azsdk-tools/create-spec-pullrequest.instructions.md @@ -1,7 +1,3 @@ ---- -mode: 'agent' -tools: ['codebase', 'CreatePullRequest', 'GetModifiedTypeSpecProjects', 'GetGitHubUserDetails', 'CheckIfSpecInPublicRepo', 'GetPullRequest', 'GetPullRequestForCurrentBranch'] ---- Your goal is to identify modified TypeSpec project in current branch and create a pull request for it. Check if a pull request already exists using GetPullRequestForCurrentBranch. If a pull request exists, inform the user and show the pull request details. If no pull request exists, create a new pull request using CreatePullRequest. diff --git a/.github/prompts/run-sdk-gen-pipeline.prompt.md b/eng/common/instructions/azsdk-tools/run-sdk-gen-pipeline.instructions.md similarity index 91% rename from .github/prompts/run-sdk-gen-pipeline.prompt.md rename to eng/common/instructions/azsdk-tools/run-sdk-gen-pipeline.instructions.md index 57d0aef7c278..3ed5cafefe9a 100644 --- a/.github/prompts/run-sdk-gen-pipeline.prompt.md +++ b/eng/common/instructions/azsdk-tools/run-sdk-gen-pipeline.instructions.md @@ -1,6 +1,4 @@ --- -mode: 'agent' -tools: ['GenerateSDK', 'GetSDKPullRequestDetails', 'GetReleasePlan', 'GetReleasePlanForPullRequest', 'GetPipelineRun', 'GetPipelineRunStatus', 'LinkSdkPullRequestToReleasePlan'] description: 'Generate SDKs from TypeSpec using pipeline' --- Your goal is to generate SDKs from the TypeSpec spec pull request. Get API spec pull request link for current branch or from user if not available in current context. diff --git a/.github/prompts/sdk-details-in-release-plan.prompt.md b/eng/common/instructions/azsdk-tools/sdk-details-in-release-plan.instructions.md similarity index 90% rename from .github/prompts/sdk-details-in-release-plan.prompt.md rename to eng/common/instructions/azsdk-tools/sdk-details-in-release-plan.instructions.md index 862b1470fb99..d6574bb8bd95 100644 --- a/.github/prompts/sdk-details-in-release-plan.prompt.md +++ b/eng/common/instructions/azsdk-tools/sdk-details-in-release-plan.instructions.md @@ -1,7 +1,5 @@ --- -mode: 'agent' description: 'Identify languages configured in the TypeSpec project and add it to release plan' -tools: ['GetReleasePlanForPullRequest', 'GetReleasePlan', 'UpdateReleasePlanSDKInfo'] --- # Step 1: Find the list of languages and package names **Goal**: Identify languages configured in the TypeSpec project and generate the json object with language and package name. @@ -36,6 +34,6 @@ tools: ['GetReleasePlanForPullRequest', 'GetReleasePlan', 'UpdateReleasePlanSDKI # Step 3: Update Release Plan with SDK Information **Goal**: Update the release plan with the languages and package names identified in Step 1. -1. Use `UpdateReleasePlanSDKInfo` to update the release plan work item with the JSON object created in Step 1. +1. Use `azsdk_update_sdk_details_in_release_plan` to update the release plan work item with the JSON object created in Step 1. 2. Confirm successful update of the release plan with the SDK information and summary of languages and package names. **Success Criteria**: Release plan updated with languages and package names. \ No newline at end of file diff --git a/.github/prompts/typespec-to-sdk.prompt.md b/eng/common/instructions/azsdk-tools/typespec-to-sdk.instructions.md similarity index 62% rename from .github/prompts/typespec-to-sdk.prompt.md rename to eng/common/instructions/azsdk-tools/typespec-to-sdk.instructions.md index 304910be1399..b8a8eb6b90ae 100644 --- a/.github/prompts/typespec-to-sdk.prompt.md +++ b/eng/common/instructions/azsdk-tools/typespec-to-sdk.instructions.md @@ -1,14 +1,8 @@ --- -mode: 'agent' description: 'Generate SDKs from TypeSpec' --- Your goal is to guide user through the process of generating SDKs from TypeSpec projects. Show all the high level steps to the user to ensure they understand the flow. Use the provided tools to perform actions and gather information as needed. -## Pre-Flight Check -- Verify ${workspaceFolder} is not on main branch -- If on main branch, prompt user: "You are currently on the main branch. Please create a new branch using `git checkout -b ` before proceeding." -- Wait for user confirmation before continuing - ## Step 1: Identify TypeSpec Project **Goal**: Locate the TypeSpec project root path **Actions**: @@ -21,7 +15,7 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec ## Step 2: Validate TypeSpec Specification **Goal**: Ensure TypeSpec specification compiles without errors **Actions**: -1. Run `/validate-typespec` command +1. Refer to #file:validate-typespec.instructions.md 2. If validation succeeds, proceed to Step 3 3. If validation fails: - Display all compilation errors to user @@ -32,22 +26,23 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec ## Step 3: Verify Authentication and Repository Status **Goal**: Ensure user is authenticated and working in correct repository **Actions**: -1. Run `GetGitHubUserDetails` to verify login status +1. Run `azsdk_get_github_user_details` to verify login status 2. If not logged in, prompt: "Please login to GitHub using `gh auth login`" 3. Once logged in, display user details to confirm identity -4. Run `CheckIfSpecInPublicRepo` to verify repository +4. Run `azsdk_check_typespec_project_in_public_repo` to verify repository 5. If not in public repo, inform: "Please make spec changes in Azure/azure-rest-api-specs public repo to generate SDKs" **Success Criteria**: User authenticated and working in public Azure repo ## Step 4: Review and Commit Changes **Goal**: Stage and commit TypeSpec modifications **Actions**: -1. Run `GetModifiedTypeSpecProjects` to identify changes +1. Run `azsdk_get_modified_typespec_projects` to identify changes 2. If no changes found, inform: "No TypeSpec projects were modified in current branch" 3. Display all modified files (excluding `.github` and `.vscode` folders) 4. Prompt user: "Please review the modified files. Do you want to commit these changes? (yes/no)" 5. If yes: - - Verify current branch is not "main" + - If on main branch, prompt user: "You are currently on the main branch. Please create a new branch using `git checkout -b ` before proceeding." + - Wait for user confirmation before continuing - Run `git add ` - Prompt for commit message - Run `git commit -m ""` @@ -61,17 +56,17 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec - Option A: "Generate SDK locally". This is currently supported only for Python. Do not recommend this for other languages. - Option B: "Use SDK generation pipeline" 2. Based on selection: - - If Option A: Run `/create-sdk-locally` and then proceed to Step 6 + - If Option A: Refer to #file:create-sdk-locally.instructions.md and then proceed to Step 6 - If Option B: Continue to Step 6 **Success Criteria**: SDK generation method selected ## Step 6: Create Specification Pull Request **Goal**: Create PR for TypeSpec changes if not already created **Actions**: -1. Check if spec PR already exists using `GetPullRequestForCurrentBranch` +1. Check if spec PR already exists using `azsdk_get_pull_request_link_for_current_branch` 2. If PR exists, display PR details and proceed to Step 7 3. If no PR exists: - - Run `/create-spec-pullrequest` + - Refer to #file:create-spec-pullrequest.instructions.md - Wait for PR creation confirmation - Display created PR details **Success Criteria**: Specification pull request exists @@ -79,12 +74,11 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec ## Step 7: Generate SDKs via Pipeline **Goal**: Create release plan and generate SDKs **Actions**: -1. Run `/create-release-plan` +1. Refer to #file:create-release-plan.instructions.md 2. If SDK PRs exist, link them to the release plan -3. Run `/sdk-details-in-release-plan` to add languages and package names to the release plan -4. If TypeSpec project is for management plane, Run `/verify-namespace-approval` to check package namespace approval. -This step should not check package readiness to verify namespace approval for management plane SDK. -5. Run `/run-sdk-gen-pipeline` with the spec PR +3. Refer to #file:sdk-details-in-release-plan.instructions.md to add languages and package names to the release plan +4. If TypeSpec project is for management plane, refer to #file:verify-namespace-approval.instructions.md to check package namespace approval. +5. Refer to #file:run-sdk-gen-pipeline.instructions.md with the spec PR 6. Monitor pipeline status and provide updates 7. Display generated SDK PR links when available **Success Criteria**: SDK generation pipeline initiated and SDKs generated @@ -92,21 +86,31 @@ This step should not check package readiness to verify namespace approval for ma ## Step 8: Show Generated SDK PRs **Goal**: Display all created SDK pull requests **Actions**: -1. Run `GetSDKPullRequestDetails` to fetch generated SDK PR info. +1. Run `azsdk_get_sdk_pull_request_link` to fetch generated SDK PR info. + +## Step 9: Validate Label and Codeowners +**Goal**: Validate the label and all codeowners for a service. Create new label and codeowner entry if none exist. +**Actions**: +1. To validate a service label refer to #file:./validate-service-label.instructions.md +2. After service label is validated or created refer to #file:./validate-codeowners.instructions.md +3. Handle post-validation actions based on results: + - **If both label and codeowners were already valid**: Prompt user "Your service label and codeowners are already properly configured. Would you like to modify the existing codeowners entry for your service?" + - **If new label or codeowner entries were created**: Display details of the label and codeowners PR if they were created, then prompt user "The following PRs have been created for your service configuration: [list PRs]. Would you like to make any additional modifications to these entries?" +**Success Criteria**: Service label exists and codeowners are properly configured with at least 2 valid owners. For created entries, showcase all PR's. -## Step 9: Create release plan +## Step 10: Create release plan **Goal**: Create a release plan for the generated SDKs **Actions**: -1. Run `/create-release-plan` to create a release plan using the spec pull request. +1. Refer to #file:create-release-plan.instructions.md to create a release plan using the spec pull request. 2. If the release plan already exists, display the existing plan details. -## Step 10: Mark Spec PR as Ready for Review +## Step 11: Mark Spec PR as Ready for Review **Goal**: Update spec PR to ready for review status **Actions**: 1. Prompt user to change spec PR to ready for review: "Please change the spec pull request to ready for review status" 2. Get approval and merge the spec PR -## Step 11: Release SDK Package +## Step 12: Release SDK Package **Goal**: Release the SDK package using the release plan **Actions**: 1. Run `ReleaseSdkPackage` to release the SDK package. diff --git a/eng/common/instructions/azsdk-tools/validate-codeowners.instructions.md b/eng/common/instructions/azsdk-tools/validate-codeowners.instructions.md new file mode 100644 index 000000000000..83167f459d02 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/validate-codeowners.instructions.md @@ -0,0 +1,74 @@ +--- +mode: 'agent' +tools: ['azsdk_check_service_label', 'azsdk_engsys_validate_codeowners_entry_for_service', 'azsdk_engsys_codeowner_update'] +--- + +## Goal: +Validate service label and ensure at least 2 valid code owners exist for SDK repositories. + +## Step 1: Validate Service Label +Use `azsdk_check_service_label` to verify the service label exists: +- **DoesNotExist/NotAServiceLabel**: Direct user to create valid service label first. Stop validation process until service label is created. +- **Exists/InReview**: Proceed to Step 2 + +## Step 2: Validate Code Owners +Ask user to specify SDK repository they want to validate codeowners for or detect from context. + +Repository name mapping: +- .NET/dotnet: use "azure-sdk-for-net" +- Python: use "azure-sdk-for-python" +- Java: use "azure-sdk-for-java" +- JavaScript: use "azure-sdk-for-js" +- Go: use "azure-sdk-for-go" + +Use `azsdk_engsys_validate_codeowners_entry_for_service` with either `serviceLabel` OR `repoPath` or both, but at least one must be used. If one isn't provided, leave the parameter field empty. + +**If entry exists**: Go to Step 3 +**If no entry exists**: Go to Step 4 + +## Step 3: Check Existing Code Owners +Valid code owners must be: +- PUBLIC members of Microsoft and Azure GitHub organizations +- Have write access to the SDK repository + +**If at least 2 valid owners**: Success - optionally add or delete additional owners +**If less than 2 valid owners**: CRITICAL - must fix before proceeding: + +After any changes, re-validate with `azsdk_engsys_validate_codeowners_entry_for_service`. + +## Step 4: Create New Code Owner Entry +When no CODEOWNERS entry exists yet: +1. Ensure you have the following information + - repo - **Required** - Repository name mapping: + - .NET/dotnet: use "azure-sdk-for-net" + - Python: use "azure-sdk-for-python" + - Java: use "azure-sdk-for-java" + - JavaScript: use "azure-sdk-for-js" + - Go: use "azure-sdk-for-go" + - typeSpecProjectRoot - **Optional** This should be acquired only if the information is present in the previous chat history, if not, ignore and input `""`. + - path - **Optional** only if there is a service label and we're not making a new entry - This should be acquired when creating a new code owner entry, if no information is present ask the user. Typically looks like `/sdk/projectpath` + - serviceLabel - **Optional** only if there is a path and we're not making a new entry - This should be acquired from the previous step of Check or Create Service Label. + - serviceOwners - **Optional** if no ServiceLabel is present. Can be either owners to add or delete, depending on isAdding. + - sourceOwners - **Optional** if no path or PRLabel are present. Can be either owners to add or delete, depending on isAdding. + - isAdding - **Required** Should be true if adding owners to an existing entry, false if deleting owners from an existing entry. Should also be false when adding a brand new entry. +1. Provide information to the user about what codeowners is for: + - [Learn about CODEOWNERS](https://eng.ms/docs/products/azure-developer-experience/develop/supporting-sdk-customers/overview) + - Service owners is for getting mentioned on issues. + - Source owners is for getting mentioned in PRs. +2. Collect service owners and source owners (GitHub usernames) +3. Use `azsdk_engsys_codeowner_update` with required parameters +4. Must have at least 2 valid owners from the start + +### Fix Options: +1. **Fix invalid owners** - If there are invalid owners after modifing the CODEOWNERS file ALWAYS provide guidance. + Follow instructions [here](https://aka.ms/azsdk/access) for: + - Joining Microsoft and Azure GitHub orgs + - Setting public visibility + - Requesting write access +2. **Add new owners** using `azsdk_engsys_codeowner_update` with `isAdding: true` +3. **Remove invalid + add valid** owners using `azsdk_engsys_codeowner_update` + +## Requirements +- **MINIMUM**: At least 2 valid code owners at all times +- **NO EXCEPTIONS**: Cannot proceed with insufficient owners +- **RESPONSE HANDLING**: If any exception occurs during validation or creation, ALWAYS provide documentation link [CODEOWNERS documentation](https://eng.ms/docs/products/azure-developer-experience/develop/supporting-sdk-customers/codeowners) \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/validate-service-label.instructions.md b/eng/common/instructions/azsdk-tools/validate-service-label.instructions.md new file mode 100644 index 000000000000..95dd71e174c2 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/validate-service-label.instructions.md @@ -0,0 +1,54 @@ +--- +mode: 'agent' +tools: ['azsdk_check_service_label', 'azsdk_create_service_label'] +--- + +## Goal +Validate service label exists or create new one for SDK release process. + +## Step 1: Provide Information + +Provide the following information about the importance of service labels: + +"Before your SDK is released, your service must have a valid service label in the Azure SDK repositories. Service labels enable automatic owner assignment and notifications across the Azure SDK ecosystem. + +When properly configured, service labels automatically: + +- Notify service owners when issues are filed against their SDK +- Add appropriate reviewers to pull requests +- Connect code changes to the right team members through CODEOWNERS integration + +Without a valid service label, the process to identify the correct service owners for issues and code reviews becomes manual and inefficient." + +## Step 2: Get Service Label + +Ask user for their service label. If they don't know their service label provide guidance: + +- Check out the [Common Labels CSV](https://github.com/Azure/azure-sdk-tools/blob/main/tools/github/data/common-labels.csv) file and look for a row whose first column contains your service's product name. + +If they don't have a service label - go to Step 3 for new service label + +## Step 3: Validate Label + +Use `azsdk_check_service_label` to check status: + +- **Exists**: Success - user can proceed with next steps in SDK release process +- **InReview**: Label pending approval - user can proceed (will be available once merged) +- **DoesNotExist**: Go to Step 3 to create new label +- **NotAServiceLabel**: Label exists but it is not a service label - go to Step 3 for new service label + +## Step 4: Create New Service Label + +If no valid service label exists, guide the user through creating a new one. + +1. **Check existing labels**: Search for related service labels, offer alternatives +2. **Generate recommendation**: Suggest label name following guidelines: + - Should match the service's official product name as described on Service Tree (e.g., "Event Hubs", "Kusto", "Cosmos", etc.) + - No "Microsoft/Azure" in name + - Title Case (except short prepositions) + - Avoid Service Groups: Use "Communication Rooms" instead of "Communication - Rooms" + - Single label per service +3. **Get confirmation**: User confirms or modifies suggested name +4. **Create label**: Use `azsdk_create_service_label` with confirmed name and documentation link given by user + +Inform user they can proceed. \ No newline at end of file diff --git a/.github/prompts/validate-typespec.prompt.md b/eng/common/instructions/azsdk-tools/validate-typespec.instructions.md similarity index 87% rename from .github/prompts/validate-typespec.prompt.md rename to eng/common/instructions/azsdk-tools/validate-typespec.instructions.md index debdc6eaa31e..c036d6cb195e 100644 --- a/.github/prompts/validate-typespec.prompt.md +++ b/eng/common/instructions/azsdk-tools/validate-typespec.instructions.md @@ -1,6 +1,4 @@ --- -mode: 'agent' -tools: ['RunTypeSpecValidation'] description: 'Validate TypeSpec' --- Your goal is identify the TypeSpec project root if not available in current context and validate TypeSpec project. diff --git a/.github/prompts/verify-namespace-approval.prompt.md b/eng/common/instructions/azsdk-tools/verify-namespace-approval.instructions.md similarity index 81% rename from .github/prompts/verify-namespace-approval.prompt.md rename to eng/common/instructions/azsdk-tools/verify-namespace-approval.instructions.md index fa78f6ef6c76..cee268de74e6 100644 --- a/.github/prompts/verify-namespace-approval.prompt.md +++ b/eng/common/instructions/azsdk-tools/verify-namespace-approval.instructions.md @@ -1,9 +1,7 @@ --- -mode: 'agent' description: 'Verify SDK namespace approval for management plane' -tools: ['GetReleasePlan', 'GetReleasePlanForPullRequest', 'LinkNameSpaceApprovalIssue'] --- -This task is required only for management plan API spec and only if a release plan exists for the API spec pull request. +This task is required only for management plane API spec and only if a release plan exists for the API spec pull request. ## Step 1: Check if release plan exists and it is for management plane SDK **Goal**: Determine if a release plan exists for the API spec pull request or work item Id or release plan Id in current context. @@ -19,6 +17,6 @@ check if they want to link a different namespace approval issue to the release p **Goal**: Link namespace approval issue to the release plan. **Actions**: 1. Collect GitHub issue created in Azure/azure-sdk repo for namespace approval. Do not use any other repo name. -2. Run `LinkNameSpaceApprovalIssue` to link the issue to the release plan work item id. +2. Run `azsdk_link_namespace_approval_issue` to link the issue to the release plan work item id. 3. Confirm successful linking of the namespace approval issue to the release plan. **Success Criteria**: Namespace approval issue linked to the release plan or confirmed as already linked. diff --git a/eng/common/mcp/README.md b/eng/common/mcp/README.md index 4a7c28e46b55..4e72c026b5f0 100644 --- a/eng/common/mcp/README.md +++ b/eng/common/mcp/README.md @@ -24,15 +24,13 @@ The script will install the latest version of the azsdk cli executable from [too Azure SDK MCP server code is in [azure-sdk-tools/tools/azsdk-cli/Azure.Sdk.Tools.Cli](https://github.com/Azure/azure-sdk-tools/tree/main/tools/azsdk-cli/Azure.Sdk.Tools.Cli). -Azure SDK MCP servers should support [stdio and sse transports](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse). +Azure SDK MCP servers [**MUST** support stdio and **SHOULD** support streamable HTTP using latest OAuth 2.1 Best Current Practices (BCPs)](https://modelcontextprotocol.io/docs/concepts/transports). -When running in copilot the default is stdio mode, but SSE is useful to support for external debugging. +When running in copilot the default is stdio mode. ### Developing MCP servers in C# See the [C# MCP SDK](https://github.com/modelcontextprotocol/csharp-sdk) -Add an [SSE transport](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/AspNetCoreSseServer) - TODO: Add the azsdk-cli project to pull in MCP server dependencies from the repo diff --git a/eng/common/mcp/azure-sdk-mcp.ps1 b/eng/common/mcp/azure-sdk-mcp.ps1 index b56fb4e96a28..331155516005 100755 --- a/eng/common/mcp/azure-sdk-mcp.ps1 +++ b/eng/common/mcp/azure-sdk-mcp.ps1 @@ -1,4 +1,7 @@ -#!/bin/env pwsh +#!/usr/bin/env pwsh + +#Requires -Version 7.0 +#Requires -PSEdition Core param( [string]$FileName = 'Azure.Sdk.Tools.Cli', @@ -9,21 +12,13 @@ param( [string]$RunDirectory = (Resolve-Path (Join-Path $PSScriptRoot .. .. ..)), [switch]$Run, [switch]$UpdateVsCodeConfig, - [switch]$Clean + [switch]$UpdatePathInProfile ) $ErrorActionPreference = "Stop" - -if (-not $InstallDirectory) -{ - $homeDir = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE } - $InstallDirectory = (Join-Path $homeDir ".azure-sdk-mcp" "azsdk") -} . (Join-Path $PSScriptRoot '..' 'scripts' 'Helpers' 'AzSdkTool-Helpers.ps1') -if ($Clean) { - Clear-Directory -Path $InstallDirectory -} +$toolInstallDirectory = $InstallDirectory ? $InstallDirectory : (Get-CommonInstallDirectory) if ($UpdateVsCodeConfig) { $vscodeConfigPath = Join-Path $PSScriptRoot ".." ".." ".." ".vscode" "mcp.json" @@ -54,13 +49,33 @@ if ($UpdateVsCodeConfig) { $vscodeConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $vscodeConfigPath -Force } -$exe = Install-Standalone-Tool ` +# Install to a temp directory first so we don't dump out all the other +# release zip contents to one of the users bin directories. +$tmp = $env:TEMP ? $env:TEMP : [System.IO.Path]::GetTempPath() +$guid = [System.Guid]::NewGuid() +$tempInstallDirectory = Join-Path $tmp "azsdk-install-$($guid)" +$tempExe = Install-Standalone-Tool ` -Version $Version ` -FileName $FileName ` -Package $Package ` - -Directory $InstallDirectory ` + -Directory $tempInstallDirectory ` -Repository $Repository +if (-not (Test-Path $toolInstallDirectory)) { + New-Item -ItemType Directory -Path $toolInstallDirectory -Force | Out-Null +} +$exeName = Split-Path $tempExe -Leaf +$exeDestination = Join-Path $toolInstallDirectory $exeName +Copy-Item -Path $tempExe -Destination $exeDestination -Force + +Write-Host "Package $package is installed at $exeDestination" +if (!$UpdatePathInProfile) { + Write-Warning "To add the tool to PATH for new shell sessions, re-run with -UpdatePathInProfile to modify the shell profile file." +} else { + Add-InstallDirectoryToPathInProfile -InstallDirectory $toolInstallDirectory + Write-Warning "'$exeName' will be available in PATH for new shell sessions." +} + if ($Run) { - Start-Process -WorkingDirectory $RunDirectory -FilePath $exe -ArgumentList 'start' -NoNewWindow -Wait + Start-Process -WorkingDirectory $RunDirectory -FilePath $exeDestination -ArgumentList 'start' -NoNewWindow -Wait } diff --git a/eng/common/pipelines/templates/archetype-typespec-emitter.yml b/eng/common/pipelines/templates/archetype-typespec-emitter.yml index a92ad9d9935e..c359c3b1ae12 100644 --- a/eng/common/pipelines/templates/archetype-typespec-emitter.yml +++ b/eng/common/pipelines/templates/archetype-typespec-emitter.yml @@ -248,11 +248,7 @@ extends: jobs: - job: Initialize steps: - - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - parameters: - Paths: - - "/*" - - "!SessionRecords" + - checkout: self - task: UseNode@1 displayName: 'Install Node.js' @@ -325,15 +321,12 @@ extends: matrixArtifactsPath: $(Pipeline.Workspace)/matrix_artifacts AzureSdkRepoName: $[format('azure-sdk/{0}', split(variables['Build.Repository.Name'], '/')[1])] steps: - - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - parameters: - Paths: - - "/*" - - "!SessionRecords" - Repositories: - - Name: $(AzureSdkRepoName) - Commitish: $(branchName) - WorkingDirectory: $(System.DefaultWorkingDirectory) + - checkout: self + - pwsh: | + git remote add azure-sdk https://github.com/$(AzureSdkRepoName).git + git fetch azure-sdk $(branchName) + git switch $(branchName) + displayName: 'Checkout PR branch $(branchName)' - task: UseNode@1 displayName: 'Install Node.js' diff --git a/eng/common/pipelines/templates/jobs/npm-publish.yml b/eng/common/pipelines/templates/jobs/npm-publish.yml index 9909ace96e9f..ce51a7b583e9 100644 --- a/eng/common/pipelines/templates/jobs/npm-publish.yml +++ b/eng/common/pipelines/templates/jobs/npm-publish.yml @@ -99,6 +99,7 @@ jobs: $packageTags = npm view $packageJson.name "dist-tags" -json -silent | ConvertFrom-Json if ($LASTEXITCODE -ne 0 -or !$packageTags) { Write-Warning "Failed to retrieve dist-tags for $packageJson.name. It is possible the package hasn't been indexed yet so ignoring." + $global:LASTEXITCODE = 0 continue } diff --git a/eng/common/pipelines/templates/steps/mark-release-completion.yml b/eng/common/pipelines/templates/steps/mark-release-completion.yml new file mode 100644 index 000000000000..91a39dc48c94 --- /dev/null +++ b/eng/common/pipelines/templates/steps/mark-release-completion.yml @@ -0,0 +1,17 @@ +parameters: + ConfigFileDir: '' + PackageArtifactName: '' + SourceRootPath: $(Build.SourcesDirectory) + +steps: + - task: AzureCLI@2 + inputs: + azureSubscription: opensource-api-connection + scriptType: pscore + scriptLocation: scriptPath + scriptPath: ${{ parameters.SourceRootPath }}/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 + arguments: -PackageInfoFilePath '${{ parameters.ConfigFileDir }}/${{ parameters.PackageArtifactName }}.json' + workingDirectory: $(Pipeline.Workspace) + displayName: Mark package as released + continueOnError: true + condition: and(succeeded(), ne(variables['Skip.MarkReleaseCompletion'], 'true')) \ No newline at end of file diff --git a/eng/common/scripts/ChangeLog-Operations.ps1 b/eng/common/scripts/ChangeLog-Operations.ps1 index f29fc12068dc..c5c4f5ed20ce 100644 --- a/eng/common/scripts/ChangeLog-Operations.ps1 +++ b/eng/common/scripts/ChangeLog-Operations.ps1 @@ -52,6 +52,7 @@ function Get-ChangeLogEntriesFromContent { $sectionHeaderRegex = "^${initialAtxHeader}${SECTION_HEADER_REGEX_SUFFIX}" $changeLogEntries | Add-Member -NotePropertyName "InitialAtxHeader" -NotePropertyValue $initialAtxHeader $releaseTitleAtxHeader = $initialAtxHeader + "#" + $headerLines = @() try { # walk the document, finding where the version specifiers are and creating lists @@ -83,6 +84,9 @@ function Get-ChangeLogEntriesFromContent { $changeLogEntry.ReleaseContent += $line } + else { + $headerLines += $line + } } } } @@ -90,6 +94,8 @@ function Get-ChangeLogEntriesFromContent { Write-Error "Error parsing Changelog." Write-Error $_ } + + $changeLogEntries | Add-Member -NotePropertyName "HeaderBlock" -NotePropertyValue ($headerLines -Join [Environment]::NewLine) return $changeLogEntries } @@ -265,8 +271,13 @@ function Set-ChangeLogContent { ) $changeLogContent = @() - $changeLogContent += "$($ChangeLogEntries.InitialAtxHeader) Release History" - $changeLogContent += "" + if ($ChangeLogEntries.HeaderBlock) { + $changeLogContent += $ChangeLogEntries.HeaderBlock + } + else { + $changeLogContent += "$($ChangeLogEntries.InitialAtxHeader) Release History" + $changeLogContent += "" + } $ChangeLogEntries = Sort-ChangeLogEntries -changeLogEntries $ChangeLogEntries diff --git a/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 b/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 index 4fa981196139..8d74ece07dd9 100644 --- a/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 +++ b/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 @@ -192,3 +192,61 @@ function Install-Standalone-Tool ( return $executable_path } + +function Get-CommonInstallDirectory { + $installDirectory = Join-Path $HOME "bin" + if (-not (Test-Path $installDirectory)) { + New-Item -ItemType Directory -Path $installDirectory -Force | Out-Null + } + + # Update PATH in current session + if (-not ($env:PATH -like "*$InstallDirectory*")) { + $env:PATH += ";$InstallDirectory" + } + + return $installDirectory +} + +function Add-InstallDirectoryToPathInProfile( + [Parameter()] + [string]$InstallDirectory = (Get-CommonInstallDirectory) +) { + $powershellProfilePath = $PROFILE + $bashrcPath = Join-Path $HOME ".bashrc" + $zshrcPath = Join-Path $HOME ".zshrc" + $markerComment = " # azsdk install path" + $pathCommand = "" + $configFile = "" + + if ($IsWindows) { # expect powershell for windows, cmd.exe path update is not currently supported + $configFile = $powershellProfilePath + $pathCommand = "if (-not (`$env:PATH -like `'*$InstallDirectory*`')) { `$env:PATH += ';$InstallDirectory`' }" + $markerComment + } + elseif ($IsLinux) { # handle bash or zsh shells for linux + if (Test-Path $zshrcPath) { + $configFile = $zshrcPath + } + else { + $configFile = $bashrcPath + } + $pathCommand = "export PATH=`"`$PATH:$InstallDirectory`"" + $markerComment + } + elseif ($IsMacOS) { # mac os should use zsh by default + $configFile = $zshrcPath + $pathCommand = "export PATH=`"`$PATH:$InstallDirectory`"" + $markerComment + } + else { + throw "Unsupported platform" + } + + if (-not (Test-Path $configFile)) { + New-Item -ItemType File -Path $configFile -Force | Out-Null + } + + $configContent = Get-Content $configFile -Raw + + if (!$configContent -or !$configContent.Contains($markerComment)) { + Write-Host "Adding installation to PATH in shell profile at '$configFile'" + Add-Content -Path $configFile -Value ([Environment]::NewLine + $pathCommand) + } +} diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 index 9a46e2c126f1..1d4076c4b6b0 100644 --- a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -1015,3 +1015,99 @@ function UpdateValidationStatus($pkgvalidationDetails, $BuildDefinition, $Pipeli Write-Host "[$($workItem.id)]$LanguageDisplayName - $pkgName($versionMajorMinor) - Updated" return $true } + + +function Get-LanguageDevOpsName($LanguageShort) +{ + switch ($LanguageShort.ToLower()) + { + "net" { return "Dotnet" } + "js" { return "JavaScript" } + "java" { return "Java" } + "go" { return "Go" } + "python" { return "Python" } + default { return $null } + } +} + +function Get-ReleasePlanForPackage($packageName) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } + + $prStatusFieldName = "SDKPullRequestStatusFor$($devopsFieldLanguage)" + $packageNameFieldName = "$($devopsFieldLanguage) Package Name" + $fields = @() + $fields += "System.ID" + $fields += "System.State" + $fields += "System.AssignedTo" + $fields += "System.Parent" + $fields += "System.Tags" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Release Plan' AND [${packageNameFieldName}] = '${packageName}'" + $query += " AND [${prStatusFieldName}] = 'merged'" + $query += " AND [System.State] IN ('In Progress')" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" + $workItems = Invoke-Query $fields $query + return $workItems +} + +function Update-ReleaseStatusInReleasePlan($releasePlanWorkItemId, $status, $version) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } + + $fields = @() + $fields += "`"ReleaseStatusFor$($devopsFieldLanguage)=$status`"" + $fields += "`"ReleasedVersionFor$($devopsFieldLanguage)=$version`"" + + Write-Host "Updating Release Plan [$releasePlanWorkItemId] with status [$status] for language [$LanguageShort]." + $workItem = UpdateWorkItem -id $releasePlanWorkItemId -fields $fields + Write-Host "Updated release status for [$LanguageShort] in Release Plan [$releasePlanWorkItemId]" +} + +function Update-PullRequestInReleasePlan($releasePlanWorkItemId, $pullRequestUrl, $status, $languageName) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $languageName + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to update release plan, language [$languageName]" + return $null + } + + $fields = @() + $fields += "`"SDKPullRequestFor$($devopsFieldLanguage)=$pullRequestUrl`"" + $fields += "`"SDKPullRequestStatusFor$($devopsFieldLanguage)=$status`"" + + Write-Host "Updating Release Plan [$releasePlanWorkItemId] with Pull Request URL for language [$languageName]." + $workItem = UpdateWorkItem -id $releasePlanWorkItemId -fields $fields + Write-Host "Updated Pull Request URL [$pullRequestUrl] for [$languageName] in Release Plan [$releasePlanWorkItemId]" +} + +function Get-ReleasePlan-Link($releasePlanWorkItemId) +{ + $fields = @() + $fields += "System.Id" + $fields += "System.Title" + $fields += "Custom.ReleasePlanLink" + $fields += "Custom.ReleasePlanSubmittedby" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + $query = "SELECT ${fieldList} FROM WorkItems WHERE [System.Id] = $releasePlanWorkItemId" + $workItem = Invoke-Query $fields $query + if (!$workItem) + { + Write-Host "Release plan with ID $releasePlanWorkItemId not found." + return $null + } + return $workItem["fields"] +} \ No newline at end of file diff --git a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 new file mode 100644 index 000000000000..2e623672daea --- /dev/null +++ b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 @@ -0,0 +1,69 @@ +param( + [Parameter(Mandatory = $true)] + [string]$PackageInfoFilePath +) + +<# +.SYNOPSIS + Marks release plan completion by identifying pull requests that changed files in a given path. + +.DESCRIPTION + This script helps to mark release plan completion by finding the active release plans for a package name + +.PARAMETER PackageInfoFilePath + The path to the package information file (required) or path to the directory containing package information files. +#> + +Set-StrictMode -Version 3 +. (Join-Path $PSScriptRoot common.ps1) +. (Join-Path $PSScriptRoot Helpers DevOps-WorkItem-Helpers.ps1) + + +#Get package properties +if (-Not (Test-Path $PackageInfoFilePath)) +{ + Write-Host "Package information file path $($PackageInfoFilePath) is invalid." + exit 0 +} + +function Process-Package([string]$packageInfoPath) +{ + # Get package info from json file created before updating version to daily dev + $pkgInfo = Get-Content $packageInfoPath | ConvertFrom-Json + $PackageVersion = $pkgInfo.Version + $PackageName = $pkgInfo.Name + if (!$PackageName -or !$PackageVersion) + { + Write-Host "Package name or version is not available in the package information file. Skipping the release plan status update for the package." + return + } + + # Check Azure DevOps Release Plan work items + Write-Host "Checking active release plan work items for package: $PackageName" + $workItems = Get-ReleasePlanForPackage $PackageName + if(!$workItems) + { + Write-Host "No active release plans found for package name: $PackageName." + return + } + + $activeReleasePlan = $workItems + if($workItems.Count -gt 1 -and ($workItems -is [System.Array])) + { + $concatenatedIds = ($workItems | Select-Object -ExpandProperty id) -join ',' + Write-Host "Multiple release plans found for package name: $PackageName with work item IDs: $concatenatedIds. Using the first release plan to update release status." + $activeReleasePlan = $workItems[0] + } + # Update release status + Write-Host "Release plan work item ID: $($activeReleasePlan["id"])" + Write-Host "Marking release completion for package, name: $PackageName version: $PackageVersion" + Update-ReleaseStatusInReleasePlan $activeReleasePlan.id "Released" $PackageVersion + Write-Host "Successfully marked release completion for package, name: $PackageName version: $PackageVersion." +} + +Write-Host "Finding all package info files in the path: $PackageInfoFilePath" +# Get all package info file under the directory given in input param and process +Get-ChildItem -Path $PackageInfoFilePath -Filter "*.json" | ForEach-Object { + Write-Host "Processing package info file: $_" + Process-Package $_.FullName +} \ No newline at end of file diff --git a/eng/common/scripts/Update-PullRequest-In-ReleasePlan.ps1 b/eng/common/scripts/Update-PullRequest-In-ReleasePlan.ps1 new file mode 100644 index 000000000000..cb40f5773067 --- /dev/null +++ b/eng/common/scripts/Update-PullRequest-In-ReleasePlan.ps1 @@ -0,0 +1,37 @@ +param( + [Parameter(Mandatory = $true)] + $ReleasePlanWorkItemId, + [Parameter(Mandatory = $true)] + $PullRequestUrl, + [Parameter(Mandatory = $true)] + $Status, + [Parameter(Mandatory = $true)] + $LanguageName +) + +<# +.SYNOPSIS +Updates the pull request URL and status in the specified release plan work item for a given programming language. + +.PARAMETER ReleasePlanWorkItemId +The ID of the release plan work item to update. + +.PARAMETER PullRequestUrl +The URL of the pull request to set in the release plan. + +.PARAMETER Status +The status of the pull request. + +.PARAMETER LanguageName +The programming language associated with the pull request. + +#> + +Set-StrictMode -Version 3 +. (Join-Path $PSScriptRoot common.ps1) +. (Join-Path $PSScriptRoot Helpers DevOps-WorkItem-Helpers.ps1) + + +LogDebug "Updating pull request in release plan" +Update-PullRequestInReleasePlan $ReleasePlanWorkItemId $PullRequestUrl $Status $LanguageName +LogDebug "Updated pull request in release plan" \ No newline at end of file diff --git a/eng/common/spelling/Invoke-Cspell.ps1 b/eng/common/spelling/Invoke-Cspell.ps1 old mode 100644 new mode 100755 index 4e5baa0bdf33..df4d2e587348 --- a/eng/common/spelling/Invoke-Cspell.ps1 +++ b/eng/common/spelling/Invoke-Cspell.ps1 @@ -1,3 +1,5 @@ +#!/usr/bin/env pwsh + <# .SYNOPSIS Invokes cspell using dependencies defined in adjacent ./package*.json diff --git a/eng/common/spelling/README.md b/eng/common/spelling/README.md new file mode 100644 index 000000000000..06e711c90a26 --- /dev/null +++ b/eng/common/spelling/README.md @@ -0,0 +1,84 @@ +# Spelling Check Scripts + +This directory contains a script to run cspell (Code Spell Checker) on the repository using the dependencies defined in the adjacent `package*.json` files. + +## Adding Legitimate Words + +If the spell checker flags legitimate words as misspelled, you can add them to the dictionary configuration file located at `.vscode/cspell.json`. + +### Where to Add Words + +There are two main places to add legitimate words. Maintain alphabetical order when adding words to keep the dictionary organized: + +1. **Root-level words array**: Add words to the `"words"` array at the root level of the configuration file. This is the preferred location for project-specific terms, technical vocabulary, and commonly used words. + +2. **Baseline dictionary**: Add words to the `"baseline"` dictionary under `"dictionaryDefinitions"`. This is typically used for words that were already present in the codebase when the spell checker was first introduced. + + +### Example + +To add new words, edit `.vscode/cspell.json` and add them to the `"words"` array: + +```json +{ + "words": [ + "myprojectname", + "customterm", + "technicalword" + ] +} +``` + +### Guidelines + +- Use lowercase for words +- Consider whether the word is truly legitimate or if it might be a typo + +## Available Scripts + +### PowerShell Version (Windows) +- **File**: `Invoke-Cspell.ps1` +- **Usage**: For Windows PowerShell environments + +## Usage Examples + +```powershell +# Check all files (default) +./eng/common/spelling/Invoke-Cspell.ps1 + +# Check specific files +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs 'sdk/*/*/PublicAPI/**/*.md' + +# Check multiple globs (powershell invocation only) +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs @('sdk/storage/**', 'sdk/keyvault/**') + +# Check single file +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs './README.md' +``` + +## Parameters + +- **Job Type**: The cspell command to run (default: `lint`) +- **Scan Globs**: File patterns to check for spelling +- **Config Path**: Location of the cspell.json configuration file +- **Spell Check Root**: Root directory for relative paths +- **Package Cache**: Working directory for npm dependencies +- **Leave Cache**: Option to preserve the npm package cache + +## Requirements + +- Node.js and npm must be installed +- The `.vscode/cspell.json` configuration file must exist +- `jq` command-line JSON processor (for bash version) + +## How It Works + +1. Creates a temporary working directory for npm packages +2. Copies `package.json` and `package-lock.json` to the working directory +3. Installs npm dependencies using `npm ci` +4. Modifies the cspell configuration to include specified file globs +5. Runs cspell with the modified configuration +6. Restores the original configuration +7. Cleans up temporary files + +The scripts ensure that a LICENSE file (or temporary file) is always included in the scan to meet cspell's requirements for the "files" array. diff --git a/eng/common/testproxy/target_version.txt b/eng/common/testproxy/target_version.txt index 977b480f4fd3..f296dfcc218b 100644 --- a/eng/common/testproxy/target_version.txt +++ b/eng/common/testproxy/target_version.txt @@ -1 +1 @@ -1.0.0-dev.20250501.1 +1.0.0-dev.20250805.1 diff --git a/eng/pipelines/spec-gen-sdk-batch.yml b/eng/pipelines/spec-gen-sdk-batch.yml index af06bfacac04..a1b8c98454b9 100644 --- a/eng/pipelines/spec-gen-sdk-batch.yml +++ b/eng/pipelines/spec-gen-sdk-batch.yml @@ -19,4 +19,3 @@ extends: SpecRepoUrl: 'https://github.com/$(Build.Repository.Name)' SdkRepoUrl: $(SdkRepoUrl) SpecBatchTypes: ${{ parameters.SpecBatchTypes }} - SkipPullRequestCreation: true diff --git a/eng/pipelines/spec-gen-sdk.yml b/eng/pipelines/spec-gen-sdk.yml index 0b5b5e849d6e..0082b8fe5185 100644 --- a/eng/pipelines/spec-gen-sdk.yml +++ b/eng/pipelines/spec-gen-sdk.yml @@ -1,8 +1,8 @@ parameters: - - name: SdkRepoCommit + - name: SdkRepoBranch type: string default: 'main' - displayName: 'SDK repository commit' + displayName: 'SDK repository branch' - name: ConfigType type: string values: @@ -25,11 +25,15 @@ parameters: - 'stable' default: 'beta' displayName: 'SDK release type' - - name: SkipPullRequestCreation + - name: CreatePullRequest type: boolean default: false - displayName: 'Skip SDK pull request creation' - + displayName: 'Create SDK pull request' + - name: ReleasePlanWorkItemId + type: number + default: 0 + displayName: 'Release plan work item id' + trigger: none extends: @@ -37,9 +41,10 @@ extends: parameters: SpecRepoUrl: 'https://github.com/$(Build.Repository.Name)' SdkRepoUrl: $(SdkRepoUrl) - SdkRepoCommit: ${{ parameters.SdkRepoCommit }} + SdkRepoBranch: ${{ parameters.SdkRepoBranch }} ConfigType: ${{ parameters.ConfigType }} ConfigPath: ${{ parameters.ConfigPath }} ApiVersion: ${{ parameters.ApiVersion }} SdkReleaseType: ${{ parameters.SdkReleaseType }} - SkipPullRequestCreation: ${{ parameters.SkipPullRequestCreation }} + CreatePullRequest: ${{ parameters.CreatePullRequest }} + ReleasePlanWorkItemId: ${{ parameters.ReleasePlanWorkItemId }} diff --git a/eng/pipelines/swagger-api-doc-preview.yml b/eng/pipelines/swagger-api-doc-preview.yml index 8174542fd87f..a51a1a0b2188 100644 --- a/eng/pipelines/swagger-api-doc-preview.yml +++ b/eng/pipelines/swagger-api-doc-preview.yml @@ -8,8 +8,8 @@ pr: # Smoke test on changed files - eng/pipelines/swagger-api-doc-preview.yml - eng/pipelines/templates/steps/set-sha-check.yml - - .github/shared/src/doc-preview.js - - .github/shared/cmd/api-doc-preview.js + - .github/workflows/src/doc-preview.js + - .github/workflows/cmd/api-doc-preview.js jobs: - job: SwaggerApiDocPreview @@ -60,7 +60,7 @@ jobs: - script: cmd/api-doc-preview.js --output ../../AzureRestPreview displayName: Generate Swagger API documentation preview - workingDirectory: .github/shared + workingDirectory: .github/workflows - template: /eng/common/pipelines/templates/steps/git-push-changes.yml parameters: @@ -159,14 +159,22 @@ jobs: Write-Host "Artifact downloaded successfully" - pwsh: | - # Read the report.json file downloaded from the docs build artifact - $reportRaw = Get-Content -Path "./report.json" -Raw - $report = $reportRaw | ConvertFrom-Json -AsHashtable - # Get build ID from buildstart.json (set during the "Start docs build" step) $buildStart = Get-Content buildstart.json | ConvertFrom-Json $buildLink = "https://dev.azure.com/apidrop/Content%20CI/_build/results?buildId=$($buildStart.id)" + if (!(Test-Path -Path "./report.json")) { + Write-Host "Report file not found. Docs build may have failed or not produced the expected artifact." + Write-Host "##vso[task.setvariable variable=CheckUrl]${buildLink}" + Write-Host "##vso[task.setvariable variable=CheckDescription]Docs build failed with no output artifacts (click to see pipeline logs)" + Write-Host "##vso[task.setvariable variable=CheckState]failure" + exit 0 + } + + # Read the report.json file downloaded from the docs build artifact + $reportRaw = Get-Content -Path "./report.json" -Raw + $report = $reportRaw | ConvertFrom-Json -AsHashtable + if ($report.status -ne "Succeeded") { Write-Host "Docs build failed with status: $($report.status)" Write-Host "Report:" diff --git a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml index 764f9e421e0b..5b8c2d53caca 100644 --- a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml +++ b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml @@ -3,9 +3,9 @@ parameters: type: string - name: SdkRepoUrl type: string - - name: SdkRepoCommit + - name: SdkRepoBranch type: string - default: 'HEAD' + default: 'main' - name: ConfigType type: string default: '' @@ -22,12 +22,15 @@ parameters: type: string default: '' displayName: 'SDK release type' - - name: SkipPullRequestCreation + - name: CreatePullRequest type: boolean default: false - name: SpecBatchTypes type: string default: '' + - name: ReleasePlanWorkItemId + type: number + default: 0 stages: - stage: Build @@ -110,8 +113,69 @@ stages: Exit 1 } + $checkoutCommitish = '${{ parameters.SdkRepoBranch }}' + Write-Host "##vso[task.setvariable variable=CheckoutCommitish]$checkoutCommitish" + Write-Host "Will use commitish for SDK repo checkout: '$checkoutCommitish'" + $prStatus = 'draft' + Write-Host "##vso[task.setvariable variable=SdkPrStatus]$prStatus" + $releasePlanInfo = '' + Write-Host "##vso[task.setvariable variable=ReleasePlanInfo]$releasePlanInfo" + $prUrl = "" + Write-Host "##vso[task.setvariable variable=PullRequestUrl]$prUrl" displayName: "Create Run Time Variables" + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: + - pwsh: | + $sdkRepoBranch = '${{ parameters.SdkRepoBranch }}' + $sdkRepoUrl = '${{ parameters.SdkRepoUrl }}' + Write-Host "Validating SDK repo branch: '$SdkRepoBranch'" + + if (($sdkRepoBranch -ne 'main') -and (-not [string]::IsNullOrWhiteSpace($sdkRepoBranch))) { + + # GitHub's limit is around 250 characters + if ($sdkRepoBranch.Length -gt 250) { + Write-Host "##vso[task.logissue type=error]The provided SDK branch name exceeds 250 characters." + Exit 1 + } + + # Validate branch name + if ($sdkRepoBranch -notmatch '^(?!refs/)(?!/)(?!.*//)(?!.*\.\.)(?!.*\.lock$)(?!@$)(?!.*\.$)(?!.*\/$)(?!.*\\)(?!.*[~^:?*\[\]\s])[\u0020-\u007E]+$') { + Write-Host "##vso[task.logissue type=error]The provided SDK branch name '$sdkRepoBranch' is invalid." + Exit 1 + } + + # Set repo owner to 'azure-sdk' when customized branch is provided + $sdkRepoOwner = 'azure-sdk' + Write-Host "##vso[task.setvariable variable=SdkRepoOwner]$sdkRepoOwner" + Write-Host "SdkRepoOwner variable set to: $sdkRepoOwner" + $sdkRepoUrl = "https://github.com/azure-sdk/$(SdkRepoName)" + + # Check if branch exists remotely + Write-Host "Checking if branch '$SdkRepoBranch' exists in remote repository '$sdkRepoUrl'..." + $branchExists = git ls-remote --heads $sdkRepoUrl "refs/heads/$SdkRepoBranch" + $gitExitCode = $LASTEXITCODE + + if ($gitExitCode -ne 0) { + Write-Host "##vso[task.logissue type=error]Failed to validate branch remotely (git exit code: $gitExitCode)" + Exit $gitExitCode + } + + if ([string]::IsNullOrEmpty($branchExists)) { + Write-Host "Branch '$SdkRepoBranch' does not exist in remote repository" + $checkoutCommitish = 'main' + } else { + Write-Host "Branch '$SdkRepoBranch' exists in remote repository" + $checkoutCommitish = $SdkRepoBranch + } + } else { + Write-Host "Using commitish as-is: '$SdkRepoBranch' (main branch)" + $checkoutCommitish = $SdkRepoBranch + } + + Write-Host "##vso[task.setvariable variable=CheckoutCommitish]$checkoutCommitish" + Write-Host "Will use commitish for SDK repo checkout: '$checkoutCommitish'" + displayName: "Validate and update SDK repository commitish" + - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml parameters: Paths: @@ -125,7 +189,7 @@ stages: Commitish: $(SpecRepoCommit) WorkingDirectory: $(SpecRepoDirectory) - Name: $(SdkRepoOwner)/$(SdkRepoName) - Commitish: ${{ parameters.SdkRepoCommit }} + Commitish: $(CheckoutCommitish) WorkingDirectory: $(SdkRepoDirectory) SkipCheckoutNone: true ${{ if and(eq(variables['System.TeamProject'], 'internal'), endsWith(variables['Build.Repository.Name'], '-pr')) }}: @@ -171,6 +235,11 @@ stages: sdk_gen_info="$sdk_gen_info API Version: ${{ parameters.ApiVersion }}, SDK Release Type: ${{ parameters.SdkReleaseType }}," fi sdk_gen_info="$sdk_gen_info and CommitSHA: '$updatedSpecRepoCommit' in SpecRepo: '${{ parameters.SpecRepoUrl }}'" + + if [ "$(Build.Reason)" = "Manual" ]; then + sdk_gen_info="$sdk_gen_info Pipeline run: $(System.CollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)" + sdk_gen_info="$sdk_gen_info Refer to https://eng.ms/docs/products/azure-developer-experience/develop/sdk-release/sdk-release-prerequisites to prepare for SDK release." + fi echo "##vso[task.setvariable variable=GeneratedSDKInformation]$sdk_gen_info" echo "$sdk_gen_info" @@ -200,15 +269,40 @@ stages: ArtifactPath: $(StagedArtifactsFolder) CustomCondition: and(succeededOrFailed(), ne(variables['StagedArtifactsFolder'], '')) - - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.SkipPullRequestCreation, false), ne(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.CreatePullRequest, true), ne(variables['Build.Reason'], 'PullRequest')) }}: + - pwsh: | + $sdkPrBranchName = "$(PrBranch)-$(Build.BuildId)" + + # use the user provided branch name when SdkRepoOwner is set as 'azure-sdk' + if ('$(SdkRepoOwner)' -eq 'azure-sdk') { + $sdkPrBranchName = '${{ parameters.SdkRepoBranch }}' + } + Write-Host "Branch name is set to: $sdkPrBranchName" + Write-Host "##vso[task.setvariable variable=SdkPullRequestSourceBranch]$sdkPrBranchName" + workingDirectory: $(SdkRepoDirectory) + displayName: "Create SDK PR branch variable" + + - ${{ if ne(parameters.ReleasePlanWorkItemId, 0) }}: + - task: AzureCLI@2 + displayName: "Get Release Plan details" + condition: succeeded() + continueOnError: true + inputs: + azureSubscription: opensource-api-connection + scriptType: pscore + scriptLocation: inlineScript + inlineScript: | + $releasePlanInfo = & "$(SpecRepoDirectory)/eng/scripts/Get-ReleasePlan-Info.ps1" ${{ parameters.ReleasePlanWorkItemId }} + Write-Host "##vso[task.setvariable variable=ReleasePlanInfo]$releasePlanInfo" + Write-Host "Release Plan Info: $releasePlanInfo" + - template: /eng/common/pipelines/templates/steps/git-push-changes.yml parameters: - BaseRepoBranch: $(PrBranch)-$(Build.BuildId) + BaseRepoBranch: $(SdkPullRequestSourceBranch) BaseRepoOwner: azure-sdk CommitMsg: $(GeneratedSDKInformation) - TargetRepoOwner: $(SdkRepoOwner) + TargetRepoOwner: azure TargetRepoName: $(SdkRepoName) - PushArgs: "--force" WorkingDirectory: $(SdkRepoDirectory) ScriptDirectory: $(SdkRepoDirectory)/eng/common/scripts @@ -220,15 +314,49 @@ stages: workingDirectory: $(SdkRepoDirectory) filePath: $(SdkRepoDirectory)/eng/common/scripts/Submit-PullRequest.ps1 arguments: > - -RepoOwner "$(SdkRepoOwner)" + -RepoOwner "azure" -RepoName "$(SdkRepoName)" -BaseBranch "main" -PROwner "azure-sdk" - -PRBranch "$(PrBranch)-$(Build.BuildId)" + -PRBranch "$(SdkPullRequestSourceBranch)" -AuthToken "$(azuresdk-github-pat)" -PRTitle "$(PrTitle)-generated-from-$(Build.DefinitionName)-$(Build.BuildId)" - -PRBody "$(GeneratedSDKInformation)" + -PRBody "$(GeneratedSDKInformation) $(ReleasePlanInfo)" -OpenAsDraft $true + + + - ${{ if ne(parameters.ReleasePlanWorkItemId, 0) }}: + - pwsh: | + $prUrl = "https://github.com/Azure/$(SdkRepoName)/pull/$(Submitted.PullRequest.Number)" + Write-Host "Pull request created: $prUrl" + Write-Host "##vso[task.setvariable variable=PullRequestUrl]$prUrl" + $prStatus = "draft" + Write-Host "##vso[task.setvariable variable=SdkPrStatus]$prStatus" + condition: and(succeeded(), eq(variables['HasChanges'], 'true'), ne(variables['Build.Reason'], 'PullRequest'), not(endsWith(variables['SdkRepoName'], '-pr'))) + displayName: "Set pull request URL variable" + + - pwsh: | + $prUrl = "" + Write-Host "##vso[task.setvariable variable=PullRequestUrl]$prUrl" + $prStatus = "Failed to generate SDK." + Write-Host "##vso[task.setvariable variable=SdkPrStatus]$prStatus" + condition: failed() + displayName: "Set pull request generation failed status variable" + + - task: AzureCLI@2 + displayName: Link pull request to release plan + condition: not(endsWith(variables['SdkRepoName'], '-pr')) + continueOnError: true + inputs: + azureSubscription: opensource-api-connection + scriptType: pscore + scriptLocation: scriptPath + scriptPath: $(SpecRepoDirectory)/eng/scripts/Update-PullRequest-In-ReleasePlan.ps1 + arguments: > + -ReleasePlanWorkItemId ${{ parameters.ReleasePlanWorkItemId }} + -PullRequestUrl "$(PullRequestUrl)" + -Status "$(SdkPrStatus)" + -SdkRepoName "$(SdkRepoName)" - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: - pwsh: | diff --git a/eng/scripts/Get-ReleasePlan-Info.ps1 b/eng/scripts/Get-ReleasePlan-Info.ps1 new file mode 100644 index 000000000000..add4db313455 --- /dev/null +++ b/eng/scripts/Get-ReleasePlan-Info.ps1 @@ -0,0 +1,33 @@ +param( + [Parameter(Mandatory = $true)] + $ReleasePlanWorkItemId +) + +<# +.SYNOPSIS +Updates the pull request URL and status in the specified release plan work item for a given programming language. + +.PARAMETER ReleasePlanWorkItemId +The ID of the release plan work item to update. + +#> + +Set-StrictMode -Version 3 + +. $PSScriptRoot/../common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 + + +Write-Host "Getting release plan info for work item ID: $ReleasePlanWorkItemId" +$releasePlan = Get-ReleasePlan-Link $ReleasePlanWorkItemId +$releasePlanInfo = "" +if ($null -eq $releasePlan) +{ + Write-Host "Failed to retrieve the Release Plan work item or the release plan work item is not present with Id [$ReleasePlanWorkItemId]." +} +else +{ + $releasePlanLink = $releasePlan["Custom.ReleasePlanLink"] + $submittedBy = $releasePlan["Custom.ReleasePlanSubmittedby"] + $releasePlanInfo = "**Release plan link:** [$releasePlanLink]($releasePlanLink) **Submitted by**: $submittedBy" +} +Write-Output $releasePlanInfo \ No newline at end of file diff --git a/eng/scripts/Swagger-Prettier-Check.ps1 b/eng/scripts/Swagger-Prettier-Check.ps1 index c0db6f9a1ab2..fc590abbca28 100644 --- a/eng/scripts/Swagger-Prettier-Check.ps1 +++ b/eng/scripts/Swagger-Prettier-Check.ps1 @@ -11,8 +11,8 @@ $repoPath = Resolve-Path "$PSScriptRoot/../.." $pathsWithErrors = @() if ($CheckAll) { - LogInfo "npm exec --no -- prettier --check $repoPath/specification/**/*.json --log-level warn" - npm exec --no -- prettier --check $repoPath/specification/**/*.json --log-level warn + LogInfo "npm exec --no -- prettier --check $repoPath/specification/**/*.json --log-level debug" + npm exec --no -- prettier --check $repoPath/specification/**/*.json --log-level debug if ($LASTEXITCODE) { $pathsWithErrors += "$repoPath/specification/**/*.json" } @@ -25,8 +25,8 @@ else } else { foreach ($file in $filesToCheck) { - LogInfo "npm exec --no -- prettier --check $repoPath/$file --log-level warn" - npm exec --no -- prettier --check $repoPath/$file --log-level warn + LogInfo "npm exec --no -- prettier --check $repoPath/$file --log-level debug" + npm exec --no -- prettier --check $repoPath/$file --log-level debug if ($LASTEXITCODE) { $pathsWithErrors += $file } diff --git a/eng/scripts/Update-PullRequest-In-ReleasePlan.ps1 b/eng/scripts/Update-PullRequest-In-ReleasePlan.ps1 new file mode 100644 index 000000000000..331d4e307c34 --- /dev/null +++ b/eng/scripts/Update-PullRequest-In-ReleasePlan.ps1 @@ -0,0 +1,36 @@ +param( + [Parameter(Mandatory = $true)] + $ReleasePlanWorkItemId, + [Parameter(Mandatory = $true)] + $PullRequestUrl, + [Parameter(Mandatory = $true)] + $Status, + [Parameter(Mandatory = $true)] + $SdkRepoName +) + +<# +.SYNOPSIS +Updates the pull request URL and status in the specified release plan work item for a given programming language. + +.PARAMETER ReleasePlanWorkItemId +The ID of the release plan work item to update. + +.PARAMETER PullRequestUrl +The URL of the pull request to set in the release plan. + +.PARAMETER Status +The status of the pull request. + +.PARAMETER SdkRepoName +The name of the repository associated with the pull request. + +#> + +Set-StrictMode -Version 3 +. $PSScriptRoot/../common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 + +$languageName = $SdkRepoName.replace('azure-sdk-for-', '') +Write-Host "Updating pull request [$PullRequestUrl] in release plan [$ReleasePlanWorkItemId] for language [$languageName]" +Update-PullRequestInReleasePlan $ReleasePlanWorkItemId $PullRequestUrl $Status $languageName +Write-Host "Updated pull request in release plan" \ No newline at end of file diff --git a/scripts/prettier-swagger-plugin.js b/eng/scripts/prettier-swagger-plugin.js similarity index 100% rename from scripts/prettier-swagger-plugin.js rename to eng/scripts/prettier-swagger-plugin.js diff --git a/eng/tools/lint-diff/package.json b/eng/tools/lint-diff/package.json index 7b2136b5f2f4..a5e657d8b48a 100644 --- a/eng/tools/lint-diff/package.json +++ b/eng/tools/lint-diff/package.json @@ -21,7 +21,9 @@ "dependencies": { "@apidevtools/json-schema-ref-parser": "^14.1.1", "@azure-tools/specs-shared": "file:../../../.github/shared", - "@microsoft.azure/openapi-validator": "^2.2.4", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.9", "autorest": "^3.7.2", "axios": "^1.8.3", "change-case": "^5.4.4", @@ -34,7 +36,7 @@ "@vitest/coverage-v8": "^3.0.2", "execa": "^9.5.2", "memfs": "^4.17.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.2" diff --git a/eng/tools/lint-diff/src/lint-diff.ts b/eng/tools/lint-diff/src/lint-diff.ts index f9af9cc72198..9b575ff9e72f 100644 --- a/eng/tools/lint-diff/src/lint-diff.ts +++ b/eng/tools/lint-diff/src/lint-diff.ts @@ -120,10 +120,8 @@ async function runLintDiff( ); } catch (error) { if (error instanceof SpecModelError) { - console.log("\n\n"); - console.log("❌ Error building Spec Model from changed file list:"); + console.log("\n❌ Error building Spec Model from changed file list:"); console.log(`${error}`); - console.log("Ensure input files and references are valid."); process.exitCode = 1; return; diff --git a/eng/tools/oav-runner/package.json b/eng/tools/oav-runner/package.json index f5b18888189e..6ab2c45bb452 100644 --- a/eng/tools/oav-runner/package.json +++ b/eng/tools/oav-runner/package.json @@ -17,15 +17,15 @@ "dependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", "js-yaml": "^4.1.0", - "oav": "^3.5.1", + "oav": "^4.0.0", "simple-git": "^3.27.0" }, "devDependencies": { "@types/node": "^20.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" diff --git a/eng/tools/oav-runner/src/cli.ts b/eng/tools/oav-runner/src/cli.ts index 9e92b08867f9..b61575c8ad59 100644 --- a/eng/tools/oav-runner/src/cli.ts +++ b/eng/tools/oav-runner/src/cli.ts @@ -8,23 +8,10 @@ import { } from "./formatting.js"; import { checkExamples, checkSpecs } from "./runner.js"; +import { getRootFolder } from "@azure-tools/specs-shared/simple-git"; import fs from "node:fs/promises"; import { parseArgs, ParseArgsConfig } from "node:util"; import { resolve } from "path"; -import { simpleGit } from "simple-git"; - -export async function getRootFolder(inputPath: string): Promise { - try { - const gitRoot = await simpleGit(inputPath).revparse("--show-toplevel"); - return resolve(gitRoot.trim()); - } catch (error) { - console.error( - `Error: Unable to determine the root folder of the git repository.`, - `Please ensure you are running this command within a git repository OR providing a targeted directory that is within a git repo.`, - ); - process.exit(1); - } -} export async function main() { const config: ParseArgsConfig = { diff --git a/eng/tools/oav-runner/src/formatting.ts b/eng/tools/oav-runner/src/formatting.ts index 49816944a3f4..9cc2d36ed7a7 100644 --- a/eng/tools/oav-runner/src/formatting.ts +++ b/eng/tools/oav-runner/src/formatting.ts @@ -56,17 +56,12 @@ export function outputErrorSummary(errors: ReportableOavError[], reportName: str checkName = "validate-example"; } - builtLines.push(`⚠️ This check is testing a new version of '${reportName}'. ⚠️`); - builtLines.push( - "Failures are expected, and should be completely ignored by spec authors and reviewers.", - ); - builtLines.push(`Meaningful results for this PR are in required check '${reportName}'.`); builtLines.push("| File | Line#Column | Code | Message |"); builtLines.push("| --- | --- | --- | --- |"); // sort the errors by file name then by error code errors.sort((a, b) => { - const nameCompare = a.file.localeCompare(b.file); + const nameCompare = (a.file || "").localeCompare(b.file || ""); if (nameCompare !== 0) { return nameCompare; } diff --git a/eng/tools/oav-runner/src/runner.ts b/eng/tools/oav-runner/src/runner.ts index 65286af5887c..392a8e69b830 100644 --- a/eng/tools/oav-runner/src/runner.ts +++ b/eng/tools/oav-runner/src/runner.ts @@ -114,9 +114,20 @@ export async function checkSpecs( async function getFiles(rootDirectory: string, directory: string): Promise { const target = path.join(rootDirectory, directory); - const items = await fs.promises.readdir(target, { - withFileTypes: true, - }); + + let items: fs.Dirent[]; + try { + items = await fs.promises.readdir(target, { + withFileTypes: true, + }); + } catch (error) { + if ((error as NodeJS.ErrnoException)?.code === "ENOENT") { + console.log(`Skipping deleted directory '${target}'`); + return []; + } else { + throw error; + } + } return items .filter((d) => d.isFile() && d.name.endsWith(".json")) diff --git a/eng/tools/oav-runner/test/cli.test.ts b/eng/tools/oav-runner/test/cli.test.ts deleted file mode 100644 index 710fd7a05e18..000000000000 --- a/eng/tools/oav-runner/test/cli.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import path from "path"; -import { describe, expect, it, vi } from "vitest"; -import { getRootFolder } from "../src/cli.js"; - -const REPOROOT = path.resolve(__dirname, "..", "..", "..", ".."); - -describe("invocation directory checks", () => { - it("Should return the same path when invoked from the root of a git repo.", async () => { - const result = await getRootFolder(REPOROOT); - expect(result).toBe(REPOROOT); - }); - - it("Should return a higher path when invoked from a path deep in a git repo.", async () => { - const result = await getRootFolder(path.join(REPOROOT, "eng", "tools", "oav-runner")); - expect(result).toBe(REPOROOT); - }); - - it("Should exit with error when invoked outside of a git directory.", async () => { - const pathOutsideRepo = path.resolve(path.join(REPOROOT, "..")); - - const exitMock = vi - .spyOn(process, "exit") - .mockImplementation((code?: string | number | null | undefined) => { - throw new Error(`Exit ${code}`); - }); - - await expect(getRootFolder(pathOutsideRepo)).rejects.toThrow("Exit 1"); - - exitMock.mockRestore(); - }); -}); diff --git a/eng/tools/oav-runner/test/runner.test.ts b/eng/tools/oav-runner/test/runner.test.ts index 13c1067c059b..cab717cf6ec1 100644 --- a/eng/tools/oav-runner/test/runner.test.ts +++ b/eng/tools/oav-runner/test/runner.test.ts @@ -59,11 +59,19 @@ describe("file processing", () => { it("should handle deleted files without error", async () => { const changedFiles = [ - "specification/serviceB/data-plane/service.B/stable/2025-06-01/serviceBspec.json", - // non-existent file. Should not throw and quietly omit + // existing spec + "specification/serviceA/resource-manager/service.A/stable/2025-06-01/serviceAspec.json", + // existing example + "specification/serviceB/data-plane/service.B/stable/2025-06-01/examples/CreateResource.json", + // non-existent spec. Should not throw and quietly omit "specification/serviceB/data-plane/service.B/stable/2025-06-01/serviceBspecDeleted.json", + // non-existent example. Should not throw and quietly omit + "specification/serviceB/data-plane/service.B/stable/2025-06-01/examples/CreateResourceDeleted.json", + // non-existent example and containing version folder. Should not throw and quietly omit + "specification/serviceB/data-plane/service.B/stable/2025-06-01-deleted/examples/CreateResource.json", ]; const expected = [ + "specification/serviceA/resource-manager/service.A/stable/2025-06-01/serviceAspec.json", "specification/serviceB/data-plane/service.B/stable/2025-06-01/serviceBspec.json", ]; diff --git a/eng/tools/openapi-diff-runner/package.json b/eng/tools/openapi-diff-runner/package.json index e12cb0555d0a..3a614b98ffd1 100644 --- a/eng/tools/openapi-diff-runner/package.json +++ b/eng/tools/openapi-diff-runner/package.json @@ -19,14 +19,14 @@ }, "dependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", - "@azure/oad": "0.10.14" + "@azure/oad": "0.12.0" }, "devDependencies": { "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" } } diff --git a/eng/tools/openapi-diff-runner/src/command-helpers.ts b/eng/tools/openapi-diff-runner/src/command-helpers.ts index b913b72f300d..f6cd01d0bff5 100644 --- a/eng/tools/openapi-diff-runner/src/command-helpers.ts +++ b/eng/tools/openapi-diff-runner/src/command-helpers.ts @@ -74,6 +74,7 @@ function getBreakingChangeCheckName(runType: BreakingChangesCheckType): string { /** * Output the breaking change labels as GitHub Actions environment variables. * This function checks the BreakingChangeLabelsToBeAdded set and sets the appropriate outputs. + * The doc describing breaking change review rules can be found here: https://aka.ms/brch-dev */ export function outputBreakingChangeLabelVariables(): void { // Output the breaking change labels as GitHub Actions environment variables @@ -89,19 +90,24 @@ export function outputBreakingChangeLabelVariables(): void { logMessage("'BreakingChangeReviewRequired' label needs to be added."); setOutput("breakingChangeReviewLabelName", BreakingChangeReviewRequiredLabel); setOutput("breakingChangeReviewLabelValue", "true"); + logMessage("'VersioningReviewRequired' label needs to be deleted."); + setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); + setOutput("versioningReviewLabelValue", "false"); } else { logMessage("'BreakingChangeReviewRequired' label needs to be deleted."); setOutput("breakingChangeReviewLabelName", BreakingChangeReviewRequiredLabel); setOutput("breakingChangeReviewLabelValue", "false"); - } - if (BreakingChangeLabelsToBeAdded.has(VersioningReviewRequiredLabel)) { - logMessage("'VersioningReviewRequired' label needs to be added."); - setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); - setOutput("versioningReviewLabelValue", "true"); - } else { - logMessage("'VersioningReviewRequired' label needs to be deleted."); - setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); - setOutput("versioningReviewLabelValue", "false"); + + // versioning review label is only set if the breaking change review label is not set + if (BreakingChangeLabelsToBeAdded.has(VersioningReviewRequiredLabel)) { + logMessage("'VersioningReviewRequired' label needs to be added."); + setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); + setOutput("versioningReviewLabelValue", "true"); + } else { + logMessage("'VersioningReviewRequired' label needs to be deleted."); + setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); + setOutput("versioningReviewLabelValue", "false"); + } } } } diff --git a/eng/tools/openapi-diff-runner/src/commands.ts b/eng/tools/openapi-diff-runner/src/commands.ts index 54ab85822025..0dea61b5a09a 100644 --- a/eng/tools/openapi-diff-runner/src/commands.ts +++ b/eng/tools/openapi-diff-runner/src/commands.ts @@ -26,6 +26,7 @@ import { LOG_PREFIX, logMessage } from "./log.js"; import { Context } from "./types/breaking-change.js"; import { RawMessageRecord, ResultMessageRecord } from "./types/message.js"; import { createOadTrace, generateOadMarkdown, setOadBaseBranch } from "./types/oad-types.js"; +import { checkPrTargetsProductionBranch } from "./utils/common-utils.js"; import { appendMarkdownToLog } from "./utils/oad-message-processor.js"; /** @@ -170,15 +171,21 @@ export async function validateBreakingChange(context: Context): Promise ({ msgs, runtimeErrors, oadViolationsCnt, errorCnt } = await checkCrossVersionBreakingChange(detectionContext)); } - const comparedSpecsTableContent = generateOadMarkdown(detectionContext.oadTracer); + const comparedSpecsTableContent = await generateOadMarkdown(detectionContext.oadTracer); // Log the markdown content to the pipeline log file if (comparedSpecsTableContent) { appendMarkdownToLog(context.oadMessageProcessorContext, comparedSpecsTableContent); } - // process breaking change labels - outputBreakingChangeLabelVariables(); + // output breaking change label variables only when the PR targets a production branch + logMessage( + `Evaluate breaking change labels: targetRepo: ${context.targetRepo}, ` + + `targetBranch: ${context.prInfo!.targetBranch}`, + ); + if (checkPrTargetsProductionBranch(context.targetRepo, context.prInfo!.targetBranch)) { + outputBreakingChangeLabelVariables(); + } // If exitCode is already defined and non-zero, we do not interfere with its value here. if (process.exitCode === undefined || process.exitCode === 0) { @@ -195,17 +202,14 @@ export async function validateBreakingChange(context: Context): Promise // - If there are messages from OAD (openapi-diff) denoting violations, but none // of them resulted in adding any breaking changes labels. // This is why we do not include 'oadViolationsCnt' in this formula at all. - // Instead, we rely on 'labelsAddedCount'. - // See https://github.com/Azure/azure-sdk-tools/issues/6396 // - If there are errors, but they are only warning-level. This happens when comparing // to previous preview version. In such cases, these errors are not included in the 'errorCnt' at all. - //process.exitCode = labelsAddedCount > 0 || errorCnt > 0 ? 1 : 0; process.exitCode = errorCnt > 0 ? 1 : 0; } logMessage( `${LOG_PREFIX}validateBreakingChange: prUrl: ${context.prUrl}, ` + - `comparisonType: ${context.runType}, labelsAddedCount: , ` + + `comparisonType: ${context.runType},` + `errorCnt: ${errorCnt}, oadViolationsCnt: ${oadViolationsCnt}, ` + `process.exitCode: ${process.exitCode}`, ); diff --git a/eng/tools/openapi-diff-runner/src/index.ts b/eng/tools/openapi-diff-runner/src/index.ts index 19ed15cc7555..3b22994fa212 100644 --- a/eng/tools/openapi-diff-runner/src/index.ts +++ b/eng/tools/openapi-diff-runner/src/index.ts @@ -173,5 +173,11 @@ export async function main() { await buildPrInfo(context); let statusCode = 0; statusCode = await validateBreakingChange(context); + + if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) { + logMessage( + `See validation report summary at: ${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`, + ); + } exit(statusCode); } diff --git a/eng/tools/openapi-diff-runner/src/types/oad-types.ts b/eng/tools/openapi-diff-runner/src/types/oad-types.ts index 0be7ae36c42c..ad0ce6ef606f 100644 --- a/eng/tools/openapi-diff-runner/src/types/oad-types.ts +++ b/eng/tools/openapi-diff-runner/src/types/oad-types.ts @@ -94,7 +94,7 @@ export const setOadBaseBranch = (traceData: OadTraceData, branchName: string): O /** * Generates markdown content from OAD trace data */ -export const generateOadMarkdown = (traceData: OadTraceData): string => { +export const generateOadMarkdown = async (traceData: OadTraceData): Promise => { const oadVersion = packageJson.dependencies?.["@azure/oad"]?.replace(/[\^~]/, "") || "unknown"; if (traceData.traces.length === 0) { return ""; @@ -108,13 +108,13 @@ export const generateOadMarkdown = (traceData: OadTraceData): string => { for (const value of traceData.traces) { // Compose each column for clarity const newFileName = basename(value.new); - const newVersion = getVersionFromInputFile(value.new, true); + const newVersion = await getVersionFromInputFile(value.new, true); // Truncate commit hash to first 8 characters for better readability const shortCommit = traceData.context.headCommit.substring(0, 8); const newCommitLink = `[${shortCommit}](${sourceBranchHref(traceData.context.sourceRepo, traceData.context.headCommit, value.new)})`; - const oldVersion = getVersionFromInputFile(value.old, true); + const oldVersion = await getVersionFromInputFile(value.old, true); const oldCommitLink = `[${traceData.baseBranch}](${specificBranchHref(traceData.context.targetRepo, value.old, traceData.baseBranch)})`; // Add a row to the markdown table with proper spacing diff --git a/eng/tools/openapi-diff-runner/src/utils/common-utils.ts b/eng/tools/openapi-diff-runner/src/utils/common-utils.ts index 28ddbc8e1dd7..68c6ec903d29 100644 --- a/eng/tools/openapi-diff-runner/src/utils/common-utils.ts +++ b/eng/tools/openapi-diff-runner/src/utils/common-utils.ts @@ -1,4 +1,4 @@ -import { existsSync, readFileSync } from "node:fs"; +import { readFile } from "node:fs/promises"; import { Context } from "../types/breaking-change.js"; import { FilePosition } from "../types/message.js"; @@ -82,7 +82,10 @@ export function specificBranchHref( ); } -export function getVersionFromInputFile(filePath: string, withPreview = false): string { +export async function getVersionFromInputFile( + filePath: string, + withPreview = false, +): Promise { const apiVersionRegex = /^\d{4}-\d{2}-\d{2}(|-preview|-privatepreview|-alpha|-beta|-rc)$/; // Normalize path separators to forward slashes for consistent processing @@ -112,14 +115,19 @@ export function getVersionFromInputFile(filePath: string, withPreview = false): } } + let version = ""; // If no regex match found, try to read version from file content - if (existsSync(filePath)) { - const fileContent = readFileSync(filePath, "utf8"); + try { + const fileContent = await readFile(filePath, "utf8"); const parsedContent = JSON.parse(fileContent); - return parsedContent?.info?.version || ""; + version = parsedContent?.info?.version; + } catch (error) { + throw new Error(`Failed to read version from file:${filePath}, cause: ${error}`); } - - return ""; + if (!version) { + throw new Error(`Version not found in file: ${filePath}`); + } + return version; } export function getArgumentValue(args: string[], flag: string, defaultValue: string): string { @@ -225,3 +233,20 @@ export function convertRawErrorToUnifiedMsg( }; return JSON.stringify(result); } + +/** + * Check if the PR targets a production branch. + * The production branches are defined at https://aka.ms/azsdk/pr-brch-deep + * @param targetRepo The repository name, e.g. "azure-rest-api-specs" or "azure-rest-api-specs-pr". + * @param targetBranch The target branch of the PR. + * @returns {boolean} True if the PR targets a production branch, false otherwise. + */ +export function checkPrTargetsProductionBranch(targetRepo: string, targetBranch: string): boolean { + const targetsPublicProductionBranch = + targetRepo.endsWith("azure-rest-api-specs") && targetBranch === "main"; + + const targetsPrivateProductionBranch = + targetRepo.endsWith("azure-rest-api-specs-pr") && targetBranch === "RPSaaSMaster"; + + return targetsPublicProductionBranch || targetsPrivateProductionBranch; +} diff --git a/eng/tools/openapi-diff-runner/src/utils/spec.ts b/eng/tools/openapi-diff-runner/src/utils/spec.ts index c595342c3747..499c9e3fb471 100644 --- a/eng/tools/openapi-diff-runner/src/utils/spec.ts +++ b/eng/tools/openapi-diff-runner/src/utils/spec.ts @@ -1,3 +1,4 @@ +import { mapAsync } from "@azure-tools/specs-shared/array"; import { basename } from "node:path"; import { logError } from "../log.js"; import { ApiVersionLifecycleStage } from "../types/breaking-change.js"; @@ -28,18 +29,22 @@ async function getPrecedingSwaggerByType( return undefined; } - const currentVersion = getVersionFromInputFile(targetSwaggerPath); + const currentVersion = await getVersionFromInputFile(targetSwaggerPath); const fileName = getBaseNameForSwagger(targetSwaggerPath, currentVersion); // Load version info for all swaggers to enable filtering - const swaggersWithVersions = availableSwaggers.map((swagger) => ({ - swagger, - version: getVersionFromInputFile(swagger.path), - fileName: getBaseNameForSwagger(swagger.path, getVersionFromInputFile(swagger.path)), - versionKind: getVersionFromInputFile(swagger.path, true).includes("preview") - ? ApiVersionLifecycleStage.PREVIEW - : ApiVersionLifecycleStage.STABLE, - })); + const swaggersWithVersions = await mapAsync(availableSwaggers, async (swagger) => { + const version = await getVersionFromInputFile(swagger.path); + const versionWithPreview = await getVersionFromInputFile(swagger.path, true); + return { + swagger, + version, + fileName: getBaseNameForSwagger(swagger.path, version), + versionKind: versionWithPreview.includes("preview") + ? ApiVersionLifecycleStage.PREVIEW + : ApiVersionLifecycleStage.STABLE, + }; + }); const versionsOfType = swaggersWithVersions.filter( (item) => @@ -72,15 +77,20 @@ export async function getExistedVersionOperations( return result; } - const currentVersion = getVersionFromInputFile(targetSwaggerPath); + const currentVersion = await getVersionFromInputFile(targetSwaggerPath); const fileName = getBaseNameForSwagger(targetSwaggerPath, currentVersion); // Load version info for all swaggers to enable filtering - const swaggersWithVersions = availableSwaggers.map((swagger) => ({ - swagger, - version: getVersionFromInputFile(swagger.path), - fileName: getBaseNameForSwagger(swagger.path, getVersionFromInputFile(swagger.path)), - })); + const swaggersWithVersions = await Promise.all( + availableSwaggers.map(async (swagger) => { + const version = await getVersionFromInputFile(swagger.path); + return { + swagger, + version, + fileName: getBaseNameForSwagger(swagger.path, version), + }; + }), + ); // Get all current and previous versions of the swaggers with different fileNames const previousVersionSwaggers = swaggersWithVersions @@ -165,12 +175,5 @@ export function getBaseNameForSwagger(filePath: string, version: string = ""): s */ export function deduplicateSwaggers(swaggers: Swagger[]): Swagger[] { // Deduplicate by path - const seen = new Set(); - return swaggers.filter((swagger) => { - if (seen.has(swagger.path)) { - return false; - } - seen.add(swagger.path); - return true; - }); + return [...new Map(swaggers.map((s) => [s.path, s])).values()]; } diff --git a/eng/tools/openapi-diff-runner/test/command-helpers.test.ts b/eng/tools/openapi-diff-runner/test/command-helpers.test.ts index 80f80c7858b7..2a433b9d9cba 100644 --- a/eng/tools/openapi-diff-runner/test/command-helpers.test.ts +++ b/eng/tools/openapi-diff-runner/test/command-helpers.test.ts @@ -892,7 +892,7 @@ describe("command-helpers", () => { "versioningReviewLabelName", VersioningReviewRequiredLabel, ); - expect(setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", "true"); + expect(setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", "false"); }); it("should handle labels set with non-review labels", async () => { diff --git a/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts b/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts index c0c24872b95a..2246d3623230 100644 --- a/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts +++ b/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts @@ -55,14 +55,14 @@ describe("OAD Trace Functions", () => { expect(updatedTrace.baseBranch).toBe("feature-branch"); }); - it("should generate empty markdown when no traces", () => { + it("should generate empty markdown when no traces", async () => { const traceData = createOadTrace(mockContext); - const markdown = generateOadMarkdown(traceData); + const markdown = await generateOadMarkdown(traceData); expect(markdown).toBe(""); }); - it("should generate markdown table when traces exist", () => { + it("should generate markdown table when traces exist", async () => { let traceData = createOadTrace(mockContext); traceData = addOadTrace( traceData, @@ -70,7 +70,7 @@ describe("OAD Trace Functions", () => { "specification/storage/resource-manager/Microsoft.Storage/stable/2021-09-01/storage.json", ); - const markdown = generateOadMarkdown(traceData); + const markdown = await generateOadMarkdown(traceData); expect(markdown).toContain("| Compared specs"); expect(markdown).toContain("storage.json"); diff --git a/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts b/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts index acbdc7679789..ea5366e707b2 100644 --- a/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts @@ -1,9 +1,10 @@ -import { existsSync, readFileSync } from "node:fs"; +import { readFile } from "node:fs/promises"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { Context } from "../../src/types/breaking-change.js"; import { blobHref, branchHref, + checkPrTargetsProductionBranch, convertRawErrorToUnifiedMsg, cutoffMsg, getArgumentValue, @@ -18,13 +19,12 @@ import { targetHref, } from "../../src/utils/common-utils.js"; -// Mock node:fs module for file content parsing tests -vi.mock("node:fs", async (importOriginal) => { - const actual = await importOriginal(); +// Mock node:fs/promises module for async file operations +vi.mock("node:fs/promises", async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, - existsSync: vi.fn(), - readFileSync: vi.fn(), + readFile: vi.fn(), }; }); @@ -237,36 +237,53 @@ describe("common-utils", () => { describe("getVersionFromInputFile", () => { beforeEach(() => { - vi.mocked(existsSync).mockReset(); - vi.mocked(readFileSync).mockReset(); + vi.mocked(readFile).mockReset(); }); - it("should extract version from data-plane path", () => { - const result = getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PATH); + it("should extract version from data-plane path", async () => { + const result = await getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PATH); expect(result).toBe(TEST_CONSTANTS.VERSION); }); - it("should extract version with preview from data-plane path", () => { - const result = getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PREVIEW_PATH, true); + it("should extract version with preview from data-plane path", async () => { + const result = await getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PREVIEW_PATH, true); expect(result).toBe(TEST_CONSTANTS.PREVIEW_VERSION); }); - it("should extract version from resource-manager path", () => { - const result = getVersionFromInputFile(TEST_CONSTANTS.RESOURCE_MANAGER_PATH); + it("should extract version from resource-manager path", async () => { + const result = await getVersionFromInputFile(TEST_CONSTANTS.RESOURCE_MANAGER_PATH); expect(result).toBe(TEST_CONSTANTS.VERSION); }); - it("should return empty string when no version found in path", () => { - const result = getVersionFromInputFile("test.json"); - expect(result).toBe(""); + it("should throw error when no version found in path", async () => { + const mockFileContent = JSON.stringify({ + info: { + title: "Test API", + }, + }); + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + await expect(getVersionFromInputFile("test.json")).rejects.toThrow( + "Version not found in file: test.json", + ); }); - it("should return empty string when no valid API version found", () => { - const result = getVersionFromInputFile("invalid/path.json"); - expect(result).toBe(""); + it("should throw error when no valid API version found", async () => { + const mockFileContent = JSON.stringify({ + info: { + title: "Test API", + }, + }); + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + await expect(getVersionFromInputFile("invalid/path.json")).rejects.toThrow( + "Version not found in file: invalid/path.json", + ); }); - it("should extract version from file content when path regex fails and file exists", () => { + it("should extract version from file content when path regex fails", async () => { const filePath = "some/custom/path/spec.json"; const mockFileContent = JSON.stringify({ info: { @@ -274,16 +291,14 @@ describe("common-utils", () => { }, }); - vi.mocked(existsSync).mockReturnValue(true); - vi.mocked(readFileSync).mockReturnValue(mockFileContent); + vi.mocked(readFile).mockResolvedValue(mockFileContent); - const result = getVersionFromInputFile(filePath); + const result = await getVersionFromInputFile(filePath); expect(result).toBe("2023-05-01"); - expect(vi.mocked(existsSync)).toHaveBeenCalledWith(filePath); - expect(vi.mocked(readFileSync)).toHaveBeenCalledWith(filePath, "utf8"); + expect(vi.mocked(readFile)).toHaveBeenCalledWith(filePath, "utf8"); }); - it("should extract preview version from file content when includePreview is true", () => { + it("should extract preview version from file content when includePreview is true", async () => { const filePath = "some/custom/path/spec.json"; const mockFileContent = JSON.stringify({ info: { @@ -291,16 +306,14 @@ describe("common-utils", () => { }, }); - vi.mocked(existsSync).mockReturnValue(true); - vi.mocked(readFileSync).mockReturnValue(mockFileContent); + vi.mocked(readFile).mockResolvedValue(mockFileContent); - const result = getVersionFromInputFile(filePath, true); + const result = await getVersionFromInputFile(filePath, true); expect(result).toBe("2023-05-01-preview"); - expect(vi.mocked(existsSync)).toHaveBeenCalledWith(filePath); - expect(vi.mocked(readFileSync)).toHaveBeenCalledWith(filePath, "utf8"); + expect(vi.mocked(readFile)).toHaveBeenCalledWith(filePath, "utf8"); }); - it("should return empty string when file content has no version", () => { + it("should throw error when file content has no version", async () => { const filePath = "some/custom/path/spec.json"; const mockFileContent = JSON.stringify({ info: { @@ -308,49 +321,32 @@ describe("common-utils", () => { }, }); - vi.mocked(existsSync).mockReturnValue(true); - vi.mocked(readFileSync).mockReturnValue(mockFileContent); + vi.mocked(readFile).mockResolvedValue(mockFileContent); - const result = getVersionFromInputFile(filePath); - expect(result).toBe(""); - expect(vi.mocked(existsSync)).toHaveBeenCalledWith(filePath); - expect(vi.mocked(readFileSync)).toHaveBeenCalledWith(filePath, "utf8"); + await expect(getVersionFromInputFile(filePath)).rejects.toThrow( + "Version not found in file: some/custom/path/spec.json", + ); }); - it("should throw error when file content is invalid JSON", () => { + it("should throw error when file content is invalid JSON", async () => { const filePath = "some/custom/path/spec.json"; const mockFileContent = "invalid json content"; - vi.mocked(existsSync).mockReturnValue(true); - vi.mocked(readFileSync).mockReturnValue(mockFileContent); - - expect(() => getVersionFromInputFile(filePath)).toThrow(); - expect(vi.mocked(existsSync)).toHaveBeenCalledWith(filePath); - expect(vi.mocked(readFileSync)).toHaveBeenCalledWith(filePath, "utf8"); - }); - - it("should return empty string when file does not exist and path regex fails", () => { - const filePath = "non/existent/path/spec.json"; - - vi.mocked(existsSync).mockReturnValue(false); + vi.mocked(readFile).mockResolvedValue(mockFileContent); - const result = getVersionFromInputFile(filePath); - expect(result).toBe(""); - expect(vi.mocked(existsSync)).toHaveBeenCalledWith(filePath); - expect(vi.mocked(readFileSync)).not.toHaveBeenCalled(); + await expect(getVersionFromInputFile(filePath)).rejects.toThrow( + "Failed to read version from file:some/custom/path/spec.json", + ); }); - it("should throw error when file read fails", () => { + it("should throw error when file read fails", async () => { const filePath = "some/custom/path/spec.json"; - vi.mocked(existsSync).mockReturnValue(true); - vi.mocked(readFileSync).mockImplementation(() => { - throw new Error("File read error"); - }); + vi.mocked(readFile).mockRejectedValue(new Error("File read error")); - expect(() => getVersionFromInputFile(filePath)).toThrow("File read error"); - expect(vi.mocked(existsSync)).toHaveBeenCalledWith(filePath); - expect(vi.mocked(readFileSync)).toHaveBeenCalledWith(filePath, "utf8"); + await expect(getVersionFromInputFile(filePath)).rejects.toThrow( + "Failed to read version from file:some/custom/path/spec.json", + ); }); }); @@ -642,4 +638,95 @@ describe("common-utils", () => { expect(parsed.extra.details).toBe(errorMsg); }); }); + + describe("checkPrTargetsProductionBranch", () => { + it("should return true for public production branch (azure-rest-api-specs + main)", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", "main"); + expect(result).toBe(true); + }); + + it("should return true for public production branch with extended repo name", () => { + const result = checkPrTargetsProductionBranch("owner/azure-rest-api-specs", "main"); + expect(result).toBe(true); + }); + + it("should return true for private production branch (azure-rest-api-specs-pr + RPSaaSMaster)", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "RPSaaSMaster"); + expect(result).toBe(true); + }); + + it("should return true for private production branch with extended repo name", () => { + const result = checkPrTargetsProductionBranch( + "owner/azure-rest-api-specs-pr", + "RPSaaSMaster", + ); + expect(result).toBe(true); + }); + + it("should return false for public repo with non-main branch", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", "feature-branch"); + expect(result).toBe(false); + }); + + it("should return false for public repo with develop branch", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", "develop"); + expect(result).toBe(false); + }); + + it("should return false for private repo with main branch instead of RPSaaSMaster", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "main"); + expect(result).toBe(false); + }); + + it("should return false for private repo with non-RPSaaSMaster branch", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "feature-branch"); + expect(result).toBe(false); + }); + + it("should return false for unrelated repository with main branch", () => { + const result = checkPrTargetsProductionBranch("some-other-repo", "main"); + expect(result).toBe(false); + }); + + it("should return false for unrelated repository with RPSaaSMaster branch", () => { + const result = checkPrTargetsProductionBranch("some-other-repo", "RPSaaSMaster"); + expect(result).toBe(false); + }); + + it("should return false for empty repository name", () => { + const result = checkPrTargetsProductionBranch("", "main"); + expect(result).toBe(false); + }); + + it("should return false for empty branch name", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", ""); + expect(result).toBe(false); + }); + + it("should return false for both empty parameters", () => { + const result = checkPrTargetsProductionBranch("", ""); + expect(result).toBe(false); + }); + + it("should be case sensitive for branch names", () => { + const result1 = checkPrTargetsProductionBranch("azure-rest-api-specs", "Main"); + const result2 = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "rpsaasmaster"); + + expect(result1).toBe(false); + expect(result2).toBe(false); + }); + + it("should handle partial repo name matches correctly", () => { + const result1 = checkPrTargetsProductionBranch("azure-rest-api", "main"); + const result2 = checkPrTargetsProductionBranch("rest-api-specs", "main"); + const result3 = checkPrTargetsProductionBranch( + "azure-rest-api-specs-private", + "RPSaaSMaster", + ); + + expect(result1).toBe(false); + expect(result2).toBe(false); + expect(result3).toBe(false); + }); + }); }); diff --git a/eng/tools/openapi-diff-runner/test/utils/spec.test.ts b/eng/tools/openapi-diff-runner/test/utils/spec.test.ts index 2200e0a29991..7258ce23cac7 100644 --- a/eng/tools/openapi-diff-runner/test/utils/spec.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/spec.test.ts @@ -103,11 +103,9 @@ describe("Helper functions for version analysis", () => { createMockSwagger(TEST_PATHS.stable2020_07_02, ApiVersionLifecycleStage.STABLE), ]; - const result = await getPrecedingSwaggers(TEST_PATHS.nonexistent, mockSwaggers); - - expectResultStructure(result); - expect(result.stable).toBeUndefined(); - expect(result.preview).toBeUndefined(); + await expect(getPrecedingSwaggers(TEST_PATHS.nonexistent, mockSwaggers)).rejects.toThrow( + `Failed to read version from file:${TEST_PATHS.nonexistent}`, + ); }); it("should handle null or undefined availableSwaggers array", async () => { @@ -308,21 +306,6 @@ describe("Helper functions for version analysis", () => { ]); }); - it("should preserve the first occurrence when duplicates exist", () => { - const swagger1 = createMockSwagger(TEST_PATHS.stable2020_07_02); - const swagger2 = createMockSwagger(TEST_PATHS.stable2020_07_02); // same path, different object - swagger1.versionKind = ApiVersionLifecycleStage.STABLE; - swagger2.versionKind = ApiVersionLifecycleStage.PREVIEW; - - const mockSwaggers: MockSwagger[] = [swagger1, swagger2]; - - const result = deduplicateSwaggers(mockSwaggers); - - expect(result).toHaveLength(1); - expect(result[0]).toBe(swagger1); // First occurrence preserved - expect(result[0].versionKind).toBe(ApiVersionLifecycleStage.STABLE); - }); - it("should handle single swagger", () => { const mockSwaggers: MockSwagger[] = [createMockSwagger(TEST_PATHS.stable2020_07_02)]; diff --git a/eng/tools/sdk-suppressions/package.json b/eng/tools/sdk-suppressions/package.json index 7c16b065fbbb..e54eff101a35 100644 --- a/eng/tools/sdk-suppressions/package.json +++ b/eng/tools/sdk-suppressions/package.json @@ -30,10 +30,10 @@ "@types/debug": "^4.1.12", "@types/lodash": "^4.14.161", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" } } diff --git a/eng/tools/spec-gen-sdk-runner/package.json b/eng/tools/spec-gen-sdk-runner/package.json index 7268ae6ff5a8..1ac2d416458a 100644 --- a/eng/tools/spec-gen-sdk-runner/package.json +++ b/eng/tools/spec-gen-sdk-runner/package.json @@ -24,13 +24,13 @@ "devDependencies": { "@eslint/js": "^9.21.0", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", + "@vitest/coverage-v8": "^3.1.2", "eslint": "^9.21.0", - "eslint-plugin-unicorn": "^59.0.0", - "prettier": "~3.5.3", + "eslint-plugin-unicorn": "^60.0.0", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "typescript-eslint": "^8.26.0", - "vitest": "^3.0.7" + "vitest": "^3.1.2" } } diff --git a/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts b/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts index 3dbda439e9ac..fc42f69a1b4b 100644 --- a/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts +++ b/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts @@ -280,6 +280,7 @@ export function getBreakingChangeInfo(executionReport: any): boolean { * @param result - The spec-gen-sdk execution result. * @param hasBreakingChange - A flag indicating whether there are breaking changes. * @param hasManagementPlaneSpecs - A flag indicating whether there are management plane specs. + * @param hasTypeSpecProjects - A flag indicating whether there are TypeSpec projects. * @param stagedArtifactsFolder - The staged artifacts folder. * @param apiViewRequestData - The API view request data. * @param sdkGenerationExecuted - A flag indicating whether the SDK generation was executed. @@ -290,6 +291,7 @@ export function generateArtifact( result: string, hasBreakingChange: boolean, hasManagementPlaneSpecs: boolean, + hasTypeSpecProjects: boolean, stagedArtifactsFolder: string, apiViewRequestData: APIViewRequestData[], sdkGenerationExecuted: boolean = true, @@ -309,6 +311,7 @@ export function generateArtifact( if (sdkGenerationExecuted) { isSpecGenSdkCheckRequired = getRequiredSettingValue( hasManagementPlaneSpecs, + hasTypeSpecProjects, commandInput.sdkLanguage as SdkName, ); } @@ -317,6 +320,7 @@ export function generateArtifact( const artifactInfo: SpecGenSdkArtifactInfo = { language: commandInput.sdkLanguage, result, + headSha: commandInput.specCommitSha, prNumber: commandInput.prNumber, labelAction: hasBreakingChange, isSpecGenSdkCheckRequired, @@ -358,13 +362,19 @@ export function getServiceFolderPath(specConfigPath: string): string { /** * Get the required setting value for the SDK check based on the spec PR types. * @param hasManagementPlaneSpecs - A flag indicating whether there are management plane specs. + * @param hasTypeSpecProjects - A flag indicating whether there are TypeSpec projects. * @param sdkName - The SDK name. * @returns boolean indicating whether the SDK check is required. */ export function getRequiredSettingValue( hasManagementPlaneSpecs: boolean, + hasTypeSpecProjects: boolean, sdkName: SdkName, ): boolean { + // If the SDK is azure-sdk-for-net, return false if there are no TypeSpec projects. + if (sdkName === "azure-sdk-for-net" && !hasTypeSpecProjects) { + return false; + } if (hasManagementPlaneSpecs) { return SpecGenSdkRequiredSettings[sdkName].managementPlane; } else { diff --git a/eng/tools/spec-gen-sdk-runner/src/commands.ts b/eng/tools/spec-gen-sdk-runner/src/commands.ts index ad4d542a68c9..07ebe91feb45 100644 --- a/eng/tools/spec-gen-sdk-runner/src/commands.ts +++ b/eng/tools/spec-gen-sdk-runner/src/commands.ts @@ -87,6 +87,7 @@ export async function generateSdkForSpecPr(): Promise { let executionReport; let changedSpecPathText = ""; let hasManagementPlaneSpecs = false; + let hasTypeSpecProjects = false; let overallRunHasBreakingChange = false; let currentRunHasBreakingChange = false; let sdkGenerationExecuted = true; @@ -147,6 +148,9 @@ export async function generateSdkForSpecPr(): Promise { // Read the execution report to aggreate the generation results executionReport = getExecutionReport(commandInput); currentExecutionResult = executionReport.executionResult; + if (executionReport.generateFromTypeSpec) { + hasTypeSpecProjects = true; + } if (executionReport.stagedArtifactsFolder) { stagedArtifactsFolder = executionReport.stagedArtifactsFolder; @@ -181,6 +185,7 @@ export async function generateSdkForSpecPr(): Promise { overallExecutionResult, overallRunHasBreakingChange, hasManagementPlaneSpecs, + hasTypeSpecProjects, stagedArtifactsFolder, apiViewRequestData, sdkGenerationExecuted, diff --git a/eng/tools/spec-gen-sdk-runner/src/types.ts b/eng/tools/spec-gen-sdk-runner/src/types.ts index e50d63947b77..7cdbee04afa6 100644 --- a/eng/tools/spec-gen-sdk-runner/src/types.ts +++ b/eng/tools/spec-gen-sdk-runner/src/types.ts @@ -44,6 +44,7 @@ export type VsoLogs = Map< export interface SpecGenSdkArtifactInfo { language: string; result: string; + headSha: string; prNumber?: string; labelAction?: boolean; isSpecGenSdkCheckRequired: boolean; diff --git a/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts b/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts index 09a42dc67ab5..be8adf2a1321 100644 --- a/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts @@ -421,13 +421,15 @@ describe("commands.ts", () => { runMode: "", localSpecRepoPath: "", localSdkRepoPath: "", + prNumber: "123", sdkRepoName: "", - specCommitSha: "", + specCommitSha: "abc123", specRepoHttpsUrl: "", }; const mockResult = "succeeded"; const mockhasBreakingChange = false; const mockhasManagementPlaneSpecs = false; + const mockhasTypeSpecProjects = false; const mockStagedArtifactsFolder = "mockStagedArtifactsFolder"; const mockApiViewRequestData: APIViewRequestData[] = []; const result = generateArtifact( @@ -435,6 +437,7 @@ describe("commands.ts", () => { mockResult, mockhasBreakingChange, mockhasManagementPlaneSpecs, + mockhasTypeSpecProjects, mockStagedArtifactsFolder, mockApiViewRequestData, ); @@ -455,6 +458,8 @@ describe("commands.ts", () => { { language: "azure-sdk-for-js", result: "succeeded", + headSha: "abc123", + prNumber: "123", labelAction: false, isSpecGenSdkCheckRequired: false, apiViewRequestData: [], @@ -505,6 +510,7 @@ describe("commands.ts", () => { const mockResult = "failed"; const mockhasBreakingChange = false; const mockhasManagementPlaneSpecs = false; + const mockhasTypeSpecProjects = false; const mockStagedArtifactsFolder = ""; const mockApiViewRequestData: APIViewRequestData[] = []; const result = generateArtifact( @@ -512,6 +518,7 @@ describe("commands.ts", () => { mockResult, mockhasBreakingChange, mockhasManagementPlaneSpecs, + mockhasTypeSpecProjects, mockStagedArtifactsFolder, mockApiViewRequestData, ); @@ -551,6 +558,7 @@ describe("commands.ts", () => { // Using true for hasManagementPlaneSpecs, which would normally make isSpecGenSdkCheckRequired=true // for Go SDK (as tested in the getRequiredSettingValue tests) const mockhasManagementPlaneSpecs = true; + const mockhasTypeSpecProjects = true; const mockStagedArtifactsFolder = "mockStagedArtifactsFolder"; const mockApiViewRequestData: APIViewRequestData[] = []; @@ -560,6 +568,7 @@ describe("commands.ts", () => { mockResult, mockhasBreakingChange, mockhasManagementPlaneSpecs, + mockhasTypeSpecProjects, mockStagedArtifactsFolder, mockApiViewRequestData, false, // sdkGenerationExecuted = false @@ -580,6 +589,7 @@ describe("commands.ts", () => { { language: "azure-sdk-for-go", result: "succeeded", + headSha: "", labelAction: false, isSpecGenSdkCheckRequired: false, // This should be false when sdkGenerationExecuted is false apiViewRequestData: [], @@ -593,23 +603,45 @@ describe("commands.ts", () => { describe("getRequiredSettingValue", () => { test("should return managementPlane setting when hasManagementPlaneSpecs is true", () => { - const result = getRequiredSettingValue(true, "azure-sdk-for-go"); + const result = getRequiredSettingValue(true, true, "azure-sdk-for-go"); // Based on the constants in types.ts, Go SDK requires check for management plane expect(result).toBe(true); - const result2 = getRequiredSettingValue(true, "azure-sdk-for-net"); - // Based on the constants in types.ts, .NET SDK does not require check for management plane + const result2 = getRequiredSettingValue(true, true, "azure-sdk-for-net"); + // .NET SDK set (managementPlane: false) expect(result2).toBe(false); }); test("should return dataPlane setting when hasManagementPlaneSpecs is false", () => { - const result = getRequiredSettingValue(false, "azure-sdk-for-go"); + const result = getRequiredSettingValue(false, true, "azure-sdk-for-go"); // Based on the constants in types.ts, Go SDK requires check for data plane expect(result).toBe(true); - const result2 = getRequiredSettingValue(false, "azure-sdk-for-js"); + const result2 = getRequiredSettingValue(false, true, "azure-sdk-for-js"); // Based on the constants in types.ts, JS SDK does not require check for data plane expect(result2).toBe(false); + + const result3 = getRequiredSettingValue(false, true, "azure-sdk-for-net"); + // .NET SDK set (dataplane: false) + expect(result3).toBe(false); + }); + + test("should return false for azure-sdk-for-net when hasTypeSpecProjects is false", () => { + // Test the special case for .NET SDK without TypeSpec projects + const result = getRequiredSettingValue(true, false, "azure-sdk-for-net"); + expect(result).toBe(false); + + const result2 = getRequiredSettingValue(false, false, "azure-sdk-for-net"); + expect(result2).toBe(false); + }); + + test("should follow normal rules for other SDKs when hasTypeSpecProjects is false", () => { + // Other SDKs should follow normal rules regardless of hasTypeSpecProjects + const result = getRequiredSettingValue(true, false, "azure-sdk-for-go"); + expect(result).toBe(true); + + const result2 = getRequiredSettingValue(false, false, "azure-sdk-for-python"); + expect(result2).toBe(true); }); }); }); diff --git a/eng/tools/spec-gen-sdk-runner/test/commands.test.ts b/eng/tools/spec-gen-sdk-runner/test/commands.test.ts index 26c02c92fdee..0b55917251dc 100644 --- a/eng/tools/spec-gen-sdk-runner/test/commands.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/commands.test.ts @@ -241,6 +241,7 @@ describe("generateSdkForSpecPr", () => { "succeeded", // overallExecutionResult false, // overallRunHasBreakingChange true, // hasManagementPlaneSpecs + false, // hasTypeSpecProjects "", // stagedArtifactsFolder [], // apiViewRequestData true, // sdkGenerationExecuted @@ -283,6 +284,7 @@ describe("generateSdkForSpecPr", () => { "succeeded", // overallExecutionResult should be set to "succeeded" false, // overallRunHasBreakingChange false, // hasManagementPlaneSpecs + false, // hasTypeSpecProjects "", // stagedArtifactsFolder [], // apiViewRequestData false, // sdkGenerationExecuted should be set to false @@ -321,6 +323,7 @@ describe("generateSdkForSpecPr", () => { "", // overallExecutionResult is empty because no spec was actually processed false, // overallRunHasBreakingChange false, // hasManagementPlaneSpecs + false, // hasTypeSpecProjects "", // stagedArtifactsFolder [], // apiViewRequestData true, // sdkGenerationExecuted is true because there were some changed specs but they had no valid config diff --git a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.Management/main.tsp b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.Management/main.tsp index 01df2ce5748a..7da56f775ab6 100644 --- a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.Management/main.tsp +++ b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.Management/main.tsp @@ -20,14 +20,10 @@ namespace Microsoft.Contoso; /** The available API versions. */ enum Versions { /** 2021-10-01-preview version */ - @useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) - @useDependency(Azure.Core.Versions.v1_0_Preview_2) @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) v2021_10_01_preview: "2021-10-01-preview", /** 2021-11-01 version */ - @useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) - @useDependency(Azure.Core.Versions.v1_0_Preview_2) @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) v2021_11_01: "2021-11-01", } diff --git a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp index 3caf321e31e4..d87d7d155c9b 100644 --- a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp +++ b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp @@ -17,11 +17,9 @@ namespace Azure.Contoso.WidgetManager; @doc("Versions info.") enum Versions { @doc("The 2022-11-01-preview version.") - @useDependency(Azure.Core.Versions.v1_0_Preview_1) v2022_11_01_Preview: "2022-11-01-preview", @doc("The 2022-12-01 version.") - @useDependency(Azure.Core.Versions.v1_0_Preview_1) v2022_12_01: "2022-12-01", } diff --git a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/main.tsp b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/main.tsp index a30b708fdb62..9b0a5b472e4c 100644 --- a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/main.tsp +++ b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/main.tsp @@ -17,7 +17,6 @@ namespace Widget; @doc("Versions info.") enum Versions { @doc("The 2022-12-01 version.") - @useDependency(Azure.Core.Versions.v1_0_Preview_1) v2022_12_01: "2022-12-01", } diff --git a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/tspconfig.yaml b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/tspconfig.yaml index 2de0c6c6c6cf..a91d8cfee3a7 100644 --- a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/tspconfig.yaml +++ b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/data-plane/widget/tspconfig.yaml @@ -22,10 +22,10 @@ options: generate-sample: true flavor: azure "@azure-tools/typespec-csharp": - package-dir: "Azure.Widget" + emitter-output-dir: "{output-dir}/{service-dir}/{namespace}" clear-output-folder: true model-namespace: false - namespace: "{package-dir}" + namespace: Azure.Widget flavor: azure "@azure-tools/typespec-ts": package-dir: "widget-rest" diff --git a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/resource-manager/Microsoft.Service1/WidgetManagement/main.tsp b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/resource-manager/Microsoft.Service1/WidgetManagement/main.tsp index 6a7f5047f36b..7058d6a23f6e 100644 --- a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/resource-manager/Microsoft.Service1/WidgetManagement/main.tsp +++ b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service1/resource-manager/Microsoft.Service1/WidgetManagement/main.tsp @@ -20,14 +20,10 @@ namespace WidgetManagement; /** The available API versions. */ enum Versions { /** 2021-10-01-preview version */ - @useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) - @useDependency(Azure.Core.Versions.v1_0_Preview_2) @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) v2021_10_01_preview: "2021-10-01-preview", /** 2021-11-01 version */ - @useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) - @useDependency(Azure.Core.Versions.v1_0_Preview_2) @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) v2021_11_01: "2021-11-01", } diff --git a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/data-plane/widget2/main.tsp b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/data-plane/widget2/main.tsp index a30b708fdb62..9b0a5b472e4c 100644 --- a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/data-plane/widget2/main.tsp +++ b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/data-plane/widget2/main.tsp @@ -17,7 +17,6 @@ namespace Widget; @doc("Versions info.") enum Versions { @doc("The 2022-12-01 version.") - @useDependency(Azure.Core.Versions.v1_0_Preview_1) v2022_12_01: "2022-12-01", } diff --git a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/resource-manager/Microsoft.Service2/WidgetManagement2/main.tsp b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/resource-manager/Microsoft.Service2/WidgetManagement2/main.tsp index 6a7f5047f36b..7058d6a23f6e 100644 --- a/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/resource-manager/Microsoft.Service2/WidgetManagement2/main.tsp +++ b/eng/tools/spec-gen-sdk-runner/test/fixtures/specification/service2/resource-manager/Microsoft.Service2/WidgetManagement2/main.tsp @@ -20,14 +20,10 @@ namespace WidgetManagement; /** The available API versions. */ enum Versions { /** 2021-10-01-preview version */ - @useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) - @useDependency(Azure.Core.Versions.v1_0_Preview_2) @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) v2021_10_01_preview: "2021-10-01-preview", /** 2021-11-01 version */ - @useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) - @useDependency(Azure.Core.Versions.v1_0_Preview_2) @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5) v2021_11_01: "2021-11-01", } diff --git a/eng/tools/summarize-impact/package.json b/eng/tools/summarize-impact/package.json index 6eed76eca68d..856c87478956 100644 --- a/eng/tools/summarize-impact/package.json +++ b/eng/tools/summarize-impact/package.json @@ -19,22 +19,21 @@ "@azure-tools/specs-shared": "file:../../../.github/shared", "@azure/openapi-markdown": "0.9.4", "@octokit/rest": "^22.0.0", - "@ts-common/commonmark-to-markdown": "^2.0.2", - "commonmark": "^0.29.0", + "commonmark": "0.31.2", "glob": "^11.0.3", "js-yaml": "^4.1.0", "lodash": "^4.17.20", "simple-git": "^3.27.0" }, "devDependencies": { - "@types/commonmark": "^0.27.4", - "@types/glob": "^8.1.0", + "@types/commonmark": "0.27.10", + "@types/glob": "^9.0.0", "@types/lodash": "^4.14.161", "@types/node": "^20.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" diff --git a/eng/tools/summarize-impact/src/ImpactAssessment.ts b/eng/tools/summarize-impact/src/ImpactAssessment.ts index e603c95cbfd0..e6c331ad052c 100644 --- a/eng/tools/summarize-impact/src/ImpactAssessment.ts +++ b/eng/tools/summarize-impact/src/ImpactAssessment.ts @@ -1,7 +1,6 @@ -import { LabelContext } from "./labelling-types.js"; - export type ImpactAssessment = { resourceManagerRequired: boolean; + dataPlaneRequired: boolean; suppressionReviewRequired: boolean; isNewApiVersion: boolean; rpaasExceptionRequired: boolean; @@ -11,6 +10,5 @@ export type ImpactAssessment = { rpaasRPMissing: boolean; typeSpecChanged: boolean; isDraft: boolean; - labelContext: LabelContext; targetBranch: string; }; diff --git a/eng/tools/summarize-impact/src/cli.ts b/eng/tools/summarize-impact/src/cli.ts index 40599b1de5f7..87c6f9e57c0f 100644 --- a/eng/tools/summarize-impact/src/cli.ts +++ b/eng/tools/summarize-impact/src/cli.ts @@ -1,30 +1,16 @@ -#!/usr/bin/env node - import { getChangedFilesStatuses } from "@azure-tools/specs-shared/changed-files"; import { setOutput } from "@azure-tools/specs-shared/error-reporting"; -import { evaluateImpact } from "./impact.js"; +import { defaultLogger } from "@azure-tools/specs-shared/logger"; +import { evaluateImpact, getRPaaSFolderList } from "./impact.js"; import { getRootFolder } from "@azure-tools/specs-shared/simple-git"; import { Octokit } from "@octokit/rest"; -import fs from "fs"; +import { writeFile } from "fs/promises"; import { parseArgs, ParseArgsConfig } from "node:util"; -import { join, resolve } from "path"; +import { resolve } from "path"; import { LabelContext } from "./labelling-types.js"; import { PRContext } from "./PRContext.js"; -export async function getRoot(inputPath: string): Promise { - try { - const gitRoot = await getRootFolder(inputPath); - return resolve(gitRoot.trim()); - } catch (error) { - console.error( - `Error: Unable to determine the root folder of the git repository.`, - `Please ensure you are running this command within a git repository OR providing a targeted directory that is within a git repo.`, - ); - process.exit(1); - } -} - export async function main() { const config: ParseArgsConfig = { options: { @@ -76,6 +62,7 @@ export async function main() { isDraft: { type: "boolean", multiple: false, + default: false, }, }, allowPositionals: true, @@ -86,9 +73,13 @@ export async function main() { // todo: refactor these opts const sourceDirectory = opts.sourceDirectory as string; const targetDirectory = opts.targetDirectory as string; - const sourceGitRoot = await getRoot(sourceDirectory); - const targetGitRoot = await getRoot(targetDirectory); - const fileList = await getChangedFilesStatuses({ cwd: sourceGitRoot, paths: ["specification"] }); + const sourceGitRoot = await getRootFolder(sourceDirectory); + const targetGitRoot = await getRootFolder(targetDirectory); + const fileList = await getChangedFilesStatuses({ + cwd: sourceGitRoot, + logger: defaultLogger, + paths: ["specification"], + }); const sha = opts.sha as string; const sourceBranch = opts.sourceBranch as string; const targetBranch = opts.targetBranch as string; @@ -111,6 +102,9 @@ export async function main() { }) ).map((label: any) => label.name); + // this is a request to get the list of RPaaS folders from azure-rest-api-specs -> main branch -> dump specification folder names + const mainSpecFolders = await getRPaaSFolderList(github, owner, repo); + const labelContext: LabelContext = { present: new Set(labels), toAdd: new Set(), @@ -128,21 +122,13 @@ export async function main() { isDraft, }); - let impact = await evaluateImpact(prContext, labelContext); - - // sets by default are not serializable, so we need to convert them to arrays - // before we can write them to the output file. - function setReplacer(_key: string, value: any) { - if (value instanceof Set) { - return [...value]; - } - return value; - } + let impact = await evaluateImpact(prContext, labelContext, mainSpecFolders); - console.log("Evaluated impact: ", JSON.stringify(impact, setReplacer, 2)); + console.log("Evaluated impact: ", JSON.stringify(impact, null, 2)); // Write to a temp file that can get picked up later. - const summaryFile = join(process.cwd(), "summary.json"); - fs.writeFileSync(summaryFile, JSON.stringify(impact, setReplacer, 2)); + // Intentionally doesn't use GITHUB_STEP_SUMMARY, since it's not a markdown summary for GH UI + const summaryFile = resolve("summary.json"); + await writeFile(summaryFile, JSON.stringify(impact, null, 2)); setOutput("summary", summaryFile); } diff --git a/eng/tools/summarize-impact/src/diff-types.ts b/eng/tools/summarize-impact/src/diff-types.ts index c34f851a873c..75e15aac7a05 100644 --- a/eng/tools/summarize-impact/src/diff-types.ts +++ b/eng/tools/summarize-impact/src/diff-types.ts @@ -1,5 +1,15 @@ -export type FileTypes = "SwaggerFile" | "TypeSpecFile" | "ExampleFile" | "ReadmeFile"; -export type ChangeTypes = "Addition" | "Deletion" | "Update"; +export enum FileTypes { + ExampleFile = "ExampleFile", + ReadmeFile = "ReadmeFile", + SwaggerFile = "SwaggerFile", + TypeSpecFile = "TypeSpecFile", +} + +export enum ChangeTypes { + Addition = "Addition", + Deletion = "Deletion", + Update = "Update", +} export type PRChange = { fileType: FileTypes; diff --git a/eng/tools/summarize-impact/src/impact.ts b/eng/tools/summarize-impact/src/impact.ts index 3a4389e6d6b0..cb9401e7d64c 100644 --- a/eng/tools/summarize-impact/src/impact.ts +++ b/eng/tools/summarize-impact/src/impact.ts @@ -1,12 +1,13 @@ #!/usr/bin/env node -import * as fs from "fs"; +import { existsSync, readFileSync } from "fs"; import { glob } from "glob"; -import * as path from "path"; +import { dirname, join, resolve } from "path"; import * as commonmark from "commonmark"; import yaml from "js-yaml"; -import * as _ from "lodash"; +import pkg from "lodash"; +const { isEqual } = pkg; import { ChangeHandler, @@ -22,9 +23,12 @@ import { Label, LabelContext, PRType } from "./labelling-types.js"; import { ImpactAssessment } from "./ImpactAssessment.js"; import { PRContext } from "./PRContext.js"; +import { dataPlane, resourceManager } from "@azure-tools/specs-shared/changed-files"; import { Readme } from "@azure-tools/specs-shared/readme"; +import { Octokit } from "@octokit/rest"; // todo: we need to populate this so that we can tell if it's a new APIVersion down stream +// TODO: move to .github/shared export async function isNewApiVersion(context: PRContext): Promise { const handlers: ChangeHandler[] = []; let isAddingNewApiVersion = false; @@ -34,7 +38,7 @@ export async function isNewApiVersion(context: PRContext): Promise { const createSwaggerFileHandler = () => { return (e: PRChange) => { - if (e.changeType === "Addition") { + if (e.changeType === ChangeTypes.Addition) { const apiVersion = getApiVersionFromSwaggerFile(e.filePath); if (apiVersion) { apiVersionSet.add(apiVersion); @@ -44,7 +48,7 @@ export async function isNewApiVersion(context: PRContext): Promise { rpFolders.add(rpFolder); } console.log(`apiVersion: ${apiVersion}, rpFolder: ${rpFolder}`); - } else if (e.changeType === "Update") { + } else if (e.changeType === ChangeTypes.Update) { const rpFolder = getRPFolderFromSwaggerFile(e.filePath); if (rpFolder !== undefined) { rpFolders.add(rpFolder); @@ -67,7 +71,7 @@ export async function isNewApiVersion(context: PRContext): Promise { return false; } - const targetBranchRPFolder = path.resolve(context.targetDirectory, firstRPFolder); + const targetBranchRPFolder = resolve(context.targetDirectory, firstRPFolder); console.log(`targetBranchRPFolder: ${targetBranchRPFolder}`); @@ -87,18 +91,21 @@ export async function isNewApiVersion(context: PRContext): Promise { export async function evaluateImpact( context: PRContext, labelContext: LabelContext, + mainSpecFolders: string[], ): Promise { const typeSpecLabelShouldBePresent = await processTypeSpec(context, labelContext); // examine changed files. if changedpaths includes data-plane, add "data-plane" // same for "resource-manager". We care about whether resourcemanager will be present for a later check - const { resourceManagerLabelShouldBePresent } = await processPRType(context, labelContext); + const { resourceManagerLabelShouldBePresent, dataPlaneShouldBePresent } = await processPRType( + context, + labelContext, + ); // Has to be run in a PR context. Uses addition and update to understand // if the suppressions have been changed. If they have, suppressionReviewRequired must be added // as a label const suppressionRequired = await processSuppression(context, labelContext); - console.log(`suppressionRequired: ${suppressionRequired}`); // needs to examine "after" context to understand if a readme that was changed is RPaaS or not const { rpaasLabelShouldBePresent } = await processRPaaS(context, labelContext); @@ -129,32 +136,33 @@ export async function evaluateImpact( labelContext, resourceManagerLabelShouldBePresent, rpaasLabelShouldBePresent, + mainSpecFolders, ); const newApiVersion = await isNewApiVersion(context); return { - suppressionReviewRequired: labelContext.toAdd.has("suppressionsReviewRequired"), + suppressionReviewRequired: suppressionRequired, rpaasChange: rpaasLabelShouldBePresent, newRP: newRPNamespaceLabelShouldBePresent, rpaasRPMissing: ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, rpaasRpNotInPrivateRepo: ciRpaasRPNotInPrivateRepoLabelShouldBePresent, resourceManagerRequired: resourceManagerLabelShouldBePresent, + dataPlaneRequired: dataPlaneShouldBePresent, rpaasExceptionRequired: rpaasExceptionLabelShouldBePresent, typeSpecChanged: typeSpecLabelShouldBePresent, isNewApiVersion: newApiVersion, isDraft: context.isDraft, - labelContext: labelContext, targetBranch: context.targetBranch, }; } export function isManagementPR(filePaths: string[]): boolean { - return filePaths.some((it) => it.includes("resource-manager")); + return filePaths.some(resourceManager); } export function isDataPlanePR(filePaths: string[]): boolean { - return filePaths.some((it) => it.includes("data-plane")); + return filePaths.some(dataPlane); } export function getAllApiVersionFromRPFolder(rpFolder: string): string[] { @@ -175,7 +183,7 @@ export function getAllApiVersionFromRPFolder(rpFolder: string): string[] { } export function getApiVersionFromSwaggerFile(swaggerFile: string): string | undefined { - const swagger = fs.readFileSync(swaggerFile).toString(); + const swagger = readFileSync(swaggerFile).toString(); const swaggerObject = JSON.parse(swagger); if (swaggerObject["info"] && swaggerObject["info"]["version"]) { return swaggerObject["info"]["version"]; @@ -244,7 +252,10 @@ async function processTypeSpec(ctx: PRContext, labelContext: LabelContext): Prom }; const swaggerFileHandler = () => { return (prChange: PRChange) => { - if (prChange.changeType !== "Deletion" && isSwaggerGeneratedByTypeSpec(prChange.filePath)) { + if ( + prChange.changeType !== ChangeTypes.Deletion && + isSwaggerGeneratedByTypeSpec(prChange.filePath) + ) { typeSpecLabel.shouldBePresent = true; } }; @@ -262,7 +273,7 @@ async function processTypeSpec(ctx: PRContext, labelContext: LabelContext): Prom function isSwaggerGeneratedByTypeSpec(swaggerFilePath: string): boolean { try { - return !!JSON.parse(fs.readFileSync(swaggerFilePath).toString())?.info["x-typespec-generated"]; + return !!JSON.parse(readFileSync(swaggerFilePath).toString())?.info["x-typespec-generated"]; } catch { return false; } @@ -323,22 +334,28 @@ export async function getPRChanges(ctx: PRContext): Promise { } function genChanges(type: FileTypes, diffs: DiffResult) { - newChanges(type, "Addition", diffs.additions); - newChanges(type, "Deletion", diffs.deletions); - newChanges(type, "Update", diffs.changes); + newChanges(type, ChangeTypes.Addition, diffs.additions); + newChanges(type, ChangeTypes.Deletion, diffs.deletions); + newChanges(type, ChangeTypes.Update, diffs.changes); } function genReadmeChanges(readmeDiffs?: DiffResult) { if (readmeDiffs) { - readmeDiffs.additions?.forEach((d) => newChange("ReadmeFile", "Addition", d.readme, d.tags)); - readmeDiffs.changes?.forEach((d) => newChange("ReadmeFile", "Update", d.readme, d.tags)); - readmeDiffs.deletions?.forEach((d) => newChange("ReadmeFile", "Deletion", d.readme, d.tags)); + readmeDiffs.additions?.forEach((d) => + newChange(FileTypes.ReadmeFile, ChangeTypes.Addition, d.readme, d.tags), + ); + readmeDiffs.changes?.forEach((d) => + newChange(FileTypes.ReadmeFile, ChangeTypes.Update, d.readme, d.tags), + ); + readmeDiffs.deletions?.forEach((d) => + newChange(FileTypes.ReadmeFile, ChangeTypes.Deletion, d.readme, d.tags), + ); } } - genChanges("SwaggerFile", ctx.getSwaggerDiffs()); - genChanges("TypeSpecFile", ctx.getTypeSpecDiffs()); - genChanges("ExampleFile", ctx.getExampleDiffs()); + genChanges(FileTypes.SwaggerFile, ctx.getSwaggerDiffs()); + genChanges(FileTypes.TypeSpecFile, ctx.getTypeSpecDiffs()); + genChanges(FileTypes.ExampleFile, ctx.getExampleDiffs()); genReadmeChanges(await ctx.getReadmeDiffs()); console.log("RETURN definition getPRChanges"); @@ -351,19 +368,19 @@ export async function getPRChanges(ctx: PRContext): Promise { async function processPRType( context: PRContext, labelContext: LabelContext, -): Promise<{ resourceManagerLabelShouldBePresent: boolean }> { +): Promise<{ resourceManagerLabelShouldBePresent: boolean; dataPlaneShouldBePresent: boolean }> { console.log("ENTER definition processPRType"); const types: PRType[] = await getPRType(context); const resourceManagerLabelShouldBePresent = processPRTypeLabel( - "resource-manager", + PRType.ResourceManager, types, labelContext, ); - processPRTypeLabel("data-plane", types, labelContext); + const dataPlaneShouldBePresent = processPRTypeLabel(PRType.DataPlane, types, labelContext); console.log("RETURN definition processPRType"); - return { resourceManagerLabelShouldBePresent }; + return { resourceManagerLabelShouldBePresent, dataPlaneShouldBePresent }; } async function getPRType(context: PRContext): Promise { @@ -377,10 +394,10 @@ async function getPRType(context: PRContext): Promise { const prTypes: PRType[] = []; if (changedFilePaths.length > 0) { if (isDataPlanePR(changedFilePaths)) { - prTypes.push("data-plane"); + prTypes.push(PRType.DataPlane); } if (isManagementPR(changedFilePaths)) { - prTypes.push("resource-manager"); + prTypes.push(PRType.ResourceManager); } } console.log("RETURN definition getPRType"); @@ -426,11 +443,11 @@ async function processSuppression(context: PRContext, labelContext: LabelContext const createReadmeFileHandler = () => { return (e: PRChange) => { if ( - (e.changeType === "Addition" && getSuppressions(e.filePath).length) || - (e.changeType === "Update" && + (e.changeType === ChangeTypes.Addition && getSuppressions(e.filePath).length) || + (e.changeType === ChangeTypes.Update && diffSuppression( - path.resolve(context.targetDirectory, e.filePath), - path.resolve(context.sourceDirectory, e.filePath), + resolve(context.targetDirectory, e.filePath), + resolve(context.sourceDirectory, e.filePath), ).length) ) { suppressionReviewRequiredLabel.shouldBePresent = true; @@ -478,7 +495,7 @@ function getSuppressions(readmePath: string) { }; let suppressionResult: any[] = []; try { - const readme = fs.readFileSync(readmePath).toString(); + const readme = readFileSync(readmePath).toString(); const codeBlocks = getAllCodeBlockNodes(new commonmark.Parser().parse(readme)); for (const block of codeBlocks) { if (block.literal) { @@ -507,7 +524,7 @@ export function diffSuppression(readmeBefore: string, readmeAfter: string) { const properties = ["suppress", "from", "where", "code", "reason"]; if ( -1 === - beforeSuppressions.findIndex((s) => properties.every((p) => _.isEqual(s[p], suppression[p]))) + beforeSuppressions.findIndex((s) => properties.every((p) => isEqual(s[p], suppression[p]))) ) { newSuppressions.push(suppression); } @@ -528,8 +545,8 @@ async function processRPaaS( const createReadmeFileHandler = () => { return async (e: PRChange) => { if ( - e.changeType !== "Deletion" && - (await isRPSaaS(path.join(context.sourceDirectory, e.filePath))) + e.changeType !== ChangeTypes.Deletion && + (await isRPSaaS(join(context.sourceDirectory, e.filePath))) ) { rpaasLabel.shouldBePresent = true; } @@ -577,12 +594,12 @@ async function processNewRPNamespace( if (!skip) { const createSwaggerFileHandler = () => { return (e: PRChange) => { - if (e.changeType === "Addition") { - const rpFolder = getRPFolderFromSwaggerFile(path.dirname(e.filePath)); + if (e.changeType === ChangeTypes.Addition) { + const rpFolder = getRPFolderFromSwaggerFile(dirname(e.filePath)); console.log(`Processing newRPNameSpace rpFolder: ${rpFolder}`); if (rpFolder !== undefined) { - const rpFolderFullPath = path.resolve(context.targetDirectory, rpFolder); - if (!fs.existsSync(rpFolderFullPath)) { + const rpFolderFullPath = resolve(context.targetDirectory, rpFolder); + if (!existsSync(rpFolderFullPath)) { console.log(`Adding newRPNameSpace rpFolder: ${rpFolder}`); newRPNamespaceLabel.shouldBePresent = true; } @@ -661,12 +678,64 @@ async function processNewRpNamespaceWithoutRpaasLabel( }; } -// CODESYNC: see entries for related labels in https://github.com/Azure/azure-rest-api-specs/blob/main/.github.amrom.workers.devment.yml +export const getRPaaSFolderList = async ( + client: Octokit, + owner: string, + repoName: string, +): Promise => { + const branch = "main"; + const folder = "specification"; + + const res = await client.rest.repos.getContent({ + owner, + repo: repoName, + path: folder, + ref: branch, + }); + + console.log( + `Get RPSaaS folder list from ${owner}/${repoName}/${folder} successfully. status: ${res.status}`, + ); + + // Extract folder names from the response + if (Array.isArray(res.data)) { + const folderNames = res.data + .filter((item: any) => item.type === "dir") // Only get directories + .map((item: any) => item.name); // Extract the name property + + console.log(`Found ${folderNames.length} folders: ${folderNames.join(", ")}`); + return folderNames; + } + + console.log("No folders found or unexpected response format"); + return []; +}; + +export function getRPRootFolderName(swaggerFile: string): string | undefined { + const RPFolder = getRPFolderFromSwaggerFile(swaggerFile); + const dirList = RPFolder?.split("/"); + let idx = 0; + //find the index of "specification" in the path + for (let i = 0; i < dirList!.length; i++) { + if (dirList![i] === "specification") { + idx = i; + break; + } + } + // The RP root folder name is the folder name after "specification" + if (idx + 1 < dirList!.length) { + return dirList![idx + 1]; + } + + return undefined; +} + async function processRpaasRpNotInPrivateRepoLabel( context: PRContext, labelContext: LabelContext, resourceManagerLabelShouldBePresent: boolean, rpaasLabelShouldBePresent: boolean, + rpFolderNames: string[], ): Promise<{ ciRpaasRPNotInPrivateRepoLabelShouldBePresent: boolean }> { console.log("ENTER definition processRpaasRpNotInPrivateRepoLabel"); const ciRpaasRPNotInPrivateRepoLabel = new Label( @@ -691,44 +760,38 @@ async function processRpaasRpNotInPrivateRepoLabel( } } - // todo: retrieve the list and populate this value properly. - // if (!skip) { - // // this is a request to get the list of RPaaS folders from azure-rest-api-specs-pr -> RPSaasMaster branch -> dump specification folder - // // names - // const rpaasRPFolderList = await getRPaaSFolderList(); - // const rpFolderNames: string[] = rpaasRPFolderList.map((f) => f.name); - - // console.log(`RPaaS RP folder list: ${rpFolderNames}`); - - // const handlers: ChangeHandler[] = []; - - // const processPrChange = () => { - // return (e: PRChange) => { - // if (e.changeType === "Addition") { - // const rpFolderName = getRPRootFolderName(e.filePath); - // console.log( - // `Processing processRpaasRpNotInPrivateRepoLabel rpFolderName: ${rpFolderName}` - // ); - - // if (rpFolderName === undefined) { - // console.log(`RP folder is undefined for changed file path '${e.filePath}'.`); - // return; - // } - - // if (!rpFolderNames.includes(rpFolderName)) { - // console.log( - // `This RP is RPSaaS RP but could not find rpFolderName: ${rpFolderName} in RPFolderNames: ${rpFolderNames}. ` - // + `Label 'CI-RpaaSRPNotInPrivateRepo' should be present.` - // ); - // ciRpaasRPNotInPrivateRepoLabel.shouldBePresent = true; - // } - // } - // }; - // }; - - // handlers.push({ SwaggerFile: processPrChange() }); - // await processPrChanges(context, handlers); - // } + if (!skip) { + console.log(`RPaaS RP folder list: ${rpFolderNames}`); + + const handlers: ChangeHandler[] = []; + + const processPrChange = () => { + return (e: PRChange) => { + if (e.changeType === ChangeTypes.Addition) { + const rpFolderName = getRPRootFolderName(e.filePath); + console.log( + `Processing processRpaasRpNotInPrivateRepoLabel rpFolderName: ${rpFolderName}`, + ); + + if (rpFolderName === undefined) { + console.log(`RP folder is undefined for changed file path '${e.filePath}'.`); + return; + } + + if (!rpFolderNames.includes(rpFolderName)) { + console.log( + `This RP is RPSaaS RP but could not find rpFolderName: ${rpFolderName} in RPFolderNames: ${rpFolderNames}. ` + + `Label 'CI-RpaaSRPNotInPrivateRepo' should be present.`, + ); + ciRpaasRPNotInPrivateRepoLabel.shouldBePresent = true; + } + } + }; + }; + + handlers.push({ SwaggerFile: processPrChange() }); + await processPrChanges(context, handlers); + } ciRpaasRPNotInPrivateRepoLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); console.log("RETURN definition processRpaasRpNotInPrivateRepoLabel"); diff --git a/eng/tools/summarize-impact/src/labelling-types.ts b/eng/tools/summarize-impact/src/labelling-types.ts index fbd3bccfb616..78fda41fa36d 100644 --- a/eng/tools/summarize-impact/src/labelling-types.ts +++ b/eng/tools/summarize-impact/src/labelling-types.ts @@ -1,4 +1,7 @@ -export type PRType = "resource-manager" | "data-plane"; +export enum PRType { + DataPlane = "data-plane", + ResourceManager = "resource-manager", +} /** * The LabelContext is used by prSummary.ts / summary() and downstream invocations. diff --git a/eng/tools/summarize-impact/src/types.ts b/eng/tools/summarize-impact/src/types.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/eng/tools/summarize-impact/test/cli.test.ts b/eng/tools/summarize-impact/test/cli.test.ts index 2c5715e0b2b1..1e44c8e87f8f 100644 --- a/eng/tools/summarize-impact/test/cli.test.ts +++ b/eng/tools/summarize-impact/test/cli.test.ts @@ -3,8 +3,9 @@ import { describe, expect, it } from "vitest"; //vi import path from "path"; import { getChangedFilesStatuses } from "@azure-tools/specs-shared/changed-files"; +import { Octokit } from "@octokit/rest"; import { PRContext } from "../src/PRContext.js"; -import { evaluateImpact } from "../src/impact.js"; +import { evaluateImpact, getRPaaSFolderList } from "../src/impact.js"; import { LabelContext } from "../src/labelling-types.js"; describe("Check Changes", () => { @@ -29,6 +30,11 @@ describe("Check Changes", () => { toRemove: new Set(), }; + const github = new Octokit({ + ...(process.env.GITHUB_TOKEN && { auth: process.env.GITHUB_TOKEN }), + }); + const rpaaSFolderList = await getRPaaSFolderList(github, "Azure", "azure-rest-api-specs"); + const prContext = new PRContext(sourceDirectory, targetDirectory, labelContext, { sha: "ad7c74cb27d2cf3ba83996aaea36b07caa4d16c8", sourceBranch: "dev/nandiniy/DTLTypeSpec", @@ -40,12 +46,13 @@ describe("Check Changes", () => { isDraft: false, }); - const result = await evaluateImpact(prContext, labelContext); + const result = await evaluateImpact(prContext, labelContext, rpaaSFolderList); expect(result).toBeDefined(); expect(result.typeSpecChanged).toBeTruthy(); - expect(result.labelContext.toAdd.has("resource-manager")).toBeTruthy(); - expect(result.labelContext.toAdd.has("SuppressionReviewRequired")).toBeTruthy(); + expect(result.dataPlaneRequired).toBeFalsy(); + expect(result.resourceManagerRequired).toBeTruthy(); + expect(result.suppressionReviewRequired).toBeTruthy(); expect(changedFileDetails).toBeDefined(); expect(changedFileDetails.total).toEqual(293); } finally { @@ -88,14 +95,12 @@ describe("Check Changes", () => { isDraft: false, }); - const result = await evaluateImpact(prContext, labelContext); + const result = await evaluateImpact(prContext, labelContext, []); expect(result.isNewApiVersion).toBeTruthy(); - expect(result.labelContext.toAdd.has("TypeSpec")).toBeTruthy(); - expect(result.labelContext.toAdd.has("resource-manager")).toBeTruthy(); + expect(result.typeSpecChanged).toBeTruthy(); + expect(result.resourceManagerRequired).toBeTruthy(); expect(result.isNewApiVersion).toBeTruthy(); - expect(result.labelContext.toAdd.has("ARMReview")).toBeTruthy(); - expect(result.labelContext.toAdd.has("RPaaS")).toBeTruthy(); - expect(result.labelContext.toAdd.has("WaitForARMFeedback")).toBeTruthy(); + expect(result.rpaasChange).toBeTruthy(); expect(result).toBeDefined(); } finally { // Restore original directory diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/readme.md new file mode 100644 index 000000000000..080f47e82d94 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/readme.md @@ -0,0 +1,44 @@ +# contosowidgermanager 2 + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +```yaml +suppressions: + - code: AvoidAnonymousTypes +``` + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +openapi-subtype: rpaas +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/readme.md new file mode 100644 index 000000000000..1efd3bcce7ff --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/readme.md @@ -0,0 +1,43 @@ +# contosowidgermanager 2 + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +```yaml +suppressions: + - code: AvoidAnonymousTypes +``` + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp new file mode 100644 index 000000000000..c731eeb01215 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp @@ -0,0 +1,7 @@ +import "./main.tsp"; +import "@azure-tools/typespec-client-generator-core"; + +using Azure.Contoso.WidgetManager; +using Azure.ClientGenerator.Core; + +@@clientName(Widgets, "ContosoWidgets", "csharp"); diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp new file mode 100644 index 000000000000..d87d7d155c9b --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp @@ -0,0 +1,63 @@ +import "@typespec/http"; +import "@typespec/rest"; +import "@typespec/versioning"; +import "@azure-tools/typespec-azure-core"; +import "../Contoso.WidgetManager.Shared"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.Core; + +@useAuth(AadOauth2Auth<["https://contoso.azure.com/.default"]>) +@service(#{ title: "Contoso Widget Manager" }) +@versioned(Contoso.WidgetManager.Versions) +namespace Azure.Contoso.WidgetManager; + +@doc("Versions info.") +enum Versions { + @doc("The 2022-11-01-preview version.") + v2022_11_01_Preview: "2022-11-01-preview", + + @doc("The 2022-12-01 version.") + v2022_12_01: "2022-12-01", +} + +@doc("A widget.") +@resource("widgets") +model WidgetSuite { + @key("widgetName") + @doc("The widget name.") + @visibility(Lifecycle.Read) + name: string; + + @doc("The ID of the widget's manufacturer.") + manufacturerId: string; + + @doc("The faked shared model.") + sharedModel?: FakedSharedModel; +} + +interface Widgets { + @doc("Fetch a Widget by name.") + getWidget is ResourceRead; + + @doc("Gets status of a Widget operation.") + getWidgetOperationStatus is GetResourceOperationStatus; + + @doc("Creates or updates a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + createOrUpdateWidget is StandardResourceOperations.LongRunningResourceCreateOrUpdate; + + @doc("Delete a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + deleteWidget is LongRunningResourceDelete; + + @doc("List Widget resources") + listWidgets is ResourceList< + WidgetSuite, + { + parameters: StandardListQueryParameters; + } + >; +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml new file mode 100644 index 000000000000..3be86af6e452 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml @@ -0,0 +1,54 @@ +parameters: + "service-dir": + default: "sdk/contosowidgetmanager" + "dependencies": + default: "" +emit: + - "@azure-tools/typespec-autorest" +linter: + extends: + - "@azure-tools/typespec-azure-rulesets/data-plane" +options: + "@azure-tools/typespec-autorest": + azure-resource-provider-folder: "data-plane" + emit-lro-options: "none" + emitter-output-dir: "{project-root}/.." + output-file: "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/widgets.json" + "@azure-tools/typespec-python": + package-dir: "azure-contoso-widgetmanager" + namespace: "azure.contoso.widgetmanager" + generate-test: true + generate-sample: true + flavor: azure + "@azure-tools/typespec-csharp": + emitter-output-dir: "{output-dir}/{service-dir}/{namespace}" + clear-output-folder: true + model-namespace: false + namespace: "Azure.Template.Contoso" + flavor: azure + "@azure-typespec/http-client-csharp": + emitter-output-dir: "{output-dir}/{service-dir}/{namespace}" + namespace: Azure.Template.Contoso + model-namespace: false + "@azure-tools/typespec-ts": + package-dir: "contoso-widgetmanager-rest" + package-details: + name: "@azure-rest/contoso-widgetmanager" + flavor: azure + "@azure-tools/typespec-java": + package-dir: "azure-contoso-widgetmanager" + namespace: com.azure.contoso.widgetmanager + flavor: azure + "@azure-tools/typespec-go": + module: "github.com/Azure/azure-sdk-for-go/{service-dir}/{package-dir}" + service-dir: "sdk/contosowidget" + package-dir: "azmanager" + module-version: "0.0.1" + generate-fakes: true + inject-spans: true + single-client: true + slice-elements-byval: true + flavor: azure + "@azure-tools/typespec-client-generator-cli": + additionalDirectories: + - "specification/contosowidgetmanager/Contoso.WidgetManager.Shared/" diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json new file mode 100644 index 000000000000..6ff90650c918 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json @@ -0,0 +1,548 @@ +{ + "swagger": "2.0", + "info": { + "title": "Contoso Widget Manager", + "version": "2022-12-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "AadOauth2Auth": [ + "https://contoso.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "AadOauth2Auth": { + "type": "oauth2", + "description": "The Azure Active Directory OAuth2 Flow", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://contoso.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "tags": [], + "paths": { + "/widgets": { + "get": { + "operationId": "Widgets_ListWidgets", + "description": "List Widget resources", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedWidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_ListWidgets": { + "$ref": "./examples/Widgets_ListWidgetsSample.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_GetWidget", + "description": "Fetch a Widget by name.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Get widget by widget name.": { + "$ref": "./examples/Widgets_GetWidgetSample.json" + } + } + }, + "patch": { + "operationId": "Widgets_CreateOrUpdateWidget", + "description": "Creates or updates a Widget asynchronously.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "resource", + "in": "body", + "description": "The resource instance.", + "required": true, + "schema": { + "$ref": "#/definitions/WidgetSuiteCreateOrUpdate" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "201": { + "description": "The request has succeeded and a new resource has been created as a result.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_CreateOrUpdateWidget": { + "$ref": "./examples/Widgets_CreateOrUpdateWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + }, + "delete": { + "operationId": "Widgets_DeleteWidget", + "description": "Delete a Widget asynchronously.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + } + }, + "required": [ + "id", + "status" + ] + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Delete widget by widget name using long-running operation.": { + "$ref": "./examples/Widgets_DeleteWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/widgets/{widgetName}/operations/{operationId}": { + "get": { + "operationId": "Widgets_GetWidgetOperationStatus", + "description": "Gets status of a Widget operation.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "operationId", + "in": "path", + "description": "The unique ID of the operation.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + }, + "result": { + "$ref": "#/definitions/WidgetSuite", + "description": "The result of the operation." + } + }, + "required": [ + "id", + "status" + ] + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_GetWidgetOperationStatus": { + "$ref": "./examples/Widgets_GetWidgetOperationStatusSample.json" + } + } + } + } + }, + "definitions": { + "Azure.Core.Foundations.Error": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/Azure.Core.Foundations.Error" + } + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "Azure.Core.Foundations.ErrorResponse": { + "type": "object", + "description": "A response containing error details.", + "properties": { + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "The error object." + } + }, + "required": [ + "error" + ] + }, + "Azure.Core.Foundations.InnerError": { + "type": "object", + "description": "An object containing more specific information about the error. As per Azure REST API guidelines - https://aka.ms/AzureRestApiGuidelines#handling-errors.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "Inner error." + } + } + }, + "Azure.Core.Foundations.OperationState": { + "type": "string", + "description": "Enum describing allowed operation states.", + "enum": [ + "NotStarted", + "Running", + "Succeeded", + "Failed", + "Canceled" + ], + "x-ms-enum": { + "name": "OperationState", + "modelAsString": true, + "values": [ + { + "name": "NotStarted", + "value": "NotStarted", + "description": "The operation has not started." + }, + { + "name": "Running", + "value": "Running", + "description": "The operation is in progress." + }, + { + "name": "Succeeded", + "value": "Succeeded", + "description": "The operation has completed successfully." + }, + { + "name": "Failed", + "value": "Failed", + "description": "The operation has failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "The operation has been canceled by the user." + } + ] + } + }, + "FakedSharedModel": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + }, + "required": [ + "tag", + "createdAt" + ] + }, + "FakedSharedModelCreateOrUpdate": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + } + }, + "PagedWidgetSuite": { + "type": "object", + "description": "Paged collection of WidgetSuite items", + "properties": { + "value": { + "type": "array", + "description": "The WidgetSuite items on this page", + "items": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "WidgetSuite": { + "type": "object", + "description": "A widget.", + "properties": { + "name": { + "type": "string", + "description": "The widget name.", + "readOnly": true + }, + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModel", + "description": "The faked shared model." + } + }, + "required": [ + "name", + "manufacturerId" + ] + }, + "WidgetSuiteCreateOrUpdate": { + "type": "object", + "description": "A widget.", + "properties": { + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModelCreateOrUpdate", + "description": "The faked shared model." + } + } + } + }, + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + } + } +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json new file mode 100644 index 000000000000..ab6ef35dc336 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json @@ -0,0 +1,548 @@ +{ + "swagger": "2.0", + "info": { + "title": "Contoso Widget Manager", + "version": "2024-12-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "AadOauth2Auth": [ + "https://contoso.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "AadOauth2Auth": { + "type": "oauth2", + "description": "The Azure Active Directory OAuth2 Flow", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://contoso.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "tags": [], + "paths": { + "/widgets": { + "get": { + "operationId": "Widgets_ListWidgets", + "description": "List Widget resources", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedWidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_ListWidgets": { + "$ref": "./examples/Widgets_ListWidgetsSample.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_GetWidget", + "description": "Fetch a Widget by name.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Get widget by widget name.": { + "$ref": "./examples/Widgets_GetWidgetSample.json" + } + } + }, + "patch": { + "operationId": "Widgets_CreateOrUpdateWidget", + "description": "Creates or updates a Widget asynchronously.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "resource", + "in": "body", + "description": "The resource instance.", + "required": true, + "schema": { + "$ref": "#/definitions/WidgetSuiteCreateOrUpdate" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "201": { + "description": "The request has succeeded and a new resource has been created as a result.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_CreateOrUpdateWidget": { + "$ref": "./examples/Widgets_CreateOrUpdateWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + }, + "delete": { + "operationId": "Widgets_DeleteWidget", + "description": "Delete a Widget asynchronously.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + } + }, + "required": [ + "id", + "status" + ] + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Delete widget by widget name using long-running operation.": { + "$ref": "./examples/Widgets_DeleteWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/widgets/{widgetName}/operations/{operationId}": { + "get": { + "operationId": "Widgets_GetWidgetOperationStatus", + "description": "Gets status of a Widget operation.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "operationId", + "in": "path", + "description": "The unique ID of the operation.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + }, + "result": { + "$ref": "#/definitions/WidgetSuite", + "description": "The result of the operation." + } + }, + "required": [ + "id", + "status" + ] + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_GetWidgetOperationStatus": { + "$ref": "./examples/Widgets_GetWidgetOperationStatusSample.json" + } + } + } + } + }, + "definitions": { + "Azure.Core.Foundations.Error": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/Azure.Core.Foundations.Error" + } + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "Azure.Core.Foundations.ErrorResponse": { + "type": "object", + "description": "A response containing error details.", + "properties": { + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "The error object." + } + }, + "required": [ + "error" + ] + }, + "Azure.Core.Foundations.InnerError": { + "type": "object", + "description": "An object containing more specific information about the error. As per Azure REST API guidelines - https://aka.ms/AzureRestApiGuidelines#handling-errors.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "Inner error." + } + } + }, + "Azure.Core.Foundations.OperationState": { + "type": "string", + "description": "Enum describing allowed operation states.", + "enum": [ + "NotStarted", + "Running", + "Succeeded", + "Failed", + "Canceled" + ], + "x-ms-enum": { + "name": "OperationState", + "modelAsString": true, + "values": [ + { + "name": "NotStarted", + "value": "NotStarted", + "description": "The operation has not started." + }, + { + "name": "Running", + "value": "Running", + "description": "The operation is in progress." + }, + { + "name": "Succeeded", + "value": "Succeeded", + "description": "The operation has completed successfully." + }, + { + "name": "Failed", + "value": "Failed", + "description": "The operation has failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "The operation has been canceled by the user." + } + ] + } + }, + "FakedSharedModel": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + }, + "required": [ + "tag", + "createdAt" + ] + }, + "FakedSharedModelCreateOrUpdate": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + } + }, + "PagedWidgetSuite": { + "type": "object", + "description": "Paged collection of WidgetSuite items", + "properties": { + "value": { + "type": "array", + "description": "The WidgetSuite items on this page", + "items": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "WidgetSuite": { + "type": "object", + "description": "A widget.", + "properties": { + "name": { + "type": "string", + "description": "The widget name.", + "readOnly": true + }, + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModel", + "description": "The faked shared model." + } + }, + "required": [ + "name", + "manufacturerId" + ] + }, + "WidgetSuiteCreateOrUpdate": { + "type": "object", + "description": "A widget.", + "properties": { + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModelCreateOrUpdate", + "description": "The faked shared model." + } + } + } + }, + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + } + } +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/readme.md new file mode 100644 index 000000000000..c68224d67f46 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/readme.md @@ -0,0 +1,69 @@ +# Contoso.WidgetManager + +> see https://aka.ms/autorest + +This is the AutoRest configuration file for Contoso.WidgetManager. + +## Configuration + +### Basic Information + +This is a TypeSpec project so we only want to readme to default the default tag and point to the outputted swagger file. +This is used for some tools such as doc generation and swagger apiview generation it isn't used for SDK code gen as we +use the native TypeSpec code generation configured in the tspconfig.yaml file. + +```yaml +openapi-type: data-plane +tag: package-2022-12-01 +``` + +### Tag: package-2022-12-01 + +These settings apply only when `--tag=package-2022-12-01` is specified on the command line. + +```yaml $(tag) == 'package-2022-12-01' +input-file: + - Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress rules that might be fixed + +These set of linting rules we expect to fixed in typespec-autorest emitter but for now suppressing. +Github issue filed at https://github.com/Azure/typespec-azure/issues/2762 + +```yaml +suppressions: + - code: LroExtension + - code: SchemaTypeAndFormat + - code: PathParameterSchema +``` diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/readme.md new file mode 100644 index 000000000000..90df859dec77 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/readme.md @@ -0,0 +1,44 @@ +# Contoso.WidgetManager + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +```yaml +suppressions: + - code: AvoidAnonymousTypes +``` + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +openapi-subtype: rpaas +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp new file mode 100644 index 000000000000..c731eeb01215 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp @@ -0,0 +1,7 @@ +import "./main.tsp"; +import "@azure-tools/typespec-client-generator-core"; + +using Azure.Contoso.WidgetManager; +using Azure.ClientGenerator.Core; + +@@clientName(Widgets, "ContosoWidgets", "csharp"); diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp new file mode 100644 index 000000000000..d87d7d155c9b --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp @@ -0,0 +1,63 @@ +import "@typespec/http"; +import "@typespec/rest"; +import "@typespec/versioning"; +import "@azure-tools/typespec-azure-core"; +import "../Contoso.WidgetManager.Shared"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.Core; + +@useAuth(AadOauth2Auth<["https://contoso.azure.com/.default"]>) +@service(#{ title: "Contoso Widget Manager" }) +@versioned(Contoso.WidgetManager.Versions) +namespace Azure.Contoso.WidgetManager; + +@doc("Versions info.") +enum Versions { + @doc("The 2022-11-01-preview version.") + v2022_11_01_Preview: "2022-11-01-preview", + + @doc("The 2022-12-01 version.") + v2022_12_01: "2022-12-01", +} + +@doc("A widget.") +@resource("widgets") +model WidgetSuite { + @key("widgetName") + @doc("The widget name.") + @visibility(Lifecycle.Read) + name: string; + + @doc("The ID of the widget's manufacturer.") + manufacturerId: string; + + @doc("The faked shared model.") + sharedModel?: FakedSharedModel; +} + +interface Widgets { + @doc("Fetch a Widget by name.") + getWidget is ResourceRead; + + @doc("Gets status of a Widget operation.") + getWidgetOperationStatus is GetResourceOperationStatus; + + @doc("Creates or updates a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + createOrUpdateWidget is StandardResourceOperations.LongRunningResourceCreateOrUpdate; + + @doc("Delete a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + deleteWidget is LongRunningResourceDelete; + + @doc("List Widget resources") + listWidgets is ResourceList< + WidgetSuite, + { + parameters: StandardListQueryParameters; + } + >; +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml new file mode 100644 index 000000000000..3be86af6e452 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml @@ -0,0 +1,54 @@ +parameters: + "service-dir": + default: "sdk/contosowidgetmanager" + "dependencies": + default: "" +emit: + - "@azure-tools/typespec-autorest" +linter: + extends: + - "@azure-tools/typespec-azure-rulesets/data-plane" +options: + "@azure-tools/typespec-autorest": + azure-resource-provider-folder: "data-plane" + emit-lro-options: "none" + emitter-output-dir: "{project-root}/.." + output-file: "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/widgets.json" + "@azure-tools/typespec-python": + package-dir: "azure-contoso-widgetmanager" + namespace: "azure.contoso.widgetmanager" + generate-test: true + generate-sample: true + flavor: azure + "@azure-tools/typespec-csharp": + emitter-output-dir: "{output-dir}/{service-dir}/{namespace}" + clear-output-folder: true + model-namespace: false + namespace: "Azure.Template.Contoso" + flavor: azure + "@azure-typespec/http-client-csharp": + emitter-output-dir: "{output-dir}/{service-dir}/{namespace}" + namespace: Azure.Template.Contoso + model-namespace: false + "@azure-tools/typespec-ts": + package-dir: "contoso-widgetmanager-rest" + package-details: + name: "@azure-rest/contoso-widgetmanager" + flavor: azure + "@azure-tools/typespec-java": + package-dir: "azure-contoso-widgetmanager" + namespace: com.azure.contoso.widgetmanager + flavor: azure + "@azure-tools/typespec-go": + module: "github.com/Azure/azure-sdk-for-go/{service-dir}/{package-dir}" + service-dir: "sdk/contosowidget" + package-dir: "azmanager" + module-version: "0.0.1" + generate-fakes: true + inject-spans: true + single-client: true + slice-elements-byval: true + flavor: azure + "@azure-tools/typespec-client-generator-cli": + additionalDirectories: + - "specification/contosowidgetmanager/Contoso.WidgetManager.Shared/" diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json new file mode 100644 index 000000000000..6ff90650c918 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json @@ -0,0 +1,548 @@ +{ + "swagger": "2.0", + "info": { + "title": "Contoso Widget Manager", + "version": "2022-12-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "AadOauth2Auth": [ + "https://contoso.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "AadOauth2Auth": { + "type": "oauth2", + "description": "The Azure Active Directory OAuth2 Flow", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://contoso.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "tags": [], + "paths": { + "/widgets": { + "get": { + "operationId": "Widgets_ListWidgets", + "description": "List Widget resources", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedWidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_ListWidgets": { + "$ref": "./examples/Widgets_ListWidgetsSample.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_GetWidget", + "description": "Fetch a Widget by name.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Get widget by widget name.": { + "$ref": "./examples/Widgets_GetWidgetSample.json" + } + } + }, + "patch": { + "operationId": "Widgets_CreateOrUpdateWidget", + "description": "Creates or updates a Widget asynchronously.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "resource", + "in": "body", + "description": "The resource instance.", + "required": true, + "schema": { + "$ref": "#/definitions/WidgetSuiteCreateOrUpdate" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "201": { + "description": "The request has succeeded and a new resource has been created as a result.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_CreateOrUpdateWidget": { + "$ref": "./examples/Widgets_CreateOrUpdateWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + }, + "delete": { + "operationId": "Widgets_DeleteWidget", + "description": "Delete a Widget asynchronously.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + } + }, + "required": [ + "id", + "status" + ] + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Delete widget by widget name using long-running operation.": { + "$ref": "./examples/Widgets_DeleteWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/widgets/{widgetName}/operations/{operationId}": { + "get": { + "operationId": "Widgets_GetWidgetOperationStatus", + "description": "Gets status of a Widget operation.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "operationId", + "in": "path", + "description": "The unique ID of the operation.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + }, + "result": { + "$ref": "#/definitions/WidgetSuite", + "description": "The result of the operation." + } + }, + "required": [ + "id", + "status" + ] + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_GetWidgetOperationStatus": { + "$ref": "./examples/Widgets_GetWidgetOperationStatusSample.json" + } + } + } + } + }, + "definitions": { + "Azure.Core.Foundations.Error": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/Azure.Core.Foundations.Error" + } + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "Azure.Core.Foundations.ErrorResponse": { + "type": "object", + "description": "A response containing error details.", + "properties": { + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "The error object." + } + }, + "required": [ + "error" + ] + }, + "Azure.Core.Foundations.InnerError": { + "type": "object", + "description": "An object containing more specific information about the error. As per Azure REST API guidelines - https://aka.ms/AzureRestApiGuidelines#handling-errors.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "Inner error." + } + } + }, + "Azure.Core.Foundations.OperationState": { + "type": "string", + "description": "Enum describing allowed operation states.", + "enum": [ + "NotStarted", + "Running", + "Succeeded", + "Failed", + "Canceled" + ], + "x-ms-enum": { + "name": "OperationState", + "modelAsString": true, + "values": [ + { + "name": "NotStarted", + "value": "NotStarted", + "description": "The operation has not started." + }, + { + "name": "Running", + "value": "Running", + "description": "The operation is in progress." + }, + { + "name": "Succeeded", + "value": "Succeeded", + "description": "The operation has completed successfully." + }, + { + "name": "Failed", + "value": "Failed", + "description": "The operation has failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "The operation has been canceled by the user." + } + ] + } + }, + "FakedSharedModel": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + }, + "required": [ + "tag", + "createdAt" + ] + }, + "FakedSharedModelCreateOrUpdate": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + } + }, + "PagedWidgetSuite": { + "type": "object", + "description": "Paged collection of WidgetSuite items", + "properties": { + "value": { + "type": "array", + "description": "The WidgetSuite items on this page", + "items": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "WidgetSuite": { + "type": "object", + "description": "A widget.", + "properties": { + "name": { + "type": "string", + "description": "The widget name.", + "readOnly": true + }, + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModel", + "description": "The faked shared model." + } + }, + "required": [ + "name", + "manufacturerId" + ] + }, + "WidgetSuiteCreateOrUpdate": { + "type": "object", + "description": "A widget.", + "properties": { + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModelCreateOrUpdate", + "description": "The faked shared model." + } + } + } + }, + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + } + } +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/readme.md b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/readme.md new file mode 100644 index 000000000000..c68224d67f46 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/readme.md @@ -0,0 +1,69 @@ +# Contoso.WidgetManager + +> see https://aka.ms/autorest + +This is the AutoRest configuration file for Contoso.WidgetManager. + +## Configuration + +### Basic Information + +This is a TypeSpec project so we only want to readme to default the default tag and point to the outputted swagger file. +This is used for some tools such as doc generation and swagger apiview generation it isn't used for SDK code gen as we +use the native TypeSpec code generation configured in the tspconfig.yaml file. + +```yaml +openapi-type: data-plane +tag: package-2022-12-01 +``` + +### Tag: package-2022-12-01 + +These settings apply only when `--tag=package-2022-12-01` is specified on the command line. + +```yaml $(tag) == 'package-2022-12-01' +input-file: + - Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress rules that might be fixed + +These set of linting rules we expect to fixed in typespec-autorest emitter but for now suppressing. +Github issue filed at https://github.com/Azure/typespec-azure/issues/2762 + +```yaml +suppressions: + - code: LroExtension + - code: SchemaTypeAndFormat + - code: PathParameterSchema +``` diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/readme.md new file mode 100644 index 000000000000..ae912e6ab9b0 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/readme.md @@ -0,0 +1,48 @@ +# containerstorage + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +openapi-subtype: rpaas +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +### Tag: package-2021-10-01-preview + +These settings apply only when `--tag=package-2021-10-01-preview` is specified on the command line. + +```yaml $(tag) == 'package-2021-10-01-preview' +input-file: + - Microsoft.Contoso/preview/2021-10-01-preview/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/impact.test.ts b/eng/tools/summarize-impact/test/impact.test.ts new file mode 100644 index 000000000000..36acfd4a0835 --- /dev/null +++ b/eng/tools/summarize-impact/test/impact.test.ts @@ -0,0 +1,207 @@ +import fs from "fs"; +import path from "path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; //vi +import { PRContext } from "../src/PRContext.js"; +import { evaluateImpact } from "../src/impact.js"; +import { LabelContext } from "../src/labelling-types.js"; + +const FIXTURE_DIRECTORY = path.join(__dirname, "fixtures"); +const BEFORE_REPO_FIXTURE = path.join(FIXTURE_DIRECTORY, "default", "before"); +const AFTER_REPO_FIXTURE = path.join(FIXTURE_DIRECTORY, "default", "after"); + +const DEFAULT_MAIN_SPEC_FOLDERS: string[] = fs + .readdirSync(path.join(BEFORE_REPO_FIXTURE, "specification"), { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => path.join(BEFORE_REPO_FIXTURE, "specification", d.name)); + +describe("Impact Detection - default fixture", () => { + let originalCwd: string; + + beforeEach(() => { + // Save the original working directory + originalCwd = process.cwd(); + // Change to the 'after' fixture directory + process.chdir(AFTER_REPO_FIXTURE); + }); + + afterEach(() => { + // Restore the original working directory + process.chdir(originalCwd); + }); + + const testCases = [ + { + description: "Should properly handle no changes", + changedFileDetails: { + additions: [], + modifications: [], + deletions: [], + renames: [], + total: 0, + }, + expectedImpact: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: false, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify typespec changes", + changedFileDetails: { + additions: [], + modifications: ["specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp"], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify when suppression review is required", + changedFileDetails: { + additions: [], + modifications: ["specification/contosowidgetmanager/resource-manager/readme.md"], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: true, + rpaasChange: true, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: false, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify a new API Version", + changedFileDetails: { + additions: [ + "specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json", + ], + modifications: [], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: true, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify an RP that wasn't present before", + changedFileDetails: { + additions: [ + "specification/contosowidgermanager2/resource-manager/readme.md", + "specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", + ], + modifications: [], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: true, + rpaasChange: true, + newRP: true, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: true, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "should properly identify a new RP that isn't of rpaas subtype", + changedFileDetails: { + additions: [ + "specification/contosowidgermanager3/resource-manager/readme.md", + "specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", + ], + modifications: [], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: true, + rpaasChange: false, + newRP: true, + rpaasRPMissing: true, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + ]; + it.each(testCases)("$description", async ({ changedFileDetails, expectedImpact }) => { + const labelContext: LabelContext = { + present: new Set(), + toAdd: new Set(), + toRemove: new Set(), + }; + + const prContext = new PRContext(AFTER_REPO_FIXTURE, BEFORE_REPO_FIXTURE, labelContext, { + sha: "2bd8350d465081401a0f4f03e633eca41f0991de", + sourceBranch: "contosotest", + targetBranch: expectedImpact.targetBranch, + repo: "azure-rest-api-specs", + prNumber: "1", + owner: "Azure", + fileList: changedFileDetails, + isDraft: false, + }); + + const actualImpact = await evaluateImpact(prContext, labelContext, DEFAULT_MAIN_SPEC_FOLDERS); + expect(actualImpact).toEqual(expectedImpact); + }); +}); diff --git a/eng/tools/suppressions/package.json b/eng/tools/suppressions/package.json index e94d0d80cfae..5d3f0f152b25 100644 --- a/eng/tools/suppressions/package.json +++ b/eng/tools/suppressions/package.json @@ -24,10 +24,10 @@ }, "devDependencies": { "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" } } diff --git a/eng/tools/tsp-client-tests/package.json b/eng/tools/tsp-client-tests/package.json index 5951effe30d2..99677d6fe794 100644 --- a/eng/tools/tsp-client-tests/package.json +++ b/eng/tools/tsp-client-tests/package.json @@ -5,10 +5,10 @@ "devDependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", "@types/node": "^20.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "scripts": { "build": "tsc --build", diff --git a/eng/tools/typespec-migration-validation/package.json b/eng/tools/typespec-migration-validation/package.json index b41785aaa4a7..717d803b0f86 100644 --- a/eng/tools/typespec-migration-validation/package.json +++ b/eng/tools/typespec-migration-validation/package.json @@ -18,7 +18,7 @@ "@typescript-eslint/eslint-plugin": "^8.32.1", "@typescript-eslint/parser": "^8.32.1", "eslint": "^9.26.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "typescript": "^5.8.3" }, "scripts": { diff --git a/eng/tools/typespec-migration-validation/scripts/download-main.ps1 b/eng/tools/typespec-migration-validation/scripts/download-main.ps1 index be60fe389cb4..320cbf453d46 100644 --- a/eng/tools/typespec-migration-validation/scripts/download-main.ps1 +++ b/eng/tools/typespec-migration-validation/scripts/download-main.ps1 @@ -1,12 +1,18 @@ param( [string]$swaggerPath, - [string]$callValidation = $false + [string]$reportFile = $null, + [bool]$isRPSaaSMaster = $false ) . $PSScriptRoot/../../../scripts/ChangedFiles-Functions.ps1 function Download-Swagger-InMain($swaggerFolder, $latestCommitId) { # sparce checkout its resource-manager swagger folder, later we also add the data-plane folder - $repoUrl = "https://github.com/Azure/azure-rest-api-specs" + if ($isRPSaaSMaster) { + $repoUrl = "https://github.com/Azure/azure-rest-api-specs-pr" + } + else { + $repoUrl = "https://github.com/Azure/azure-rest-api-specs" + } $repoRoot = git rev-parse --show-toplevel $cloneDir = Join-Path $repoRoot "sparse-spec" if (!(Test-Path $cloneDir)) { @@ -74,7 +80,12 @@ if ($swaggerPath -eq "") { } # Get latest commit id from main branch -$latestCommitId = git ls-remote "https://github.com/Azure/azure-rest-api-specs.git" main | Select-String -Pattern "refs/heads/main" | ForEach-Object { $_.ToString().Split("`t")[0] } +if ($isRPSaaSMaster) { + $latestCommitId = git ls-remote "https://github.com/Azure/azure-rest-api-specs-pr.git" RPSaaSMaster | Select-String -Pattern "refs/heads/RPSaaSMaster" | ForEach-Object { $_.ToString().Split("`t")[0] } +} else { + $latestCommitId = git ls-remote "https://github.com/Azure/azure-rest-api-specs.git" main | Select-String -Pattern "refs/heads/main" | ForEach-Object { $_.ToString().Split("`t")[0] } +} + Write-Host "Latest commit id from main branch: $latestCommitId" $swaggerFolder = "" @@ -95,11 +106,11 @@ if ($swaggerPath.StartsWith("specification")) { $swaggerPath = Join-Path $repoRoot $swaggerPath } -if ($callValidation -eq $true) { - Write-Host "Executing TypeSpec migration validation..." - npx tsmv $swaggerInMain $swaggerPath +if ([string]::IsNullOrEmpty($reportFile)) { + Write-Host "Your next command: npx tsmv $swaggerInMain $swaggerPath --outputFolder {outputFolder}" } -else { - Write-Host "Your next command: npx tsmv $swaggerInMain $swaggerPath {outputFolder}" +else { + Write-Host "Executing TypeSpec migration validation..." + npx tsmv $swaggerInMain $swaggerPath --reportFile $reportFile } diff --git a/eng/tools/typespec-migration-validation/src/commonType.ts b/eng/tools/typespec-migration-validation/src/commonType.ts new file mode 100644 index 000000000000..e67e93be1e5e --- /dev/null +++ b/eng/tools/typespec-migration-validation/src/commonType.ts @@ -0,0 +1,61 @@ +import path from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +import { readFileContent } from './helper.js'; +import { OpenAPI2Document, OpenAPI2Schema } from '@azure-tools/typespec-autorest'; +import { isRef } from './compare.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +export function getCommonTypeDefinition(refPath: string): [OpenAPI2Schema, OpenAPI2Document] | undefined { + const commonTypeInfo = getCommonTypeInfo(refPath); + if (!commonTypeInfo) { + return undefined; + } + const [filePath, definitionName] = commonTypeInfo; + const fileContent = readCommonTypeFile(filePath); + const commonTypeDefinition = fileContent.definitions?.[definitionName]; + if (!commonTypeDefinition) { + return undefined; + } + return [resolveCommonTypeDefinition(commonTypeDefinition, fileContent), fileContent]; +} + +// For all the properties, we need to inline its type if is a reference to another common type, because we cannot resolve it outside the common type file +function resolveCommonTypeDefinition(commonTypeDefinition: OpenAPI2Schema, fileContent: OpenAPI2Document): OpenAPI2Schema { + for (const property in commonTypeDefinition.properties) { + const propertySchema = commonTypeDefinition.properties[property]; + if (isRef(propertySchema)) { + const refPath = propertySchema.$ref; + if (!refPath.startsWith('#/definitions/')) { + console.warn(`Reference ${propertySchema.$ref} is not a local definition, skipping.`); + continue; + } + const refDefinitionName = refPath.replace('#/definitions/', ''); + const refDefinitionSchema = fileContent.definitions?.[refDefinitionName]; + commonTypeDefinition.properties[property] = refDefinitionSchema ? resolveCommonTypeDefinition(refDefinitionSchema, fileContent) : propertySchema; + } + } + return commonTypeDefinition; +} + +function getCommonTypeInfo(refPath: string): [string, string] | undefined { + const matchResult = refPath.match(/\/common-types\/resource-management\/(v\d)\/(.*)#\/definitions\/(.*)/); + if (!matchResult) { + return undefined; + } + const [, version, fileName, definitionName] = matchResult; + return [path.join(__dirname, '..', '..', '..', '..', '..', 'specification', 'common-types', 'resource-management', version, fileName), definitionName]; +} + +const commonTypeFileCache: Record = {}; +function readCommonTypeFile(filePath: string): OpenAPI2Document { + if (commonTypeFileCache[filePath]) { + return commonTypeFileCache[filePath]; + } + const fileContent = readFileContent(filePath); + const jsonContent: OpenAPI2Document = JSON.parse(fileContent); + commonTypeFileCache[filePath] = jsonContent; + return jsonContent; +} \ No newline at end of file diff --git a/eng/tools/typespec-migration-validation/src/compare.ts b/eng/tools/typespec-migration-validation/src/compare.ts new file mode 100644 index 000000000000..1981967e34fd --- /dev/null +++ b/eng/tools/typespec-migration-validation/src/compare.ts @@ -0,0 +1,473 @@ +import { HttpMethod, OpenAPI2Document, OpenAPI2Operation, OpenAPI2Response, OpenAPI2Schema, OpenAPI2SchemaProperty, Ref, Refable } from "@azure-tools/typespec-autorest"; +import { getCommonTypeDefinition } from "./commonType.js"; + +interface Diff { + before: any; + after: any; + level: "warning" | "error"; +} + +/** + * path: path for the same operationId should be the same + * parameters: parameters for the same operationId should have the same count + * pageable: whether the operation is pageable or not for the same operationId should be the same + * longrunning: whether the operation is long-running or not for the same operationId should be the same + * finalstate: whether the final state of the long-running operation is the same for the same operationId + * finalresult: the final result schema of the long-running operation should be the same as the schema of 200 response + * responses: the response codes for the same operationId should have the same count and same response code + * response: the response schema for the same response code should be the same + */ +interface PathDiff extends Diff { + operationId: string; + type: "path" | "parameters" | "pageable" | "longrunning" | "finalstate" | "finalresult" | "responses" | "response"; +} + +/** + * properties: the property names of the definition should be the same + * property: the property should have same schema + * required: the required properties of the definition should be the same + */ +interface DefinitionDiff extends Diff { + name: string; + propertyName?: string; + changeType?: string; + type: "properties" | "property" | "required"; +} + +export function printDiff(diff: PathDiff | DefinitionDiff): string { + if ("operationId" in diff) { + return `| ${diff.type} | ${diff.level} | ${getPathDiffMessage(diff)} ${JSON.stringify(diff.before)} -> ${JSON.stringify(diff.after)} |\n`; + } else { + return `| ${diff.type} | ${diff.level} | ${getDefinitionDiffMessage(diff)} ${JSON.stringify(diff.before)} -> ${JSON.stringify(diff.after)} |\n`; + } +} + +function getPathDiffMessage(diff: PathDiff): string { + switch (diff.type) { + case "path": + return `The path for operation "${diff.operationId}" changed:`; + case "parameters": + return `The number of parameters for operation "${diff.operationId}" changed:`; + case "pageable": + return `The pageable for operation "${diff.operationId}" changed:`; + case "longrunning": + return `The long-running status for operation "${diff.operationId}" changed:`; + case "finalstate": + return `The final state for operation "${diff.operationId}" changed:`; + case "finalresult": + return `The final result schema for operation "${diff.operationId}" changed:`; + case "responses": + return `The response codes for operation "${diff.operationId}" changed:`; + case "response": + return `The response schema for operation "${diff.operationId}" changed:`; + default: + return `The ${diff.type} for operation "${diff.operationId}" changed:`; + } +} + +function getDefinitionDiffMessage(diff: DefinitionDiff): string { + switch (diff.type) { + case "properties": + return `The property names of definition "${diff.name}" changed:`; + case "property": + return `The ${diff.changeType} of property "${diff.propertyName}" in definition "${diff.name}" changed:`; + case "required": + return `The required properties of definition "${diff.name}" changed:`; + } +} + +export function compareDocuments(oldDocument: OpenAPI2Document, newDocument: OpenAPI2Document): (PathDiff | DefinitionDiff)[] { + const diffs: (PathDiff | DefinitionDiff)[] = []; + diffs.push(...comparePaths(oldDocument, newDocument)); + + // If not exists in the new document, it might be an orphaned definition previously + for (const definition in newDocument.definitions ?? {}) { + // If not exists in old document, it might be an anonymous definition previously + if (oldDocument.definitions?.[definition]) { + diffs.push(...compareNamedDefinition(oldDocument.definitions[definition], oldDocument, newDocument.definitions![definition], newDocument, definition)); + } + } + return diffs; +} + +const definitionNameCache: Set = new Set(); +function compareNamedDefinition(oldDefinition: OpenAPI2Schema, oldDocument: OpenAPI2Document, newDefinition: OpenAPI2Schema, newDocument: OpenAPI2Document, name: string): DefinitionDiff[] { + if (definitionNameCache.has(name)) { + console.warn(`Definition "${name}" has been compared before, skipping.`); + return []; + } + definitionNameCache.add(name); + + const diffs: DefinitionDiff[] = []; + + const oldRequired = oldDefinition.required || []; + const newRequired = newDefinition.required || []; + + if (oldRequired.length !== newRequired.length || + !oldRequired.every(item => newRequired.includes(item)) || + !newRequired.every(item => oldRequired.includes(item))) { + diffs.push({ + before: oldRequired, + after: newRequired, + name, + type: "required", + level: "warning", + }); + } + + const oldProperties = getAllProperties(oldDefinition, oldDocument); + const sortedOldProperties = Object.keys(oldProperties).sort().reduce((obj: Record, key) => { + obj[key] = oldProperties[key]; + return obj; + }, {}); + + const newProperties = getAllProperties(newDefinition, newDocument); + const sortedNewProperties = Object.keys(newProperties).sort().reduce((obj: Record, key) => { + obj[key] = newProperties[key]; + return obj; + }, {}); + + // First check if the properties are the same + const oldKeys = Object.keys(sortedOldProperties); + const newKeys = Object.keys(sortedNewProperties); + if (oldKeys.length !== newKeys.length) { + // Check if newKeys has exactly one more key and it's systemData + const isSystemDataOnlyDifference = newKeys.length === oldKeys.length + 1 && + newKeys.filter(key => !oldKeys.includes(key)).length === 1 && + newKeys.filter(key => !oldKeys.includes(key))[0] === 'systemData'; + + diffs.push({ + before: oldKeys, + after: newKeys, + name, + type: "properties", + level: isSystemDataOnlyDifference ? "warning" : "error" + }); + } + for (const key of oldKeys) { + if (!newKeys.includes(key)) { + diffs.push({ + before: oldKeys, + after: newKeys, + name, + type: "properties", + level: "error" + }); + } + } + for (const key of newKeys) { + if (!oldKeys.includes(key)) { + // Check if the only additional key is systemData + const additionalKeys = newKeys.filter(k => !oldKeys.includes(k)); + const isOnlySystemData = additionalKeys.length === 1 && additionalKeys[0] === 'systemData'; + + diffs.push({ + before: oldKeys, + after: newKeys, + name, + type: "properties", + level: isOnlySystemData ? "warning" : "error" + }); + } + } + + // Then check if the property types are the same + for (const key of oldKeys) { + const oldProperty = sortedOldProperties[key]; + const newProperty = sortedNewProperties[key]; + if (oldProperty && newProperty) { + diffs.push(...compareDefinitionProperty(oldProperty, newProperty, oldDocument, newDocument, key, name)); + } + } + + return diffs; +} + +function compareDefinitionProperty(oldProperty: OpenAPI2SchemaProperty, newProperty: OpenAPI2SchemaProperty, oldDocument: OpenAPI2Document, newDocument: OpenAPI2Document, propertyName: string, modelName: string): DefinitionDiff[] { + const oldPropertySchema = resolveCommonType(oldProperty); + const newPropertySchema = resolveCommonType(newProperty); + if (isRef(oldPropertySchema) && isRef(newPropertySchema)) { + if (oldPropertySchema.$ref !== newPropertySchema.$ref) { + return [{ + before: oldPropertySchema.$ref, + after: newPropertySchema.$ref, + name: modelName, + propertyName, + type: "property", + changeType: "reference", + level: "warning" + }]; + } + } + else if (!isRef(oldPropertySchema) && !isRef(newPropertySchema)) { + return compareNamedDefinition(oldPropertySchema, oldDocument, newPropertySchema, newDocument, `${modelName}.${propertyName}`); + } + + return []; +} + +function comparePaths(oldDocument: OpenAPI2Document, newDocument: OpenAPI2Document): PathDiff[] { + const oldOperations = organizeOperationById(oldDocument); + const newOperations = organizeOperationById(newDocument); + + const pathDiffs: PathDiff[] = []; + for (const operationId in oldOperations) { + if (!newOperations[operationId]) { + pathDiffs.push({ + before: oldOperations[operationId][0], + after: null, + operationId, + type: "path", + level: "error" + }); + } + else { + pathDiffs.push(...compareOperation(oldOperations[operationId][1], newOperations[operationId][1], operationId)); + } + } + for (const operationId in newOperations) { + if (!oldOperations[operationId]) { + pathDiffs.push({ + before: null, + after: newOperations[operationId][0], + operationId, + type: "path", + level: "error" + }); + } + } + return pathDiffs; +} + +function compareOperation(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + const pathDiffs: PathDiff[] = []; + + if (oldOperation.parameters.length !== newOperation.parameters.length) { + pathDiffs.push({ + before: oldOperation.parameters.length, + after: newOperation.parameters.length, + operationId: operationId, + type: "parameters", + level: "error" + }); + } + + pathDiffs.push(...comparePagination(oldOperation, newOperation, operationId)); + pathDiffs.push(...compareLongRunning(oldOperation, newOperation, operationId)); + pathDiffs.push(...compareResponses(oldOperation, newOperation, operationId)); + + // TO-DO: Compare parameters in detail + return pathDiffs; +} + +function compareResponses(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + const pathDiffs: PathDiff[] = []; + + const oldResponseCodes = Object.keys(oldOperation.responses ?? {}).filter(code => !code.startsWith("x-")); + const newResponseCodes = Object.keys(newOperation.responses ?? {}).filter(code => !code.startsWith("x-")); + + let sameResponseCodes = true; + for (const code of oldResponseCodes) { + if (!newResponseCodes.includes(code)) { + sameResponseCodes = false; + break; + } + } + for (const code of newResponseCodes) { + if (!oldResponseCodes.includes(code)) { + sameResponseCodes = false; + break; + } + } + + if (!sameResponseCodes) { + pathDiffs.push({ + before: oldResponseCodes, + after: newResponseCodes, + operationId: operationId, + type: "responses", + level: "error" + }); + } + + for (const code of oldResponseCodes) { + pathDiffs.push(...compareResponse(oldOperation.responses?.[code], newOperation.responses?.[code], operationId)); + } + + return pathDiffs; +} + +function compareResponse(oldResponse: Refable | undefined, newResponse: Refable | undefined, operationId: string): PathDiff[] { + let oldSchema = getResponseSchema(oldResponse); + let newSchema = getResponseSchema(newResponse); + if (oldSchema !== newSchema) { + return [{ + before: oldResponse, + after: newResponse, + operationId: operationId, + type: "response", + level: "error" + }]; + } + + return []; +} + +function comparePagination(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + if ((oldOperation["x-ms-pageable"] === undefined && newOperation["x-ms-pageable"] !== undefined) || (oldOperation["x-ms-pageable"] !== undefined && newOperation["x-ms-pageable"] === undefined)) { + return [{ + before: oldOperation["x-ms-pageable"], + after: newOperation["x-ms-pageable"], + operationId: operationId, + type: "pageable", + level: "error" + }]; + } + + return []; +} + +function compareLongRunning(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + const pathDiffs: PathDiff[] = []; + + const oldLongRunning = oldOperation["x-ms-long-running-operation"] === true; + const newLongRunning = newOperation["x-ms-long-running-operation"] === true; + if (oldLongRunning !== newLongRunning) { + pathDiffs.push({ + before: oldLongRunning, + after: newLongRunning, + operationId: operationId, + type: "longrunning", + level: "error" + }); + } + + const oldFinalState = oldOperation["x-ms-long-running-operation-options"]?.["final-state-via"] ?? "location"; + const newFinalState = newOperation["x-ms-long-running-operation-options"]?.["final-state-via"] ?? "location"; + if (oldFinalState !== newFinalState) { + pathDiffs.push({ + before: oldFinalState, + after: newFinalState, + operationId: operationId, + type: "finalstate", + level: "error" + }); + } + + if (oldLongRunning) { + const newFinalResult = newOperation["x-ms-long-running-operation-options"]?.["final-state-schema"]; + const newFinalResultSchema = newFinalResult?.split("/").pop(); + if (newFinalResultSchema !== getResponseSchema(oldOperation.responses?.["200"])) { + pathDiffs.push({ + before: getResponseSchema(oldOperation.responses?.["200"]), + after: newFinalResultSchema, + operationId: operationId, + type: "finalresult", + level: "error" + }); + } + } + + return pathDiffs; +} + +function getResponseSchema(response: Refable | undefined): string | undefined { + if (response && (response as OpenAPI2Response).schema && ((response as OpenAPI2Response).schema as any).$ref) { + const refPath = ((response as OpenAPI2Response).schema as any).$ref; + return refPath.split("/").pop(); + } + + return undefined; +} + + +function organizeOperationById(document: OpenAPI2Document): Record { + function isHttpMethod(key: string): key is HttpMethod { + const httpMethods: HttpMethod[] = [ + "get", + "put", + "post", + "delete", + "options", + "head", + "patch", + "trace", + ]; + return httpMethods.includes(key as HttpMethod); + } + + const operationMap: Record = {}; + if (!document.paths) { + return operationMap; + } + + for (const route in document.paths) { + const pathItem = document.paths[route]; + for (const verb in pathItem) { + if (isHttpMethod(verb)) { + const operation = pathItem[verb]; + if (operation && operation.operationId) { + operationMap[operation.operationId] = [route, operation]; + } + } + } + } + + return operationMap; +} + +function getAllProperties(schema: OpenAPI2Schema, document: OpenAPI2Document, properties: Record = {}): Record { + for (const baseSchema of schema.allOf ?? []) { + if (isRef(baseSchema)) { + const refPath = baseSchema.$ref; + const commonTypeDefinition = getCommonTypeDefinition(refPath); + if (commonTypeDefinition) { + getAllProperties(commonTypeDefinition[0], commonTypeDefinition[1], properties); + } + else { + const baseDefinition = getLocalDefinition(refPath, document); + if (baseDefinition) { + getAllProperties(baseDefinition, document, properties); + } + } + } + else { + if (baseSchema.properties) { + for (const key in baseSchema.properties) { + properties[key] = baseSchema.properties[key]; + } + } + } + } + + if (schema.properties) { + for (const key in schema.properties) { + properties[key] = schema.properties[key]; + } + } + return properties; +} + +function resolveCommonType(property: OpenAPI2SchemaProperty): OpenAPI2SchemaProperty { + if (isRef(property)) { + const { $ref, ...propertyWithoutRef } = property; + const commonTypeDefinition = getCommonTypeDefinition($ref); + if (commonTypeDefinition) { + return { ...propertyWithoutRef, ...commonTypeDefinition[0] }; + } + } + + return property; +} + +function getLocalDefinition(refPath: string, document: OpenAPI2Document): OpenAPI2Schema | undefined { + const definitionName = refPath.split("/").pop(); + if (!document.definitions || !document.definitions[definitionName!]) { + console.warn(`Reference to ${definitionName} cannot be found, skipping.`); + } + return document.definitions?.[definitionName!]; +} + +export function isRef(value: Refable): value is Ref { + return (value as Ref).$ref !== undefined; +} \ No newline at end of file diff --git a/eng/tools/typespec-migration-validation/src/configuration.ts b/eng/tools/typespec-migration-validation/src/configuration.ts index 43ffbb407599..37141bd82263 100644 --- a/eng/tools/typespec-migration-validation/src/configuration.ts +++ b/eng/tools/typespec-migration-validation/src/configuration.ts @@ -2,10 +2,12 @@ interface configuration { ignoreDescription: boolean; enumNameToCamelCase: boolean; ignorePathCase: boolean; + ignoreDefinitionCase: boolean; } export const configuration: configuration = { ignoreDescription: true, enumNameToCamelCase: true, - ignorePathCase: true, // Normalize the path + ignorePathCase: true, // Normalize the path + ignoreDefinitionCase: false, // Sort in "definitions" ignores casing }; diff --git a/eng/tools/typespec-migration-validation/src/document.ts b/eng/tools/typespec-migration-validation/src/document.ts index b355219e236d..13c3819ed7a2 100644 --- a/eng/tools/typespec-migration-validation/src/document.ts +++ b/eng/tools/typespec-migration-validation/src/document.ts @@ -188,7 +188,7 @@ function processResponse(response: OpenAPI2Response): OpenAPI2Response { newResponse.description = "ignore"; if (newResponse.headers) { for (const header in newResponse.headers) { - if (header === "Location" || header === "Retry-After" || header === "Azure-AsyncOperation") { + if (header === "Retry-After") { delete newResponse.headers[header]; } } diff --git a/eng/tools/typespec-migration-validation/src/index.ts b/eng/tools/typespec-migration-validation/src/index.ts index 02f85bdcd75d..2c9934af6c4a 100644 --- a/eng/tools/typespec-migration-validation/src/index.ts +++ b/eng/tools/typespec-migration-validation/src/index.ts @@ -9,7 +9,7 @@ import { generatePrompts } from "./fix/troubleshooting.js"; import { mergeFiles, readFileContent } from "./helper.js"; import { addIgnorePath, processIgnoreList } from "./ignore.js"; import { jsonOutput } from "./jsonOutput.js"; -import { logHeader, logWarning } from "./log.js"; +import { logError, logHeader, logWarning } from "./log.js"; import { findChangedPaths, findDifferences, @@ -18,6 +18,8 @@ import { formatDifferenceReport, formatModifiedValuesReport, } from "./summary.js"; +import { compareDocuments, printDiff } from "./compare.js"; +import { writeFile } from "fs/promises"; function parseArguments() { return yargs(hideBin(process.argv)) @@ -49,6 +51,18 @@ function parseArguments() { "Compare two swagger specs", ) .example("$0 oldSpecPath newSpecPath", "Compare using positional arguments") + .example( + "$0 --oldPath ./old-spec --newPath ./new-spec --reportFile ./custom-report.md", + "Compare specs with custom report file", + ) + .example( + "$0 --oldPath ./old-spec --newPath ./new-spec --outputFolder ./validation-results", + "Compare specs with custom output folder", + ) + .example( + "$0 --oldPath ./old-spec --newPath ./new-spec --ignoreDefinitionCase", + "Compare specs with case-insensitive definition sorting", + ) .example( "$0 add-ignore --path \"paths['/api/resource'].put.parameters[0].required__added\" --outputFolder ./results", "Add a path to ignore file", @@ -67,6 +81,11 @@ function parseArguments() { alias: "out", describe: "Output folder for analysis results", type: "string", + }) + .option("reportFile", { + alias: "r", + describe: "Path to the report file", + type: "string", }) .option("ignoreDescription", { description: "Ignore description differences", @@ -77,6 +96,10 @@ function parseArguments() { description: "Set case insensitive for the segments before provider, e.g. resourceGroups", type: "boolean", }) + .option("ignoreDefinitionCase", { + description: "Sort definitions case-insensitively. Use this when definitions in Swagger specification is not in PascalCase.", + type: "boolean", + }) .option("jsonOutput", { description: "Also output in JSON format", type: "boolean", @@ -94,9 +117,6 @@ function parseArguments() { if (!argv.newPath && positional.length > 1) { argv.newPath = positional[1]!.toString(); } - if (!argv.outputFolder && positional.length > 2) { - argv.outputFolder = positional[2]!.toString(); - } if (!argv.oldPath || !argv.newPath) { throw new Error("Both oldPath and newPath are required"); @@ -149,26 +169,64 @@ function handleAddIgnore(path: string, outputFolder: string) { process.exit(0); } +/** + * Sorts the definitions object case-insensitively + * @param document OpenAPI document to sort definitions for + * @returns Document with case-insensitively sorted definitions + */ +function sortDefinitionsCaseInsensitive(document: any): any { + if (!document.definitions) { + return document; + } + + const sortedDefinitions: any = {}; + const definitionKeys = Object.keys(document.definitions); + + // Sort keys case-insensitively + const sortedKeys = definitionKeys.sort((a, b) => + a.toLowerCase().localeCompare(b.toLowerCase()) + ); + + // Rebuild definitions object with sorted keys + for (const key of sortedKeys) { + sortedDefinitions[key] = document.definitions[key]; + } + + return { + ...document, + definitions: sortedDefinitions + }; +} + export async function main() { const args = parseArguments(); // If using add-ignore command, the command handler will exit the process - const { oldPath, newPath, outputFolder, ignoreDescription, ignorePathCase } = args; + const { oldPath, newPath, reportFile, outputFolder, ignoreDescription, ignorePathCase, ignoreDefinitionCase } = args; configuration.ignoreDescription = ignoreDescription; if (ignorePathCase !== undefined) { configuration.ignorePathCase = ignorePathCase; } + if (ignoreDefinitionCase !== undefined) { + configuration.ignoreDefinitionCase = ignoreDefinitionCase; + } logHeader(`Processing old swagger from: ${oldPath}...`); const mergedOldfile = mergeFiles(oldPath!); const processedOldFile = processDocument(mergedOldfile); - const sortedOldFile = sortOpenAPIDocument(processedOldFile); + let sortedOldFile = sortOpenAPIDocument(processedOldFile); + if (configuration.ignoreDefinitionCase) { + sortedOldFile = sortDefinitionsCaseInsensitive(sortedOldFile); + } logHeader(`Processing new swagger from: ${newPath}...`); - const newFile = readFileContent(newPath!); - const processedNewFile = processDocument(JSON.parse(newFile.toString())); - const sortedNewFile = sortOpenAPIDocument(processedNewFile); + const newFile = JSON.parse(readFileContent(newPath!).toString()); + const processedNewFile = processDocument(newFile); + let sortedNewFile = sortOpenAPIDocument(processedNewFile); + if (configuration.ignoreDefinitionCase) { + sortedNewFile = sortDefinitionsCaseInsensitive(sortedNewFile); + } logHeader("Comparing old and new Swagger files..."); if (outputFolder) { @@ -192,45 +250,66 @@ export async function main() { JSON.stringify(sortedNewFile, null, 2), ); } + logHeader("Comparing finished."); - let report: string = ""; - const diffForFile = diff(sortedOldFile, sortedNewFile); + let outputMarkdown = ""; + const compareResult = compareDocuments( + mergedOldfile, + newFile, + ); + if (compareResult.length === 0) { + logHeader("No differences found."); + } + else { + outputMarkdown += "| Type | Level | Message |\n"; + outputMarkdown += "| ---- | ----- | ------- |\n"; + for (const diff of compareResult) { + outputMarkdown += printDiff(diff); + } + console.log(outputMarkdown); + } - // // TO-DELETE: Read the diff file from disk - // const diffForFile = JSON.parse(fs.readFileSync(`C:/Users/pashao/GIT/azure-rest-api-specs/specification/agrifood/validation-results/diff.json`, 'utf-8')); + if (outputFolder) { + let report: string = ""; + const diffForFile = diff(sortedOldFile, sortedNewFile); + if (diffForFile === undefined || Object.keys(diffForFile).length === 0) { + return; + } - const changedPaths = findChangedPaths(diffForFile); - if (changedPaths.length > 0) { - logWarning( - `Found ${changedPaths.length} changed paths in the diff.`, - ); - const changedPathsReport = formatChangedPathsReport(changedPaths); - console.log(changedPathsReport); - report += changedPathsReport; - } + // // TO-DELETE: Read the diff file from disk + // const diffForFile = JSON.parse(fs.readFileSync(`C:/Users/pashao/GIT/azure-rest-api-specs/specification/agrifood/validation-results/diff.json`, 'utf-8')); - const differences = findDifferences(diffForFile); - const differencesReport = formatDifferenceReport(differences); - console.log(differencesReport); - report += differencesReport; + const changedPaths = findChangedPaths(diffForFile); + if (changedPaths.length > 0) { + logWarning( + `Found ${changedPaths.length} changed paths in the diff.`, + ); + const changedPathsReport = formatChangedPathsReport(changedPaths); + report += changedPathsReport; + } - const modifiedValues = findModifiedValues(diffForFile); - const modifiedValuesReport = formatModifiedValuesReport(modifiedValues); - console.log(modifiedValuesReport); - report += modifiedValuesReport; + const differences = findDifferences(diffForFile); + const differencesReport = formatDifferenceReport(differences); + report += differencesReport; - if (outputFolder) { - fs.writeFileSync(`${outputFolder}/diff.json`, JSON.stringify(diffForFile, null, 2)); - fs.writeFileSync(`${outputFolder}/API_CHANGES.md`, report); - logHeader(`Difference report written to ${outputFolder}/API_CHANGES.md`); - - const suggestedPrompt = generatePrompts(diffForFile); - if (suggestedPrompt.length > 0) { - logWarning(`Considering these suggested prompts for the diff:`); - suggestedPrompt.forEach((prompt) => { - console.log(prompt); - }); + const modifiedValues = findModifiedValues(diffForFile); + const modifiedValuesReport = formatModifiedValuesReport(modifiedValues); + report += modifiedValuesReport; + + if (diffForFile) { + fs.writeFileSync(`${outputFolder}/diff.json`, JSON.stringify(diffForFile, null, 2)); + fs.writeFileSync(`${outputFolder}/API_CHANGES.md`, report); + logHeader(`Difference report written to ${outputFolder}/API_CHANGES.md`); + + const suggestedPrompt = generatePrompts(diffForFile); + if (suggestedPrompt.length > 0) { + logWarning(`Considering these suggested prompts for the diff:`); + suggestedPrompt.forEach((prompt) => { + console.log(prompt); + }); + } } + if (args.jsonOutput) { fs.writeFileSync(`${outputFolder}/tsmv_output.json`, JSON.stringify(jsonOutput, null, 2)); logHeader(`JSON output written to ${outputFolder}/tsmv_output.json`); @@ -238,7 +317,14 @@ export async function main() { `---- Start of Json Output ----\n${JSON.stringify(jsonOutput, null, 2)}\n---- End of Json Output ----`, ); } - } else { - console.log(report); + } + else if (reportFile) { + if (compareResult.length > 0) { + await writeFile(reportFile, outputMarkdown); + } + if (compareResult.filter((x) => x.level === "error").length > 0) { + logError("Differences found. Please fix the issues before proceeding."); + process.exit(1); + } } } diff --git a/eng/tools/typespec-requirement/package.json b/eng/tools/typespec-requirement/package.json index 31905ba7ada5..fa1b47aa1b52 100644 --- a/eng/tools/typespec-requirement/package.json +++ b/eng/tools/typespec-requirement/package.json @@ -5,10 +5,10 @@ "devDependencies": { "@types/node": "^20.0.0", "execa": "^9.3.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "scripts": { "build": "tsc --build", diff --git a/eng/tools/typespec-validation/package.json b/eng/tools/typespec-validation/package.json index 0f8d06aa4862..be9b9e24d606 100644 --- a/eng/tools/typespec-validation/package.json +++ b/eng/tools/typespec-validation/package.json @@ -19,11 +19,11 @@ "devDependencies": { "@types/debug": "^4.1.12", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "scripts": { "build": "tsc --build", diff --git a/eng/tools/typespec-validation/src/rules/folder-structure.ts b/eng/tools/typespec-validation/src/rules/folder-structure.ts index 5a777e6db198..95637ed4aee9 100644 --- a/eng/tools/typespec-validation/src/rules/folder-structure.ts +++ b/eng/tools/typespec-validation/src/rules/folder-structure.ts @@ -150,7 +150,7 @@ export class FolderStructureRule implements Rule { if (!serviceRegex.test(serviceFolder)) { success = false; - errorOutput += `Service folder '${serviceFolder}' does not match regex ${serviceRegex}`; + errorOutput += `Service folder '${serviceFolder}' does not match regex ${serviceRegex}. Service folders must use PascalCase without any special characters (e.g. dot, hyphen, underscore).`; } } diff --git a/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts b/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts index 40961fcc6397..c337cc30fd68 100644 --- a/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts +++ b/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts @@ -209,26 +209,48 @@ function skipForNonModularOrDataPlaneInTsEmitter(config: any, folder: string): S // ----- common sub rules ----- export class TspConfigCommonAzServiceDirMatchPatternSubRule extends TspconfigParameterSubRuleBase { constructor() { - super("service-dir", /^sdk\/[^\/]*$/); + super("service-dir", /^(\{output-dir\}\/)?sdk\/[^\/]*$/); } } // ----- Java sub rules ----- -export class TspConfigJavaAzPackageDirectorySubRule extends TspconfigEmitterOptionsSubRuleBase { +export class TspConfigJavaAzEmitterOutputDirMatchPatternSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { - super("@azure-tools/typespec-java", "package-dir", new RegExp(/^azure(-\w+)+$/)); + super( + "@azure-tools/typespec-java", + "emitter-output-dir", + new RegExp(/^(\{output-dir\}\/)?\{service-dir\}\/azure(-\w+)+$/), + ); + } + + protected validate(config: any): RuleResult { + const option = this.tryFindOption(config); + if (option === undefined) { + // at present, we don't require service use emitter-output-dir + return { success: true }; + } + return super.validate(config); } } -export class TspConfigJavaMgmtPackageDirFormatSubRule extends TspconfigEmitterOptionsSubRuleBase { +export class TspConfigJavaMgmtEmitterOutputDirMatchPatternSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { super( "@azure-tools/typespec-java", - "package-dir", - new RegExp(/^azure-resourcemanager-[^\/]+$/), // Matches "azure-resourcemanager-" with no restriction on characters after the hyphen + "emitter-output-dir", + new RegExp(/^(\{output-dir\}\/)?\{service-dir\}\/azure-resourcemanager(-\w+)+$/), ); } + protected validate(config: any): RuleResult { + const option = this.tryFindOption(config); + if (option === undefined) { + // at present, we don't require service use emitter-output-dir + return { success: true }; + } + return super.validate(config); + } + protected skip(_: any, folder: string) { return skipForDataPlane(folder); // Ensures this rule only applies to management plane SDKs } @@ -358,7 +380,11 @@ export class TspConfigGoDpModuleMatchPatternSubRule extends TspconfigEmitterOpti // ----- Go Mgmt plane sub rules ----- export class TspConfigGoMgmtServiceDirMatchPatternSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { - super("@azure-tools/typespec-go", "service-dir", new RegExp(/^sdk\/resourcemanager\/[^\/]*$/)); + super( + "@azure-tools/typespec-go", + "service-dir", + new RegExp(/^(\{output-dir\}\/)?sdk\/resourcemanager\/[^\/]*$/), + ); } protected skip(_: any, folder: string) { return skipForDataPlane(folder); @@ -387,15 +413,6 @@ export class TspConfigGoMgmtModuleEqualStringSubRule extends TspconfigEmitterOpt } } -export class TspConfigGoMgmtFixConstStutteringTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { - constructor() { - super("@azure-tools/typespec-go", "fix-const-stuttering", true); - } - protected skip(_: any, folder: string) { - return skipForDataPlane(folder); - } -} - export class TspConfigGoMgmtGenerateSamplesTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { super("@azure-tools/typespec-go", "generate-samples", true); @@ -414,13 +431,17 @@ export class TspConfigGoMgmtHeadAsBooleanTrueSubRule extends TspconfigEmitterOpt } } -// ----- Go az sub rules ----- -export class TspConfigGoAzGenerateFakesTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { +export class TspConfigGoMgmtGenerateFakesTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { super("@azure-tools/typespec-go", "generate-fakes", true); } + protected skip(_: any, folder: string) { + return skipForDataPlane(folder); + } } +// ----- Go az sub rules ----- + export class TspConfigGoAzInjectSpansTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { super("@azure-tools/typespec-go", "inject-spans", true); @@ -475,38 +496,9 @@ export class TspConfigPythonDpPackageDirectorySubRule extends TspconfigEmitterOp } // ----- CSharp sub rules ----- -export class TspConfigCsharpAzPackageDirectorySubRule extends TspconfigEmitterOptionsSubRuleBase { - constructor() { - super("@azure-tools/typespec-csharp", "package-dir", new RegExp(/^Azure\./)); - } -} - -export class TspConfigCsharpAzNamespaceEqualStringSubRule extends TspconfigEmitterOptionsSubRuleBase { +export class TspConfigCsharpAzNamespaceSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { - super("@azure-tools/typespec-csharp", "namespace", "{package-dir}"); - } - override validate(config: any): RuleResult { - const option = this.tryFindOption(config); - - if (option === undefined) - return this.createFailedResult( - `Failed to find "options.${this.emitterName}.${this.keyToValidate}" with expected value "${this.expectedValue}"`, - `Please add "options.${this.emitterName}.${this.keyToValidate}" with expected value "${this.expectedValue}".`, - ); - - const packageDir = config?.options?.[this.emitterName]?.["package-dir"]; - const actualValue = option as unknown as undefined | string | boolean; - if ( - this.validateValue(actualValue, this.expectedValue) || - (packageDir !== undefined && this.validateValue(actualValue, packageDir)) - ) { - return { success: true }; - } - - return this.createFailedResult( - `The value of options.${this.emitterName}.${this.keyToValidate} "${actualValue}" does not match "${this.expectedValue}" or the value of "package-dir" option or parameter`, - `Please update the value of "options.${this.emitterName}.${this.keyToValidate}" to match "${this.expectedValue}" or the value of "package-dir" option or parameter`, - ); + super("@azure-tools/typespec-csharp", "namespace", new RegExp(/^Azure\./)); } } @@ -516,9 +508,9 @@ export class TspConfigCsharpAzClearOutputFolderTrueSubRule extends TspconfigEmit } } -export class TspConfigCsharpMgmtPackageDirectorySubRule extends TspconfigEmitterOptionsSubRuleBase { +export class TspConfigCsharpMgmtNamespaceSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { - super("@azure-tools/typespec-csharp", "package-dir", new RegExp(/^Azure\.ResourceManager\./)); + super("@azure-tools/typespec-csharp", "namespace", new RegExp(/^Azure\.ResourceManager\./)); } protected skip(_: any, folder: string) { return skipForDataPlane(folder); @@ -527,8 +519,8 @@ export class TspConfigCsharpMgmtPackageDirectorySubRule extends TspconfigEmitter export const defaultRules = [ new TspConfigCommonAzServiceDirMatchPatternSubRule(), - new TspConfigJavaAzPackageDirectorySubRule(), - new TspConfigJavaMgmtPackageDirFormatSubRule(), + new TspConfigJavaAzEmitterOutputDirMatchPatternSubRule(), + new TspConfigJavaMgmtEmitterOutputDirMatchPatternSubRule(), new TspConfigJavaMgmtNamespaceFormatSubRule(), new TspConfigTsMgmtModularExperimentalExtensibleEnumsTrueSubRule(), new TspConfigTsMgmtModularPackageDirectorySubRule(), @@ -539,9 +531,8 @@ export const defaultRules = [ new TspConfigGoMgmtServiceDirMatchPatternSubRule(), new TspConfigGoMgmtPackageDirectorySubRule(), new TspConfigGoMgmtModuleEqualStringSubRule(), - new TspConfigGoMgmtFixConstStutteringTrueSubRule(), new TspConfigGoMgmtGenerateSamplesTrueSubRule(), - new TspConfigGoAzGenerateFakesTrueSubRule(), + new TspConfigGoMgmtGenerateFakesTrueSubRule(), new TspConfigGoMgmtHeadAsBooleanTrueSubRule(), new TspConfigGoAzInjectSpansTrueSubRule(), new TspConfigGoDpServiceDirMatchPatternSubRule(), @@ -552,10 +543,9 @@ export const defaultRules = [ new TspConfigPythonDpPackageDirectorySubRule(), new TspConfigPythonMgmtPackageGenerateSampleTrueSubRule(), new TspConfigPythonMgmtPackageGenerateTestTrueSubRule(), - new TspConfigCsharpAzPackageDirectorySubRule(), - new TspConfigCsharpAzNamespaceEqualStringSubRule(), + new TspConfigCsharpAzNamespaceSubRule(), new TspConfigCsharpAzClearOutputFolderTrueSubRule(), - new TspConfigCsharpMgmtPackageDirectorySubRule(), + new TspConfigCsharpMgmtNamespaceSubRule(), ]; export class SdkTspConfigValidationRule implements Rule { @@ -596,7 +586,7 @@ export class SdkTspConfigValidationRule implements Rule { const emitterName = emitterOptionSubRule.getEmitterName(); if (emitterName === "@azure-tools/typespec-csharp" && isSubRuleSuccess === false) { console.warn( - `Validation on option "${emitterOptionSubRule.getPathOfKeyToValidate()}" in "${emitterName}" are failed. However, per ${emitterName}’s decision, we will treat it as passed.`, + `Validation on option "${emitterOptionSubRule.getPathOfKeyToValidate()}" in "${emitterName}" are failed. However, per ${emitterName}’s decision, we will treat it as passed, please refer to https://eng.ms/docs/products/azure-developer-experience/onboard/request-exception`, ); isSubRuleSuccess = true; } @@ -607,7 +597,7 @@ export class SdkTspConfigValidationRule implements Rule { const stdOutputFailedResults = failedResults.length > 0 - ? `${failedResults.map((r) => r.errorOutput).join("\n")}\nPlease see https://aka.ms/azsdk/spec-gen-sdk-config for more info.\nFor additional information on TypeSpec validation, please refer to https://aka.ms/azsdk/specs/typespec-validation` + ? `${failedResults.map((r) => r.errorOutput).join("\n")}\nPlease see https://aka.ms/azsdk/spec-gen-sdk-config for more info.\nFor additional information on TypeSpec validation, please refer to https://aka.ms/azsdk/specs/typespec-validation\nFor exception requests, please refer to https://eng.ms/docs/products/azure-developer-experience/onboard/request-exception` : ""; return { diff --git a/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts b/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts index cff5a059c4fd..6643ccd3d784 100644 --- a/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts +++ b/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts @@ -8,21 +8,20 @@ import { SdkTspConfigValidationRule, TspConfigCommonAzServiceDirMatchPatternSubRule, TspConfigCsharpAzClearOutputFolderTrueSubRule, - TspConfigCsharpAzNamespaceEqualStringSubRule, - TspConfigCsharpAzPackageDirectorySubRule, - TspConfigCsharpMgmtPackageDirectorySubRule, - TspConfigGoAzGenerateFakesTrueSubRule, + TspConfigCsharpAzNamespaceSubRule, + TspConfigCsharpMgmtNamespaceSubRule, TspConfigGoAzInjectSpansTrueSubRule, TspConfigGoDpModuleMatchPatternSubRule, TspConfigGoDpPackageDirectoryMatchPatternSubRule, TspConfigGoDpServiceDirMatchPatternSubRule, - TspConfigGoMgmtFixConstStutteringTrueSubRule, + TspConfigGoMgmtGenerateFakesTrueSubRule, TspConfigGoMgmtGenerateSamplesTrueSubRule, TspConfigGoMgmtHeadAsBooleanTrueSubRule, TspConfigGoMgmtModuleEqualStringSubRule, TspConfigGoMgmtPackageDirectorySubRule, TspConfigGoMgmtServiceDirMatchPatternSubRule, - TspConfigJavaAzPackageDirectorySubRule, + TspConfigJavaAzEmitterOutputDirMatchPatternSubRule, + TspConfigJavaMgmtEmitterOutputDirMatchPatternSubRule, TspConfigJavaMgmtNamespaceFormatSubRule, TspConfigPythonDpPackageDirectorySubRule, TspConfigPythonMgmtNamespaceSubRule, @@ -204,6 +203,14 @@ const commonAzureServiceDirTestCases = createParameterTestCases( [new TspConfigCommonAzServiceDirMatchPatternSubRule()], ); +const commonAzureServiceDirWithOutputDirTestCases = createParameterTestCases( + "", + "service-dir", + "{output-dir}/sdk/aaa", + "{output-dir}/sdka/aaa", + [new TspConfigCommonAzServiceDirMatchPatternSubRule()], +); + const tsManagementExperimentalExtensibleEnumsTestCases = createEmitterOptionTestCases( "@azure-tools/typespec-ts", managementTspconfigFolder, @@ -287,15 +294,6 @@ const goManagementModuleTestCases = createEmitterOptionTestCases( [new TspConfigGoMgmtModuleEqualStringSubRule()], ); -const goManagementFixConstStutteringTestCases = createEmitterOptionTestCases( - "@azure-tools/typespec-go", - managementTspconfigFolder, - "fix-const-stuttering", - true, - false, - [new TspConfigGoMgmtFixConstStutteringTrueSubRule()], -); - const goManagementGenerateExamplesTestCases = createEmitterOptionTestCases( "@azure-tools/typespec-go", managementTspconfigFolder, @@ -311,16 +309,7 @@ const goManagementGenerateFakesTestCases = createEmitterOptionTestCases( "generate-fakes", true, false, - [new TspConfigGoAzGenerateFakesTrueSubRule()], -); - -const goDpGenerateFakesTestCases = createEmitterOptionTestCases( - "@azure-tools/typespec-go", - "", - "generate-fakes", - true, - false, - [new TspConfigGoAzGenerateFakesTrueSubRule()], + [new TspConfigGoMgmtGenerateFakesTrueSubRule()], ); const goManagementHeadAsBooleanTestCases = createEmitterOptionTestCases( @@ -378,13 +367,24 @@ const goDpServiceDirTestCases = createEmitterOptionTestCases( [new TspConfigGoDpServiceDirMatchPatternSubRule()], ); -const javaManagementPackageDirTestCases = createEmitterOptionTestCases( +const javaAzEmitterOutputDirTestCases = createEmitterOptionTestCases( "@azure-tools/typespec-java", "", - "package-dir", - "azure-aaa", + "emitter-output-dir", + "{output-dir}/{service-dir}/azure-aaa", "aaa", - [new TspConfigJavaAzPackageDirectorySubRule()], + [new TspConfigJavaAzEmitterOutputDirMatchPatternSubRule()], + true, +); + +const javaMgmtEmitterOutputDirTestCases = createEmitterOptionTestCases( + "@azure-tools/typespec-java", + managementTspconfigFolder, + "emitter-output-dir", + "{service-dir}/azure-resourcemanager-aaa-bbb", + "azure-aaa", + [new TspConfigJavaMgmtEmitterOutputDirMatchPatternSubRule()], + true, ); const javaMgmtNamespaceTestCases = createEmitterOptionTestCases( @@ -505,60 +505,15 @@ const pythonDpPackageDirTestCases = createEmitterOptionTestCases( [new TspConfigPythonDpPackageDirectorySubRule()], ); -const csharpAzPackageDirTestCases = createEmitterOptionTestCases( - "@azure-tools/typespec-csharp", - "", - "package-dir", - "Azure.AAA", - "AAA", - [new TspConfigCsharpAzPackageDirectorySubRule()], -); - const csharpAzNamespaceTestCases = createEmitterOptionTestCases( "@azure-tools/typespec-csharp", "", "namespace", - "{package-dir}", + "Azure.AAA", "AAA", - [new TspConfigCsharpAzNamespaceEqualStringSubRule()], + [new TspConfigCsharpAzNamespaceSubRule()], ); -const csharpAzNamespaceWithPackageDirTestCases: Case[] = [ - { - description: `Validate csharp\'s option: namespace is equal to {package-dir} and package-dir exists`, - folder: "", - tspconfigContent: createEmitterOptionExample( - "@azure-tools/typespec-csharp", - { key: "namespace", value: "{package-dir}" }, - { key: "package-dir", value: "Azure.AAA" }, - ), - success: true, - subRules: [new TspConfigCsharpAzNamespaceEqualStringSubRule()], - }, - { - description: `Validate csharp\'s option: namespace is equal to package-dir`, - folder: "", - tspconfigContent: createEmitterOptionExample( - "@azure-tools/typespec-csharp", - { key: "namespace", value: "Azure.AAA" }, - { key: "package-dir", value: "Azure.AAA" }, - ), - success: true, - subRules: [new TspConfigCsharpAzNamespaceEqualStringSubRule()], - }, - { - description: `Validate csharp\'s option: namespace is not equal to package-dir`, - folder: "", - tspconfigContent: createEmitterOptionExample( - "@azure-tools/typespec-csharp", - { key: "namespace", value: "namespace" }, - { key: "package-dir", value: "Azure.AAA" }, - ), - success: shouldBeTrueOnFailSubRuleValidation("@azure-tools/typespec-csharp"), - subRules: [new TspConfigCsharpAzNamespaceEqualStringSubRule()], - }, -]; - const csharpAzClearOutputFolderTestCases = createEmitterOptionTestCases( "@azure-tools/typespec-csharp", "", @@ -568,13 +523,13 @@ const csharpAzClearOutputFolderTestCases = createEmitterOptionTestCases( [new TspConfigCsharpAzClearOutputFolderTrueSubRule()], ); -const csharpMgmtPackageDirTestCases = createEmitterOptionTestCases( +const csharpMgmtNamespaceTestCases = createEmitterOptionTestCases( "@azure-tools/typespec-csharp", managementTspconfigFolder, - "package-dir", + "namespace", "Azure.ResourceManager.AAA", "Azure.Management.AAA", - [new TspConfigCsharpMgmtPackageDirectorySubRule()], + [new TspConfigCsharpMgmtNamespaceSubRule()], ); const suppressEntireRuleTestCase: Case = { @@ -619,7 +574,6 @@ options: subRules: [ new TspConfigGoMgmtPackageDirectorySubRule(), new TspConfigGoMgmtModuleEqualStringSubRule(), - new TspConfigGoMgmtFixConstStutteringTrueSubRule(), ], tspconfigContent: ` options: @@ -650,6 +604,7 @@ describe("tspconfig", function () { it.each([ // common ...commonAzureServiceDirTestCases, + ...commonAzureServiceDirWithOutputDirTestCases, // ts ...tsManagementExperimentalExtensibleEnumsTestCases, ...tsManagementPackageDirTestCases, @@ -661,18 +616,17 @@ describe("tspconfig", function () { ...goManagementServiceDirTestCases, ...goManagementPackageDirTestCases, ...goManagementModuleTestCases, - ...goManagementFixConstStutteringTestCases, ...goManagementGenerateExamplesTestCases, ...goManagementGenerateFakesTestCases, ...goManagementHeadAsBooleanTestCases, ...goManagementInjectSpansTestCases, - ...goDpGenerateFakesTestCases, ...goDpInjectSpansTestCases, ...goDpModuleTestCases, ...goDpPackageDirTestCases, ...goDpServiceDirTestCases, // java - ...javaManagementPackageDirTestCases, + ...javaAzEmitterOutputDirTestCases, + ...javaMgmtEmitterOutputDirTestCases, ...javaMgmtNamespaceTestCases, ...javaMgmtNamespaceExtendedTestCases, // python @@ -682,11 +636,10 @@ describe("tspconfig", function () { ...pythonManagementGenerateSampleTestCases, ...pythonDpPackageDirTestCases, // csharp - ...csharpAzPackageDirTestCases, + ...csharpAzNamespaceTestCases, ...csharpAzNamespaceTestCases, ...csharpAzClearOutputFolderTestCases, - ...csharpMgmtPackageDirTestCases, - ...csharpAzNamespaceWithPackageDirTestCases, + ...csharpMgmtNamespaceTestCases, ])(`$description`, async (c: Case) => { readTspConfigSpy.mockImplementation(async (_folder: string) => c.tspconfigContent); vi.spyOn(utils, "getSuppressions").mockImplementation(async (_path: string) => [ diff --git a/package-lock.json b/package-lock.json index 1c9106567e6c..09ad21794f73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,33 +6,36 @@ "": { "name": "azure-rest-api-specs", "devDependencies": { - "@autorest/openapi-to-typespec": "0.11.4", - "@azure-tools/spec-gen-sdk": "~0.8.0", + "@autorest/openapi-to-typespec": "0.11.9", + "@azure-tools/spec-gen-sdk": "~0.9.1", "@azure-tools/specs-shared": "file:.github/shared", "@azure-tools/typespec-apiview": "0.7.2", - "@azure-tools/typespec-autorest": "0.58.1", - "@azure-tools/typespec-azure-core": "0.58.0", - "@azure-tools/typespec-azure-portal-core": "0.58.0", - "@azure-tools/typespec-azure-resource-manager": "0.58.1", - "@azure-tools/typespec-azure-rulesets": "0.58.0", - "@azure-tools/typespec-client-generator-cli": "0.26.0", - "@azure-tools/typespec-client-generator-core": "0.58.0", + "@azure-tools/typespec-autorest": "0.60.0", + "@azure-tools/typespec-azure-core": "0.60.0", + "@azure-tools/typespec-azure-portal-core": "0.60.0", + "@azure-tools/typespec-azure-resource-manager": "0.60.0", + "@azure-tools/typespec-azure-rulesets": "0.60.0", + "@azure-tools/typespec-client-generator-cli": "0.28.1", + "@azure-tools/typespec-client-generator-core": "0.60.0", "@azure-tools/typespec-liftr-base": "0.8.0", "@azure/avocado": "^0.9.1", - "@typespec/compiler": "1.2.1", - "@typespec/events": "0.72.1", - "@typespec/http": "1.2.1", - "@typespec/openapi": "1.2.1", - "@typespec/openapi3": "1.2.1", - "@typespec/prettier-plugin-typespec": "1.2.1", - "@typespec/rest": "0.72.1", - "@typespec/sse": "0.72.1", - "@typespec/streams": "0.72.1", - "@typespec/versioning": "0.72.1", - "@typespec/xml": "0.72.1", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.7", + "@typespec/compiler": "1.4.0", + "@typespec/events": "0.74.0", + "@typespec/http": "1.4.0", + "@typespec/openapi": "1.4.0", + "@typespec/openapi3": "1.4.0", + "@typespec/prettier-plugin-typespec": "1.4.0", + "@typespec/rest": "0.74.0", + "@typespec/sse": "0.74.0", + "@typespec/streams": "0.74.0", + "@typespec/versioning": "0.74.0", + "@typespec/xml": "0.74.0", "azure-rest-api-specs-eng-tools": "file:eng/tools", - "oav": "^3.6.3", - "prettier": "~3.5.3", + "oav": "^4.0.0", + "prettier": "~3.6.2", "typescript": "~5.8.2" }, "engines": { @@ -51,7 +54,6 @@ "simple-git": "^3.27.0" }, "bin": { - "api-doc-preview": "cmd/api-doc-preview.js", "spec-model": "cmd/spec-model.js" }, "devDependencies": { @@ -60,21 +62,21 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", + "@vitest/coverage-v8": "^3.1.2", "cross-env": "^7.0.3", "eslint": "^9.22.0", "globals": "^16.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" } }, ".github/shared/node_modules/marked": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.1.tgz", - "integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.0.tgz", + "integrity": "sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg==", "dev": true, "license": "MIT", "bin": { @@ -108,7 +110,9 @@ "dependencies": { "@apidevtools/json-schema-ref-parser": "^14.1.1", "@azure-tools/specs-shared": "file:../../../.github/shared", - "@microsoft.azure/openapi-validator": "^2.2.4", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.9", "autorest": "^3.7.2", "axios": "^1.8.3", "change-case": "^5.4.4", @@ -124,7 +128,7 @@ "@vitest/coverage-v8": "^3.0.2", "execa": "^9.5.2", "memfs": "^4.17.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.2" @@ -134,15 +138,278 @@ } }, "eng/tools/lint-diff/node_modules/@types/node": { - "version": "18.19.120", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.120.tgz", - "integrity": "sha512-WtCGHFXnVI8WHLxDAt5TbnCM4eSE+nI0QN2NJtwzcgMhht2eNz6V9evJrk+lwC8bCY8OWV5Ym8Jz7ZEyGnKnMA==", + "version": "18.19.123", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", + "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, + "eng/tools/node_modules/@azure/oad": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@azure/oad/-/oad-0.12.0.tgz", + "integrity": "sha512-o7JURA54DgTk0DXDjJuWMUtHBo9Qbhz/OR0Dw+QYwnPzEwdqjWhoe7WBlvtrGu5JhRnjLDfbTA4te0rkWkiwBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ts-common/fs": "^0.2.0", + "@ts-common/iterator": "^0.3.6", + "@ts-common/json": "^0.3.1", + "@ts-common/json-parser": "^0.9.0", + "@ts-common/source-map": "^0.5.0", + "@ts-common/string-map": "^0.3.0", + "acorn": "^5.7.4", + "autorest": "^3.6.1", + "glob": "^7.1.3", + "js-yaml": "^3.13.1", + "json-pointer": "^0.6.2", + "json-refs": "^3.0.15", + "kind-of": "^6.0.3", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "set-value": "^4.1.0", + "source-map": "^0.7.4", + "tslib": "^2.6.3", + "winston": "^3.13.0", + "yargs": "^13.2.2", + "yargs-parser": "^13.1.2" + }, + "bin": { + "oad": "dist/cli.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "eng/tools/node_modules/@azure/oad/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "eng/tools/node_modules/@azure/oad/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "eng/tools/node_modules/@azure/oad/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "eng/tools/node_modules/@microsoft.azure/openapi-validator-rulesets": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@microsoft.azure/openapi-validator-rulesets/-/openapi-validator-rulesets-2.1.9.tgz", + "integrity": "sha512-sn3y0BNTFoH5HwrY7U6l5u1xolLenyc0vWkM1TvDcHlyTJ6ef5MJ42Cu07t/i4H1OIkR6C8v3opxan1FqgO3gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.9", + "@microsoft.azure/openapi-validator-core": "^1.0.0", + "@stoplight/json-ref-resolver": "^3.1.6", + "@stoplight/spectral-core": "^1.18.3", + "@stoplight/spectral-formats": "^1.6.0", + "@stoplight/spectral-functions": "^1.7.2", + "@stoplight/types": "^14.1.1", + "jsonpath-plus": "^8.1.0", + "lodash": "^4.17.21", + "string.prototype.matchall": "^4.0.11", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "eng/tools/node_modules/@microsoft.azure/openapi-validator-rulesets/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "eng/tools/node_modules/@ts-common/iterator": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@ts-common/iterator/-/iterator-0.3.6.tgz", + "integrity": "sha512-nNdcleTj3qLlchH17HI/xqOc6sNgOqJ5DdRR0nOEVdJVZCo5bfqoQTu6+Q9ZwMhuETuR2d86MSlmaL2FVHnPjQ==", + "dev": true, + "license": "Apache-2.0" + }, + "eng/tools/node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "eng/tools/node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -161,9 +428,9 @@ } }, "eng/tools/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -186,6 +453,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "eng/tools/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "eng/tools/node_modules/cliui": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", @@ -201,6 +478,24 @@ "node": ">=20" } }, + "eng/tools/node_modules/commonmark": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.31.2.tgz", + "integrity": "sha512-2fRLTyb9r/2835k5cwcAwOj0DEc44FARnMp5veGsJ+mEAZdi52sNopLu07ZyElQUz058H43whzlERDIaaSw4rg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "entities": "~3.0.1", + "mdurl": "~1.0.1", + "minimist": "~1.2.8" + }, + "bin": { + "commonmark": "bin/commonmark" + }, + "engines": { + "node": "*" + } + }, "eng/tools/node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -208,6 +503,32 @@ "dev": true, "license": "MIT" }, + "eng/tools/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "eng/tools/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "eng/tools/node_modules/glob": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", @@ -232,6 +553,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "eng/tools/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "eng/tools/node_modules/jackspeak": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", @@ -255,6 +586,34 @@ "dev": true, "license": "MIT" }, + "eng/tools/node_modules/jsonpath-plus": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-8.1.0.tgz", + "integrity": "sha512-qVTiuKztFGw0dGhYi3WNqvddx3/SHtyDT0xJaeyz4uP0d1tkpG+0y5uYQ4OcIo1TLAz3PE/qDOW9F0uDt3+CTw==", + "dev": true, + "license": "MIT", + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "eng/tools/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "eng/tools/node_modules/lru-cache": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", @@ -266,9 +625,9 @@ } }, "eng/tools/node_modules/marked": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.1.tgz", - "integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.0.tgz", + "integrity": "sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg==", "dev": true, "license": "MIT", "bin": { @@ -294,6 +653,45 @@ "url": "https://github.com/sponsors/isaacs" } }, + "eng/tools/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "eng/tools/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "eng/tools/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "eng/tools/node_modules/path-scurry": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -311,6 +709,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "eng/tools/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "eng/tools/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -404,7 +812,7 @@ "dependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", "js-yaml": "^4.1.0", - "oav": "^3.5.1", + "oav": "^4.0.0", "simple-git": "^3.27.0" }, "bin": { @@ -412,10 +820,10 @@ }, "devDependencies": { "@types/node": "^20.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -426,18 +834,18 @@ "dev": true, "dependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", - "@azure/oad": "0.10.14" + "@azure/oad": "0.12.0" }, "bin": { "openapi-diff-runner": "cmd/openapi-diff-runner.js" }, "devDependencies": { "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -462,11 +870,11 @@ "@types/debug": "^4.1.12", "@types/lodash": "^4.14.161", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -482,14 +890,14 @@ "devDependencies": { "@eslint/js": "^9.21.0", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", + "@vitest/coverage-v8": "^3.1.2", "eslint": "^9.21.0", - "eslint-plugin-unicorn": "^59.0.0", - "prettier": "~3.5.3", + "eslint-plugin-unicorn": "^60.0.0", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "typescript-eslint": "^8.26.0", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -503,8 +911,7 @@ "@azure-tools/specs-shared": "file:../../../.github/shared", "@azure/openapi-markdown": "0.9.4", "@octokit/rest": "^22.0.0", - "@ts-common/commonmark-to-markdown": "^2.0.2", - "commonmark": "^0.29.0", + "commonmark": "0.31.2", "glob": "^11.0.3", "js-yaml": "^4.1.0", "lodash": "^4.17.20", @@ -514,14 +921,14 @@ "summarize-impact": "cmd/summarize-impact.js" }, "devDependencies": { - "@types/commonmark": "^0.27.4", - "@types/glob": "^8.1.0", + "@types/commonmark": "0.27.10", + "@types/glob": "^9.0.0", "@types/lodash": "^4.14.161", "@types/node": "^20.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -540,11 +947,11 @@ }, "devDependencies": { "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -556,10 +963,10 @@ "devDependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", "@types/node": "^20.0.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -583,7 +990,7 @@ "@typescript-eslint/eslint-plugin": "^8.32.1", "@typescript-eslint/parser": "^8.32.1", "eslint": "^9.26.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "typescript": "^5.8.3" }, "engines": { @@ -591,9 +998,9 @@ } }, "eng/tools/typespec-migration-validation/node_modules/@types/node": { - "version": "18.19.120", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.120.tgz", - "integrity": "sha512-WtCGHFXnVI8WHLxDAt5TbnCM4eSE+nI0QN2NJtwzcgMhht2eNz6V9evJrk+lwC8bCY8OWV5Ym8Jz7ZEyGnKnMA==", + "version": "18.19.123", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", + "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", "dev": true, "license": "MIT", "dependencies": { @@ -606,10 +1013,10 @@ "devDependencies": { "@types/node": "^20.0.0", "execa": "^9.3.0", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -634,11 +1041,11 @@ "devDependencies": { "@types/debug": "^4.1.12", "@types/node": "^20.0.0", - "@vitest/coverage-v8": "^3.0.7", - "prettier": "~3.5.3", + "@vitest/coverage-v8": "^3.1.2", + "prettier": "~3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", - "vitest": "^3.0.7" + "vitest": "^3.1.2" }, "engines": { "node": ">=20.0.0" @@ -659,13 +1066,12 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.1.1.tgz", - "integrity": "sha512-uGF1YGOzzD50L7HLNWclXmsEhQflw8/zZHIz0/AzkJrKL5r9PceUipZxR/cp/8veTk4TVfdDJLyIwXLjaP5ePg==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.2.0.tgz", + "integrity": "sha512-NaGMMWwppbByagq+LwQMq6PMXHFWVu6kSwwx+eJfYTJ5zdpOvb9TIk6ZWxEEeXMUvGdVOZq3JalYsjsTZDvtkA==", "dev": true, "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" }, "engines": { @@ -673,6 +1079,9 @@ }, "funding": { "url": "https://github.com/sponsors/philsturgeon" + }, + "peerDependencies": { + "@types/json-schema": "^7.0.15" } }, "node_modules/@apidevtools/openapi-schemas": { @@ -810,9 +1219,9 @@ } }, "node_modules/@autorest/openapi-to-typespec": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/@autorest/openapi-to-typespec/-/openapi-to-typespec-0.11.4.tgz", - "integrity": "sha512-3yb3C+bPUD+aBKcSMU2FUSVwILJXsKQV18KKbq9kKszI1AL+aaWx+JUSt5lp4KmXZ5MCVAtebjlwa09HroBLkw==", + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@autorest/openapi-to-typespec/-/openapi-to-typespec-0.11.9.tgz", + "integrity": "sha512-vDE0+uj6+krWED6ux0ODGZ/40CDwRmDSv/5MK5918wyr8I7ZywlHH0xFME1XxnGKXaLk1TpEdo84clKL2KO0VA==", "dev": true, "license": "MIT", "dependencies": { @@ -820,13 +1229,29 @@ "@autorest/extension-base": "~3.6.1", "@azure-tools/codegen": "~2.10.1", "@azure-tools/openapi": "~3.6.1", - "@typespec/prettier-plugin-typespec": "^1.2.1", + "@typespec/prettier-plugin-typespec": "^1.3.0", "change-case-all": "~2.1.0", "lodash": "~4.17.20", "pluralize": "^8.0.0", "prettier": "~3.5.3" } }, + "node_modules/@autorest/openapi-to-typespec/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/@autorest/schemas": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/@autorest/schemas/-/schemas-1.3.6.tgz", @@ -978,9 +1403,9 @@ "link": true }, "node_modules/@azure-tools/spec-gen-sdk": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@azure-tools/spec-gen-sdk/-/spec-gen-sdk-0.8.1.tgz", - "integrity": "sha512-Im994/0TxjYouIsu41YFafIgfqM9DsJRrScLK71yn7uXp8LN2rbmWZGcj4nW0DFu39COQoDr8RoYvEa+fThfZw==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@azure-tools/spec-gen-sdk/-/spec-gen-sdk-0.9.1.tgz", + "integrity": "sha512-H6hDGHtbHWl/dldB+y5wrYtx965kYNleDEWFjWvMIS29yYwDCljlo9AAadLCmlXvi46ksXh2nePYxBjeTafv9A==", "dev": true, "license": "MIT", "dependencies": { @@ -1061,55 +1486,55 @@ } }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.58.1", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.58.1.tgz", - "integrity": "sha512-cPQphQRJqK16/eet4t+eTQ5JcXoC0LLd+IBTewuVkTnyLZQBtJOQ6wGDfpjECkvqp69bGd59toaua1atNgSuRQ==", + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.60.0.tgz", + "integrity": "sha512-aIRr1e4g3irkjLTpxqzJ8BFnNFYwj4nlcG6cKGPuhNtiHhJgHjUhLVUNIW1A9O4jx+3RSErL9AkAl1ep+ZbiuA==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@azure-tools/typespec-azure-resource-manager": "^0.58.1", - "@azure-tools/typespec-client-generator-core": "^0.58.0", - "@typespec/compiler": "^1.2.0", - "@typespec/http": "^1.2.0", - "@typespec/openapi": "^1.2.0", - "@typespec/rest": "^0.72.0", - "@typespec/versioning": "^0.72.0" + "@azure-tools/typespec-azure-core": "^0.60.0", + "@azure-tools/typespec-azure-resource-manager": "^0.60.0", + "@azure-tools/typespec-client-generator-core": "^0.60.0", + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0", + "@typespec/openapi": "^1.4.0", + "@typespec/rest": "^0.74.0", + "@typespec/versioning": "^0.74.0" } }, "node_modules/@azure-tools/typespec-azure-core": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.58.0.tgz", - "integrity": "sha512-Z4vX+ic85hCPr27t9DKCVTj2MjHDaXDvh10Z4wVokXIR2/GjAQrHQp4OFu/0R+cwqmuXb6nnuBZNikwKk7dNzw==", + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.60.0.tgz", + "integrity": "sha512-Pmm7blxnEZZ7lhMJWWsiIqMrFthaCK6uu7f+ONN7dq0Mjc/O9w8+43tAIXwnGz1OKAWmiToh3EDbaxeWyt/FhQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.0", - "@typespec/http": "^1.2.0", - "@typespec/rest": "^0.72.0" + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0", + "@typespec/rest": "^0.74.0" } }, "node_modules/@azure-tools/typespec-azure-portal-core": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-portal-core/-/typespec-azure-portal-core-0.58.0.tgz", - "integrity": "sha512-t5yc4PxoyK6LqzihYeHMNpc74kg42rsNFgmBEdSUdMc9ZVDeNSf3d0nER+t0LkbJt/MIbTcPCT5/Eri11Nsnhg==", + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-portal-core/-/typespec-azure-portal-core-0.60.0.tgz", + "integrity": "sha512-s7ksW/P2IZEtbSnZiXk7/0SzoQChKswF3jLCrihSpxvPygykKROYsBMW1xQd4wbii27nZ/9D55VcHr2Vm0gpMg==", "dev": true, "license": "MIT", "peerDependencies": { - "@azure-tools/typespec-azure-resource-manager": "^0.58.0", - "@typespec/compiler": "^1.2.0" + "@azure-tools/typespec-azure-resource-manager": "^0.60.0", + "@typespec/compiler": "^1.4.0" } }, "node_modules/@azure-tools/typespec-azure-resource-manager": { - "version": "0.58.1", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.58.1.tgz", - "integrity": "sha512-gAifEZxuU0ZB00YbxwkM2Y/bktGjGltvbHN1R76wbM68L/JZhVzmOsrZP2nA+n0Diz/SXtZ4LLwpRrL29CD5Iw==", + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.60.0.tgz", + "integrity": "sha512-u0gqgSO5lIcpZN11MBAmRnR7kM1eoBLO8cKB86bXrxLt2vAkYpuL4hWp0A+QJjQy/YI5rrijeBF0visqBJnR4A==", "dev": true, "license": "MIT", "dependencies": { @@ -1120,34 +1545,34 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@typespec/compiler": "^1.2.0", - "@typespec/http": "^1.2.0", - "@typespec/openapi": "^1.2.0", - "@typespec/rest": "^0.72.0", - "@typespec/versioning": "^0.72.0" + "@azure-tools/typespec-azure-core": "^0.60.0", + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0", + "@typespec/openapi": "^1.4.0", + "@typespec/rest": "^0.74.0", + "@typespec/versioning": "^0.74.0" } }, "node_modules/@azure-tools/typespec-azure-rulesets": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.58.0.tgz", - "integrity": "sha512-AkbhFxbeD3mYHbdnQEd1iWhSuQ2KxoN5JOU6BFP7mo/RVjIgGjj2ekro1tCnq381LOXdFfEoJievSFI5fvyRxQ==", + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.60.0.tgz", + "integrity": "sha512-4sx9StBWkmnBfLJ9b23RSwCs0TkTElaU9+6a/cS6JS0F7UggP/KLQd6LG59D0u9ByXM2x9pvYPO8l/K7UOXoPg==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@azure-tools/typespec-azure-resource-manager": "^0.58.0", - "@azure-tools/typespec-client-generator-core": "^0.58.0", - "@typespec/compiler": "^1.2.0" + "@azure-tools/typespec-azure-core": "^0.60.0", + "@azure-tools/typespec-azure-resource-manager": "^0.60.0", + "@azure-tools/typespec-client-generator-core": "^0.60.0", + "@typespec/compiler": "^1.4.0" } }, "node_modules/@azure-tools/typespec-client-generator-cli": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-cli/-/typespec-client-generator-cli-0.26.0.tgz", - "integrity": "sha512-VltnBY0OLk0eU97EpKd6P6HFxkIvEOJvtxZKgpx9oAo8ao7RgCQjVPfBTShpN5jfEtsK/tAVrv9aVGhFGrq0JA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-cli/-/typespec-client-generator-cli-0.28.1.tgz", + "integrity": "sha512-OrC529UNZsMsn6A3kXFyLqWslhP+Ji1lGJna1FhNwfJClO0Pi/6yc3p6lbnMCDAq+gzsYkbfXRTdXpPXfd5m9A==", "dev": true, "license": "MIT", "dependencies": { @@ -1175,9 +1600,9 @@ } }, "node_modules/@azure-tools/typespec-client-generator-core": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.58.0.tgz", - "integrity": "sha512-PK9WjPFXR14hrGyUwhstHuNVC4fkkQeNVHvvYhz3VaP6wh+iD7P3IOeIzzRJv/qSyv82t7BrVXdQAYir434ysw==", + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.60.0.tgz", + "integrity": "sha512-JV9uiqxDCzVVFeMctm4ebmUI4982B3dCdKx0nmonjfRf9a2/MXilEKExPIaNVR3ZqbFPXM/IepheY8t9YZNqSQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1189,16 +1614,16 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@typespec/compiler": "^1.2.0", - "@typespec/events": "^0.72.0", - "@typespec/http": "^1.2.0", - "@typespec/openapi": "^1.2.0", - "@typespec/rest": "^0.72.0", - "@typespec/sse": "^0.72.0", - "@typespec/streams": "^0.72.0", - "@typespec/versioning": "^0.72.0", - "@typespec/xml": "^0.72.0" + "@azure-tools/typespec-azure-core": "^0.60.0", + "@typespec/compiler": "^1.4.0", + "@typespec/events": "^0.74.0", + "@typespec/http": "^1.4.0", + "@typespec/openapi": "^1.4.0", + "@typespec/rest": "^0.74.0", + "@typespec/sse": "^0.74.0", + "@typespec/streams": "^0.74.0", + "@typespec/versioning": "^0.74.0", + "@typespec/xml": "^0.74.0" } }, "node_modules/@azure-tools/typespec-liftr-base": { @@ -1647,278 +2072,11 @@ } }, "node_modules/@azure/ms-rest-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@azure/oad": { - "version": "0.10.14", - "resolved": "https://registry.npmjs.org/@azure/oad/-/oad-0.10.14.tgz", - "integrity": "sha512-lfiIsacGPoRFdossLRPptHw2OW7XBcnLCpZt3bTb/BWTR+3K1/M4t+BUOXB6aKA+iQQjurMkw4iksUWzSUVLnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ts-common/fs": "^0.2.0", - "@ts-common/iterator": "^0.3.6", - "@ts-common/json": "^0.3.1", - "@ts-common/json-parser": "^0.9.0", - "@ts-common/source-map": "^0.5.0", - "@ts-common/string-map": "^0.3.0", - "acorn": "^5.7.4", - "autorest": "^3.6.1", - "glob": "^7.1.3", - "js-yaml": "^3.13.1", - "json-pointer": "^0.6.2", - "json-refs": "^3.0.15", - "kind-of": "^6.0.3", - "lodash": "^4.17.21", - "minimist": "^1.2.8", - "request": "^2.88.0", - "set-value": "^4.1.0", - "shell-quote": "^1.8.3", - "source-map": "^0.7.4", - "tslib": "^2.6.3", - "winston": "^3.13.0", - "yargs": "^13.2.2", - "yargs-parser": "^13.1.2" - }, - "bin": { - "oad": "dist/cli.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/oad/node_modules/@ts-common/iterator": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@ts-common/iterator/-/iterator-0.3.6.tgz", - "integrity": "sha512-nNdcleTj3qLlchH17HI/xqOc6sNgOqJ5DdRR0nOEVdJVZCo5bfqoQTu6+Q9ZwMhuETuR2d86MSlmaL2FVHnPjQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@azure/oad/node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@azure/oad/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@azure/oad/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@azure/oad/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/@azure/oad/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@azure/oad/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@azure/oad/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@azure/oad/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@azure/oad/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@azure/oad/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@azure/oad/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@azure/oad/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@azure/oad/node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/@azure/oad/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@azure/oad/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@azure/oad/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@azure/oad/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/@azure/oad/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" }, "node_modules/@azure/openapi-markdown": { "version": "0.9.4", @@ -2043,13 +2201,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -2083,12 +2241,11 @@ } }, "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.1.90" } @@ -2106,9 +2263,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -2123,9 +2280,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -2140,9 +2297,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -2157,9 +2314,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -2174,9 +2331,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -2191,9 +2348,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -2208,9 +2365,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -2225,9 +2382,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -2242,9 +2399,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -2259,9 +2416,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -2276,9 +2433,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -2293,9 +2450,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -2310,9 +2467,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -2327,9 +2484,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -2344,9 +2501,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -2361,9 +2518,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -2378,9 +2535,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -2395,9 +2552,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -2412,9 +2569,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -2429,9 +2586,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -2446,9 +2603,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -2463,9 +2620,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", "cpu": [ "arm64" ], @@ -2480,9 +2637,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -2497,9 +2654,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -2514,9 +2671,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -2531,9 +2688,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -2605,9 +2762,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2615,9 +2772,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2665,9 +2822,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, "license": "MIT", "engines": { @@ -2688,13 +2845,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -2710,14 +2867,6 @@ "heap": ">= 0.2.0" } }, - "node_modules/@faker-js/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", - "deprecated": "Please update to a newer version.", - "dev": true, - "license": "MIT" - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -2785,13 +2934,13 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", - "integrity": "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.2.tgz", + "integrity": "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", @@ -2810,13 +2959,13 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", - "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.16.tgz", + "integrity": "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/type": "^3.0.8" }, "engines": { @@ -2832,9 +2981,9 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.15", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.15.tgz", - "integrity": "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", + "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", "dev": true, "license": "MIT", "dependencies": { @@ -2860,15 +3009,15 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.15", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.15.tgz", - "integrity": "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ==", + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz", + "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8", - "external-editor": "^3.1.0" + "@inquirer/core": "^10.2.0", + "@inquirer/external-editor": "^1.0.1", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -2883,13 +3032,13 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", - "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.18.tgz", + "integrity": "sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, @@ -2905,6 +3054,28 @@ } } }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", + "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.6.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@inquirer/figures": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", @@ -2916,13 +3087,13 @@ } }, "node_modules/@inquirer/input": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", - "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.2.tgz", + "integrity": "sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/type": "^3.0.8" }, "engines": { @@ -2938,13 +3109,13 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", - "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.18.tgz", + "integrity": "sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/type": "^3.0.8" }, "engines": { @@ -2960,13 +3131,13 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", - "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.18.tgz", + "integrity": "sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2" }, @@ -2983,22 +3154,22 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.7.1.tgz", - "integrity": "sha512-XDxPrEWeWUBy8scAXzXuFY45r/q49R0g72bUzgQXZ1DY/xEFX+ESDMkTQolcb5jRBzaNJX2W8XQl6krMNDTjaA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.4.tgz", + "integrity": "sha512-MuxVZ1en1g5oGamXV3DWP89GEkdD54alcfhHd7InUW5BifAdKQEK9SLFa/5hlWbvuhMPlobF0WAx7Okq988Jxg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.2.0", - "@inquirer/confirm": "^5.1.14", - "@inquirer/editor": "^4.2.15", - "@inquirer/expand": "^4.0.17", - "@inquirer/input": "^4.2.1", - "@inquirer/number": "^3.0.17", - "@inquirer/password": "^4.0.17", - "@inquirer/rawlist": "^4.1.5", - "@inquirer/search": "^3.0.17", - "@inquirer/select": "^4.3.1" + "@inquirer/checkbox": "^4.2.2", + "@inquirer/confirm": "^5.1.16", + "@inquirer/editor": "^4.2.18", + "@inquirer/expand": "^4.0.18", + "@inquirer/input": "^4.2.2", + "@inquirer/number": "^3.0.18", + "@inquirer/password": "^4.0.18", + "@inquirer/rawlist": "^4.1.6", + "@inquirer/search": "^3.1.1", + "@inquirer/select": "^4.3.2" }, "engines": { "node": ">=18" @@ -3013,13 +3184,13 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", - "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.6.tgz", + "integrity": "sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, @@ -3036,13 +3207,13 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.17.tgz", - "integrity": "sha512-CuBU4BAGFqRYors4TNCYzy9X3DpKtgIW4Boi0WNkm4Ei1hvY9acxKdBdyqzqBCEe4YxSdaQQsasJlFlUJNgojw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.1.tgz", + "integrity": "sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" @@ -3060,13 +3231,13 @@ } }, "node_modules/@inquirer/select": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", - "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.2.tgz", + "integrity": "sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.15", + "@inquirer/core": "^10.2.0", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", @@ -3144,9 +3315,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -3252,9 +3423,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -3273,16 +3444,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3353,17 +3524,75 @@ "tslib": "2" } }, + "node_modules/@jsonjoy.com/buffers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.0.0.tgz", + "integrity": "sha512-NDigYR3PHqCnQLXYyoLbnEdzMMvzeiCWo1KOut7Q0CoIqg9tUAPKJ1iq/2nFhc5kZtexzutNY0LFjdwWL3Dw3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/@jsonjoy.com/json-pack": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz", - "integrity": "sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.11.0.tgz", + "integrity": "sha512-nLqSTAYwpk+5ZQIoVp7pfd/oSKNWlEdvTq2LzVA4r2wtWZg6v+5u0VgBOaDJuUfNOuw/4Ysq6glN5QKSrOCgrA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/base64": "^1.1.1", - "@jsonjoy.com/util": "^1.1.2", + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.1", + "@jsonjoy.com/util": "^1.9.0", "hyperdyperid": "^1.2.0", - "thingies": "^1.20.0" + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" }, "engines": { "node": ">=10.0" @@ -3377,11 +3606,15 @@ } }, "node_modules/@jsonjoy.com/util": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz", - "integrity": "sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, "engines": { "node": ">=10.0" }, @@ -3737,64 +3970,10 @@ "node": ">=14" } }, - "node_modules/@postman/form-data": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", - "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@postman/tough-cookie": { - "version": "4.1.3-postman.1", - "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", - "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@postman/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/@postman/tunnel-agent": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz", - "integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.1.tgz", + "integrity": "sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw==", "cpu": [ "arm" ], @@ -3806,9 +3985,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.1.tgz", + "integrity": "sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ==", "cpu": [ "arm64" ], @@ -3820,9 +3999,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.1.tgz", + "integrity": "sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A==", "cpu": [ "arm64" ], @@ -3834,9 +4013,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz", + "integrity": "sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ==", "cpu": [ "x64" ], @@ -3848,9 +4027,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.1.tgz", + "integrity": "sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ==", "cpu": [ "arm64" ], @@ -3862,9 +4041,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.1.tgz", + "integrity": "sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ==", "cpu": [ "x64" ], @@ -3876,9 +4055,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.1.tgz", + "integrity": "sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ==", "cpu": [ "arm" ], @@ -3890,9 +4069,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.1.tgz", + "integrity": "sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q==", "cpu": [ "arm" ], @@ -3904,9 +4083,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.1.tgz", + "integrity": "sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ==", "cpu": [ "arm64" ], @@ -3918,9 +4097,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.1.tgz", + "integrity": "sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ==", "cpu": [ "arm64" ], @@ -3932,9 +4111,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.1.tgz", + "integrity": "sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ==", "cpu": [ "loong64" ], @@ -3945,10 +4124,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.1.tgz", + "integrity": "sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ==", "cpu": [ "ppc64" ], @@ -3960,9 +4139,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.1.tgz", + "integrity": "sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw==", "cpu": [ "riscv64" ], @@ -3974,9 +4153,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.1.tgz", + "integrity": "sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA==", "cpu": [ "riscv64" ], @@ -3988,9 +4167,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.1.tgz", + "integrity": "sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ==", "cpu": [ "s390x" ], @@ -4002,9 +4181,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.1.tgz", + "integrity": "sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA==", "cpu": [ "x64" ], @@ -4016,9 +4195,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.1.tgz", + "integrity": "sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg==", "cpu": [ "x64" ], @@ -4030,9 +4209,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.1.tgz", + "integrity": "sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ==", "cpu": [ "arm64" ], @@ -4044,9 +4223,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.1.tgz", + "integrity": "sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ==", "cpu": [ "ia32" ], @@ -4058,9 +4237,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.1.tgz", + "integrity": "sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg==", "cpu": [ "x64" ], @@ -4218,13 +4397,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@stoplight/json/node_modules/safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==", - "dev": true, - "license": "MIT" - }, "node_modules/@stoplight/ordered-object-literal": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", @@ -4912,14 +5084,14 @@ "license": "MIT" }, "node_modules/@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-9.0.0.tgz", + "integrity": "sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA==", + "deprecated": "This is a stub types definition. glob provides its own type definitions, so you do not need this installed.", "dev": true, "license": "MIT", "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" + "glob": "*" } }, "node_modules/@types/js-yaml": { @@ -4950,13 +5122,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -4965,9 +5130,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", - "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", "dev": true, "license": "MIT", "dependencies": { @@ -4975,14 +5140,14 @@ } }, "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", - "form-data": "^4.0.0" + "form-data": "^4.0.4" } }, "node_modules/@types/retry": { @@ -5034,17 +5199,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", + "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/type-utils": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -5058,9 +5223,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.38.0", + "@typescript-eslint/parser": "^8.41.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -5074,16 +5239,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz", + "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4" }, "engines": { @@ -5095,18 +5260,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", + "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/tsconfig-utils": "^8.41.0", + "@typescript-eslint/types": "^8.41.0", "debug": "^4.3.4" }, "engines": { @@ -5117,18 +5282,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", + "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5139,9 +5304,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", + "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==", "dev": true, "license": "MIT", "engines": { @@ -5152,19 +5317,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", + "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -5177,13 +5342,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz", + "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==", "dev": true, "license": "MIT", "engines": { @@ -5195,16 +5360,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", + "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/project-service": "8.41.0", + "@typescript-eslint/tsconfig-utils": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -5220,7 +5385,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -5250,16 +5415,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5270,17 +5435,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", + "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/types": "8.41.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5292,22 +5457,22 @@ } }, "node_modules/@typespec/asset-emitter": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/asset-emitter/-/asset-emitter-0.72.1.tgz", - "integrity": "sha512-lk41TinsVknczgl64OrEVQ+S6K5WiLAzDgIclaOVKu0ld1vNADz9grqwOtnTiYCz0pWRyZE+xhrq/9XkszU3lg==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/asset-emitter/-/asset-emitter-0.74.0.tgz", + "integrity": "sha512-DWIdlSNhRgBeZ8exfqubfUn0H6mRg4gr0s7zLTdBMUEDHL3Yh0ljnRPkd8AXTZhoW3maTFT69loWTrqx09T5oQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.4.0" } }, "node_modules/@typespec/compiler": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.2.1.tgz", - "integrity": "sha512-lUdHCRBPtianNN6QKt0G9qyyuSu7azbqKcYNimNLYQwrEIDcgSfQAUnoja9s+gtzCQQRzfbUZ8WLBC2b9cC81Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.4.0.tgz", + "integrity": "sha512-/AFiU3ImuhH/vHKzSGv7I2peewdJ7YLhgMCfFDNk6Ae0a5Ylrc8R1GOATVilisEPBFG9lnjHn3uUcyaZs5VWRw==", "dev": true, "license": "MIT", "dependencies": { @@ -5320,7 +5485,7 @@ "is-unicode-supported": "^2.1.0", "mustache": "~4.2.0", "picocolors": "~1.1.1", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "semver": "^7.7.1", "tar": "^7.4.3", "temporal-polyfill": "^0.3.0", @@ -5355,9 +5520,9 @@ } }, "node_modules/@typespec/compiler/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -5490,30 +5655,30 @@ } }, "node_modules/@typespec/events": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.72.1.tgz", - "integrity": "sha512-vUtA/mQD9csOCRLy9/EPS3oaUEmOiBXNhDkxtb7RYaZLA5975cprP+6o4ntSk6yCYQEo0/YtDcCbS4th2VGIqQ==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.74.0.tgz", + "integrity": "sha512-CY6JTtheMKAUlxiPmwx2fLIAWEwezsXmQYUMRhyuW44Q73unQIkexE43LUnNWOJSZckYucqUp+ihXh7jxzWeVQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.4.0" } }, "node_modules/@typespec/http": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.2.1.tgz", - "integrity": "sha512-HEPHgVFO2oQL6uZCtpqnRYVZizfSu9BO6vAgdRl1FYJWD2G0f/A4/hK6LEgpyZP44k39M1xMSqVrll2KZ5zpnw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.4.0.tgz", + "integrity": "sha512-Y0PDDtBu+oZnwivfhbL0lN6Mk3QiCxZ66DgB5kFjcgKNpnXf0u440PPyaL42a8lbchzz5lVwz+cinyIMI89FIQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/streams": "^0.72.1" + "@typespec/compiler": "^1.4.0", + "@typespec/streams": "^0.74.0" }, "peerDependenciesMeta": { "@typespec/streams": { @@ -5522,28 +5687,28 @@ } }, "node_modules/@typespec/openapi": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.2.1.tgz", - "integrity": "sha512-PSoM6c5M7epiFdFDPL4zIJKRPUgJepMtOtO1vVOSIFuz26DcFQpc8xzBy7LBsRneSfp8b6XbsiaNXNcBP/9A1w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.4.0.tgz", + "integrity": "sha512-ZfrCsmZG/Zt1laLaWC0pKvnZr4jqrm/YS/YuZe/gVrSYKBxGLopXle7H0wrSSMYkIVCNCLiC68/HqRxV6XTfoA==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/http": "^1.2.1" + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0" } }, "node_modules/@typespec/openapi3": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.2.1.tgz", - "integrity": "sha512-PG4+yDTm1YI1rrxFAS3B8WZc6S66pl2WPK+9pP/5b0He9NkFmA53BIodgXpV2QuhvChCbEjr/CDa94ufv8+cKw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.4.0.tgz", + "integrity": "sha512-krMSBe23NU42qXiLTmkr7T2NRfsIVv70pp3w/+uyWbrxdwdLEdb8PpbHeYV417D/YU+JLj00fQ7uYzEQGZX82w==", "dev": true, "license": "MIT", "dependencies": { "@apidevtools/swagger-parser": "~12.0.0", - "@typespec/asset-emitter": "^0.72.1", + "@typespec/asset-emitter": "^0.74.0", "openapi-types": "~12.1.3", "yaml": "~2.8.0" }, @@ -5554,11 +5719,11 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/http": "^1.2.1", - "@typespec/json-schema": "^1.2.1", - "@typespec/openapi": "^1.2.1", - "@typespec/versioning": "^0.72.1" + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0", + "@typespec/json-schema": "^1.4.0", + "@typespec/openapi": "^1.4.0", + "@typespec/versioning": "^0.74.0" }, "peerDependenciesMeta": { "@typespec/json-schema": { @@ -5573,56 +5738,56 @@ } }, "node_modules/@typespec/prettier-plugin-typespec": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/prettier-plugin-typespec/-/prettier-plugin-typespec-1.2.1.tgz", - "integrity": "sha512-RCMHl8r5BRS8+xi+Pj60d9W5lLCG/iQfVuctnL69bqUVYHnrK2ffcjHTTr0TRmDqXpxKFqFTtYJdurBaW1OjCg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/prettier-plugin-typespec/-/prettier-plugin-typespec-1.4.0.tgz", + "integrity": "sha512-P4ATX7Ryu4IKlzJWv36WbEPCn4xrusaJdb2tbm2wJWobhHW6jt7bWGpJXrXvdHMcfk2IYyIpbOx0+EZb7Aof+g==", "dev": true, "license": "MIT", "dependencies": { - "prettier": "~3.5.3" + "prettier": "~3.6.2" } }, "node_modules/@typespec/rest": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.72.1.tgz", - "integrity": "sha512-w0C91JhrVos8mAdd3OVwrcS6aSjuKlw7LtoazHenAmou/zSACKZbH4g6ko1BY8fv5lgl+q7VZ3/52uEWHOTxpw==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.74.0.tgz", + "integrity": "sha512-dE+Xmv01AQ7m8jUvEbGsUQLSVo3sLzMpnHRbQEOnJX42oDqtIsz/2GEOXKQpNm1AKBISK66E2FFB5boz999Ziw==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/http": "^1.2.1" + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0" } }, "node_modules/@typespec/sse": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.72.1.tgz", - "integrity": "sha512-J5Qitfi7uGhgkWO9aPKqHsEojt3aZHv2QbWrFvO1AkWPXHPML+1l66dmHg3XIQTmGAiUnCAj/JzS4W0E0yp9Dg==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.74.0.tgz", + "integrity": "sha512-+m7/elbGp7q/kqCGaBRj8v8wVMWKVEV8AsZOjf1PY2MkMUrux9ivOijBIktgoLBXDn+ocO2qVfFrHWG2slZSaw==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/events": "^0.72.1", - "@typespec/http": "^1.2.1", - "@typespec/streams": "^0.72.1" + "@typespec/compiler": "^1.4.0", + "@typespec/events": "^0.74.0", + "@typespec/http": "^1.4.0", + "@typespec/streams": "^0.74.0" } }, "node_modules/@typespec/streams": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.72.1.tgz", - "integrity": "sha512-TJdFxpW9lgazOluDdT9N8Ojnb7T/hXMZOL094D2idBf33aeqJvSHZtWgY4po8hTsQLk8Y4m4WJJ70nT9DUEOdg==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.74.0.tgz", + "integrity": "sha512-LIWizQgzGt8qN8ravte4DrPLPNOk9ge73bV9Us2TOECagTVQWwgMVy7+o/Beff3sOLQO/sEOwfzvmnNpSlauHg==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.4.0" } }, "node_modules/@typespec/ts-http-runtime": { @@ -5641,29 +5806,29 @@ } }, "node_modules/@typespec/versioning": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.72.1.tgz", - "integrity": "sha512-v1tBN2TcJilYpmb67v96YIVCGy8Su/c9hmuU6WABudWnYr26O4O+6gK2zx69RFxXORw+iw/LiDCU3XtFHbb7IQ==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.74.0.tgz", + "integrity": "sha512-eFIa23tycWJgv3Lxyu6jUlRi02dhtQE4Jjx3Ui5vEbwHW8pMEzuyF7ALt1c+V9HOLkfDkS4dJkiOVIoikZHPvQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.4.0" } }, "node_modules/@typespec/xml": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.72.1.tgz", - "integrity": "sha512-CmHXpwOojFT7cRI6ooB682OBIUVP9jMMx5bSoF9nrXg047Quaj7J0qPIwrG7d3O5lkogwPxqtPJPYaVq0+9gfg==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.74.0.tgz", + "integrity": "sha512-NiXatOfpyPxU94f2tEBAygxJeS7CvIr5lvnfZkC0tUHwkiJeLrI1jt13kDVB5CE6zNK6I3d7c37xsQs9WXGFAQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.4.0" } }, "node_modules/@vitest/coverage-v8": { @@ -6035,26 +6200,6 @@ "dev": true, "license": "MIT" }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -6066,13 +6211,13 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.29", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } @@ -6095,9 +6240,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, @@ -6158,23 +6303,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "dev": true, - "license": "MIT" - }, "node_modules/axios": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", @@ -6198,37 +6326,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", @@ -6267,20 +6364,10 @@ "node": ">=8" } }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.1.2" - } - }, "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", "dev": true, "funding": [ { @@ -6298,8 +6385,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -6411,9 +6498,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001737", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", "dev": true, "funding": [ { @@ -6431,17 +6518,10 @@ ], "license": "CC-BY-4.0" }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/chai": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -6456,9 +6536,9 @@ } }, "node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", "dev": true, "license": "MIT", "engines": { @@ -6489,22 +6569,12 @@ } }, "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", "dev": true, "license": "MIT" }, - "node_modules/charset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", - "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -6564,35 +6634,6 @@ "node": ">=0.8.0" } }, - "node_modules/cli-progress": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", - "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -6733,13 +6774,14 @@ } }, "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "license": "MIT", + "optional": true, "engines": { - "node": ">=16" + "node": "^12.20.0 || >=14" } }, "node_modules/commonmark": { @@ -6786,13 +6828,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.44.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", + "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.25.1" + "browserslist": "^4.25.3" }, "funding": { "type": "opencollective", @@ -6800,9 +6842,9 @@ } }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, "license": "MIT" }, @@ -6840,34 +6882,14 @@ "node": ">= 8" } }, - "node_modules/csv-parse": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", - "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==", - "dev": true, - "license": "MIT" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", "dev": true, "license": "MIT", "engines": { - "node": ">= 12" + "node": ">= 6" } }, "node_modules/data-view-buffer": { @@ -7046,17 +7068,6 @@ "node": ">=4" } }, - "node_modules/des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -7127,21 +7138,10 @@ "dev": true, "license": "MIT" }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/electron-to-chromium": { - "version": "1.5.190", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz", - "integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==", + "version": "1.5.209", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz", + "integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==", "dev": true, "license": "ISC" }, @@ -7353,9 +7353,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7366,32 +7366,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escalade": { @@ -7418,20 +7418,20 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -7479,65 +7479,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-scope": { @@ -7738,35 +7712,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/execa/node_modules/parse-ms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/pretty-ms": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", - "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse-ms": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -7784,31 +7729,6 @@ "dev": true, "license": "MIT" }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, "node_modules/fast-content-type-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", @@ -7892,9 +7812,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, "funding": [ { @@ -8013,16 +7933,6 @@ "node": "*" } }, - "node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/file-uri-to-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", @@ -8081,16 +7991,6 @@ "node": ">=10" } }, - "node_modules/filesize": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.4.tgz", - "integrity": "sha512-ryBwPIIeErmxgPnm6cbESAzXjuEFubs+yKYLBZvg3CaiNcmkJChoOGcBSrZ6IwkMwPABwPpVXE6IlNdGJJrvEg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 10.4.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -8163,9 +8063,9 @@ "license": "MIT" }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -8223,16 +8123,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/form-data": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", @@ -8330,18 +8220,18 @@ } }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=6 <7 || >=8" } }, "node_modules/fs.realpath": { @@ -8379,33 +8269,6 @@ "node": ">=0.8.0" } }, - "node_modules/ftp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/ftp/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/ftp/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -8575,61 +8438,6 @@ "node": ">= 6" } }, - "node_modules/get-uri/node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/get-uri/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/get-uri/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/get-uri/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -8665,6 +8473,23 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regex.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.0.1.tgz", + "integrity": "sha512-CG/iEvgQqfzoVsMUbxSJcwbG2JwyZ3naEqPkeltwl0BSS8Bp83k3xlGms+0QdWFUAwV+uvo80wNswKF6FWEkKg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/globals": { "version": "16.3.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", @@ -8792,31 +8617,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -8925,75 +8725,18 @@ "dev": true, "license": "MIT" }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http-reasons": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", - "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/httpntlm": { - "version": "1.8.13", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", - "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==", - "dev": true, - "funding": [ - { - "type": "paypal", - "url": "https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC" - }, - { - "type": "buymeacoffee", - "url": "https://www.buymeacoffee.com/samdecrock" - } - ], - "dependencies": { - "des.js": "^1.0.1", - "httpreq": ">=0.4.22", - "js-md4": "^0.3.2", - "underscore": "~1.12.1" - }, - "engines": { - "node": ">=10.4.0" - } - }, - "node_modules/httpreq": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", - "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">= 6.15.1" + "node": ">= 14" } }, "node_modules/https-proxy-agent": { @@ -9038,13 +8781,13 @@ } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -9579,13 +9322,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, "node_modules/is-unicode-supported": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", @@ -9659,9 +9395,9 @@ } }, "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true, "license": "MIT" }, @@ -9682,13 +9418,6 @@ "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "license": "MIT" - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -9730,9 +9459,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9759,30 +9488,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jose": { - "version": "4.14.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", - "integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/js-md4": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", - "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-sha512": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz", - "integrity": "sha512-mirki9WS/SUahm+1TbAPkqvbCiCfOAAsyXeHxK1UkullnJVVqoJG2pL9ObvT05CN+tM7fxhfYm0NbXn+1hWoZg==", - "dev": true, - "license": "MIT" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9803,13 +9508,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "license": "MIT" - }, "node_modules/jsep": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", @@ -9945,13 +9643,6 @@ "node": ">=8" } }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9966,13 +9657,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC" - }, "node_modules/jsonc-parser": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", @@ -9981,14 +9665,11 @@ "license": "MIT" }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -10022,22 +9703,6 @@ "node": ">=0.10.0" } }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "node_modules/junit-report-builder": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/junit-report-builder/-/junit-report-builder-3.2.1.tgz", @@ -10131,16 +9796,6 @@ "node": ">= 0.8.0" } }, - "node_modules/liquid-json": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", - "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10219,51 +9874,38 @@ "node": ">= 12.0.0" } }, - "node_modules/logform/node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "node_modules/logform/node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.1.90" + "node": ">=10" } }, "node_modules/loupe": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", - "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, "license": "MIT" }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lru-cache/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, "license": "ISC" }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { @@ -10338,15 +9980,17 @@ "license": "MIT" }, "node_modules/memfs": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz", - "integrity": "sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==", + "version": "4.38.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.38.1.tgz", + "integrity": "sha512-exfrOkkU3m0EpbQ0iQJP93HUbkprnIBU7IUnobSNAzHkBUzsklLwENGLEm8ZwJmMuLoFEfv1pYQ54wSpkay4kQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.3.0", - "tree-dump": "^1.0.1", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", "tslib": "^2.0.0" }, "engines": { @@ -10414,16 +10058,6 @@ "node": ">= 0.6" } }, - "node_modules/mime-format": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.1.tgz", - "integrity": "sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "charset": "^1.0.0" - } - }, "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", @@ -10447,13 +10081,6 @@ "node": ">=4" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "license": "ISC" - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -10590,130 +10217,6 @@ "dev": true, "license": "MIT" }, - "node_modules/newman": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/newman/-/newman-6.2.1.tgz", - "integrity": "sha512-Zq8Sr5GFF+OXs5yIbyglLMKMh1WNMjYVV0yZaSBZ+DIgQOIWcxT8QTfbrl/YUGrLyT4rjpu+yZ/Z+kozw79GEA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@postman/tough-cookie": "4.1.3-postman.1", - "async": "3.2.5", - "chardet": "2.0.0", - "cli-progress": "3.12.0", - "cli-table3": "0.6.5", - "colors": "1.4.0", - "commander": "11.1.0", - "csv-parse": "4.16.3", - "filesize": "10.1.4", - "liquid-json": "0.3.1", - "lodash": "4.17.21", - "mkdirp": "3.0.1", - "postman-collection": "4.4.0", - "postman-collection-transformer": "4.1.8", - "postman-request": "2.88.1-postman.34", - "postman-runtime": "7.39.1", - "pretty-ms": "7.0.1", - "semver": "7.6.3", - "serialised-error": "1.1.3", - "word-wrap": "1.2.5", - "xmlbuilder": "15.1.1" - }, - "bin": { - "newman": "bin/newman.js" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/newman/node_modules/chardet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.0.0.tgz", - "integrity": "sha512-xVgPpulCooDjY6zH4m9YW3jbkaBe3FKIAvF5sj5t7aBNsVl2ljIE+xwJ4iNgiDZHFQvNIpjdKdVOQvvk5ZfxbQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/newman/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/newman/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/newman/node_modules/postman-collection": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.4.0.tgz", - "integrity": "sha512-2BGDFcUwlK08CqZFUlIC8kwRJueVzPjZnnokWPtJCd9f2J06HBQpGL7t2P1Ud1NEsK9NHq9wdipUhWLOPj5s/Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@faker-js/faker": "5.5.3", - "file-type": "3.9.0", - "http-reasons": "0.1.0", - "iconv-lite": "0.6.3", - "liquid-json": "0.3.1", - "lodash": "4.17.21", - "mime-format": "2.0.1", - "mime-types": "2.1.35", - "postman-url-encoder": "3.0.5", - "semver": "7.5.4", - "uuid": "8.3.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/newman/node_modules/postman-collection/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/newman/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/nimma": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.3.tgz", @@ -10774,14 +10277,14 @@ "url": "https://opencollective.com/node-fetch" } }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "node_modules/node-fetch/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true, - "license": "(BSD-3-Clause OR GPL-2.0)", + "license": "MIT", "engines": { - "node": ">= 6.13.0" + "node": ">= 12" } }, "node_modules/node-notifier": { @@ -10799,13 +10302,6 @@ "which": "^2.0.2" } }, - "node_modules/node-oauth1": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-oauth1/-/node-oauth1-1.3.0.tgz", - "integrity": "sha512-0yggixNfrA1KcBwvh/Hy2xAS1Wfs9dcg6TdFf2zN7gilcAigMdrtZ4ybrBSXBgLvGDw9V1p2MRnGBMq7XjTWLg==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/node-object-hash": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/node-object-hash/-/node-object-hash-1.4.2.tgz", @@ -10853,20 +10349,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/oav": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/oav/-/oav-3.6.3.tgz", - "integrity": "sha512-TPIyDVJUsuujIlTTRuTZkwlJxJEZI+dTtxnbZwIdX7ZARgpXnxKSVFFpYITbEi/8/w1NoHT+f8Usi+P8IUGrFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/oav/-/oav-4.0.0.tgz", + "integrity": "sha512-qUtBfVwhrPRmf1VEwjbblqD7cgaQbV0bMXlmrUfh3zRkoSXZ6xey3QbuCQmaRrb6uy0P9Rpj4ScMKRBUhHgYxA==", "dev": true, "license": "MIT", "dependencies": { @@ -10899,9 +10385,7 @@ "mkdirp": "^1.0.4", "moment": "^2.29.3", "mustache": "^4.2.0", - "newman": "^6.0.0", "path-to-regexp": "^6.2.1", - "postman-collection": "^4.1.7", "reflect-metadata": "^0.1.13", "toposort": "^2.0.2", "uuid": "^3.4.0", @@ -11010,6 +10494,34 @@ "node": ">=8" } }, + "node_modules/oav/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/oav/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/oav/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -11065,6 +10577,16 @@ "node": ">=8" } }, + "node_modules/oav/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/oav/node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -11120,16 +10642,6 @@ "node": ">=6" } }, - "node_modules/object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -11219,16 +10731,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -11310,13 +10812,16 @@ } }, "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/path-exists": { @@ -11375,14 +10880,7 @@ }, "funding": { "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" + } }, "node_modules/path-to-regexp": { "version": "6.3.0", @@ -11421,13 +10919,6 @@ "node": ">= 14.16" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -11507,306 +10998,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postman-collection": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.5.0.tgz", - "integrity": "sha512-152JSW9pdbaoJihwjc7Q8lc3nPg/PC9lPTHdMk7SHnHhu/GBJB7b2yb9zG7Qua578+3PxkQ/HYBuXpDSvsf7GQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@faker-js/faker": "5.5.3", - "file-type": "3.9.0", - "http-reasons": "0.1.0", - "iconv-lite": "0.6.3", - "liquid-json": "0.3.1", - "lodash": "4.17.21", - "mime-format": "2.0.1", - "mime-types": "2.1.35", - "postman-url-encoder": "3.0.5", - "semver": "7.6.3", - "uuid": "8.3.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-collection-transformer": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.8.tgz", - "integrity": "sha512-smJ6X7Z7kbg6hp7JZPFixrSN3J3WkQed7DrWCC5tF7IxOMpFLqhtTtGssY8nD1inP8+mJf+N72Pf2ttUAHgBKw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "commander": "8.3.0", - "inherits": "2.0.4", - "lodash": "4.17.21", - "semver": "7.5.4", - "strip-json-comments": "3.1.1" - }, - "bin": { - "postman-collection-transformer": "bin/transform-collection.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-collection-transformer/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/postman-collection-transformer/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-collection/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postman-collection/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-request": { - "version": "2.88.1-postman.34", - "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.34.tgz", - "integrity": "sha512-GkolJ4cIzgamcwHRDkeZc/taFWO1u2HuGNML47K9ZAsFH2LdEkS5Yy8QanpzhjydzV3WWthl9v60J8E7SjKodQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@postman/form-data": "~3.1.1", - "@postman/tough-cookie": "~4.1.3-postman.1", - "@postman/tunnel-agent": "^0.6.3", - "aws-sign2": "~0.7.0", - "aws4": "^1.12.0", - "brotli": "^1.3.3", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "har-validator": "~5.1.3", - "http-signature": "~1.3.1", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "^2.1.35", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.3", - "safe-buffer": "^5.1.2", - "stream-length": "^1.0.2", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/postman-runtime": { - "version": "7.39.1", - "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.39.1.tgz", - "integrity": "sha512-IRNrBE0l1K3ZqQhQVYgF6MPuqOB9HqYncal+a7RpSS+sysKLhJMkC9SfUn1HVuOpokdPkK92ykvPzj8kCOLYAg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@postman/tough-cookie": "4.1.3-postman.1", - "async": "3.2.5", - "aws4": "1.12.0", - "handlebars": "4.7.8", - "httpntlm": "1.8.13", - "jose": "4.14.4", - "js-sha512": "0.9.0", - "lodash": "4.17.21", - "mime-types": "2.1.35", - "node-forge": "1.3.1", - "node-oauth1": "1.3.0", - "performance-now": "2.1.0", - "postman-collection": "4.4.0", - "postman-request": "2.88.1-postman.34", - "postman-sandbox": "4.7.1", - "postman-url-encoder": "3.0.5", - "serialised-error": "1.1.3", - "strip-json-comments": "3.1.1", - "uuid": "8.3.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/postman-runtime/node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true, - "license": "MIT" - }, - "node_modules/postman-runtime/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postman-runtime/node_modules/postman-collection": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.4.0.tgz", - "integrity": "sha512-2BGDFcUwlK08CqZFUlIC8kwRJueVzPjZnnokWPtJCd9f2J06HBQpGL7t2P1Ud1NEsK9NHq9wdipUhWLOPj5s/Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@faker-js/faker": "5.5.3", - "file-type": "3.9.0", - "http-reasons": "0.1.0", - "iconv-lite": "0.6.3", - "liquid-json": "0.3.1", - "lodash": "4.17.21", - "mime-format": "2.0.1", - "mime-types": "2.1.35", - "postman-url-encoder": "3.0.5", - "semver": "7.5.4", - "uuid": "8.3.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-runtime/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-sandbox": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.7.1.tgz", - "integrity": "sha512-H2wYSLK0mB588IaxoLrLoPbpmxsIcwFtgaK2c8gAsAQ+TgYFePwb4qdeVcYDMqmwrLd77/ViXkjasP/sBMz1sQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "lodash": "4.17.21", - "postman-collection": "4.4.0", - "teleport-javascript": "1.0.0", - "uvm": "2.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-sandbox/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postman-sandbox/node_modules/postman-collection": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.4.0.tgz", - "integrity": "sha512-2BGDFcUwlK08CqZFUlIC8kwRJueVzPjZnnokWPtJCd9f2J06HBQpGL7t2P1Ud1NEsK9NHq9wdipUhWLOPj5s/Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@faker-js/faker": "5.5.3", - "file-type": "3.9.0", - "http-reasons": "0.1.0", - "iconv-lite": "0.6.3", - "liquid-json": "0.3.1", - "lodash": "4.17.21", - "mime-format": "2.0.1", - "mime-types": "2.1.35", - "postman-url-encoder": "3.0.5", - "semver": "7.5.4", - "uuid": "8.3.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-sandbox/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-url-encoder": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz", - "integrity": "sha512-jOrdVvzUXBC7C+9gkIkpDJ3HIxOHTIqjpQ4C1EMt1ZGeMvSEpbFCKq23DEfgsj46vMnDgyQf+1ZLp2Wm+bKSsA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11818,9 +11009,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -11851,16 +11042,16 @@ } }, "node_modules/pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", "dev": true, "license": "MIT", "dependencies": { - "parse-ms": "^2.1.0" + "parse-ms": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -11917,19 +11108,6 @@ "dev": true, "license": "MIT" }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -11940,23 +11118,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11979,18 +11140,16 @@ "license": "MIT" }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, "node_modules/reflect-metadata": { @@ -12080,97 +11239,6 @@ "node": ">=6" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/request/node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12198,13 +11266,6 @@ "dev": true, "license": "ISC" }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -12237,9 +11298,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.1.tgz", + "integrity": "sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg==", "dev": true, "license": "MIT", "dependencies": { @@ -12253,26 +11314,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.48.1", + "@rollup/rollup-android-arm64": "4.48.1", + "@rollup/rollup-darwin-arm64": "4.48.1", + "@rollup/rollup-darwin-x64": "4.48.1", + "@rollup/rollup-freebsd-arm64": "4.48.1", + "@rollup/rollup-freebsd-x64": "4.48.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.48.1", + "@rollup/rollup-linux-arm-musleabihf": "4.48.1", + "@rollup/rollup-linux-arm64-gnu": "4.48.1", + "@rollup/rollup-linux-arm64-musl": "4.48.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.48.1", + "@rollup/rollup-linux-ppc64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-musl": "4.48.1", + "@rollup/rollup-linux-s390x-gnu": "4.48.1", + "@rollup/rollup-linux-x64-gnu": "4.48.1", + "@rollup/rollup-linux-x64-musl": "4.48.1", + "@rollup/rollup-win32-arm64-msvc": "4.48.1", + "@rollup/rollup-win32-ia32-msvc": "4.48.1", + "@rollup/rollup-win32-x64-msvc": "4.48.1", "fsevents": "~2.3.2" } }, @@ -12320,6 +11381,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -12358,6 +11426,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -12377,14 +11452,11 @@ } }, "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -12413,29 +11485,6 @@ "node": ">=10" } }, - "node_modules/serialised-error": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/serialised-error/-/serialised-error-1.1.3.tgz", - "integrity": "sha512-vybp3GItaR1ZtO2nxZZo8eOo7fnVaNtP3XE2vJKgzkKR2bagCkdJ1EpYYhEMd3qu/80DwQk9KjsNSxE3fXWq0g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "object-hash": "^1.1.2", - "stack-trace": "0.0.9", - "uuid": "^3.0.0" - } - }, - "node_modules/serialised-error/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -12534,19 +11583,6 @@ "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -12736,37 +11772,12 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/stack-trace": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -12799,32 +11810,12 @@ "node": ">= 0.4" } }, - "node_modules/stream-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", - "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", - "dev": true, - "license": "WTFPL", - "dependencies": { - "bluebird": "^2.6.2" - } - }, - "node_modules/stream-length/node_modules/bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", - "dev": true, - "license": "MIT" - }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } + "license": "MIT" }, "node_modules/string-width": { "version": "4.2.3", @@ -13135,6 +12126,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/superagent/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/superagent/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13193,13 +12209,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/teleport-javascript": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/teleport-javascript/-/teleport-javascript-1.0.0.tgz", - "integrity": "sha512-j1llvWVFyEn/6XIFDfX5LAU43DXe0GCt3NfXDwJ8XpRRMkS+i50SAkonAONBy+vxwPFBd50MFU8a2uj8R/ccLg==", - "dev": true, - "license": "ISC" - }, "node_modules/temporal-polyfill": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.3.0.tgz", @@ -13287,14 +12296,18 @@ "license": "MIT" }, "node_modules/thingies": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", - "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz", + "integrity": "sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==", "dev": true, - "license": "Unlicense", + "license": "MIT", "engines": { "node": ">=10.18" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, "peerDependencies": { "tslib": "^2" } @@ -13314,14 +12327,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -13331,11 +12344,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -13398,19 +12414,6 @@ "tslib": "^2.0.3" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -13431,20 +12434,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -13509,26 +12498,6 @@ "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -13648,16 +12617,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", - "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", + "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.38.0", - "@typescript-eslint/parser": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0" + "@typescript-eslint/eslint-plugin": "8.41.0", + "@typescript-eslint/parser": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -13668,7 +12637,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/uglify-js": { @@ -13704,13 +12673,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", - "dev": true, - "license": "MIT" - }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -13749,13 +12711,13 @@ "license": "ISC" }, "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/update-browserslist-db": { @@ -13806,17 +12768,6 @@ "dev": true, "license": "MIT" }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13844,26 +12795,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/uvm": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.1.1.tgz", - "integrity": "sha512-BZ5w8adTpNNr+zczOBRpaX/hH8UPKAf7fmCnidrcsqt3bn8KT9bDIfuS7hgRU9RXgiN01su2pwysBONY6w8W5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "flatted": "3.2.6" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/uvm/node_modules/flatted": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", - "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", - "dev": true, - "license": "ISC" - }, "node_modules/validator": { "version": "13.15.15", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", @@ -13874,34 +12805,19 @@ "node": ">= 0.10" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "node_modules/vite": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", - "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", + "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -13988,11 +12904,14 @@ } }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -14251,6 +13170,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", @@ -14354,14 +13280,64 @@ "node": ">= 12.0.0" } }, - "node_modules/winston/node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston-transport/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">=0.1.90" + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/winston/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" } }, "node_modules/word-wrap": { @@ -14533,9 +13509,9 @@ } }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "dev": true, "license": "ISC", "bin": { @@ -14588,9 +13564,9 @@ } }, "node_modules/yoctocolors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", "dev": true, "license": "MIT", "engines": { @@ -14601,9 +13577,9 @@ } }, "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", "dev": true, "license": "MIT", "engines": { @@ -14634,21 +13610,10 @@ "commander": "^9.4.1" } }, - "node_modules/z-schema/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/zod": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.8.tgz", - "integrity": "sha512-+MSh9cZU9r3QKlHqrgHMTSr3QwMGv4PLfR0M4N/sYWV5/x67HgXEhIGObdBkpnX8G78pTgWnIrBL2lZcNJOtfg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.1.tgz", + "integrity": "sha512-SgMZK/h8Tigt9nnKkfJMvB/mKjiJXaX26xegP4sa+0wHIFVFWVlsQGdhklDmuargBD3Hsi3rsQRIzwJIhTPJHA==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index 7f636aee0e6c..55baf27ec3e8 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,40 @@ { "name": "azure-rest-api-specs", "devDependencies": { - "@azure-tools/spec-gen-sdk": "~0.8.0", + "@azure-tools/spec-gen-sdk": "~0.9.1", "@azure-tools/specs-shared": "file:.github/shared", "@azure-tools/typespec-apiview": "0.7.2", - "@azure-tools/typespec-autorest": "0.58.1", - "@azure-tools/typespec-azure-core": "0.58.0", - "@azure-tools/typespec-azure-portal-core": "0.58.0", - "@azure-tools/typespec-azure-resource-manager": "0.58.1", - "@azure-tools/typespec-azure-rulesets": "0.58.0", - "@azure-tools/typespec-client-generator-cli": "0.26.0", - "@azure-tools/typespec-client-generator-core": "0.58.0", + "@azure-tools/typespec-autorest": "0.60.0", + "@azure-tools/typespec-azure-core": "0.60.0", + "@azure-tools/typespec-azure-portal-core": "0.60.0", + "@azure-tools/typespec-azure-resource-manager": "0.60.0", + "@azure-tools/typespec-azure-rulesets": "0.60.0", + "@azure-tools/typespec-client-generator-cli": "0.28.1", + "@azure-tools/typespec-client-generator-core": "0.60.0", "@azure-tools/typespec-liftr-base": "0.8.0", - "@autorest/openapi-to-typespec": "0.11.4", + "@autorest/openapi-to-typespec": "0.11.9", "@azure/avocado": "^0.9.1", - "@typespec/compiler": "1.2.1", - "@typespec/http": "1.2.1", - "@typespec/sse": "0.72.1", - "@typespec/events": "0.72.1", - "@typespec/openapi": "1.2.1", - "@typespec/openapi3": "1.2.1", - "@typespec/prettier-plugin-typespec": "1.2.1", - "@typespec/rest": "0.72.1", - "@typespec/streams": "0.72.1", - "@typespec/versioning": "0.72.1", - "@typespec/xml": "0.72.1", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.7", + "@typespec/compiler": "1.4.0", + "@typespec/http": "1.4.0", + "@typespec/sse": "0.74.0", + "@typespec/events": "0.74.0", + "@typespec/openapi": "1.4.0", + "@typespec/openapi3": "1.4.0", + "@typespec/prettier-plugin-typespec": "1.4.0", + "@typespec/rest": "0.74.0", + "@typespec/streams": "0.74.0", + "@typespec/versioning": "0.74.0", + "@typespec/xml": "0.74.0", "azure-rest-api-specs-eng-tools": "file:eng/tools", - "oav": "^3.6.3", - "prettier": "~3.5.3", + "oav": "^4.0.0", + "prettier": "~3.6.2", "typescript": "~5.8.2" }, "overrides": { - "@typespec/asset-emitter": "0.72.1", + "@typespec/asset-emitter": "0.74.0", "jsonpath-plus": "^10.3.0" }, "engines": { diff --git a/scripts/datacontainer-windows/Dockerfile b/scripts/datacontainer-windows/Dockerfile deleted file mode 100644 index 660504c48b49..000000000000 --- a/scripts/datacontainer-windows/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM mcr.microsoft.com/windows/servercore:ltsc2022 -SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'Continue'; $verbosePreference='Continue';"] - -RUN mkdir Users\\Administrator\\specrepo - -COPY .git C:\\Users\\Administrator\\specrepo\\.git - -WORKDIR C:\\Users\\Administrator\\scripts -ADD https://github.com/git-for-windows/git/releases/download/v2.12.2.windows.2/MinGit-2.12.2.2-64-bit.zip MinGit.zip -RUN Expand-Archive C:\\Users\\Administrator\\scripts\\MinGit.zip -DestinationPath c:\\MinGit - -COPY run.ps1 run.ps1 - -WORKDIR C:\\Users\\Administrator\\openapispecs -ENTRYPOINT C:\\Users\\Administrator\\scripts\\run.ps1 \ No newline at end of file diff --git a/scripts/datacontainer-windows/run.ps1 b/scripts/datacontainer-windows/run.ps1 deleted file mode 100644 index 2e46e9c94886..000000000000 --- a/scripts/datacontainer-windows/run.ps1 +++ /dev/null @@ -1,20 +0,0 @@ - -echo "[$timestamp] Adding git to path" -$env:PATH = $env:PATH + ';C:\MinGit\cmd\;C:\MinGit\cmd' -Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\' -Name Path -Value $env:PATH - -$timestamp= Get-Date -UFormat "%A %m/%d/%Y %R %Z" -echo "[$timestamp] Copying files to \Users\Administrator\openapispecs" -Copy-Item -Path "C:\Users\Administrator\specrepo\.git" -Destination "C:\Users\Administrator\openapispecs\.git" -Recurse - -cd "C:\Users\Administrator\openapispecs\" -$timestamp= Get-Date -UFormat "%A %m/%d/%Y %R %Z" -echo "[$timestamp] Running 'git checkout [$specRetrievalGitBranch] -f'" -git checkout $specRetrievalGitBranch -f - -$timestamp= Get-Date -UFormat "%A %m/%d/%Y %R %Z" -echo "[$timestamp] Deleting directory \Users\Administrator\specrepo" -Remove-Item -Path "C:\Users\Administrator\specrepo" -Recurse - -$timestamp= Get-Date -UFormat "%A %m/%d/%Y %R %Z" -echo "[$timestamp] Init completed successfully. OpenAPI specs are present in C:\Users\Administrator\openapispecs" diff --git a/scripts/datacontainer/Dockerfile b/scripts/datacontainer/Dockerfile deleted file mode 100644 index e2cb6c38c7ef..000000000000 --- a/scripts/datacontainer/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM mcr.microsoft.com/cbl-mariner/base/core:2.0 - -RUN tdnf -y update && \ - tdnf -y install git && \ - tdnf clean all - -RUN mkdir -p /usr/data/specrepo/ - -COPY .git /usr/data/specrepo/.git - -WORKDIR /usr/data/scripts/ -COPY run.sh run.sh -RUN chmod +x run.sh - -RUN mkdir -p /usr/data/openapispecs/ -WORKDIR /usr/data/openapispecs -ENTRYPOINT /usr/data/scripts/run.sh diff --git a/scripts/datacontainer/run.sh b/scripts/datacontainer/run.sh deleted file mode 100644 index 71cfda09fea6..000000000000 --- a/scripts/datacontainer/run.sh +++ /dev/null @@ -1,18 +0,0 @@ - -#!/bin/bash - -timestamp=`date +%Y/%m/%d-%H:%M:%S` -echo "[$timestamp] Copying files to /usr/data/openapispecs" -mkdir -p /usr/data/openapispecs/.git -cp -R /usr/data/specrepo/.git/* /usr/data/openapispecs/.git/ - -timestamp=`date +%Y/%m/%d-%H:%M:%S` -echo "[$timestamp] Running 'git checkout [$specRetrievalGitBranch] -f'" -git checkout $specRetrievalGitBranch -f - -timestamp=`date +%Y/%m/%d-%H:%M:%S` -echo "[$timestamp] Deleting directory /usr/data/specrepo" -rm -R /usr/data/specrepo/ - -timestamp=`date +%Y/%m/%d-%H:%M:%S` -echo "[$timestamp] Init completed successfully. OpenAPI specs are present in /usr/data/openapispecs." diff --git a/scripts/resource/TakeAction.png b/scripts/resource/TakeAction.png deleted file mode 100644 index c3dc564845e8..000000000000 Binary files a/scripts/resource/TakeAction.png and /dev/null differ diff --git a/scripts/resource/message.png b/scripts/resource/message.png deleted file mode 100644 index be8e8e7d35c1..000000000000 Binary files a/scripts/resource/message.png and /dev/null differ diff --git a/scripts/resource/readme.md b/scripts/resource/readme.md deleted file mode 100644 index 2c1ed7de5fe9..000000000000 --- a/scripts/resource/readme.md +++ /dev/null @@ -1,4 +0,0 @@ -# Icons used to send teams notification to users in spec PR review process by bots - -Message.png is used to send message only. -TakeAction.png is used to send instruction of required actions. diff --git a/specification/app/resource-manager/Microsoft.App/preview/2023-05-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json b/specification/app/resource-manager/Microsoft.App/preview/2023-05-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json index 10fc099e8e1f..22f6d1b1352e 100644 --- a/specification/app/resource-manager/Microsoft.App/preview/2023-05-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json +++ b/specification/app/resource-manager/Microsoft.App/preview/2023-05-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json @@ -1,168 +1,168 @@ -{ - "parameters": { - "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", - "resourceGroupName": "examplerg", - "environmentName": "testcontainerenv", - "api-version": "2023-05-02-preview", - "environmentEnvelope": { - "location": "East US", - "properties": { - "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string", - "sharedKey": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "dnsSuffix": "www.my-name.com", - "certificateValue": "Y2VydA==", - "certificatePassword": "1234" - }, - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "responses": { - "200": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "Succeeded", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "201": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "InitializationInProgress", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - } - } -} +{ + "parameters": { + "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", + "resourceGroupName": "examplerg", + "environmentName": "testcontainerenv", + "api-version": "2023-05-02-preview", + "environmentEnvelope": { + "location": "East US", + "properties": { + "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string", + "sharedKey": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "dnsSuffix": "www.my-name.com", + "certificateValue": "Y2VydA==", + "certificatePassword": "1234" + }, + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "responses": { + "200": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "Succeeded", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "201": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "InitializationInProgress", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + } + } +} diff --git a/specification/app/resource-manager/Microsoft.App/preview/2023-08-01-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json b/specification/app/resource-manager/Microsoft.App/preview/2023-08-01-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json index e81164dd1b32..93c20479b374 100644 --- a/specification/app/resource-manager/Microsoft.App/preview/2023-08-01-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json +++ b/specification/app/resource-manager/Microsoft.App/preview/2023-08-01-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json @@ -1,168 +1,168 @@ -{ - "parameters": { - "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", - "resourceGroupName": "examplerg", - "environmentName": "testcontainerenv", - "api-version": "2023-08-01-preview", - "environmentEnvelope": { - "location": "East US", - "properties": { - "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string", - "sharedKey": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "dnsSuffix": "www.my-name.com", - "certificateValue": "Y2VydA==", - "certificatePassword": "1234" - }, - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "responses": { - "200": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "Succeeded", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "201": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "InitializationInProgress", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - } - } -} +{ + "parameters": { + "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", + "resourceGroupName": "examplerg", + "environmentName": "testcontainerenv", + "api-version": "2023-08-01-preview", + "environmentEnvelope": { + "location": "East US", + "properties": { + "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string", + "sharedKey": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "dnsSuffix": "www.my-name.com", + "certificateValue": "Y2VydA==", + "certificatePassword": "1234" + }, + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "responses": { + "200": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "Succeeded", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "201": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "InitializationInProgress", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + } + } +} diff --git a/specification/app/resource-manager/Microsoft.App/preview/2023-11-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json b/specification/app/resource-manager/Microsoft.App/preview/2023-11-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json index f9314ac04b69..5e08aec248ba 100644 --- a/specification/app/resource-manager/Microsoft.App/preview/2023-11-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json +++ b/specification/app/resource-manager/Microsoft.App/preview/2023-11-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json @@ -1,168 +1,168 @@ -{ - "parameters": { - "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", - "resourceGroupName": "examplerg", - "environmentName": "testcontainerenv", - "api-version": "2023-11-02-preview", - "environmentEnvelope": { - "location": "East US", - "properties": { - "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string", - "sharedKey": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "dnsSuffix": "www.my-name.com", - "certificateValue": "Y2VydA==", - "certificatePassword": "1234" - }, - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "responses": { - "200": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "Succeeded", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "201": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "InitializationInProgress", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - } - } -} +{ + "parameters": { + "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", + "resourceGroupName": "examplerg", + "environmentName": "testcontainerenv", + "api-version": "2023-11-02-preview", + "environmentEnvelope": { + "location": "East US", + "properties": { + "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string", + "sharedKey": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "dnsSuffix": "www.my-name.com", + "certificateValue": "Y2VydA==", + "certificatePassword": "1234" + }, + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "responses": { + "200": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "Succeeded", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "201": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "InitializationInProgress", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + } + } +} diff --git a/specification/app/resource-manager/Microsoft.App/preview/2024-02-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json b/specification/app/resource-manager/Microsoft.App/preview/2024-02-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json index 3806215cb0a8..29e89717415f 100644 --- a/specification/app/resource-manager/Microsoft.App/preview/2024-02-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json +++ b/specification/app/resource-manager/Microsoft.App/preview/2024-02-02-preview/examples/ManagedEnvironments_CustomInfrastructureResourceGroup_Create.json @@ -1,168 +1,168 @@ -{ - "parameters": { - "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", - "resourceGroupName": "examplerg", - "environmentName": "testcontainerenv", - "api-version": "2024-02-02-preview", - "environmentEnvelope": { - "location": "East US", - "properties": { - "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string", - "sharedKey": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "dnsSuffix": "www.my-name.com", - "certificateValue": "Y2VydA==", - "certificatePassword": "1234" - }, - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "responses": { - "200": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "Succeeded", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - }, - "201": { - "body": { - "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", - "name": "testcontainerenv", - "type": "Microsoft.App/managedEnvironments", - "location": "East US", - "properties": { - "provisioningState": "InitializationInProgress", - "deploymentErrors": null, - "defaultDomain": "testcontainerenv.k4apps.io", - "staticIp": "1.2.3.4", - "appLogsConfiguration": { - "logAnalyticsConfiguration": { - "customerId": "string" - } - }, - "zoneRedundant": true, - "vnetConfiguration": { - "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" - }, - "customDomainConfiguration": { - "customDomainVerificationId": "custom domain verification id", - "dnsSuffix": "www.my-name.com", - "subjectName": "CN=www.my-name.com", - "expirationDate": "2022-11-06T04:00:00Z", - "thumbprint": "CERTIFICATE_THUMBPRINT" - }, - "eventStreamEndpoint": "testEndpoint", - "workloadProfiles": [ - { - "name": "My-GP-01", - "workloadProfileType": "GeneralPurpose", - "minimumCount": 3, - "maximumCount": 12 - }, - { - "name": "My-MO-01", - "workloadProfileType": "MemoryOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-CO-01", - "workloadProfileType": "ComputeOptimized", - "minimumCount": 3, - "maximumCount": 6 - }, - { - "name": "My-consumption-01", - "workloadProfileType": "Consumption" - } - ], - "infrastructureResourceGroup": "myInfrastructureRgName" - } - } - } - } -} +{ + "parameters": { + "subscriptionId": "34adfa4f-cedf-4dc0-ba29-b6d1a69ab345", + "resourceGroupName": "examplerg", + "environmentName": "testcontainerenv", + "api-version": "2024-02-02-preview", + "environmentEnvelope": { + "location": "East US", + "properties": { + "daprAIConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://northcentralus-0.in.applicationinsights.azure.com/", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string", + "sharedKey": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "dnsSuffix": "www.my-name.com", + "certificateValue": "Y2VydA==", + "certificatePassword": "1234" + }, + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "responses": { + "200": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "Succeeded", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + }, + "201": { + "body": { + "id": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/examplerg/providers/Microsoft.App/managedEnvironments/testcontainerenv", + "name": "testcontainerenv", + "type": "Microsoft.App/managedEnvironments", + "location": "East US", + "properties": { + "provisioningState": "InitializationInProgress", + "deploymentErrors": null, + "defaultDomain": "testcontainerenv.k4apps.io", + "staticIp": "1.2.3.4", + "appLogsConfiguration": { + "logAnalyticsConfiguration": { + "customerId": "string" + } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "/subscriptions/34adfa4f-cedf-4dc0-ba29-b6d1a69ab345/resourceGroups/RGName/providers/Microsoft.Network/virtualNetworks/VNetName/subnets/subnetName1" + }, + "customDomainConfiguration": { + "customDomainVerificationId": "custom domain verification id", + "dnsSuffix": "www.my-name.com", + "subjectName": "CN=www.my-name.com", + "expirationDate": "2022-11-06T04:00:00Z", + "thumbprint": "CERTIFICATE_THUMBPRINT" + }, + "eventStreamEndpoint": "testEndpoint", + "workloadProfiles": [ + { + "name": "My-GP-01", + "workloadProfileType": "GeneralPurpose", + "minimumCount": 3, + "maximumCount": 12 + }, + { + "name": "My-MO-01", + "workloadProfileType": "MemoryOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-CO-01", + "workloadProfileType": "ComputeOptimized", + "minimumCount": 3, + "maximumCount": 6 + }, + { + "name": "My-consumption-01", + "workloadProfileType": "Consumption" + } + ], + "infrastructureResourceGroup": "myInfrastructureRgName" + } + } + } + } +} diff --git a/specificationRepositoryConfiguration.json b/specificationRepositoryConfiguration.json index 0b2c1930d549..079f1daa51d6 100644 --- a/specificationRepositoryConfiguration.json +++ b/specificationRepositoryConfiguration.json @@ -67,6 +67,7 @@ "@azure-tools/typespec-python": "azure-sdk-for-python", "@azure-tools/typespec-java": "azure-sdk-for-java", "@azure-tools/typespec-csharp": "azure-sdk-for-net", + "@azure-typespec/http-client-csharp": "azure-sdk-for-net", "@azure-tools/typespec-ts": "azure-sdk-for-js", "@azure-tools/typespec-go": "azure-sdk-for-go" }